From ba549197e4f0ecb7285613d3846e34067d9ca9ac Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sat, 19 Sep 2020 22:02:32 +0200 Subject: [PATCH 01/23] Start Gameboy architecture, implement basic loads. --- Archs/GB/CGameboyInstruction.cpp | 99 +++++++++++++++++++ Archs/GB/CGameboyInstruction.h | 27 +++++ Archs/GB/Gameboy.cpp | 36 +++++++ Archs/GB/Gameboy.h | 27 +++++ Archs/GB/GameboyOpcodes.cpp | 56 +++++++++++ Archs/GB/GameboyOpcodes.h | 53 ++++++++++ Archs/GB/GameboyParser.cpp | 164 +++++++++++++++++++++++++++++++ Archs/GB/GameboyParser.h | 23 +++++ CMakeLists.txt | 9 ++ Parser/DirectivesParser.cpp | 13 ++- 10 files changed, 506 insertions(+), 1 deletion(-) create mode 100644 Archs/GB/CGameboyInstruction.cpp create mode 100644 Archs/GB/CGameboyInstruction.h create mode 100644 Archs/GB/Gameboy.cpp create mode 100644 Archs/GB/Gameboy.h create mode 100644 Archs/GB/GameboyOpcodes.cpp create mode 100644 Archs/GB/GameboyOpcodes.h create mode 100644 Archs/GB/GameboyParser.cpp create mode 100644 Archs/GB/GameboyParser.h diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp new file mode 100644 index 00000000..bc99016a --- /dev/null +++ b/Archs/GB/CGameboyInstruction.cpp @@ -0,0 +1,99 @@ +#include "Archs/GB/CGameboyInstruction.h" +#include "Archs/GB/GameboyOpcodes.h" +#include "Core/Common.h" +#include "Core/Expression.h" +#include "Core/FileManager.h" +#include "Core/Misc.h" + +CGameboyInstruction::CGameboyInstruction(const tGameboyOpcode& sourceOpcode, GameboyOpcodeVariables& vars) +{ + this->Opcode = sourceOpcode; + this->Vars = vars; +} + +bool CGameboyInstruction::Validate(const ValidateState& state) +{ + if (Opcode.flags & GB_LOAD_REG8_REG8) + { + if (Vars.LeftParam.num == GB_REG8_MEMHL && Vars.RightParam.num == GB_REG8_MEMHL) + { + Logger::queueError(Logger::Error, L"ld (hl),(hl) not allowed"); + return false; + } + } + if (Opcode.flags & (GB_IMMEDIATE_S8 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_U16)) + { + ExpressionValue value = Vars.ImmediateExpression.evaluate(); + if (value.isValid() && value.isInt()) + { + Vars.Immediate = value.intValue; + } + + int64_t min = 0; + int64_t max = 0; + if (Opcode.flags & GB_IMMEDIATE_U8) + { + min = 0; + max = 255; + } + else if (Opcode.flags & GB_IMMEDIATE_S8) + { + min = -128; + max = 127; + } + else if (Opcode.flags & GB_IMMEDIATE_U16) + { + min = 0; + max = 65535; + } + + if (Vars.Immediate < min || Vars.Immediate > max) + { + Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate); + return false; + } + } + + g_fileManager->advanceMemory(Opcode.length); + + return false; +} + +void CGameboyInstruction::Encode() const +{ + unsigned char encoding = Opcode.encoding; + + if (Opcode.flags & GB_PREFIX) + { + g_fileManager->writeU8(0xCB); + } + + if (Opcode.lhs && Opcode.lhsShift >= 0) + { + encoding |= Vars.LeftParam.num << Opcode.lhsShift; + } + if (Opcode.rhs && Opcode.rhsShift >= 0) + { + encoding |= Vars.RightParam.num << Opcode.rhsShift; + } + + g_fileManager->writeU8(encoding); + + + if (Opcode.flags & GB_IMMEDIATE_U16) + { + g_fileManager->writeU16((uint16_t)(Vars.Immediate & 0xFFFF)); + } + else if (Opcode.flags & (GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8)) + { + g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF)); + } + else if (Opcode.flags & GB_STOP) + { + g_fileManager->writeU8(0x00); + } +} + +void CGameboyInstruction::writeTempData(TempData& tempData) const +{ +} diff --git a/Archs/GB/CGameboyInstruction.h b/Archs/GB/CGameboyInstruction.h new file mode 100644 index 00000000..3d3d5cd9 --- /dev/null +++ b/Archs/GB/CGameboyInstruction.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Archs/GB/Gameboy.h" +#include "Archs/GB/GameboyOpcodes.h" +#include "Commands/CAssemblerCommand.h" + +struct GameboyOpcodeVariables +{ + GameboyRegisterValue LeftParam; + GameboyRegisterValue RightParam; + Expression ImmediateExpression; + int64_t Immediate; +}; + +class CGameboyInstruction: public CAssemblerCommand +{ +public: + CGameboyInstruction(const tGameboyOpcode& sourceOpcode, GameboyOpcodeVariables& vars); + bool Validate(const ValidateState& state) override; + void Encode() const override; + void writeTempData(TempData& tempData) const override; +private: + GameboyOpcodeVariables Vars; + tGameboyOpcode Opcode; + + // Inherited via CAssemblerCommand +}; diff --git a/Archs/GB/Gameboy.cpp b/Archs/GB/Gameboy.cpp new file mode 100644 index 00000000..cf544f98 --- /dev/null +++ b/Archs/GB/Gameboy.cpp @@ -0,0 +1,36 @@ +#include "Archs/GB/CGameboyInstruction.h" +#include "Archs/GB/Gameboy.h" +#include "Archs/GB/GameboyParser.h" +#include "Parser/Parser.h" + +CGameboyArchitecture Gameboy; + +CGameboyArchitecture::CGameboyArchitecture() +{ +} + +std::unique_ptr CGameboyArchitecture::parseDirective(Parser& parser) +{ + GameboyParser gameboyParser; + + return gameboyParser.parseDirective(parser); +} + +std::unique_ptr CGameboyArchitecture::parseOpcode(Parser& parser) +{ + GameboyParser gameboyParser; + + return gameboyParser.parseOpcode(parser); +} + +void CGameboyArchitecture::NextSection() +{ +} + +void CGameboyArchitecture::Pass2() +{ +} + +void CGameboyArchitecture::Revalidate() +{ +} diff --git a/Archs/GB/Gameboy.h b/Archs/GB/Gameboy.h new file mode 100644 index 00000000..577844ab --- /dev/null +++ b/Archs/GB/Gameboy.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Archs/Architecture.h" +#include "Core/ELF/ElfRelocator.h" +#include "Core/Expression.h" + +struct GameboyRegisterValue +{ + std::wstring name; + int num; +}; + +class CGameboyArchitecture: public CArchitecture +{ +public: + CGameboyArchitecture(); + + 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; }; +}; + +extern CGameboyArchitecture Gameboy; diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp new file mode 100644 index 00000000..66f5398b --- /dev/null +++ b/Archs/GB/GameboyOpcodes.cpp @@ -0,0 +1,56 @@ +#include "Archs/GB/GameboyOpcodes.h" + +const tGameboyOpcode GameboyOpcodes[] = { + // Name Len Encode Left param Right param LShift RShift Flags + { L"nop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"ld", 1, 0x40, GB_PARAM_REG8_MEMHL, GB_PARAM_REG8_MEMHL, 3, 0, GB_LOAD_REG8_REG8 }, + { L"ld", 1, 0x0A, GB_PARAM_A, GB_PARAM_MEMBC_MEMDE, -1, 4, 0 }, + { L"ld", 1, 0x02, GB_PARAM_MEMBC_MEMDE, GB_PARAM_A, 4, -1, 0 }, + { L"ld", 2, 0x06, GB_PARAM_REG8_MEMHL, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U8 }, + { L"ld", 3, 0x01, GB_PARAM_REG16, GB_PARAM_IMMEDIATE, 4, -1, GB_IMMEDIATE_U16 }, + { L"ld", 3, 0x08, GB_PARAM_MEMIMMEDIATE, GB_PARAM_SP, -1, -1, GB_IMMEDIATE_U16 }, +// { L"ldi", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"ldd", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"push", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"pop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"add", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"adc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"sub", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"sbc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"and", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"xor", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"or", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"cp", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"inc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"dec", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"daa", 1, 0x27, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"cpl", 1, 0x2F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rlca", 1, 0x07, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rla", 1, 0x17, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rrca", 1, 0x0F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rra", 1, 0x1F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"rlc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"rl", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"rrc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"rr", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"sla", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"swap", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"sra", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"srl", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"bit", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"set", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"res", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"ccf", 1, 0x3F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"scf", 1, 0x37, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"halt", 1, 0x76, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"stop", 2, 0x10, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, GB_STOP }, + { L"di", 1, 0xF3, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"ei", 1, 0xFB, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"jp", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"jr", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"call", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"ret", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"reti", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +// { L"rst", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { nullptr, 0, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, +}; diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h new file mode 100644 index 00000000..508d113c --- /dev/null +++ b/Archs/GB/GameboyOpcodes.h @@ -0,0 +1,53 @@ +#pragma once + +#define GB_PARAM_NONE 0x00 +#define GB_PARAM_A 0x01 // a +#define GB_PARAM_MEMBC_MEMDE 0x02 // (bc), (de) +#define GB_PARAM_REG8_MEMHL 0x03 // b, c, d, e, h, l, (hl), a +#define GB_PARAM_REG16 0x04 // bc, de, hl, sp +#define GB_PARAM_SP 0x05 // sp +#define GB_PARAM_IMMEDIATE 0x06 // imm +#define GB_PARAM_MEMIMMEDIATE 0x07 // (imm) + +#define GB_REG8_B 0x00 // b +#define GB_REG8_C 0x01 // c +#define GB_REG8_D 0x02 // d +#define GB_REG8_E 0x03 // e +#define GB_REG8_H 0x04 // h +#define GB_REG8_L 0x05 // l +#define GB_REG8_MEMHL 0x06 // (hl) +#define GB_REG8_A 0x07 // a +#define GB_REG8_BIT_ALL ( GB_REG_BIT(GB_REG8_B) | GB_REG_BIT(GB_REG8_C) \ + | GB_REG_BIT(GB_REG8_D) | GB_REG_BIT(GB_REG8_E) \ + | GB_REG_BIT(GB_REG8_H) | GB_REG_BIT(GB_REG8_L) \ + | GB_REG_BIT(GB_REG8_A) ) + +#define GB_REG16_BC 0x00 // bc +#define GB_REG16_DE 0x01 // de +#define GB_REG16_HL 0x02 // hl +#define GB_REG16_SP 0x03 // sp +#define GB_REG16_BIT_ALL ( GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE) \ + | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) ) + +#define GB_PREFIX 0x00000001 +#define GB_IMMEDIATE_U8 0x00000002 +#define GB_IMMEDIATE_S8 0x00000004 +#define GB_IMMEDIATE_U16 0x00000008 +#define GB_STOP 0x00000010 +#define GB_LOAD_REG8_REG8 0x00000020 + +#define GB_REG_BIT(reg) (1 << reg) + +struct tGameboyOpcode +{ + const wchar_t* name; + unsigned char length; + unsigned char encoding; + unsigned char lhs : 4; + unsigned char rhs : 4; + char lhsShift : 4; + char rhsShift : 4; + int flags; +}; + +extern const tGameboyOpcode GameboyOpcodes[]; diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp new file mode 100644 index 00000000..bc58a5a1 --- /dev/null +++ b/Archs/GB/GameboyParser.cpp @@ -0,0 +1,164 @@ +#include "Archs/GB/CGameboyInstruction.h" +#include "Archs/GB/Gameboy.h" +#include "Archs/GB/GameboyOpcodes.h" +#include "Archs/GB/GameboyParser.h" +#include "Core/Expression.h" +#include "Parser/DirectivesParser.h" +#include "Parser/Tokenizer.h" + +#define CHECK(exp) if (!(exp)) return false; + +const GameboyParameterDescriptor gameboyRegs8[] = { + { L"b", GB_REG8_B }, { L"c", GB_REG8_C }, + { L"d", GB_REG8_D }, { L"e", GB_REG8_E }, + { L"h", GB_REG8_H }, { L"l", GB_REG8_L }, + { L"a", GB_REG8_A }, +}; + +const GameboyParameterDescriptor gameboyRegs16[] = { + { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, + { L"hl", GB_REG16_HL }, { L"sp", GB_REG16_SP }, +}; + +const DirectiveMap gameboyDirectives = { }; + +std::unique_ptr GameboyParser::parseDirective(Parser& parser) +{ + return parser.parseDirective(gameboyDirectives); +} + +bool GameboyParser::parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyParameterDescriptor* table, size_t count, int allowed) +{ + const Token& token = parser.peekToken(); + + if (token.type != TokenType::Identifier) + return false; + + 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 GameboyParser::parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, gameboyRegs8, std::size(gameboyRegs8), allowed); +} + +bool GameboyParser::parseRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, gameboyRegs16, std::size(gameboyRegs16), allowed); +} + +bool GameboyParser::parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) +{ + CHECK(parser.matchToken(TokenType::LParen)); + CHECK(parseRegister16(parser, dest, allowed)); + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool GameboyParser::parseMemoryImmediate(Parser& parser, Expression& dest) +{ + CHECK(parser.matchToken(TokenType::LParen)); + dest = parser.parseExpression(); + CHECK(dest.isLoaded()); + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm) +{ + switch (paramType) + { + case GB_PARAM_REG8_MEMHL: + if (parseRegister8(parser, destReg, GB_REG8_BIT_ALL)) + { + return true; + } + if (parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL))) + { + destReg.num = GB_REG8_MEMHL; + return true; + } + return false; + case GB_PARAM_REG16: + return parseRegister16(parser, destReg, GB_REG16_BIT_ALL); + case GB_PARAM_A: + return parseRegister8(parser, destReg, GB_REG_BIT(GB_REG8_A)); + case GB_PARAM_MEMBC_MEMDE: + return parseMemoryRegister16(parser, destReg, + GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE)); + case GB_PARAM_SP: + return parseRegister16(parser, destReg, GB_REG_BIT(GB_REG16_SP)); + case GB_PARAM_IMMEDIATE: + destImm = parser.parseExpression(); + return destImm.isLoaded(); + case GB_PARAM_MEMIMMEDIATE: + return parseMemoryImmediate(parser, destImm); + default: + return false; + } +} + +bool GameboyParser::parseOpcodeParameterList(Parser& parser, const tGameboyOpcode opcode, GameboyOpcodeVariables& vars) +{ + if (opcode.lhs) + { + CHECK(parseOpcodeParameter(parser, opcode.lhs, vars.LeftParam, vars.ImmediateExpression)); + } + if (opcode.rhs) + { + CHECK(parser.matchToken(TokenType::Comma)); + CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression)); + } + + return true; +} + +std::unique_ptr GameboyParser::parseOpcode(Parser& parser) +{ + if (parser.peekToken().type != TokenType::Identifier) + return nullptr; + + const Token& token = parser.nextToken(); + + GameboyOpcodeVariables vars; + bool paramFail = false; + + const std::wstring stringValue = token.getStringValue(); + for (int z = 0; GameboyOpcodes[z].name != nullptr; z++) + { + if (stringValue == GameboyOpcodes[z].name) + { + TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); + + if (parseOpcodeParameterList(parser, GameboyOpcodes[z], vars)) + { + // success, return opcode + return std::make_unique(GameboyOpcodes[z], vars); + } + + parser.getTokenizer()->setPosition(tokenPos); + paramFail = true; + } + } + + if (paramFail) + parser.printError(token, L"Gameboy parameter failure in %S", stringValue); + else + parser.printError(token, L"Invalid Gameboy opcode: %S", stringValue); + + return nullptr; +} diff --git a/Archs/GB/GameboyParser.h b/Archs/GB/GameboyParser.h new file mode 100644 index 00000000..84731764 --- /dev/null +++ b/Archs/GB/GameboyParser.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Parser/Parser.h" + +struct GameboyParameterDescriptor { + const wchar_t* name; + int num; +}; + +class GameboyParser +{ +public: + std::unique_ptr parseDirective(Parser& parser); + std::unique_ptr parseOpcode(Parser& parser); +private: + bool parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyParameterDescriptor* table, size_t count, int allowed); + bool parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseMemoryImmediate(Parser& parser, Expression& dest); + bool parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm); + bool parseOpcodeParameterList(Parser& parser, const tGameboyOpcode, GameboyOpcodeVariables& vars); +}; diff --git a/CMakeLists.txt b/CMakeLists.txt index 663cc195..e2465b2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,15 @@ armips_target_sources(armips PRIVATE Archs/ARM/ThumbOpcodes.cpp Archs/ARM/ThumbOpcodes.h + Archs/GB/CGameboyInstruction.h + Archs/GB/CGameboyInstruction.cpp + Archs/GB/Gameboy.cpp + Archs/GB/Gameboy.h + Archs/GB/GameboyOpcodes.cpp + Archs/GB/GameboyOpcodes.h + Archs/GB/GameboyParser.cpp + Archs/GB/GameboyParser.h + Archs/MIPS/CMipsInstruction.cpp Archs/MIPS/CMipsInstruction.h Archs/MIPS/Mips.cpp diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index 0a3d9b2a..292cc060 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -1,6 +1,7 @@ #include "Parser/DirectivesParser.h" #include "Archs/ARM/Arm.h" +#include "Archs/GB/Gameboy.h" #include "Archs/MIPS/Mips.h" #include "Commands/CAssemblerLabel.h" #include "Commands/CDirectiveArea.h" @@ -445,6 +446,13 @@ std::unique_ptr parseDirectiveArmArch(Parser& parser, int fla return nullptr; } +std::unique_ptr parseDirectiveGameboyArch(Parser& parser, int flags) +{ + Arch = &Gameboy; + + return std::make_unique(L".gb", L""); +} + std::unique_ptr parseDirectiveArea(Parser& parser, int flags) { std::vector parameters; @@ -775,7 +783,10 @@ const DirectiveMap directives = { { L".3ds", { &parseDirectiveArmArch, DIRECTIVE_ARM_3DS } }, { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - + + { L".gb", { &parseDirectiveGameboyArch, 0 } }, + { L".gbc", { &parseDirectiveGameboyArch, 0 } }, + { L".area", { &parseDirectiveArea, 0 } }, { L".autoregion", { &parseDirectiveAutoRegion, 0 } }, { L".region", { &parseDirectiveArea, DIRECTIVE_AREA_SHARED } }, From 315bf725b0fec80da7d8eb378e3085c60449aa1c Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 01:20:28 +0200 Subject: [PATCH 02/23] Add the funky load types --- Archs/GB/CGameboyInstruction.cpp | 59 ++++++++++++++--- Archs/GB/CGameboyInstruction.h | 6 ++ Archs/GB/GameboyOpcodes.cpp | 21 +++++- Archs/GB/GameboyOpcodes.h | 13 +++- Archs/GB/GameboyParser.cpp | 107 +++++++++++++++++++++++++++---- Archs/GB/GameboyParser.h | 9 ++- 6 files changed, 186 insertions(+), 29 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index bc99016a..96660e15 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -13,6 +13,13 @@ CGameboyInstruction::CGameboyInstruction(const tGameboyOpcode& sourceOpcode, Gam bool CGameboyInstruction::Validate(const ValidateState& state) { + Vars.Length = Opcode.length; + Vars.Encoding = Opcode.encoding; + Vars.WritePrefix = Opcode.flags & GB_PREFIX; + Vars.WriteImmediate8 = Opcode.flags & (GB_IMMEDIATE_S8 | GB_IMMEDIATE_U8); + Vars.WriteImmediate16 = Opcode.flags & GB_IMMEDIATE_U16; + + // ld (hl),(hl) equivalent to halt if (Opcode.flags & GB_LOAD_REG8_REG8) { if (Vars.LeftParam.num == GB_REG8_MEMHL && Vars.RightParam.num == GB_REG8_MEMHL) @@ -21,12 +28,18 @@ bool CGameboyInstruction::Validate(const ValidateState& state) return false; } } - if (Opcode.flags & (GB_IMMEDIATE_S8 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_U16)) + + // Evaluate immediate + if (Vars.WriteImmediate8 || Vars.WriteImmediate16) { - ExpressionValue value = Vars.ImmediateExpression.evaluate(); - if (value.isValid() && value.isInt()) + if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate)) + { + Logger::queueError(Logger::Error, L"Invalid expression"); + return false; + } + if (Vars.IsNegative) { - Vars.Immediate = value.intValue; + Vars.Immediate = -Vars.Immediate; } int64_t min = 0; @@ -35,16 +48,44 @@ bool CGameboyInstruction::Validate(const ValidateState& state) { min = 0; max = 255; + Vars.WriteImmediate8 = true; + Vars.WriteImmediate16 = false; } else if (Opcode.flags & GB_IMMEDIATE_S8) { min = -128; max = 127; + Vars.WriteImmediate8 = true; + Vars.WriteImmediate16 = false; } else if (Opcode.flags & GB_IMMEDIATE_U16) { min = 0; max = 65535; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = true; + } + + // Special loads in range 0xFF00 - 0xFFFF + if (Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) + { + // 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 == GB_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) @@ -54,16 +95,16 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } } - g_fileManager->advanceMemory(Opcode.length); + g_fileManager->advanceMemory(Vars.Length); return false; } void CGameboyInstruction::Encode() const { - unsigned char encoding = Opcode.encoding; + unsigned char encoding = Vars.Encoding; - if (Opcode.flags & GB_PREFIX) + if (Vars.WritePrefix) { g_fileManager->writeU8(0xCB); } @@ -80,11 +121,11 @@ void CGameboyInstruction::Encode() const g_fileManager->writeU8(encoding); - if (Opcode.flags & GB_IMMEDIATE_U16) + if (Vars.WriteImmediate16) { g_fileManager->writeU16((uint16_t)(Vars.Immediate & 0xFFFF)); } - else if (Opcode.flags & (GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8)) + else if (Vars.WriteImmediate8) { g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF)); } diff --git a/Archs/GB/CGameboyInstruction.h b/Archs/GB/CGameboyInstruction.h index 3d3d5cd9..997a6ffd 100644 --- a/Archs/GB/CGameboyInstruction.h +++ b/Archs/GB/CGameboyInstruction.h @@ -10,6 +10,12 @@ struct GameboyOpcodeVariables GameboyRegisterValue RightParam; Expression ImmediateExpression; int64_t Immediate; + unsigned char Length; + unsigned char Encoding; + bool IsNegative : 1; + bool WritePrefix : 1; + bool WriteImmediate8 : 1; + bool WriteImmediate16 : 1; }; class CGameboyInstruction: public CAssemblerCommand diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index 66f5398b..b2ff5542 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -1,16 +1,31 @@ #include "Archs/GB/GameboyOpcodes.h" +// Order: +// - Everything else +// - SP_IMM +// - MEMIMMEDIATE +// - IMMEDIATE const tGameboyOpcode GameboyOpcodes[] = { // Name Len Encode Left param Right param LShift RShift Flags { L"nop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"ld", 1, 0x40, GB_PARAM_REG8_MEMHL, GB_PARAM_REG8_MEMHL, 3, 0, GB_LOAD_REG8_REG8 }, - { L"ld", 1, 0x0A, GB_PARAM_A, GB_PARAM_MEMBC_MEMDE, -1, 4, 0 }, { L"ld", 1, 0x02, GB_PARAM_MEMBC_MEMDE, GB_PARAM_A, 4, -1, 0 }, + { L"ld", 1, 0x0A, GB_PARAM_A, GB_PARAM_MEMBC_MEMDE, -1, 4, 0 }, + { L"ld", 1, 0x22, GB_PARAM_HLI_HLD, GB_PARAM_A, 4, -1, 0 }, + { L"ld", 1, 0x2A, GB_PARAM_A, GB_PARAM_HLI_HLD, -1, 4, 0 }, + { L"ld", 1, 0xE2, GB_PARAM_FF00_C, GB_PARAM_A, -1, -1, 0 }, + { L"ld", 1, 0xF2, GB_PARAM_A, GB_PARAM_FF00_C, -1, -1, 0 }, + { L"ld", 1, 0xF9, GB_PARAM_SP, GB_PARAM_HL, -1, -1, 0 }, + { L"ld", 2, 0xF8, GB_PARAM_HL, GB_PARAM_SP_IMM, -1, -1, GB_IMMEDIATE_S8 }, + { L"ld", 3, 0xFA, GB_PARAM_A, GB_PARAM_MEMIMMEDIATE, -1, -1, GB_IMMEDIATE_U16 }, { L"ld", 2, 0x06, GB_PARAM_REG8_MEMHL, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U8 }, { L"ld", 3, 0x01, GB_PARAM_REG16, GB_PARAM_IMMEDIATE, 4, -1, GB_IMMEDIATE_U16 }, { L"ld", 3, 0x08, GB_PARAM_MEMIMMEDIATE, GB_PARAM_SP, -1, -1, GB_IMMEDIATE_U16 }, -// { L"ldi", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"ldd", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"ld", 3, 0xEA, GB_PARAM_MEMIMMEDIATE, GB_PARAM_A, -1, -1, GB_IMMEDIATE_U16 }, + { L"ldi", 1, 0x22, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, + { L"ldi", 1, 0x2A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, + { L"ldd", 1, 0x32, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, + { L"ldd", 1, 0x3A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, // { L"push", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"pop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"add", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index 508d113c..64c779a6 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -5,9 +5,14 @@ #define GB_PARAM_MEMBC_MEMDE 0x02 // (bc), (de) #define GB_PARAM_REG8_MEMHL 0x03 // b, c, d, e, h, l, (hl), a #define GB_PARAM_REG16 0x04 // bc, de, hl, sp -#define GB_PARAM_SP 0x05 // sp -#define GB_PARAM_IMMEDIATE 0x06 // imm -#define GB_PARAM_MEMIMMEDIATE 0x07 // (imm) +#define GB_PARAM_HL 0x05 // hl +#define GB_PARAM_MEMHL 0x06 // (hl) +#define GB_PARAM_HLI_HLD 0x07 // hli, hld, hl+, hl- +#define GB_PARAM_SP 0x08 // sp +#define GB_PARAM_IMMEDIATE 0x09 // imm +#define GB_PARAM_MEMIMMEDIATE 0x0A // (imm) +#define GB_PARAM_FF00_C 0x0B // (0xFF00+c) +#define GB_PARAM_SP_IMM 0x0C // sp+s8 #define GB_REG8_B 0x00 // b #define GB_REG8_C 0x01 // c @@ -29,6 +34,8 @@ #define GB_REG16_BIT_ALL ( GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE) \ | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) ) +#define GB_REG_BIT_ALL 0xFFFFFFFF + #define GB_PREFIX 0x00000001 #define GB_IMMEDIATE_U8 0x00000002 #define GB_IMMEDIATE_S8 0x00000004 diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp index bc58a5a1..e1b097ce 100644 --- a/Archs/GB/GameboyParser.cpp +++ b/Archs/GB/GameboyParser.cpp @@ -8,18 +8,22 @@ #define CHECK(exp) if (!(exp)) return false; -const GameboyParameterDescriptor gameboyRegs8[] = { +const GameboyRegisterDescriptor gameboyRegs8[] = { { L"b", GB_REG8_B }, { L"c", GB_REG8_C }, { L"d", GB_REG8_D }, { L"e", GB_REG8_E }, { L"h", GB_REG8_H }, { L"l", GB_REG8_L }, { L"a", GB_REG8_A }, }; -const GameboyParameterDescriptor gameboyRegs16[] = { +const GameboyRegisterDescriptor gameboyRegs16[] = { { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, { L"hl", GB_REG16_HL }, { L"sp", GB_REG16_SP }, }; +const GameboyRegisterDescriptor gameboyHLIncDec16[] = { + { L"hli", 0 }, { L"hld", 1 }, +}; + const DirectiveMap gameboyDirectives = { }; std::unique_ptr GameboyParser::parseDirective(Parser& parser) @@ -27,12 +31,10 @@ std::unique_ptr GameboyParser::parseDirective(Parser& parser) return parser.parseDirective(gameboyDirectives); } -bool GameboyParser::parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyParameterDescriptor* table, size_t count, int allowed) +bool GameboyParser::parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyRegisterDescriptor* table, size_t count, int allowed) { const Token& token = parser.peekToken(); - - if (token.type != TokenType::Identifier) - return false; + CHECK(token.type == TokenType::Identifier); const std::wstring stringValue = token.getStringValue(); for (size_t i = 0; i < count; i++) @@ -59,6 +61,37 @@ bool GameboyParser::parseRegister16(Parser& parser, GameboyRegisterValue& dest, return parseRegisterTable(parser, dest, gameboyRegs16, std::size(gameboyRegs16), allowed); } +bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) +{ + CHECK(parser.matchToken(TokenType::LParen)); + + // hli / hld + if (!parseRegisterTable(parser, dest, gameboyHLIncDec16, std::size(gameboyHLIncDec16), GB_REG_BIT_ALL)) + { + // hl+ / hl- + CHECK(parseRegister16(parser, dest, GB_REG_BIT(GB_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)); +} + bool GameboyParser::parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) { CHECK(parser.matchToken(TokenType::LParen)); @@ -78,7 +111,48 @@ bool GameboyParser::parseMemoryImmediate(Parser& parser, Expression& dest) return true; } -bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm) +bool GameboyParser::parseFF00PlusC(Parser& parser) +{ + CHECK(parser.matchToken(TokenType::LParen)); + + const Token& token = parser.nextToken(); + CHECK(token.type == TokenType::Integer); + CHECK(token.intValue == 0xFF00); + + CHECK(parser.matchToken(TokenType::Plus)); + + GameboyRegisterValue tempReg; + CHECK(parseRegister8(parser, tempReg, GB_REG_BIT(GB_REG8_C))); + + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool GameboyParser::parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative) +{ + isNegative = false; + + GameboyRegisterValue tempReg; + CHECK(parseRegister16(parser, tempReg, GB_REG_BIT(GB_REG16_SP))); + + const Token& token = parser.peekToken(); + if (token.type != TokenType::Plus && token.type != TokenType::Minus) + { + // Treat as +0 + dest = createConstExpression(0); + return true; + } + parser.eatToken(); + isNegative = token.type == TokenType::Minus; + + dest = parser.parseExpression(); + CHECK(dest.isLoaded()); + + return true; +} + +bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm, bool& isNegative) { switch (paramType) { @@ -98,8 +172,13 @@ bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType case GB_PARAM_A: return parseRegister8(parser, destReg, GB_REG_BIT(GB_REG8_A)); case GB_PARAM_MEMBC_MEMDE: - return parseMemoryRegister16(parser, destReg, - GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE)); + return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE)); + case GB_PARAM_HL: + return parseRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL)); + case GB_PARAM_MEMHL: + return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL)); + case GB_PARAM_HLI_HLD: + return parseHLIncDec(parser, destReg); case GB_PARAM_SP: return parseRegister16(parser, destReg, GB_REG_BIT(GB_REG16_SP)); case GB_PARAM_IMMEDIATE: @@ -107,6 +186,10 @@ bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType return destImm.isLoaded(); case GB_PARAM_MEMIMMEDIATE: return parseMemoryImmediate(parser, destImm); + case GB_PARAM_FF00_C: + return parseFF00PlusC(parser); + case GB_PARAM_SP_IMM: + return parseSPImmediate(parser, destImm, isNegative); default: return false; } @@ -114,15 +197,17 @@ bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType bool GameboyParser::parseOpcodeParameterList(Parser& parser, const tGameboyOpcode opcode, GameboyOpcodeVariables& vars) { + bool isNegative = false; if (opcode.lhs) { - CHECK(parseOpcodeParameter(parser, opcode.lhs, vars.LeftParam, vars.ImmediateExpression)); + CHECK(parseOpcodeParameter(parser, opcode.lhs, vars.LeftParam, vars.ImmediateExpression, isNegative)); } if (opcode.rhs) { CHECK(parser.matchToken(TokenType::Comma)); - CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression)); + CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); } + vars.IsNegative = isNegative; return true; } diff --git a/Archs/GB/GameboyParser.h b/Archs/GB/GameboyParser.h index 84731764..d289e4fe 100644 --- a/Archs/GB/GameboyParser.h +++ b/Archs/GB/GameboyParser.h @@ -2,7 +2,7 @@ #include "Parser/Parser.h" -struct GameboyParameterDescriptor { +struct GameboyRegisterDescriptor { const wchar_t* name; int num; }; @@ -13,11 +13,14 @@ class GameboyParser std::unique_ptr parseDirective(Parser& parser); std::unique_ptr parseOpcode(Parser& parser); private: - bool parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyParameterDescriptor* table, size_t count, int allowed); + bool parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyRegisterDescriptor* table, size_t count, int allowed); bool parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseHLIncDec(Parser& parser, GameboyRegisterValue& dest); bool parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseMemoryImmediate(Parser& parser, Expression& dest); - bool parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm); + bool parseFF00PlusC(Parser& parser); + bool parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative); + bool parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm, bool& isNegative); bool parseOpcodeParameterList(Parser& parser, const tGameboyOpcode, GameboyOpcodeVariables& vars); }; From dfae289557ada9207cfbe2628cd4e8bd7acfb6f7 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 02:10:14 +0200 Subject: [PATCH 03/23] push, pop --- Archs/GB/GameboyOpcodes.cpp | 6 +++--- Archs/GB/GameboyOpcodes.h | 23 +++++++++++++---------- Archs/GB/GameboyParser.cpp | 34 ++++++++++++++++++++++++---------- Archs/GB/GameboyParser.h | 3 ++- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index b2ff5542..66fb0e32 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -19,15 +19,15 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"ld", 2, 0xF8, GB_PARAM_HL, GB_PARAM_SP_IMM, -1, -1, GB_IMMEDIATE_S8 }, { L"ld", 3, 0xFA, GB_PARAM_A, GB_PARAM_MEMIMMEDIATE, -1, -1, GB_IMMEDIATE_U16 }, { L"ld", 2, 0x06, GB_PARAM_REG8_MEMHL, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U8 }, - { L"ld", 3, 0x01, GB_PARAM_REG16, GB_PARAM_IMMEDIATE, 4, -1, GB_IMMEDIATE_U16 }, + { L"ld", 3, 0x01, GB_PARAM_REG16_SP, GB_PARAM_IMMEDIATE, 4, -1, GB_IMMEDIATE_U16 }, { L"ld", 3, 0x08, GB_PARAM_MEMIMMEDIATE, GB_PARAM_SP, -1, -1, GB_IMMEDIATE_U16 }, { L"ld", 3, 0xEA, GB_PARAM_MEMIMMEDIATE, GB_PARAM_A, -1, -1, GB_IMMEDIATE_U16 }, { L"ldi", 1, 0x22, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, { L"ldi", 1, 0x2A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, { L"ldd", 1, 0x32, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, { L"ldd", 1, 0x3A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, -// { L"push", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"pop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"push", 1, 0xC5, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, + { L"pop", 1, 0xC1, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, // { L"add", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"adc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"sub", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index 64c779a6..79987970 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -4,15 +4,16 @@ #define GB_PARAM_A 0x01 // a #define GB_PARAM_MEMBC_MEMDE 0x02 // (bc), (de) #define GB_PARAM_REG8_MEMHL 0x03 // b, c, d, e, h, l, (hl), a -#define GB_PARAM_REG16 0x04 // bc, de, hl, sp -#define GB_PARAM_HL 0x05 // hl -#define GB_PARAM_MEMHL 0x06 // (hl) -#define GB_PARAM_HLI_HLD 0x07 // hli, hld, hl+, hl- -#define GB_PARAM_SP 0x08 // sp -#define GB_PARAM_IMMEDIATE 0x09 // imm -#define GB_PARAM_MEMIMMEDIATE 0x0A // (imm) -#define GB_PARAM_FF00_C 0x0B // (0xFF00+c) -#define GB_PARAM_SP_IMM 0x0C // sp+s8 +#define GB_PARAM_REG16_SP 0x04 // bc, de, hl, sp +#define GB_PARAM_REG16_AF 0x05 // bc, de, hl, af +#define GB_PARAM_HL 0x06 // hl +#define GB_PARAM_MEMHL 0x07 // (hl) +#define GB_PARAM_HLI_HLD 0x08 // hli, hld, hl+, hl- +#define GB_PARAM_SP 0x09 // sp +#define GB_PARAM_IMMEDIATE 0x0A // imm +#define GB_PARAM_MEMIMMEDIATE 0x0B // (imm) +#define GB_PARAM_FF00_C 0x0C // (0xFF00+c) +#define GB_PARAM_SP_IMM 0x0D // sp+s8 #define GB_REG8_B 0x00 // b #define GB_REG8_C 0x01 // c @@ -31,8 +32,10 @@ #define GB_REG16_DE 0x01 // de #define GB_REG16_HL 0x02 // hl #define GB_REG16_SP 0x03 // sp +#define GB_REG16_AF 0x03 // af (yes, same as sp) #define GB_REG16_BIT_ALL ( GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE) \ - | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) ) + | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) \ + | GB_REG_BIT(GB_REG16_AF) ) #define GB_REG_BIT_ALL 0xFFFFFFFF diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp index e1b097ce..1ae53884 100644 --- a/Archs/GB/GameboyParser.cpp +++ b/Archs/GB/GameboyParser.cpp @@ -15,11 +15,16 @@ const GameboyRegisterDescriptor gameboyRegs8[] = { { L"a", GB_REG8_A }, }; -const GameboyRegisterDescriptor gameboyRegs16[] = { +const GameboyRegisterDescriptor gameboyRegs16SP[] = { { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, { L"hl", GB_REG16_HL }, { L"sp", GB_REG16_SP }, }; +const GameboyRegisterDescriptor gameboyRegs16AF[] = { // kinda hacky + { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, + { L"hl", GB_REG16_HL }, { L"af", GB_REG16_AF }, +}; + const GameboyRegisterDescriptor gameboyHLIncDec16[] = { { L"hli", 0 }, { L"hld", 1 }, }; @@ -56,9 +61,14 @@ bool GameboyParser::parseRegister8(Parser& parser, GameboyRegisterValue& dest, i return parseRegisterTable(parser, dest, gameboyRegs8, std::size(gameboyRegs8), allowed); } -bool GameboyParser::parseRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) +bool GameboyParser::parseRegister16SP(Parser& parser, GameboyRegisterValue& dest, int allowed) +{ + return parseRegisterTable(parser, dest, gameboyRegs16SP, std::size(gameboyRegs16SP), allowed); +} + +bool GameboyParser::parseRegister16AF(Parser& parser, GameboyRegisterValue& dest, int allowed) { - return parseRegisterTable(parser, dest, gameboyRegs16, std::size(gameboyRegs16), allowed); + return parseRegisterTable(parser, dest, gameboyRegs16AF, std::size(gameboyRegs16AF), allowed); } bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) @@ -69,7 +79,7 @@ bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) if (!parseRegisterTable(parser, dest, gameboyHLIncDec16, std::size(gameboyHLIncDec16), GB_REG_BIT_ALL)) { // hl+ / hl- - CHECK(parseRegister16(parser, dest, GB_REG_BIT(GB_REG16_HL))); + CHECK(parseRegister16SP(parser, dest, GB_REG_BIT(GB_REG16_HL))); const Token& token = parser.nextToken(); if (token.type == TokenType::Plus) @@ -90,12 +100,14 @@ bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) } CHECK(parser.matchToken(TokenType::RParen)); + + return true; } bool GameboyParser::parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) { CHECK(parser.matchToken(TokenType::LParen)); - CHECK(parseRegister16(parser, dest, allowed)); + CHECK(parseRegister16SP(parser, dest, allowed)); CHECK(parser.matchToken(TokenType::RParen)); return true; @@ -134,7 +146,7 @@ bool GameboyParser::parseSPImmediate(Parser& parser, Expression& dest, bool& isN isNegative = false; GameboyRegisterValue tempReg; - CHECK(parseRegister16(parser, tempReg, GB_REG_BIT(GB_REG16_SP))); + CHECK(parseRegister16SP(parser, tempReg, GB_REG_BIT(GB_REG16_SP))); const Token& token = parser.peekToken(); if (token.type != TokenType::Plus && token.type != TokenType::Minus) @@ -167,20 +179,22 @@ bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType return true; } return false; - case GB_PARAM_REG16: - return parseRegister16(parser, destReg, GB_REG16_BIT_ALL); + case GB_PARAM_REG16_SP: + return parseRegister16SP(parser, destReg, GB_REG16_BIT_ALL); + case GB_PARAM_REG16_AF: + return parseRegister16AF(parser, destReg, GB_REG16_BIT_ALL); case GB_PARAM_A: return parseRegister8(parser, destReg, GB_REG_BIT(GB_REG8_A)); case GB_PARAM_MEMBC_MEMDE: return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE)); case GB_PARAM_HL: - return parseRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL)); + return parseRegister16SP(parser, destReg, GB_REG_BIT(GB_REG16_HL)); case GB_PARAM_MEMHL: return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL)); case GB_PARAM_HLI_HLD: return parseHLIncDec(parser, destReg); case GB_PARAM_SP: - return parseRegister16(parser, destReg, GB_REG_BIT(GB_REG16_SP)); + return parseRegister16SP(parser, destReg, GB_REG_BIT(GB_REG16_SP)); case GB_PARAM_IMMEDIATE: destImm = parser.parseExpression(); return destImm.isLoaded(); diff --git a/Archs/GB/GameboyParser.h b/Archs/GB/GameboyParser.h index d289e4fe..ec64cfa9 100644 --- a/Archs/GB/GameboyParser.h +++ b/Archs/GB/GameboyParser.h @@ -15,7 +15,8 @@ class GameboyParser private: bool parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyRegisterDescriptor* table, size_t count, int allowed); bool parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed); - bool parseRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseRegister16SP(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseRegister16AF(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseHLIncDec(Parser& parser, GameboyRegisterValue& dest); bool parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseMemoryImmediate(Parser& parser, Expression& dest); From 170da32e19974aeceb5c983581b7c638812c25e8 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 02:10:31 +0200 Subject: [PATCH 04/23] add, adc, sub, sbc, inc, dec --- Archs/GB/CGameboyInstruction.cpp | 32 ++++++++++++++++++++++++++++++++ Archs/GB/GameboyOpcodes.cpp | 21 +++++++++++++++------ Archs/GB/GameboyOpcodes.h | 3 +++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index 96660e15..d9640da8 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -66,6 +66,38 @@ bool CGameboyInstruction::Validate(const ValidateState& state) Vars.WriteImmediate16 = true; } + // add <-> sub + if ((Opcode.flags & (GB_ADD_IMMEDIATE | GB_SUB_IMMEDIATE)) && Vars.Immediate < 0) + { + // Change opcode + Vars.Encoding ^= 0x10; + Vars.Immediate = -Vars.Immediate; + } + if (Opcode.flags & GB_NEGATE_IMM) + { + Vars.Immediate = -Vars.Immediate; + } + // add a,1 -> inc a + if ((Opcode.flags & GB_ADD_IMMEDIATE) && Vars.LeftParam.num == GB_REG8_A && Vars.Immediate == 1) + { + // Change opcode + Vars.Encoding = 0x3C; + Vars.Length = 1; + Vars.LeftParam.num = 0; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; + } + // sub a,1 -> dec a + if ((Opcode.flags & GB_SUB_IMMEDIATE) && Vars.LeftParam.num == GB_REG8_A && Vars.Immediate == 1) + { + // Change opcode + Vars.Encoding = 0x3D; + Vars.Length = 1; + Vars.LeftParam.num = 0; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; + } + // Special loads in range 0xFF00 - 0xFFFF if (Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) { diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index 66fb0e32..b67e8759 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -28,16 +28,25 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"ldd", 1, 0x3A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, { L"push", 1, 0xC5, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, { L"pop", 1, 0xC1, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, -// { L"add", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"adc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"sub", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"sbc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"add", 1, 0x09, GB_PARAM_HL, GB_PARAM_REG16_SP, -1, 4, 0 }, + { L"add", 1, 0x80, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"add", 2, 0xC6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_IMMEDIATE }, + { L"add", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 }, + { L"adc", 1, 0x88, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"adc", 2, 0xCE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_IMMEDIATE }, + { L"sub", 1, 0x90, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sub", 2, 0xD6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_SUB_IMMEDIATE }, + { L"sub", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 | GB_NEGATE_IMM }, + { L"sbc", 1, 0x98, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sbc", 2, 0xDE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_SUB_IMMEDIATE }, // { L"and", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"xor", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"or", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"cp", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"inc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"dec", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"inc", 1, 0x04, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, + { L"inc", 1, 0x03, GB_PARAM_REG16_SP, GB_PARAM_NONE, 4, -1, 0 }, + { L"dec", 1, 0x05, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, + { L"dec", 1, 0x0B, GB_PARAM_REG16_SP, GB_PARAM_NONE, 4, -1, 0 }, { L"daa", 1, 0x27, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"cpl", 1, 0x2F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"rlca", 1, 0x07, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index 79987970..608339a9 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -45,6 +45,9 @@ #define GB_IMMEDIATE_U16 0x00000008 #define GB_STOP 0x00000010 #define GB_LOAD_REG8_REG8 0x00000020 +#define GB_ADD_IMMEDIATE 0x00000040 +#define GB_SUB_IMMEDIATE 0x00000080 +#define GB_NEGATE_IMM 0x00000100 #define GB_REG_BIT(reg) (1 << reg) From 30a3a442795f119c27f5d759db498584f1fddab0 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 02:22:49 +0200 Subject: [PATCH 05/23] Remove conversion of add a,1 -> inc a and sub a,1 -> dec a because they add and inc set different flags --- Archs/GB/CGameboyInstruction.cpp | 22 +--------------------- Archs/GB/GameboyOpcodes.cpp | 8 ++++---- Archs/GB/GameboyOpcodes.h | 5 ++--- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index d9640da8..5fb2a566 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -67,7 +67,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // add <-> sub - if ((Opcode.flags & (GB_ADD_IMMEDIATE | GB_SUB_IMMEDIATE)) && Vars.Immediate < 0) + if ((Opcode.flags & GB_ADD_SUB_IMMEDIATE) && Vars.Immediate < 0) { // Change opcode Vars.Encoding ^= 0x10; @@ -77,26 +77,6 @@ bool CGameboyInstruction::Validate(const ValidateState& state) { Vars.Immediate = -Vars.Immediate; } - // add a,1 -> inc a - if ((Opcode.flags & GB_ADD_IMMEDIATE) && Vars.LeftParam.num == GB_REG8_A && Vars.Immediate == 1) - { - // Change opcode - Vars.Encoding = 0x3C; - Vars.Length = 1; - Vars.LeftParam.num = 0; - Vars.WriteImmediate8 = false; - Vars.WriteImmediate16 = false; - } - // sub a,1 -> dec a - if ((Opcode.flags & GB_SUB_IMMEDIATE) && Vars.LeftParam.num == GB_REG8_A && Vars.Immediate == 1) - { - // Change opcode - Vars.Encoding = 0x3D; - Vars.Length = 1; - Vars.LeftParam.num = 0; - Vars.WriteImmediate8 = false; - Vars.WriteImmediate16 = false; - } // Special loads in range 0xFF00 - 0xFFFF if (Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index b67e8759..8c4e5620 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -30,15 +30,15 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"pop", 1, 0xC1, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, { L"add", 1, 0x09, GB_PARAM_HL, GB_PARAM_REG16_SP, -1, 4, 0 }, { L"add", 1, 0x80, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"add", 2, 0xC6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_IMMEDIATE }, + { L"add", 2, 0xC6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, { L"add", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 }, { L"adc", 1, 0x88, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"adc", 2, 0xCE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_IMMEDIATE }, + { L"adc", 2, 0xCE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, { L"sub", 1, 0x90, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sub", 2, 0xD6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_SUB_IMMEDIATE }, + { L"sub", 2, 0xD6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, { L"sub", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 | GB_NEGATE_IMM }, { L"sbc", 1, 0x98, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sbc", 2, 0xDE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_SUB_IMMEDIATE }, + { L"sbc", 2, 0xDE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, // { L"and", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"xor", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, // { L"or", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index 608339a9..99ecf7d5 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -45,9 +45,8 @@ #define GB_IMMEDIATE_U16 0x00000008 #define GB_STOP 0x00000010 #define GB_LOAD_REG8_REG8 0x00000020 -#define GB_ADD_IMMEDIATE 0x00000040 -#define GB_SUB_IMMEDIATE 0x00000080 -#define GB_NEGATE_IMM 0x00000100 +#define GB_ADD_SUB_IMMEDIATE 0x00000040 +#define GB_NEGATE_IMM 0x00000080 #define GB_REG_BIT(reg) (1 << reg) From 4789036e0aa04293a397dd59ebc21c4ae266193e Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 02:40:25 +0200 Subject: [PATCH 06/23] and, xor, or, cp, rlc, rrc, rl, rr, sla, sra, swap, srl, bit, res, set --- Archs/GB/CGameboyInstruction.cpp | 19 +++++++++++++++--- Archs/GB/GameboyOpcodes.cpp | 34 ++++++++++++++++++-------------- Archs/GB/GameboyOpcodes.h | 15 +++++++------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index 5fb2a566..e5a8ee5d 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -16,8 +16,8 @@ bool CGameboyInstruction::Validate(const ValidateState& state) Vars.Length = Opcode.length; Vars.Encoding = Opcode.encoding; Vars.WritePrefix = Opcode.flags & GB_PREFIX; - Vars.WriteImmediate8 = Opcode.flags & (GB_IMMEDIATE_S8 | GB_IMMEDIATE_U8); - Vars.WriteImmediate16 = Opcode.flags & GB_IMMEDIATE_U16; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; // ld (hl),(hl) equivalent to halt if (Opcode.flags & GB_LOAD_REG8_REG8) @@ -30,7 +30,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // Evaluate immediate - if (Vars.WriteImmediate8 || Vars.WriteImmediate16) + if (Opcode.flags & (GB_IMMEDIATE_U3 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8 | GB_IMMEDIATE_U16)) { if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate)) { @@ -44,6 +44,13 @@ bool CGameboyInstruction::Validate(const ValidateState& state) int64_t min = 0; int64_t max = 0; + if (Opcode.flags & GB_IMMEDIATE_U3) + { + min = 0; + max = 8; + Vars.WriteImmediate8 = false; + Vars.WriteImmediate16 = false; + } if (Opcode.flags & GB_IMMEDIATE_U8) { min = 0; @@ -105,6 +112,12 @@ bool CGameboyInstruction::Validate(const ValidateState& state) Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate); return false; } + + // Move small immediate to lhs + if (Opcode.flags & GB_IMMEDIATE_U3) + { + Vars.LeftParam.num = Vars.Immediate; + } } g_fileManager->advanceMemory(Vars.Length); diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index 8c4e5620..62f8e70b 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -39,10 +39,14 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"sub", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 | GB_NEGATE_IMM }, { L"sbc", 1, 0x98, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sbc", 2, 0xDE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, -// { L"and", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"xor", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"or", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"cp", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"and", 1, 0xA0, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"and", 2, 0xE6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, + { L"xor", 1, 0xA8, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"xor", 2, 0xEE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, + { L"or", 1, 0xB0, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"or", 2, 0xF6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, + { L"cp", 1, 0xB8, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"cp", 2, 0xFE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, { L"inc", 1, 0x04, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, { L"inc", 1, 0x03, GB_PARAM_REG16_SP, GB_PARAM_NONE, 4, -1, 0 }, { L"dec", 1, 0x05, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, @@ -53,17 +57,17 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"rla", 1, 0x17, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"rrca", 1, 0x0F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"rra", 1, 0x1F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"rlc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"rl", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"rrc", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"rr", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"sla", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"swap", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"sra", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"srl", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"bit", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"set", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"res", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rlc", 2, 0x00, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"rrc", 2, 0x08, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"rl", 2, 0x10, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"rr", 2, 0x18, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"sla", 2, 0x20, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"sra", 2, 0x28, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"swap", 2, 0x30, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"srl", 2, 0x38, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, + { L"bit", 2, 0x40, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, + { L"res", 2, 0x80, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, + { L"set", 2, 0xC0, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, { L"ccf", 1, 0x3F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"scf", 1, 0x37, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"halt", 1, 0x76, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index 99ecf7d5..a511ffc7 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -40,13 +40,14 @@ #define GB_REG_BIT_ALL 0xFFFFFFFF #define GB_PREFIX 0x00000001 -#define GB_IMMEDIATE_U8 0x00000002 -#define GB_IMMEDIATE_S8 0x00000004 -#define GB_IMMEDIATE_U16 0x00000008 -#define GB_STOP 0x00000010 -#define GB_LOAD_REG8_REG8 0x00000020 -#define GB_ADD_SUB_IMMEDIATE 0x00000040 -#define GB_NEGATE_IMM 0x00000080 +#define GB_IMMEDIATE_U3 0x00000002 +#define GB_IMMEDIATE_U8 0x00000004 +#define GB_IMMEDIATE_S8 0x00000008 +#define GB_IMMEDIATE_U16 0x00000010 +#define GB_STOP 0x00000020 +#define GB_LOAD_REG8_REG8 0x00000040 +#define GB_ADD_SUB_IMMEDIATE 0x00000080 +#define GB_NEGATE_IMM 0x00000100 #define GB_REG_BIT(reg) (1 << reg) From e4775fb73368c559604609e2b05d622f6398d802 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 03:22:11 +0200 Subject: [PATCH 07/23] jp, jr, call, ret, reti --- Archs/GB/CGameboyInstruction.cpp | 18 ++++++++++++++++-- Archs/GB/CGameboyInstruction.h | 3 +-- Archs/GB/GameboyOpcodes.cpp | 16 ++++++++++------ Archs/GB/GameboyOpcodes.h | 10 ++++++++++ Archs/GB/GameboyParser.cpp | 12 ++++++++++++ Archs/GB/GameboyParser.h | 1 + 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index e5a8ee5d..21510331 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -9,10 +9,13 @@ CGameboyInstruction::CGameboyInstruction(const tGameboyOpcode& sourceOpcode, Gam { this->Opcode = sourceOpcode; this->Vars = vars; + this->RamPos = 0; } bool CGameboyInstruction::Validate(const ValidateState& state) { + RamPos = g_fileManager->getVirtualAddress(); + Vars.Length = Opcode.length; Vars.Encoding = Opcode.encoding; Vars.WritePrefix = Opcode.flags & GB_PREFIX; @@ -30,7 +33,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // Evaluate immediate - if (Opcode.flags & (GB_IMMEDIATE_U3 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8 | GB_IMMEDIATE_U16)) + if (Opcode.flags & GB_HAS_IMMEDIATE) { if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate)) { @@ -41,6 +44,10 @@ bool CGameboyInstruction::Validate(const ValidateState& state) { Vars.Immediate = -Vars.Immediate; } + if (Opcode.flags & GB_JUMP_RELATIVE) + { + Vars.Immediate = (Vars.Immediate - RamPos - 2); + } int64_t min = 0; int64_t max = 0; @@ -109,7 +116,14 @@ bool CGameboyInstruction::Validate(const ValidateState& state) if (Vars.Immediate < min || Vars.Immediate > max) { - Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate); + if (Opcode.flags & GB_JUMP_RELATIVE) + { + Logger::queueError(Logger::Error, L"Jump target %04X out of range", Vars.Immediate); + } + else + { + Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate); + } return false; } diff --git a/Archs/GB/CGameboyInstruction.h b/Archs/GB/CGameboyInstruction.h index 997a6ffd..e0ae8d48 100644 --- a/Archs/GB/CGameboyInstruction.h +++ b/Archs/GB/CGameboyInstruction.h @@ -28,6 +28,5 @@ class CGameboyInstruction: public CAssemblerCommand private: GameboyOpcodeVariables Vars; tGameboyOpcode Opcode; - - // Inherited via CAssemblerCommand + int64_t RamPos; }; diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index 62f8e70b..f6b296ae 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -74,11 +74,15 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"stop", 2, 0x10, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, GB_STOP }, { L"di", 1, 0xF3, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"ei", 1, 0xFB, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"jp", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"jr", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"call", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"ret", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"reti", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -// { L"rst", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"jp", 3, 0xC2, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U16 }, + { L"jp", 1, 0xE9, GB_PARAM_HL, GB_PARAM_NONE, -1, -1, 0 }, + { L"jp", 3, 0xC3, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_U16 }, + { L"jr", 2, 0x20, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_S8 | GB_JUMP_RELATIVE }, + { L"jr", 2, 0x18, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_S8 | GB_JUMP_RELATIVE }, + { L"call", 3, 0xC4, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U16 }, + { L"call", 3, 0xCD, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_U16 }, + { L"ret", 1, 0xC0, GB_PARAM_CONDITION, GB_PARAM_NONE, 3, -1, 0 }, + { L"ret", 1, 0xC9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"reti", 1, 0xD9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { nullptr, 0, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, }; diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index a511ffc7..b080088b 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -14,6 +14,7 @@ #define GB_PARAM_MEMIMMEDIATE 0x0B // (imm) #define GB_PARAM_FF00_C 0x0C // (0xFF00+c) #define GB_PARAM_SP_IMM 0x0D // sp+s8 +#define GB_PARAM_CONDITION 0x0E // nz, z, nc, c #define GB_REG8_B 0x00 // b #define GB_REG8_C 0x01 // c @@ -37,6 +38,11 @@ | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) \ | GB_REG_BIT(GB_REG16_AF) ) +#define GB_COND_NZ 0x00 // nz +#define GB_COND_Z 0x01 // z +#define GB_COND_NC 0x02 // nc +#define GB_COND_C 0x03 // c + #define GB_REG_BIT_ALL 0xFFFFFFFF #define GB_PREFIX 0x00000001 @@ -48,6 +54,10 @@ #define GB_LOAD_REG8_REG8 0x00000040 #define GB_ADD_SUB_IMMEDIATE 0x00000080 #define GB_NEGATE_IMM 0x00000100 +#define GB_JUMP_RELATIVE 0x00000200 + +#define GB_HAS_IMMEDIATE ( GB_IMMEDIATE_U3 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8 \ + | GB_IMMEDIATE_U16 | GB_JUMP_RELATIVE ) #define GB_REG_BIT(reg) (1 << reg) diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp index 1ae53884..7d32cb94 100644 --- a/Archs/GB/GameboyParser.cpp +++ b/Archs/GB/GameboyParser.cpp @@ -29,6 +29,11 @@ const GameboyRegisterDescriptor gameboyHLIncDec16[] = { { L"hli", 0 }, { L"hld", 1 }, }; +const GameboyRegisterDescriptor gameboyConds[] = { + { L"nz", GB_COND_NZ }, { L"z", GB_COND_Z }, + { L"nc", GB_COND_NC }, { L"c", GB_COND_C }, +}; + const DirectiveMap gameboyDirectives = { }; std::unique_ptr GameboyParser::parseDirective(Parser& parser) @@ -71,6 +76,11 @@ bool GameboyParser::parseRegister16AF(Parser& parser, GameboyRegisterValue& dest return parseRegisterTable(parser, dest, gameboyRegs16AF, std::size(gameboyRegs16AF), allowed); } +bool GameboyParser::parseCondition(Parser& parser, GameboyRegisterValue& dest) +{ + return parseRegisterTable(parser, dest, gameboyConds, std::size(gameboyConds), GB_REG_BIT_ALL); +} + bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) { CHECK(parser.matchToken(TokenType::LParen)); @@ -204,6 +214,8 @@ bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType return parseFF00PlusC(parser); case GB_PARAM_SP_IMM: return parseSPImmediate(parser, destImm, isNegative); + case GB_PARAM_CONDITION: + return parseCondition(parser, destReg); default: return false; } diff --git a/Archs/GB/GameboyParser.h b/Archs/GB/GameboyParser.h index ec64cfa9..76c76af5 100644 --- a/Archs/GB/GameboyParser.h +++ b/Archs/GB/GameboyParser.h @@ -17,6 +17,7 @@ class GameboyParser bool parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseRegister16SP(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseRegister16AF(Parser& parser, GameboyRegisterValue& dest, int allowed); + bool parseCondition(Parser& parser, GameboyRegisterValue& dest); bool parseHLIncDec(Parser& parser, GameboyRegisterValue& dest); bool parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); bool parseMemoryImmediate(Parser& parser, Expression& dest); From e848bab07d7b16c2e0cf97258acd9bebcaa1cf2a Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 03:22:38 +0200 Subject: [PATCH 08/23] rst --- Archs/GB/CGameboyInstruction.cpp | 18 ++++++++++++++---- Archs/GB/GameboyOpcodes.cpp | 1 + Archs/GB/GameboyOpcodes.h | 11 ++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index 21510331..b11555a7 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -49,8 +49,8 @@ bool CGameboyInstruction::Validate(const ValidateState& state) Vars.Immediate = (Vars.Immediate - RamPos - 2); } - int64_t min = 0; - int64_t max = 0; + int64_t min = INT64_MIN; + int64_t max = INT64_MAX; if (Opcode.flags & GB_IMMEDIATE_U3) { min = 0; @@ -126,10 +126,20 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } return false; } + if (Opcode.flags & GB_RST) + { + 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) + { + Logger::queueError(Logger::Error, L"Invalid RST target %i", Vars.Immediate); + return false; + } + } - // Move small immediate to lhs - if (Opcode.flags & GB_IMMEDIATE_U3) + // Move immediate to lhs + if (Opcode.flags & (GB_IMMEDIATE_U3 | GB_RST)) { + Vars.LeftParam.name = L"imm"; Vars.LeftParam.num = Vars.Immediate; } } diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index f6b296ae..ab0d8406 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -84,5 +84,6 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"ret", 1, 0xC0, GB_PARAM_CONDITION, GB_PARAM_NONE, 3, -1, 0 }, { L"ret", 1, 0xC9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, { L"reti", 1, 0xD9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, + { L"rst", 1, 0xC7, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, 0, -1, GB_RST }, { nullptr, 0, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, }; diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h index b080088b..8e8ea34e 100644 --- a/Archs/GB/GameboyOpcodes.h +++ b/Archs/GB/GameboyOpcodes.h @@ -51,13 +51,14 @@ #define GB_IMMEDIATE_S8 0x00000008 #define GB_IMMEDIATE_U16 0x00000010 #define GB_STOP 0x00000020 -#define GB_LOAD_REG8_REG8 0x00000040 -#define GB_ADD_SUB_IMMEDIATE 0x00000080 -#define GB_NEGATE_IMM 0x00000100 -#define GB_JUMP_RELATIVE 0x00000200 +#define GB_RST 0x00000040 +#define GB_LOAD_REG8_REG8 0x00000080 +#define GB_ADD_SUB_IMMEDIATE 0x00000100 +#define GB_NEGATE_IMM 0x00000200 +#define GB_JUMP_RELATIVE 0x00000400 #define GB_HAS_IMMEDIATE ( GB_IMMEDIATE_U3 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8 \ - | GB_IMMEDIATE_U16 | GB_JUMP_RELATIVE ) + | GB_IMMEDIATE_U16 | GB_RST | GB_JUMP_RELATIVE ) #define GB_REG_BIT(reg) (1 << reg) From e1857cb8855f17ebad884658f8c83eb90d3cd01f Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 03:25:05 +0200 Subject: [PATCH 09/23] "Implement" writeTempData --- Archs/GB/CGameboyInstruction.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index b11555a7..7ddf02ee 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -4,6 +4,7 @@ #include "Core/Expression.h" #include "Core/FileManager.h" #include "Core/Misc.h" +#include "Util/Util.h" CGameboyInstruction::CGameboyInstruction(const tGameboyOpcode& sourceOpcode, GameboyOpcodeVariables& vars) { @@ -186,4 +187,11 @@ void CGameboyInstruction::Encode() const void CGameboyInstruction::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)); } From c475a621038fb95a527118f55383f5ddfcda8d4f Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 12:40:18 +0200 Subject: [PATCH 10/23] Support ld (c),a as alias for ld (0xFF00+c),a; support ldh and ldhl aliases. --- Archs/GB/CGameboyInstruction.cpp | 2 +- Archs/GB/GameboyOpcodes.cpp | 2 ++ Archs/GB/GameboyParser.cpp | 12 ++++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/GB/CGameboyInstruction.cpp index 7ddf02ee..985f5fc7 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/GB/CGameboyInstruction.cpp @@ -94,7 +94,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // Special loads in range 0xFF00 - 0xFFFF - if (Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) + if (!(Opcode.flags & GB_IMMEDIATE_U3) && Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) { // ld (0xFF00+u8),a can be encoded as E0 XX instead Vars.Encoding = 0xE0; diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp index ab0d8406..47956a9f 100644 --- a/Archs/GB/GameboyOpcodes.cpp +++ b/Archs/GB/GameboyOpcodes.cpp @@ -26,6 +26,8 @@ const tGameboyOpcode GameboyOpcodes[] = { { L"ldi", 1, 0x2A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, { L"ldd", 1, 0x32, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, { L"ldd", 1, 0x3A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, + { L"ldh", 2, 0xE0, GB_PARAM_MEMIMMEDIATE, GB_PARAM_A, -1, -1, GB_IMMEDIATE_U8 }, + { L"ldhl", 2, 0xF8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 }, { L"push", 1, 0xC5, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, { L"pop", 1, 0xC1, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, { L"add", 1, 0x09, GB_PARAM_HL, GB_PARAM_REG16_SP, -1, 4, 0 }, diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp index 7d32cb94..8156fa02 100644 --- a/Archs/GB/GameboyParser.cpp +++ b/Archs/GB/GameboyParser.cpp @@ -137,11 +137,15 @@ bool GameboyParser::parseFF00PlusC(Parser& parser) { CHECK(parser.matchToken(TokenType::LParen)); - const Token& token = parser.nextToken(); - CHECK(token.type == TokenType::Integer); - CHECK(token.intValue == 0xFF00); + // 0xFF00+ optional + const Token& token = parser.peekToken(); + if (token.type == TokenType::Integer) + { + CHECK(token.intValue == 0xFF00); + parser.eatToken(); - CHECK(parser.matchToken(TokenType::Plus)); + CHECK(parser.matchToken(TokenType::Plus)); + } GameboyRegisterValue tempReg; CHECK(parseRegister8(parser, tempReg, GB_REG_BIT(GB_REG8_C))); From 263d5eebd4df71e950a698a1e858895c32026bef Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 12:52:21 +0200 Subject: [PATCH 11/23] Rename Gameboy to Z80 --- Archs/GB/Gameboy.cpp | 36 --- Archs/GB/GameboyOpcodes.cpp | 91 ------ Archs/GB/GameboyOpcodes.h | 77 ----- Archs/GB/GameboyParser.cpp | 279 ------------------ Archs/GB/GameboyParser.h | 28 -- .../CZ80Instruction.cpp} | 46 +-- .../CZ80Instruction.h} | 18 +- Archs/Z80/Z80.cpp | 36 +++ Archs/{GB/Gameboy.h => Z80/Z80.h} | 8 +- Archs/Z80/Z80Opcodes.cpp | 91 ++++++ Archs/Z80/Z80Opcodes.h | 77 +++++ Archs/Z80/Z80Parser.cpp | 279 ++++++++++++++++++ Archs/Z80/Z80Parser.h | 28 ++ CMakeLists.txt | 18 +- Parser/DirectivesParser.cpp | 10 +- 15 files changed, 561 insertions(+), 561 deletions(-) delete mode 100644 Archs/GB/Gameboy.cpp delete mode 100644 Archs/GB/GameboyOpcodes.cpp delete mode 100644 Archs/GB/GameboyOpcodes.h delete mode 100644 Archs/GB/GameboyParser.cpp delete mode 100644 Archs/GB/GameboyParser.h rename Archs/{GB/CGameboyInstruction.cpp => Z80/CZ80Instruction.cpp} (74%) rename Archs/{GB/CGameboyInstruction.h => Z80/CZ80Instruction.h} (55%) create mode 100644 Archs/Z80/Z80.cpp rename Archs/{GB/Gameboy.h => Z80/Z80.h} (79%) create mode 100644 Archs/Z80/Z80Opcodes.cpp create mode 100644 Archs/Z80/Z80Opcodes.h create mode 100644 Archs/Z80/Z80Parser.cpp create mode 100644 Archs/Z80/Z80Parser.h diff --git a/Archs/GB/Gameboy.cpp b/Archs/GB/Gameboy.cpp deleted file mode 100644 index cf544f98..00000000 --- a/Archs/GB/Gameboy.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Archs/GB/CGameboyInstruction.h" -#include "Archs/GB/Gameboy.h" -#include "Archs/GB/GameboyParser.h" -#include "Parser/Parser.h" - -CGameboyArchitecture Gameboy; - -CGameboyArchitecture::CGameboyArchitecture() -{ -} - -std::unique_ptr CGameboyArchitecture::parseDirective(Parser& parser) -{ - GameboyParser gameboyParser; - - return gameboyParser.parseDirective(parser); -} - -std::unique_ptr CGameboyArchitecture::parseOpcode(Parser& parser) -{ - GameboyParser gameboyParser; - - return gameboyParser.parseOpcode(parser); -} - -void CGameboyArchitecture::NextSection() -{ -} - -void CGameboyArchitecture::Pass2() -{ -} - -void CGameboyArchitecture::Revalidate() -{ -} diff --git a/Archs/GB/GameboyOpcodes.cpp b/Archs/GB/GameboyOpcodes.cpp deleted file mode 100644 index 47956a9f..00000000 --- a/Archs/GB/GameboyOpcodes.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "Archs/GB/GameboyOpcodes.h" - -// Order: -// - Everything else -// - SP_IMM -// - MEMIMMEDIATE -// - IMMEDIATE -const tGameboyOpcode GameboyOpcodes[] = { - // Name Len Encode Left param Right param LShift RShift Flags - { L"nop", 1, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"ld", 1, 0x40, GB_PARAM_REG8_MEMHL, GB_PARAM_REG8_MEMHL, 3, 0, GB_LOAD_REG8_REG8 }, - { L"ld", 1, 0x02, GB_PARAM_MEMBC_MEMDE, GB_PARAM_A, 4, -1, 0 }, - { L"ld", 1, 0x0A, GB_PARAM_A, GB_PARAM_MEMBC_MEMDE, -1, 4, 0 }, - { L"ld", 1, 0x22, GB_PARAM_HLI_HLD, GB_PARAM_A, 4, -1, 0 }, - { L"ld", 1, 0x2A, GB_PARAM_A, GB_PARAM_HLI_HLD, -1, 4, 0 }, - { L"ld", 1, 0xE2, GB_PARAM_FF00_C, GB_PARAM_A, -1, -1, 0 }, - { L"ld", 1, 0xF2, GB_PARAM_A, GB_PARAM_FF00_C, -1, -1, 0 }, - { L"ld", 1, 0xF9, GB_PARAM_SP, GB_PARAM_HL, -1, -1, 0 }, - { L"ld", 2, 0xF8, GB_PARAM_HL, GB_PARAM_SP_IMM, -1, -1, GB_IMMEDIATE_S8 }, - { L"ld", 3, 0xFA, GB_PARAM_A, GB_PARAM_MEMIMMEDIATE, -1, -1, GB_IMMEDIATE_U16 }, - { L"ld", 2, 0x06, GB_PARAM_REG8_MEMHL, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U8 }, - { L"ld", 3, 0x01, GB_PARAM_REG16_SP, GB_PARAM_IMMEDIATE, 4, -1, GB_IMMEDIATE_U16 }, - { L"ld", 3, 0x08, GB_PARAM_MEMIMMEDIATE, GB_PARAM_SP, -1, -1, GB_IMMEDIATE_U16 }, - { L"ld", 3, 0xEA, GB_PARAM_MEMIMMEDIATE, GB_PARAM_A, -1, -1, GB_IMMEDIATE_U16 }, - { L"ldi", 1, 0x22, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, - { L"ldi", 1, 0x2A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, - { L"ldd", 1, 0x32, GB_PARAM_MEMHL, GB_PARAM_A, -1, -1, 0 }, - { L"ldd", 1, 0x3A, GB_PARAM_A, GB_PARAM_MEMHL, -1, -1, 0 }, - { L"ldh", 2, 0xE0, GB_PARAM_MEMIMMEDIATE, GB_PARAM_A, -1, -1, GB_IMMEDIATE_U8 }, - { L"ldhl", 2, 0xF8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 }, - { L"push", 1, 0xC5, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, - { L"pop", 1, 0xC1, GB_PARAM_REG16_AF, GB_PARAM_NONE, 4, -1, 0 }, - { L"add", 1, 0x09, GB_PARAM_HL, GB_PARAM_REG16_SP, -1, 4, 0 }, - { L"add", 1, 0x80, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"add", 2, 0xC6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, - { L"add", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 }, - { L"adc", 1, 0x88, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"adc", 2, 0xCE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, - { L"sub", 1, 0x90, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sub", 2, 0xD6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, - { L"sub", 2, 0xE8, GB_PARAM_SP, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_S8 | GB_NEGATE_IMM }, - { L"sbc", 1, 0x98, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sbc", 2, 0xDE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 | GB_ADD_SUB_IMMEDIATE }, - { L"and", 1, 0xA0, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"and", 2, 0xE6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, - { L"xor", 1, 0xA8, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"xor", 2, 0xEE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, - { L"or", 1, 0xB0, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"or", 2, 0xF6, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, - { L"cp", 1, 0xB8, GB_PARAM_A, GB_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"cp", 2, 0xFE, GB_PARAM_A, GB_PARAM_IMMEDIATE, -1, -1, GB_IMMEDIATE_U8 }, - { L"inc", 1, 0x04, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, - { L"inc", 1, 0x03, GB_PARAM_REG16_SP, GB_PARAM_NONE, 4, -1, 0 }, - { L"dec", 1, 0x05, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 3, -1, 0 }, - { L"dec", 1, 0x0B, GB_PARAM_REG16_SP, GB_PARAM_NONE, 4, -1, 0 }, - { L"daa", 1, 0x27, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"cpl", 1, 0x2F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rlca", 1, 0x07, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rla", 1, 0x17, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rrca", 1, 0x0F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rra", 1, 0x1F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rlc", 2, 0x00, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"rrc", 2, 0x08, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"rl", 2, 0x10, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"rr", 2, 0x18, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"sla", 2, 0x20, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"sra", 2, 0x28, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"swap", 2, 0x30, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"srl", 2, 0x38, GB_PARAM_REG8_MEMHL, GB_PARAM_NONE, 0, -1, GB_PREFIX }, - { L"bit", 2, 0x40, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, - { L"res", 2, 0x80, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, - { L"set", 2, 0xC0, GB_PARAM_IMMEDIATE, GB_PARAM_REG8_MEMHL, 3, 0, GB_PREFIX | GB_IMMEDIATE_U3 }, - { L"ccf", 1, 0x3F, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"scf", 1, 0x37, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"halt", 1, 0x76, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"stop", 2, 0x10, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, GB_STOP }, - { L"di", 1, 0xF3, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"ei", 1, 0xFB, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"jp", 3, 0xC2, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U16 }, - { L"jp", 1, 0xE9, GB_PARAM_HL, GB_PARAM_NONE, -1, -1, 0 }, - { L"jp", 3, 0xC3, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_U16 }, - { L"jr", 2, 0x20, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_S8 | GB_JUMP_RELATIVE }, - { L"jr", 2, 0x18, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_S8 | GB_JUMP_RELATIVE }, - { L"call", 3, 0xC4, GB_PARAM_CONDITION, GB_PARAM_IMMEDIATE, 3, -1, GB_IMMEDIATE_U16 }, - { L"call", 3, 0xCD, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, -1, -1, GB_IMMEDIATE_U16 }, - { L"ret", 1, 0xC0, GB_PARAM_CONDITION, GB_PARAM_NONE, 3, -1, 0 }, - { L"ret", 1, 0xC9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"reti", 1, 0xD9, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, - { L"rst", 1, 0xC7, GB_PARAM_IMMEDIATE, GB_PARAM_NONE, 0, -1, GB_RST }, - { nullptr, 0, 0x00, GB_PARAM_NONE, GB_PARAM_NONE, -1, -1, 0 }, -}; diff --git a/Archs/GB/GameboyOpcodes.h b/Archs/GB/GameboyOpcodes.h deleted file mode 100644 index 8e8ea34e..00000000 --- a/Archs/GB/GameboyOpcodes.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#define GB_PARAM_NONE 0x00 -#define GB_PARAM_A 0x01 // a -#define GB_PARAM_MEMBC_MEMDE 0x02 // (bc), (de) -#define GB_PARAM_REG8_MEMHL 0x03 // b, c, d, e, h, l, (hl), a -#define GB_PARAM_REG16_SP 0x04 // bc, de, hl, sp -#define GB_PARAM_REG16_AF 0x05 // bc, de, hl, af -#define GB_PARAM_HL 0x06 // hl -#define GB_PARAM_MEMHL 0x07 // (hl) -#define GB_PARAM_HLI_HLD 0x08 // hli, hld, hl+, hl- -#define GB_PARAM_SP 0x09 // sp -#define GB_PARAM_IMMEDIATE 0x0A // imm -#define GB_PARAM_MEMIMMEDIATE 0x0B // (imm) -#define GB_PARAM_FF00_C 0x0C // (0xFF00+c) -#define GB_PARAM_SP_IMM 0x0D // sp+s8 -#define GB_PARAM_CONDITION 0x0E // nz, z, nc, c - -#define GB_REG8_B 0x00 // b -#define GB_REG8_C 0x01 // c -#define GB_REG8_D 0x02 // d -#define GB_REG8_E 0x03 // e -#define GB_REG8_H 0x04 // h -#define GB_REG8_L 0x05 // l -#define GB_REG8_MEMHL 0x06 // (hl) -#define GB_REG8_A 0x07 // a -#define GB_REG8_BIT_ALL ( GB_REG_BIT(GB_REG8_B) | GB_REG_BIT(GB_REG8_C) \ - | GB_REG_BIT(GB_REG8_D) | GB_REG_BIT(GB_REG8_E) \ - | GB_REG_BIT(GB_REG8_H) | GB_REG_BIT(GB_REG8_L) \ - | GB_REG_BIT(GB_REG8_A) ) - -#define GB_REG16_BC 0x00 // bc -#define GB_REG16_DE 0x01 // de -#define GB_REG16_HL 0x02 // hl -#define GB_REG16_SP 0x03 // sp -#define GB_REG16_AF 0x03 // af (yes, same as sp) -#define GB_REG16_BIT_ALL ( GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE) \ - | GB_REG_BIT(GB_REG16_HL) | GB_REG_BIT(GB_REG16_SP) \ - | GB_REG_BIT(GB_REG16_AF) ) - -#define GB_COND_NZ 0x00 // nz -#define GB_COND_Z 0x01 // z -#define GB_COND_NC 0x02 // nc -#define GB_COND_C 0x03 // c - -#define GB_REG_BIT_ALL 0xFFFFFFFF - -#define GB_PREFIX 0x00000001 -#define GB_IMMEDIATE_U3 0x00000002 -#define GB_IMMEDIATE_U8 0x00000004 -#define GB_IMMEDIATE_S8 0x00000008 -#define GB_IMMEDIATE_U16 0x00000010 -#define GB_STOP 0x00000020 -#define GB_RST 0x00000040 -#define GB_LOAD_REG8_REG8 0x00000080 -#define GB_ADD_SUB_IMMEDIATE 0x00000100 -#define GB_NEGATE_IMM 0x00000200 -#define GB_JUMP_RELATIVE 0x00000400 - -#define GB_HAS_IMMEDIATE ( GB_IMMEDIATE_U3 | GB_IMMEDIATE_U8 | GB_IMMEDIATE_S8 \ - | GB_IMMEDIATE_U16 | GB_RST | GB_JUMP_RELATIVE ) - -#define GB_REG_BIT(reg) (1 << reg) - -struct tGameboyOpcode -{ - const wchar_t* name; - unsigned char length; - unsigned char encoding; - unsigned char lhs : 4; - unsigned char rhs : 4; - char lhsShift : 4; - char rhsShift : 4; - int flags; -}; - -extern const tGameboyOpcode GameboyOpcodes[]; diff --git a/Archs/GB/GameboyParser.cpp b/Archs/GB/GameboyParser.cpp deleted file mode 100644 index 8156fa02..00000000 --- a/Archs/GB/GameboyParser.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include "Archs/GB/CGameboyInstruction.h" -#include "Archs/GB/Gameboy.h" -#include "Archs/GB/GameboyOpcodes.h" -#include "Archs/GB/GameboyParser.h" -#include "Core/Expression.h" -#include "Parser/DirectivesParser.h" -#include "Parser/Tokenizer.h" - -#define CHECK(exp) if (!(exp)) return false; - -const GameboyRegisterDescriptor gameboyRegs8[] = { - { L"b", GB_REG8_B }, { L"c", GB_REG8_C }, - { L"d", GB_REG8_D }, { L"e", GB_REG8_E }, - { L"h", GB_REG8_H }, { L"l", GB_REG8_L }, - { L"a", GB_REG8_A }, -}; - -const GameboyRegisterDescriptor gameboyRegs16SP[] = { - { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, - { L"hl", GB_REG16_HL }, { L"sp", GB_REG16_SP }, -}; - -const GameboyRegisterDescriptor gameboyRegs16AF[] = { // kinda hacky - { L"bc", GB_REG16_BC }, { L"de", GB_REG16_DE }, - { L"hl", GB_REG16_HL }, { L"af", GB_REG16_AF }, -}; - -const GameboyRegisterDescriptor gameboyHLIncDec16[] = { - { L"hli", 0 }, { L"hld", 1 }, -}; - -const GameboyRegisterDescriptor gameboyConds[] = { - { L"nz", GB_COND_NZ }, { L"z", GB_COND_Z }, - { L"nc", GB_COND_NC }, { L"c", GB_COND_C }, -}; - -const DirectiveMap gameboyDirectives = { }; - -std::unique_ptr GameboyParser::parseDirective(Parser& parser) -{ - return parser.parseDirective(gameboyDirectives); -} - -bool GameboyParser::parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyRegisterDescriptor* 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 GameboyParser::parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed) -{ - return parseRegisterTable(parser, dest, gameboyRegs8, std::size(gameboyRegs8), allowed); -} - -bool GameboyParser::parseRegister16SP(Parser& parser, GameboyRegisterValue& dest, int allowed) -{ - return parseRegisterTable(parser, dest, gameboyRegs16SP, std::size(gameboyRegs16SP), allowed); -} - -bool GameboyParser::parseRegister16AF(Parser& parser, GameboyRegisterValue& dest, int allowed) -{ - return parseRegisterTable(parser, dest, gameboyRegs16AF, std::size(gameboyRegs16AF), allowed); -} - -bool GameboyParser::parseCondition(Parser& parser, GameboyRegisterValue& dest) -{ - return parseRegisterTable(parser, dest, gameboyConds, std::size(gameboyConds), GB_REG_BIT_ALL); -} - -bool GameboyParser::parseHLIncDec(Parser& parser, GameboyRegisterValue& dest) -{ - CHECK(parser.matchToken(TokenType::LParen)); - - // hli / hld - if (!parseRegisterTable(parser, dest, gameboyHLIncDec16, std::size(gameboyHLIncDec16), GB_REG_BIT_ALL)) - { - // hl+ / hl- - CHECK(parseRegister16SP(parser, dest, GB_REG_BIT(GB_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 GameboyParser::parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed) -{ - CHECK(parser.matchToken(TokenType::LParen)); - CHECK(parseRegister16SP(parser, dest, allowed)); - CHECK(parser.matchToken(TokenType::RParen)); - - return true; -} - -bool GameboyParser::parseMemoryImmediate(Parser& parser, Expression& dest) -{ - CHECK(parser.matchToken(TokenType::LParen)); - dest = parser.parseExpression(); - CHECK(dest.isLoaded()); - CHECK(parser.matchToken(TokenType::RParen)); - - return true; -} - -bool GameboyParser::parseFF00PlusC(Parser& parser) -{ - 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)); - } - - GameboyRegisterValue tempReg; - CHECK(parseRegister8(parser, tempReg, GB_REG_BIT(GB_REG8_C))); - - CHECK(parser.matchToken(TokenType::RParen)); - - return true; -} - -bool GameboyParser::parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative) -{ - isNegative = false; - - GameboyRegisterValue tempReg; - CHECK(parseRegister16SP(parser, tempReg, GB_REG_BIT(GB_REG16_SP))); - - const Token& token = parser.peekToken(); - if (token.type != TokenType::Plus && token.type != TokenType::Minus) - { - // Treat as +0 - dest = createConstExpression(0); - return true; - } - parser.eatToken(); - isNegative = token.type == TokenType::Minus; - - dest = parser.parseExpression(); - CHECK(dest.isLoaded()); - - return true; -} - -bool GameboyParser::parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm, bool& isNegative) -{ - switch (paramType) - { - case GB_PARAM_REG8_MEMHL: - if (parseRegister8(parser, destReg, GB_REG8_BIT_ALL)) - { - return true; - } - if (parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL))) - { - destReg.num = GB_REG8_MEMHL; - return true; - } - return false; - case GB_PARAM_REG16_SP: - return parseRegister16SP(parser, destReg, GB_REG16_BIT_ALL); - case GB_PARAM_REG16_AF: - return parseRegister16AF(parser, destReg, GB_REG16_BIT_ALL); - case GB_PARAM_A: - return parseRegister8(parser, destReg, GB_REG_BIT(GB_REG8_A)); - case GB_PARAM_MEMBC_MEMDE: - return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_BC) | GB_REG_BIT(GB_REG16_DE)); - case GB_PARAM_HL: - return parseRegister16SP(parser, destReg, GB_REG_BIT(GB_REG16_HL)); - case GB_PARAM_MEMHL: - return parseMemoryRegister16(parser, destReg, GB_REG_BIT(GB_REG16_HL)); - case GB_PARAM_HLI_HLD: - return parseHLIncDec(parser, destReg); - case GB_PARAM_SP: - return parseRegister16SP(parser, destReg, GB_REG_BIT(GB_REG16_SP)); - case GB_PARAM_IMMEDIATE: - destImm = parser.parseExpression(); - return destImm.isLoaded(); - case GB_PARAM_MEMIMMEDIATE: - return parseMemoryImmediate(parser, destImm); - case GB_PARAM_FF00_C: - return parseFF00PlusC(parser); - case GB_PARAM_SP_IMM: - return parseSPImmediate(parser, destImm, isNegative); - case GB_PARAM_CONDITION: - return parseCondition(parser, destReg); - default: - return false; - } -} - -bool GameboyParser::parseOpcodeParameterList(Parser& parser, const tGameboyOpcode opcode, GameboyOpcodeVariables& 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)); - CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); - } - vars.IsNegative = isNegative; - - return true; -} - -std::unique_ptr GameboyParser::parseOpcode(Parser& parser) -{ - if (parser.peekToken().type != TokenType::Identifier) - return nullptr; - - const Token& token = parser.nextToken(); - - GameboyOpcodeVariables vars; - bool paramFail = false; - - const std::wstring stringValue = token.getStringValue(); - for (int z = 0; GameboyOpcodes[z].name != nullptr; z++) - { - if (stringValue == GameboyOpcodes[z].name) - { - TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); - - if (parseOpcodeParameterList(parser, GameboyOpcodes[z], vars)) - { - // success, return opcode - return std::make_unique(GameboyOpcodes[z], vars); - } - - parser.getTokenizer()->setPosition(tokenPos); - paramFail = true; - } - } - - if (paramFail) - parser.printError(token, L"Gameboy parameter failure in %S", stringValue); - else - parser.printError(token, L"Invalid Gameboy opcode: %S", stringValue); - - return nullptr; -} diff --git a/Archs/GB/GameboyParser.h b/Archs/GB/GameboyParser.h deleted file mode 100644 index 76c76af5..00000000 --- a/Archs/GB/GameboyParser.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "Parser/Parser.h" - -struct GameboyRegisterDescriptor { - const wchar_t* name; - int num; -}; - -class GameboyParser -{ -public: - std::unique_ptr parseDirective(Parser& parser); - std::unique_ptr parseOpcode(Parser& parser); -private: - bool parseRegisterTable(Parser& parser, GameboyRegisterValue& dest, const GameboyRegisterDescriptor* table, size_t count, int allowed); - bool parseRegister8(Parser& parser, GameboyRegisterValue& dest, int allowed); - bool parseRegister16SP(Parser& parser, GameboyRegisterValue& dest, int allowed); - bool parseRegister16AF(Parser& parser, GameboyRegisterValue& dest, int allowed); - bool parseCondition(Parser& parser, GameboyRegisterValue& dest); - bool parseHLIncDec(Parser& parser, GameboyRegisterValue& dest); - bool parseMemoryRegister16(Parser& parser, GameboyRegisterValue& dest, int allowed); - bool parseMemoryImmediate(Parser& parser, Expression& dest); - bool parseFF00PlusC(Parser& parser); - bool parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative); - bool parseOpcodeParameter(Parser& parser, unsigned char paramType, GameboyRegisterValue& destReg, Expression& destImm, bool& isNegative); - bool parseOpcodeParameterList(Parser& parser, const tGameboyOpcode, GameboyOpcodeVariables& vars); -}; diff --git a/Archs/GB/CGameboyInstruction.cpp b/Archs/Z80/CZ80Instruction.cpp similarity index 74% rename from Archs/GB/CGameboyInstruction.cpp rename to Archs/Z80/CZ80Instruction.cpp index 985f5fc7..e13a701d 100644 --- a/Archs/GB/CGameboyInstruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -1,32 +1,32 @@ -#include "Archs/GB/CGameboyInstruction.h" -#include "Archs/GB/GameboyOpcodes.h" +#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" -CGameboyInstruction::CGameboyInstruction(const tGameboyOpcode& sourceOpcode, GameboyOpcodeVariables& vars) +CZ80Instruction::CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars) { this->Opcode = sourceOpcode; this->Vars = vars; this->RamPos = 0; } -bool CGameboyInstruction::Validate(const ValidateState& state) +bool CZ80Instruction::Validate(const ValidateState& state) { RamPos = g_fileManager->getVirtualAddress(); Vars.Length = Opcode.length; Vars.Encoding = Opcode.encoding; - Vars.WritePrefix = Opcode.flags & GB_PREFIX; + Vars.WritePrefix = Opcode.flags & Z80_PREFIX_CB; Vars.WriteImmediate8 = false; Vars.WriteImmediate16 = false; // ld (hl),(hl) equivalent to halt - if (Opcode.flags & GB_LOAD_REG8_REG8) + if (Opcode.flags & Z80_LOAD_REG8_REG8) { - if (Vars.LeftParam.num == GB_REG8_MEMHL && Vars.RightParam.num == GB_REG8_MEMHL) + if (Vars.LeftParam.num == Z80_REG8_MEMHL && Vars.RightParam.num == Z80_REG8_MEMHL) { Logger::queueError(Logger::Error, L"ld (hl),(hl) not allowed"); return false; @@ -34,7 +34,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // Evaluate immediate - if (Opcode.flags & GB_HAS_IMMEDIATE) + if (Opcode.flags & Z80_HAS_IMMEDIATE) { if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate)) { @@ -45,35 +45,35 @@ bool CGameboyInstruction::Validate(const ValidateState& state) { Vars.Immediate = -Vars.Immediate; } - if (Opcode.flags & GB_JUMP_RELATIVE) + if (Opcode.flags & Z80_JUMP_RELATIVE) { Vars.Immediate = (Vars.Immediate - RamPos - 2); } int64_t min = INT64_MIN; int64_t max = INT64_MAX; - if (Opcode.flags & GB_IMMEDIATE_U3) + if (Opcode.flags & Z80_IMMEDIATE_U3) { min = 0; max = 8; Vars.WriteImmediate8 = false; Vars.WriteImmediate16 = false; } - if (Opcode.flags & GB_IMMEDIATE_U8) + if (Opcode.flags & Z80_IMMEDIATE_U8) { min = 0; max = 255; Vars.WriteImmediate8 = true; Vars.WriteImmediate16 = false; } - else if (Opcode.flags & GB_IMMEDIATE_S8) + else if (Opcode.flags & Z80_IMMEDIATE_S8) { min = -128; max = 127; Vars.WriteImmediate8 = true; Vars.WriteImmediate16 = false; } - else if (Opcode.flags & GB_IMMEDIATE_U16) + else if (Opcode.flags & Z80_IMMEDIATE_U16) { min = 0; max = 65535; @@ -82,19 +82,19 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // add <-> sub - if ((Opcode.flags & GB_ADD_SUB_IMMEDIATE) && Vars.Immediate < 0) + if ((Opcode.flags & Z80_ADD_SUB_IMMEDIATE) && Vars.Immediate < 0) { // Change opcode Vars.Encoding ^= 0x10; Vars.Immediate = -Vars.Immediate; } - if (Opcode.flags & GB_NEGATE_IMM) + if (Opcode.flags & Z80_NEGATE_IMM) { Vars.Immediate = -Vars.Immediate; } // Special loads in range 0xFF00 - 0xFFFF - if (!(Opcode.flags & GB_IMMEDIATE_U3) && Vars.RightParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) + if (!(Opcode.flags & Z80_IMMEDIATE_U3) && Vars.RightParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00) { // ld (0xFF00+u8),a can be encoded as E0 XX instead Vars.Encoding = 0xE0; @@ -104,7 +104,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) Vars.WriteImmediate8 = true; Vars.WriteImmediate16 = false; } - else if (Vars.LeftParam.num == GB_REG8_A && Vars.Immediate >= 0xFF00) + 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; @@ -117,7 +117,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) if (Vars.Immediate < min || Vars.Immediate > max) { - if (Opcode.flags & GB_JUMP_RELATIVE) + if (Opcode.flags & Z80_JUMP_RELATIVE) { Logger::queueError(Logger::Error, L"Jump target %04X out of range", Vars.Immediate); } @@ -127,7 +127,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } return false; } - if (Opcode.flags & GB_RST) + if (Opcode.flags & Z80_RST) { 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) @@ -138,7 +138,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) } // Move immediate to lhs - if (Opcode.flags & (GB_IMMEDIATE_U3 | GB_RST)) + if (Opcode.flags & (Z80_IMMEDIATE_U3 | Z80_RST)) { Vars.LeftParam.name = L"imm"; Vars.LeftParam.num = Vars.Immediate; @@ -150,7 +150,7 @@ bool CGameboyInstruction::Validate(const ValidateState& state) return false; } -void CGameboyInstruction::Encode() const +void CZ80Instruction::Encode() const { unsigned char encoding = Vars.Encoding; @@ -179,13 +179,13 @@ void CGameboyInstruction::Encode() const { g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF)); } - else if (Opcode.flags & GB_STOP) + else if (Opcode.flags & Z80_STOP) { g_fileManager->writeU8(0x00); } } -void CGameboyInstruction::writeTempData(TempData& tempData) const +void CZ80Instruction::writeTempData(TempData& tempData) const { char str[256]; diff --git a/Archs/GB/CGameboyInstruction.h b/Archs/Z80/CZ80Instruction.h similarity index 55% rename from Archs/GB/CGameboyInstruction.h rename to Archs/Z80/CZ80Instruction.h index e0ae8d48..98632fe7 100644 --- a/Archs/GB/CGameboyInstruction.h +++ b/Archs/Z80/CZ80Instruction.h @@ -1,13 +1,13 @@ #pragma once -#include "Archs/GB/Gameboy.h" -#include "Archs/GB/GameboyOpcodes.h" +#include "Archs/Z80/Z80.h" +#include "Archs/Z80/Z80Opcodes.h" #include "Commands/CAssemblerCommand.h" -struct GameboyOpcodeVariables +struct Z80OpcodeVariables { - GameboyRegisterValue LeftParam; - GameboyRegisterValue RightParam; + Z80RegisterValue LeftParam; + Z80RegisterValue RightParam; Expression ImmediateExpression; int64_t Immediate; unsigned char Length; @@ -18,15 +18,15 @@ struct GameboyOpcodeVariables bool WriteImmediate16 : 1; }; -class CGameboyInstruction: public CAssemblerCommand +class CZ80Instruction: public CAssemblerCommand { public: - CGameboyInstruction(const tGameboyOpcode& sourceOpcode, GameboyOpcodeVariables& vars); + CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars); bool Validate(const ValidateState& state) override; void Encode() const override; void writeTempData(TempData& tempData) const override; private: - GameboyOpcodeVariables Vars; - tGameboyOpcode Opcode; + 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..7bef25d4 --- /dev/null +++ b/Archs/Z80/Z80.cpp @@ -0,0 +1,36 @@ +#include "Archs/Z80/CZ80Instruction.h" +#include "Archs/Z80/Z80.h" +#include "Archs/Z80/Z80Parser.h" +#include "Parser/Parser.h" + +CZ80Architecture Z80; + +CZ80Architecture::CZ80Architecture() +{ +} + +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/GB/Gameboy.h b/Archs/Z80/Z80.h similarity index 79% rename from Archs/GB/Gameboy.h rename to Archs/Z80/Z80.h index 577844ab..f092b2cf 100644 --- a/Archs/GB/Gameboy.h +++ b/Archs/Z80/Z80.h @@ -4,16 +4,16 @@ #include "Core/ELF/ElfRelocator.h" #include "Core/Expression.h" -struct GameboyRegisterValue +struct Z80RegisterValue { std::wstring name; int num; }; -class CGameboyArchitecture: public CArchitecture +class CZ80Architecture: public CArchitecture { public: - CGameboyArchitecture(); + CZ80Architecture(); virtual std::unique_ptr parseDirective(Parser& parser); virtual std::unique_ptr parseOpcode(Parser& parser); @@ -24,4 +24,4 @@ class CGameboyArchitecture: public CArchitecture virtual Endianness getEndianness() { return Endianness::Little; }; }; -extern CGameboyArchitecture Gameboy; +extern CZ80Architecture Z80; diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp new file mode 100644 index 00000000..6cae7ac1 --- /dev/null +++ b/Archs/Z80/Z80Opcodes.cpp @@ -0,0 +1,91 @@ +#include "Archs/Z80/Z80Opcodes.h" + +// Order: +// - Everything else +// - SP_IMM +// - MEMIMMEDIATE +// - IMMEDIATE +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, 0 }, + { L"ld", 1, 0x40, Z80_PARAM_REG8_MEMHL, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_LOAD_REG8_REG8 }, + { L"ld", 1, 0x02, Z80_PARAM_MEMBC_MEMDE, Z80_PARAM_A, 4, -1, 0 }, + { L"ld", 1, 0x0A, Z80_PARAM_A, Z80_PARAM_MEMBC_MEMDE, -1, 4, 0 }, + { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, 0 }, + { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, 0 }, + { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, 0 }, + { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, 0 }, + { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, 0 }, + { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 }, + { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 }, + { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U8 }, + { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 }, + { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, 0 }, + { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, 0 }, + { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, 0 }, + { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, 0 }, + { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U8 }, + { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 }, + { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, + { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, + { L"add", 1, 0x09, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, 0 }, + { L"add", 1, 0x80, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"add", 2, 0xC6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 }, + { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM }, + { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"xor", 2, 0xEE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"or", 1, 0xB0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"or", 2, 0xF6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"cp", 1, 0xB8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"cp", 2, 0xFE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"inc", 1, 0x04, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, + { L"inc", 1, 0x03, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, + { L"dec", 1, 0x05, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, + { L"dec", 1, 0x0B, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, + { L"daa", 1, 0x27, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"cpl", 1, 0x2F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rlca", 1, 0x07, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rla", 1, 0x17, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rrca", 1, 0x0F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rra", 1, 0x1F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rlc", 2, 0x00, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rrc", 2, 0x08, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rl", 2, 0x10, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rr", 2, 0x18, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"sla", 2, 0x20, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"sra", 2, 0x28, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"srl", 2, 0x38, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"bit", 2, 0x40, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"res", 2, 0x80, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"set", 2, 0xC0, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"ccf", 1, 0x3F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"scf", 1, 0x37, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"halt", 1, 0x76, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"stop", 2, 0x10, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_STOP }, + { L"di", 1, 0xF3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"ei", 1, 0xFB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, + { L"jp", 1, 0xE9, Z80_PARAM_HL, Z80_PARAM_NONE, -1, -1, 0 }, + { L"jp", 3, 0xC3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, + { L"jr", 2, 0x20, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"jr", 2, 0x18, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"call", 3, 0xC4, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, + { L"call", 3, 0xCD, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, + { L"ret", 1, 0xC0, Z80_PARAM_CONDITION, Z80_PARAM_NONE, 3, -1, 0 }, + { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_RST }, + { 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..bc104f40 --- /dev/null +++ b/Archs/Z80/Z80Opcodes.h @@ -0,0 +1,77 @@ +#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 + +#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_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) ) + +#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_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_HAS_IMMEDIATE ( Z80_IMMEDIATE_U3 | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE_S8 \ + | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE ) + +#define Z80_REG_BIT(reg) (1 << reg) + +struct tZ80Opcode +{ + const wchar_t* name; + unsigned char length; + unsigned char encoding; + unsigned char lhs : 4; + unsigned char rhs : 4; + 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..53e97041 --- /dev/null +++ b/Archs/Z80/Z80Parser.cpp @@ -0,0 +1,279 @@ +#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 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 }, +}; + +const DirectiveMap Z80Directives = { }; + +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::parseCondition(Parser& parser, Z80RegisterValue& dest) +{ + return parseRegisterTable(parser, dest, Z80Conds, std::size(Z80Conds), Z80_REG_BIT_ALL); +} + +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::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) +{ + 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)); + } + + Z80RegisterValue tempReg; + CHECK(parseRegister8(parser, tempReg, Z80_REG_BIT(Z80_REG8_C))); + + CHECK(parser.matchToken(TokenType::RParen)); + + return true; +} + +bool Z80Parser::parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative) +{ + isNegative = false; + + Z80RegisterValue tempReg; + CHECK(parseRegister16SP(parser, tempReg, Z80_REG_BIT(Z80_REG16_SP))); + + const Token& token = parser.peekToken(); + if (token.type != TokenType::Plus && token.type != TokenType::Minus) + { + // Treat as +0 + dest = createConstExpression(0); + return true; + } + parser.eatToken(); + isNegative = token.type == TokenType::Minus; + + dest = parser.parseExpression(); + CHECK(dest.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_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_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_HLI_HLD: + return parseHLIncDec(parser, destReg); + case Z80_PARAM_SP: + return parseRegister16SP(parser, destReg, Z80_REG_BIT(Z80_REG16_SP)); + 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); + case Z80_PARAM_SP_IMM: + return parseSPImmediate(parser, destImm, isNegative); + case Z80_PARAM_CONDITION: + return parseCondition(parser, destReg); + default: + return false; + } +} + +bool Z80Parser::parseOpcodeParameterList(Parser& parser, const tZ80Opcode opcode, 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)); + CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); + } + vars.IsNegative = isNegative; + + 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++) + { + if (stringValue == Z80Opcodes[z].name) + { + TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); + + if (parseOpcodeParameterList(parser, Z80Opcodes[z], vars)) + { + // success, return opcode + return std::make_unique(Z80Opcodes[z], vars); + } + + parser.getTokenizer()->setPosition(tokenPos); + paramFail = true; + } + } + + if (paramFail) + parser.printError(token, L"Z80 parameter failure in %S", stringValue); + else + parser.printError(token, L"Invalid Z80 opcode: %S", stringValue); + + return nullptr; +} diff --git a/Archs/Z80/Z80Parser.h b/Archs/Z80/Z80Parser.h new file mode 100644 index 00000000..ed863170 --- /dev/null +++ b/Archs/Z80/Z80Parser.h @@ -0,0 +1,28 @@ +#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 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 parseCondition(Parser& parser, Z80RegisterValue& dest); + bool parseHLIncDec(Parser& parser, Z80RegisterValue& dest); + bool parseMemoryRegister16(Parser& parser, Z80RegisterValue& dest, int allowed); + bool parseMemoryImmediate(Parser& parser, Expression& dest); + bool parseFF00PlusC(Parser& parser); + bool parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative); + bool parseOpcodeParameter(Parser& parser, unsigned char paramType, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative); + bool parseOpcodeParameterList(Parser& parser, const tZ80Opcode, Z80OpcodeVariables& vars); +}; diff --git a/CMakeLists.txt b/CMakeLists.txt index e2465b2f..96df8139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,15 +106,6 @@ armips_target_sources(armips PRIVATE Archs/ARM/ThumbOpcodes.cpp Archs/ARM/ThumbOpcodes.h - Archs/GB/CGameboyInstruction.h - Archs/GB/CGameboyInstruction.cpp - Archs/GB/Gameboy.cpp - Archs/GB/Gameboy.h - Archs/GB/GameboyOpcodes.cpp - Archs/GB/GameboyOpcodes.h - Archs/GB/GameboyParser.cpp - Archs/GB/GameboyParser.h - Archs/MIPS/CMipsInstruction.cpp Archs/MIPS/CMipsInstruction.h Archs/MIPS/Mips.cpp @@ -133,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/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index 292cc060..c62f7233 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -1,7 +1,7 @@ #include "Parser/DirectivesParser.h" #include "Archs/ARM/Arm.h" -#include "Archs/GB/Gameboy.h" +#include "Archs/Z80/Z80.h" #include "Archs/MIPS/Mips.h" #include "Commands/CAssemblerLabel.h" #include "Commands/CDirectiveArea.h" @@ -446,9 +446,9 @@ std::unique_ptr parseDirectiveArmArch(Parser& parser, int fla return nullptr; } -std::unique_ptr parseDirectiveGameboyArch(Parser& parser, int flags) +std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int flags) { - Arch = &Gameboy; + Arch = &Z80; return std::make_unique(L".gb", L""); } @@ -784,8 +784,8 @@ const DirectiveMap directives = { { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - { L".gb", { &parseDirectiveGameboyArch, 0 } }, - { L".gbc", { &parseDirectiveGameboyArch, 0 } }, + { L".gb", { &parseDirectiveZ80Arch, 0 } }, + { L".gbc", { &parseDirectiveZ80Arch, 0 } }, { L".area", { &parseDirectiveArea, 0 } }, { L".autoregion", { &parseDirectiveAutoRegion, 0 } }, From 9d95df9788d4b7e7135489674b3324f522adfefd Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 14:49:16 +0200 Subject: [PATCH 12/23] Support some more RST and LDH syntaxes --- Archs/Z80/CZ80Instruction.cpp | 8 +++++++- Archs/Z80/Z80Opcodes.cpp | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp index e13a701d..dce850f1 100644 --- a/Archs/Z80/CZ80Instruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -129,7 +129,13 @@ bool CZ80Instruction::Validate(const ValidateState& state) } if (Opcode.flags & Z80_RST) { - if (Vars.Immediate != 0x00 && Vars.Immediate != 0x08 && Vars.Immediate != 0x10 && Vars.Immediate != 0x18 && + if (Vars.Immediate >= 0x00 && Vars.Immediate <= 0x7) + { + // Nintendo syntax + // 1 -> 8, 2 -> 16, 3 -> 24, etc + Vars.Immediate <<= 3; + } + 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) { Logger::queueError(Logger::Error, L"Invalid RST target %i", Vars.Immediate); diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 6cae7ac1..d9aae204 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -27,6 +27,7 @@ const tZ80Opcode Z80Opcodes[] = { { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, 0 }, { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, 0 }, { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U8 }, + { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 }, { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, From e5ad7d7ce3c343020f1db7b6f60bfef848f1b5fb Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 14:50:17 +0200 Subject: [PATCH 13/23] Check for Gameboy-specific opcodes, add .z80 directive --- Archs/Z80/CZ80Instruction.cpp | 41 +++++++++++++++++++---------------- Archs/Z80/Z80.h | 6 +++++ Archs/Z80/Z80Opcodes.cpp | 40 +++++++++++++++++----------------- Archs/Z80/Z80Opcodes.h | 1 + Archs/Z80/Z80Parser.cpp | 3 +++ Parser/DirectivesParser.cpp | 20 ++++++++++++++--- Parser/DirectivesParser.h | 5 +++++ 7 files changed, 74 insertions(+), 42 deletions(-) diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp index dce850f1..78fb8f85 100644 --- a/Archs/Z80/CZ80Instruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -93,26 +93,29 @@ bool CZ80Instruction::Validate(const ValidateState& state) Vars.Immediate = -Vars.Immediate; } - // Special loads in range 0xFF00 - 0xFFFF - if (!(Opcode.flags & Z80_IMMEDIATE_U3) && Vars.RightParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00) + if (Z80.GetVersion() == ZARCH_GAMEBOY) { - // 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; + // 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) diff --git a/Archs/Z80/Z80.h b/Archs/Z80/Z80.h index f092b2cf..9544b005 100644 --- a/Archs/Z80/Z80.h +++ b/Archs/Z80/Z80.h @@ -4,6 +4,8 @@ #include "Core/ELF/ElfRelocator.h" #include "Core/Expression.h" +enum Z80ArchType { ZARCH_Z80 = 0, ZARCH_GAMEBOY, ZARCH_INVALID }; + struct Z80RegisterValue { std::wstring name; @@ -22,6 +24,10 @@ class CZ80Architecture: public CArchitecture 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; }; +private: + Z80ArchType Version; }; extern CZ80Architecture Z80; diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index d9aae204..926b07af 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -11,35 +11,35 @@ const tZ80Opcode Z80Opcodes[] = { { L"ld", 1, 0x40, Z80_PARAM_REG8_MEMHL, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_LOAD_REG8_REG8 }, { L"ld", 1, 0x02, Z80_PARAM_MEMBC_MEMDE, Z80_PARAM_A, 4, -1, 0 }, { L"ld", 1, 0x0A, Z80_PARAM_A, Z80_PARAM_MEMBC_MEMDE, -1, 4, 0 }, - { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, 0 }, - { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, 0 }, - { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, 0 }, - { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, 0 }, + { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, Z80_GAMEBOY }, + { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, Z80_GAMEBOY }, + { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, + { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, Z80_GAMEBOY }, { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, 0 }, - { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 }, - { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 }, + { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, + { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U8 }, { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 }, - { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 }, - { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 }, - { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, 0 }, - { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, 0 }, - { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, 0 }, - { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, 0 }, - { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U8 }, - { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 }, + { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, + { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, + { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, + { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_GAMEBOY }, + { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, + { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_GAMEBOY }, + { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U8 | Z80_GAMEBOY }, + { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_GAMEBOY }, + { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, { L"add", 1, 0x09, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, 0 }, { L"add", 1, 0x80, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"add", 2, 0xC6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 }, + { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM }, + { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM | Z80_GAMEBOY }, { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, @@ -66,7 +66,7 @@ const tZ80Opcode Z80Opcodes[] = { { L"rr", 2, 0x18, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, { L"sla", 2, 0x20, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, { L"sra", 2, 0x28, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB | Z80_GAMEBOY }, { L"srl", 2, 0x38, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, { L"bit", 2, 0x40, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, { L"res", 2, 0x80, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, @@ -74,7 +74,7 @@ const tZ80Opcode Z80Opcodes[] = { { L"ccf", 1, 0x3F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"scf", 1, 0x37, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"halt", 1, 0x76, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"stop", 2, 0x10, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_STOP }, + { L"stop", 2, 0x10, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_STOP | Z80_GAMEBOY }, { L"di", 1, 0xF3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"ei", 1, 0xFB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, @@ -86,7 +86,7 @@ const tZ80Opcode Z80Opcodes[] = { { L"call", 3, 0xCD, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, { L"ret", 1, 0xC0, Z80_PARAM_CONDITION, Z80_PARAM_NONE, 3, -1, 0 }, { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, + { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_GAMEBOY }, { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_RST }, { nullptr, 0, 0x00, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, }; diff --git a/Archs/Z80/Z80Opcodes.h b/Archs/Z80/Z80Opcodes.h index bc104f40..6f93ce30 100644 --- a/Archs/Z80/Z80Opcodes.h +++ b/Archs/Z80/Z80Opcodes.h @@ -56,6 +56,7 @@ #define Z80_ADD_SUB_IMMEDIATE 0x00000100 #define Z80_NEGATE_IMM 0x00000200 #define Z80_JUMP_RELATIVE 0x00000400 +#define Z80_GAMEBOY 0x00000800 #define Z80_HAS_IMMEDIATE ( Z80_IMMEDIATE_U3 | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE_S8 \ | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE ) diff --git a/Archs/Z80/Z80Parser.cpp b/Archs/Z80/Z80Parser.cpp index 53e97041..cefd379a 100644 --- a/Archs/Z80/Z80Parser.cpp +++ b/Archs/Z80/Z80Parser.cpp @@ -255,6 +255,9 @@ std::unique_ptr Z80Parser::parseOpcode(Parser& parser) const std::wstring stringValue = token.getStringValue(); for (int z = 0; Z80Opcodes[z].name != nullptr; z++) { + if ((Z80Opcodes[z].flags & Z80_GAMEBOY) && Z80.GetVersion() != ZARCH_GAMEBOY) + continue; + if (stringValue == Z80Opcodes[z].name) { TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index c62f7233..b772c541 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -450,7 +450,20 @@ std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int fla { Arch = &Z80; - return std::make_unique(L".gb", L""); + switch (flags) + { + case DIRECTIVE_Z80_Z80: + Z80.SetVersion(ZARCH_Z80); + return std::make_unique(L".z80", L""); + case DIRECTIVE_Z80_GB: + Z80.SetVersion(ZARCH_GAMEBOY); + return std::make_unique(L".gb", L""); + case DIRECTIVE_Z80_GBC: + Z80.SetVersion(ZARCH_GAMEBOY); + return std::make_unique(L".gbc", L""); + } + + return nullptr; } std::unique_ptr parseDirectiveArea(Parser& parser, int flags) @@ -784,8 +797,9 @@ const DirectiveMap directives = { { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - { L".gb", { &parseDirectiveZ80Arch, 0 } }, - { L".gbc", { &parseDirectiveZ80Arch, 0 } }, + { L".z80", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_Z80 } }, + { L".gb", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GB } }, + { L".gbc", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GBC } }, { L".area", { &parseDirectiveArea, 0 } }, { L".autoregion", { &parseDirectiveAutoRegion, 0 } }, diff --git a/Parser/DirectivesParser.h b/Parser/DirectivesParser.h index 51fa2bb3..55fc9280 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_GBC 0x00000003 + // Area directive flags #define DIRECTIVE_AREA_SHARED 0x00000001 From 491234844db9869c85a74c9d68899a1faec3b697 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 14:57:08 +0200 Subject: [PATCH 14/23] Add test cases for Gameboy --- Tests/Z80/GB Loads/GB Loads.asm | 84 +++++ Tests/Z80/GB Loads/expected.bin | Bin 0 -> 122 bytes Tests/Z80/GB Opcodes/GB Opcodes.asm | 508 ++++++++++++++++++++++++++++ Tests/Z80/GB Opcodes/expected.bin | Bin 0 -> 816 bytes Tests/Z80/GB RST/GB RST.asm | 26 ++ Tests/Z80/GB RST/expected.bin | 1 + 6 files changed, 619 insertions(+) create mode 100644 Tests/Z80/GB Loads/GB Loads.asm create mode 100644 Tests/Z80/GB Loads/expected.bin create mode 100644 Tests/Z80/GB Opcodes/GB Opcodes.asm create mode 100644 Tests/Z80/GB Opcodes/expected.bin create mode 100644 Tests/Z80/GB RST/GB RST.asm create mode 100644 Tests/Z80/GB RST/expected.bin 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 0000000000000000000000000000000000000000..240add91227eb1245f3a611dbe6d94641a87b13c GIT binary patch literal 122 zcmXBEAqs#%7zN?67zGVR!3~BDf{ST`xA-sVsl>QROtQhJA|Epgi%LpWRBCC}(P?1N z$fyDesfJqWp_e9@BK7CTYnChOkV?DgLc4txE#eLv63uJ!8y!lpM+ literal 0 HcmV?d00001 diff --git a/Tests/Z80/GB Opcodes/GB Opcodes.asm b/Tests/Z80/GB Opcodes/GB Opcodes.asm new file mode 100644 index 00000000..e618ca46 --- /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 a, b + sub a, c + sub a, d + sub a, e + sub a, h + sub a, l + sub a, (hl) + sub a, 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 a, b + and a, c + and a, d + and a, e + and a, h + and a, l + and a, (hl) + and a, a + xor a, b + xor a, c + xor a, d + xor a, e + xor a, h + xor a, l + xor a, (hl) + xor a, a + or a, b + or a, c + or a, d + or a, e + or a, h + or a, l + or a, (hl) + or a, a + cp a, b + cp a, c + cp a, d + cp a, e + cp a, h + cp a, l + cp a, (hl) + cp a, 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 a, 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 a, 0x55 + rst 4 + add sp, -0x56 + jp hl + ld (0x1234), a + xor a, 0x55 + rst 5 + ld a, (0xFF00+0x55) + pop af + ld a, (0xFF00+c) + di + push af + or a, 0x55 + rst 6 + ldhl sp, 0x55 + ld sp, hl + ld a, (0x1234) + ei + cp a, 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 0000000000000000000000000000000000000000..0b1ede3405a146ef7715948ca45be3d815977ffa GIT binary patch literal 816 zcmV~$2S5%$0D#e?B2k>(k}YIjNJ)x}B;jl_%W4^^R45}WdqhQK6;ifP_V}pmm6p9i zkp@5SX;qOzyQ+?^o~l+|3jG=eHES6vquN?^q!4xM)o-90HqtaUHZe6bS4}K5O{Hko zyhY1as&yNUW!rY`tvaZVoiv@L=+aeMcT?SaXl#1g+V!$my&d{EI`!@6+<(BpK`w(` zhqw)OA2xi18aZn8n6cx=PnhU2Y4Vh*)24fR&6qiB_8e~?UqAo3^X4xISQr=-yeK3z zZ1Iw%;StN0uUHwmYW146>!Q|g*tlu)maW^i?}*;HD<(E>_n!E@`x5paIC$uAV$zYL z$C8hyq@Fl=>hzhj=gy~HxOgc&jk_!ng0#`wxj9KS@!fzI|6eic5Z#s^4X*yh4gfjrxZcRnVp? zI_RQDHL9ae4GgGBEetWDHgyoxr5^QZKtmeQ7-LK@#SC+rU_n!w(VP~vq!q1cgC%Wg zM|-U3Ku0>!nJ#oiVof)?(*qlNVv8NUu%|Z;^uZA)`qB?)`ZIum48n!MxH1GchT_gJ zhBE@iNJcT5F^pv#)__^I1Ru3kf8M zU=|TVC}AvS2}=nlf@Lgc1uKbU6{}gpTGkQ8dN#0;O>AZhTiM2Tb`Z@@b`e7?aqMOf z@$6+E3GC+p2RX!H5=r6+M>$3^$4McT6P)A}r#Zt}&T*bJE^v`cq?5sAu5guWT;~Qi zxy5bn0C%}ZCRt>2p9ehT5s!JoQ=ajh7rZ2gSLBjMJ_Qu=nm4@V9q;+TM?UeHFMOql aZ+zzm#gy=qUzGBjGRmo-l0W=K!@vJ*gU{Ch literal 0 HcmV?d00001 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 From 71f39ec10d475b58fe2831e087c4e11953710afa Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 16:41:11 +0200 Subject: [PATCH 15/23] Support Z80-specific main opcodes and ED-prefixed opcodes. --- Archs/Z80/CZ80Instruction.cpp | 37 ++++++++++++++------ Archs/Z80/CZ80Instruction.h | 1 - Archs/Z80/Z80.h | 11 +++++- Archs/Z80/Z80Opcodes.cpp | 40 +++++++++++++++++++++ Archs/Z80/Z80Opcodes.h | 32 +++++++++++++++-- Archs/Z80/Z80Parser.cpp | 65 +++++++++++++++++++++++++++++++---- Archs/Z80/Z80Parser.h | 3 ++ Core/SymbolTable.cpp | 9 ----- Core/SymbolTable.h | 1 - Parser/DirectivesParser.cpp | 6 ++-- Parser/Tokenizer.cpp | 12 ++++++- 11 files changed, 181 insertions(+), 36 deletions(-) diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp index 78fb8f85..866b36b5 100644 --- a/Archs/Z80/CZ80Instruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -19,7 +19,6 @@ bool CZ80Instruction::Validate(const ValidateState& state) Vars.Length = Opcode.length; Vars.Encoding = Opcode.encoding; - Vars.WritePrefix = Opcode.flags & Z80_PREFIX_CB; Vars.WriteImmediate8 = false; Vars.WriteImmediate16 = false; @@ -52,32 +51,34 @@ bool CZ80Instruction::Validate(const ValidateState& state) int64_t min = INT64_MIN; int64_t max = INT64_MAX; - if (Opcode.flags & Z80_IMMEDIATE_U3) + 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 = 8; - Vars.WriteImmediate8 = false; - Vars.WriteImmediate16 = false; } - if (Opcode.flags & Z80_IMMEDIATE_U8) + else if (Opcode.flags & Z80_IMMEDIATE_U8) { min = 0; max = 255; Vars.WriteImmediate8 = true; - Vars.WriteImmediate16 = false; } else if (Opcode.flags & Z80_IMMEDIATE_S8) { min = -128; max = 127; Vars.WriteImmediate8 = true; - Vars.WriteImmediate16 = false; } else if (Opcode.flags & Z80_IMMEDIATE_U16) { min = 0; max = 65535; - Vars.WriteImmediate8 = false; Vars.WriteImmediate16 = true; } @@ -93,7 +94,7 @@ bool CZ80Instruction::Validate(const ValidateState& state) Vars.Immediate = -Vars.Immediate; } - if (Z80.GetVersion() == ZARCH_GAMEBOY) + 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)) @@ -124,15 +125,20 @@ bool CZ80Instruction::Validate(const ValidateState& state) { 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_RST) { - if (Vars.Immediate >= 0x00 && Vars.Immediate <= 0x7) + if (Z80.GetVersion() == Z80ArchType::Gameboy && Vars.Immediate >= 0x00 && Vars.Immediate <= 0x7) { // Nintendo syntax // 1 -> 8, 2 -> 16, 3 -> 24, etc @@ -152,6 +158,11 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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); @@ -163,10 +174,14 @@ void CZ80Instruction::Encode() const { unsigned char encoding = Vars.Encoding; - if (Vars.WritePrefix) + if (Opcode.flags & Z80_PREFIX_CB) { g_fileManager->writeU8(0xCB); } + if (Opcode.flags & Z80_PREFIX_ED) + { + g_fileManager->writeU8(0xED); + } if (Opcode.lhs && Opcode.lhsShift >= 0) { diff --git a/Archs/Z80/CZ80Instruction.h b/Archs/Z80/CZ80Instruction.h index 98632fe7..4afa04d7 100644 --- a/Archs/Z80/CZ80Instruction.h +++ b/Archs/Z80/CZ80Instruction.h @@ -13,7 +13,6 @@ struct Z80OpcodeVariables unsigned char Length; unsigned char Encoding; bool IsNegative : 1; - bool WritePrefix : 1; bool WriteImmediate8 : 1; bool WriteImmediate16 : 1; }; diff --git a/Archs/Z80/Z80.h b/Archs/Z80/Z80.h index 9544b005..785f0793 100644 --- a/Archs/Z80/Z80.h +++ b/Archs/Z80/Z80.h @@ -4,7 +4,7 @@ #include "Core/ELF/ElfRelocator.h" #include "Core/Expression.h" -enum Z80ArchType { ZARCH_Z80 = 0, ZARCH_GAMEBOY, ZARCH_INVALID }; +enum class Z80ArchType { Z80 = 0, Gameboy, Invalid }; struct Z80RegisterValue { @@ -26,6 +26,15 @@ class CZ80Architecture: public CArchitecture 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"; + default: + return L"Z80"; + } + } private: Z80ArchType Version; }; diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 926b07af..896ee498 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -14,12 +14,20 @@ const tZ80Opcode Z80Opcodes[] = { { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, Z80_GAMEBOY }, { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, Z80_GAMEBOY }, { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, + { L"ld", 2, 0x57, Z80_PARAM_A, Z80_PARAM_IR, -1, 3, Z80_Z80 | Z80_PREFIX_ED }, { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, Z80_GAMEBOY }, { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, 0 }, { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, + { L"ld", 3, 0x2A, Z80_PARAM_HL, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, + { L"ld", 4, 0x4B, Z80_PARAM_REG16_SP, Z80_PARAM_MEMIMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, + { L"ld", 3, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U8 }, { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 }, + { L"ld", 2, 0x47, Z80_PARAM_IR, Z80_PARAM_A, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ld", 3, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_HL, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, + { L"ld", 4, 0x43, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_REG16_SP, -1, 4, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, + { L"ld", 3, 0x32, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, @@ -37,11 +45,13 @@ const tZ80Opcode Z80Opcodes[] = { { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"adc", 2, 0x4A, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM | Z80_GAMEBOY }, { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, + { L"sbc", 2, 0x42, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, @@ -88,5 +98,35 @@ const tZ80Opcode Z80Opcodes[] = { { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_GAMEBOY }, { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_RST }, + { L"ex", 1, 0x08, Z80_PARAM_AF, Z80_PARAM_AF_PRIME, -1, -1, Z80_Z80 }, + { L"ex", 1, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_HL, -1, -1, Z80_Z80 }, + { L"ex", 1, 0xEB, Z80_PARAM_DE, Z80_PARAM_HL, -1, -1, Z80_Z80 }, + { L"exx", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 }, + { L"djnz", 2, 0x10, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, + { L"out", 2, 0x41, Z80_PARAM_MEMC, Z80_PARAM_REG8, -1, 3, Z80_Z80 | Z80_PREFIX_ED }, + { L"out", 2, 0xD3, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_Z80 | Z80_IMMEDIATE_U8 }, + { L"in", 2, 0x40, Z80_PARAM_REG8, Z80_PARAM_MEMC, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"in", 2, 0xDB, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_Z80 | Z80_IMMEDIATE_U8 }, + { L"neg", 2, 0x44, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"retn", 2, 0x45, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"im", 2, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 3, -1, Z80_Z80 | Z80_PREFIX_ED | Z80_INTERRUPT_MODE }, + { L"rrd", 2, 0x67, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"rld", 2, 0x6F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ldi", 2, 0xA0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"cpi", 2, 0xA1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ini", 2, 0xA2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"outi", 2, 0xA3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ldd", 2, 0xA8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"cpd", 2, 0xA9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ind", 2, 0xAA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"outd", 2, 0xAB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ldir", 2, 0xB0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"cpir", 2, 0xB1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"inir", 2, 0xB2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"otir", 2, 0xB3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"lddr", 2, 0xB8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"cpdr", 2, 0xB9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"indr", 2, 0xBA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"otdr", 2, 0xBB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, { nullptr, 0, 0x00, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, }; diff --git a/Archs/Z80/Z80Opcodes.h b/Archs/Z80/Z80Opcodes.h index 6f93ce30..08bcec81 100644 --- a/Archs/Z80/Z80Opcodes.h +++ b/Archs/Z80/Z80Opcodes.h @@ -15,6 +15,14 @@ #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 +#define Z80_PARAM_AF 0x0F // af +#define Z80_PARAM_AF_PRIME 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_REG8_B 0x00 // b #define Z80_REG8_C 0x01 // c @@ -42,6 +50,20 @@ #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_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_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 @@ -57,9 +79,13 @@ #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_HAS_IMMEDIATE ( Z80_IMMEDIATE_U3 | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE_S8 \ - | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE ) + | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE \ + | Z80_INTERRUPT_MODE ) #define Z80_REG_BIT(reg) (1 << reg) @@ -68,8 +94,8 @@ struct tZ80Opcode const wchar_t* name; unsigned char length; unsigned char encoding; - unsigned char lhs : 4; - unsigned char rhs : 4; + unsigned char lhs; + unsigned char rhs; char lhsShift : 4; char rhsShift : 4; int flags; diff --git a/Archs/Z80/Z80Parser.cpp b/Archs/Z80/Z80Parser.cpp index cefd379a..e4b3d110 100644 --- a/Archs/Z80/Z80Parser.cpp +++ b/Archs/Z80/Z80Parser.cpp @@ -30,8 +30,14 @@ const Z80RegisterDescriptor Z80HLIncDec16[] = { }; const Z80RegisterDescriptor Z80Conds[] = { - { L"nz", Z80_COND_NZ }, { L"z", Z80_COND_Z }, - { L"nc", Z80_COND_NC }, { L"c", Z80_COND_C }, + { 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 = { }; @@ -76,9 +82,29 @@ bool Z80Parser::parseRegister16AF(Parser& parser, Z80RegisterValue& dest, int al 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::parseAFPrime(Parser& parser) +{ + const Token &token = parser.nextToken(); + CHECK(token.type == TokenType::Identifier); + CHECK(token.getStringValue() == L"af'"); + + return true; +} + bool Z80Parser::parseCondition(Parser& parser, Z80RegisterValue& dest) { - return parseRegisterTable(parser, dest, Z80Conds, std::size(Z80Conds), Z80_REG_BIT_ALL); + int allowed = Z80_COND_BIT_ALL; + if (Z80.GetVersion() == Z80ArchType::Gameboy) + { + allowed = Z80_COND_BIT_GB; + } + + return parseRegisterTable(parser, dest, Z80Conds, std::size(Z80Conds), allowed); } bool Z80Parser::parseHLIncDec(Parser& parser, Z80RegisterValue& dest) @@ -114,6 +140,15 @@ bool Z80Parser::parseHLIncDec(Parser& parser, Z80RegisterValue& dest) 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)); @@ -193,22 +228,38 @@ bool Z80Parser::parseOpcodeParameter(Parser& parser, unsigned char paramType, Z8 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_PRIME: + return parseAFPrime(parser); case Z80_PARAM_IMMEDIATE: destImm = parser.parseExpression(); return destImm.isLoaded(); @@ -255,7 +306,9 @@ std::unique_ptr Z80Parser::parseOpcode(Parser& parser) const std::wstring stringValue = token.getStringValue(); for (int z = 0; Z80Opcodes[z].name != nullptr; z++) { - if ((Z80Opcodes[z].flags & Z80_GAMEBOY) && Z80.GetVersion() != ZARCH_GAMEBOY) + if ((Z80Opcodes[z].flags & Z80_Z80) && Z80.GetVersion() != Z80ArchType::Z80) + continue; + if ((Z80Opcodes[z].flags & Z80_GAMEBOY) && Z80.GetVersion() != Z80ArchType::Gameboy) continue; if (stringValue == Z80Opcodes[z].name) @@ -274,9 +327,9 @@ std::unique_ptr Z80Parser::parseOpcode(Parser& parser) } if (paramFail) - parser.printError(token, L"Z80 parameter failure in %S", stringValue); + parser.printError(token, L"%s parameter failure in %S", Z80.GetName(), stringValue); else - parser.printError(token, L"Invalid Z80 opcode: %S", stringValue); + 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 index ed863170..7471f81e 100644 --- a/Archs/Z80/Z80Parser.h +++ b/Archs/Z80/Z80Parser.h @@ -17,8 +17,11 @@ class Z80Parser 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 parseAFPrime(Parser& parser); 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); 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 b772c541..5a0ced77 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -453,13 +453,13 @@ std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int fla switch (flags) { case DIRECTIVE_Z80_Z80: - Z80.SetVersion(ZARCH_Z80); + Z80.SetVersion(Z80ArchType::Z80); return std::make_unique(L".z80", L""); case DIRECTIVE_Z80_GB: - Z80.SetVersion(ZARCH_GAMEBOY); + Z80.SetVersion(Z80ArchType::Gameboy); return std::make_unique(L".gb", L""); case DIRECTIVE_Z80_GBC: - Z80.SetVersion(ZARCH_GAMEBOY); + Z80.SetVersion(Z80ArchType::Gameboy); return std::make_unique(L".gbc", L""); } 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; From c8b58fff66efb2afb5527b16222c0b87b60ed8d1 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 18:45:27 +0200 Subject: [PATCH 16/23] Add single-operand sub, and, xor, or & cp, which are the canonical forms; but keep sub a, b etc for convenience --- Archs/Z80/Z80Opcodes.cpp | 10 ++++ Tests/Z80/GB Opcodes/GB Opcodes.asm | 90 ++++++++++++++--------------- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 896ee498..3546021c 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -49,17 +49,27 @@ const tZ80Opcode Z80Opcodes[] = { { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM | Z80_GAMEBOY }, + { L"sub", 1, 0x90, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"sub", 2, 0xD6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sbc", 2, 0x42, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"and", 1, 0xA0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"and", 2, 0xE6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"xor", 2, 0xEE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"xor", 1, 0xA8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"xor", 2, 0xEE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"or", 1, 0xB0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"or", 2, 0xF6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"or", 1, 0xB0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"or", 2, 0xF6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"cp", 1, 0xB8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, { L"cp", 2, 0xFE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, + { L"cp", 1, 0xB8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"cp", 2, 0xFE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"inc", 1, 0x04, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, { L"inc", 1, 0x03, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, { L"dec", 1, 0x05, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, diff --git a/Tests/Z80/GB Opcodes/GB Opcodes.asm b/Tests/Z80/GB Opcodes/GB Opcodes.asm index e618ca46..b19f7082 100644 --- a/Tests/Z80/GB Opcodes/GB Opcodes.asm +++ b/Tests/Z80/GB Opcodes/GB Opcodes.asm @@ -146,14 +146,14 @@ adc a, l adc a, (hl) adc a, a - sub a, b - sub a, c - sub a, d - sub a, e - sub a, h - sub a, l - sub a, (hl) - sub 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 @@ -162,38 +162,38 @@ sbc a, l sbc a, (hl) sbc a, a - and a, b - and a, c - and a, d - and a, e - and a, h - and a, l - and a, (hl) - and a, a - xor a, b - xor a, c - xor a, d - xor a, e - xor a, h - xor a, l - xor a, (hl) - xor a, a - or a, b - or a, c - or a, d - or a, e - or a, h - or a, l - or a, (hl) - or a, a - cp a, b - cp a, c - cp a, d - cp a, e - cp a, h - cp a, l - cp a, (hl) - cp 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 @@ -214,7 +214,7 @@ jp nc, 0x1234 call nc, 0x1234 push de - sub a, 0x55 + sub 0x55 rst 2 ret c reti @@ -226,25 +226,25 @@ pop hl ld (0xFF00+c), a push hl - and a, 0x55 + and 0x55 rst 4 add sp, -0x56 jp hl ld (0x1234), a - xor a, 0x55 + xor 0x55 rst 5 ld a, (0xFF00+0x55) pop af ld a, (0xFF00+c) di push af - or a, 0x55 + or 0x55 rst 6 ldhl sp, 0x55 ld sp, hl ld a, (0x1234) ei - cp a, 0x55 + cp 0x55 rst 7 // CB 00-FF From 5068f0eabf5dfcbe96b5f7c654ed9e2fdebc983e Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 19:03:43 +0200 Subject: [PATCH 17/23] Add jp (hl), the canonical (but stupid) representation of jp hl. --- Archs/Z80/Z80Opcodes.cpp | 1 + Tests/Z80/GB Opcodes/GB Opcodes.asm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 3546021c..ed5eecbf 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -98,6 +98,7 @@ const tZ80Opcode Z80Opcodes[] = { { L"di", 1, 0xF3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"ei", 1, 0xFB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, + { L"jp", 1, 0xE9, Z80_PARAM_MEMHL, Z80_PARAM_NONE, -1, -1, 0 }, { L"jp", 1, 0xE9, Z80_PARAM_HL, Z80_PARAM_NONE, -1, -1, 0 }, { L"jp", 3, 0xC3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, { L"jr", 2, 0x20, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, diff --git a/Tests/Z80/GB Opcodes/GB Opcodes.asm b/Tests/Z80/GB Opcodes/GB Opcodes.asm index b19f7082..bbc41439 100644 --- a/Tests/Z80/GB Opcodes/GB Opcodes.asm +++ b/Tests/Z80/GB Opcodes/GB Opcodes.asm @@ -229,7 +229,7 @@ and 0x55 rst 4 add sp, -0x56 - jp hl + jp (hl) ld (0x1234), a xor 0x55 rst 5 From 5de352e8832494851065bec3a1731a3b1e0fd87c Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 22:17:25 +0200 Subject: [PATCH 18/23] Implement IX/IY instructions --- Archs/Z80/CZ80Instruction.cpp | 72 ++++++++++++++++++++------- Archs/Z80/CZ80Instruction.h | 2 + Archs/Z80/Z80Opcodes.cpp | 46 +++++++++++++++++- Archs/Z80/Z80Opcodes.h | 17 +++++-- Archs/Z80/Z80Parser.cpp | 92 +++++++++++++++++++++++++++++------ Archs/Z80/Z80Parser.h | 8 +-- 6 files changed, 198 insertions(+), 39 deletions(-) diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp index 866b36b5..2a765f7b 100644 --- a/Archs/Z80/CZ80Instruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -22,16 +22,6 @@ bool CZ80Instruction::Validate(const ValidateState& state) Vars.WriteImmediate8 = false; Vars.WriteImmediate16 = false; - // ld (hl),(hl) equivalent to halt - if (Opcode.flags & Z80_LOAD_REG8_REG8) - { - if (Vars.LeftParam.num == Z80_REG8_MEMHL && Vars.RightParam.num == Z80_REG8_MEMHL) - { - Logger::queueError(Logger::Error, L"ld (hl),(hl) not allowed"); - return false; - } - } - // Evaluate immediate if (Opcode.flags & Z80_HAS_IMMEDIATE) { @@ -40,6 +30,11 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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; @@ -51,6 +46,8 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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) @@ -61,7 +58,7 @@ bool CZ80Instruction::Validate(const ValidateState& state) else if (Opcode.flags & Z80_IMMEDIATE_U3) { min = 0; - max = 8; + max = 7; } else if (Opcode.flags & Z80_IMMEDIATE_U8) { @@ -81,6 +78,11 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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) @@ -135,6 +137,11 @@ bool CZ80Instruction::Validate(const ValidateState& state) } 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) { @@ -173,28 +180,48 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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++; } - if (Opcode.flags & Z80_PREFIX_ED) + else if (Opcode.flags & Z80_PREFIX_ED) { g_fileManager->writeU8(0xED); + prefixes++; } if (Opcode.lhs && Opcode.lhsShift >= 0) { - encoding |= Vars.LeftParam.num << Opcode.lhsShift; + encoding |= (Vars.LeftParam.num & 0x7F) << Opcode.lhsShift; } if (Opcode.rhs && Opcode.rhsShift >= 0) { - encoding |= Vars.RightParam.num << Opcode.rhsShift; + encoding |= (Vars.RightParam.num & 0x7F) << Opcode.rhsShift; } - g_fileManager->writeU8(encoding); - + // 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)); @@ -203,7 +230,18 @@ void CZ80Instruction::Encode() const { g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF)); } - else if (Opcode.flags & Z80_STOP) + 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); } diff --git a/Archs/Z80/CZ80Instruction.h b/Archs/Z80/CZ80Instruction.h index 4afa04d7..44600ff8 100644 --- a/Archs/Z80/CZ80Instruction.h +++ b/Archs/Z80/CZ80Instruction.h @@ -9,7 +9,9 @@ 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; diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index ed5eecbf..31e6da25 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -2,7 +2,7 @@ // Order: // - Everything else -// - SP_IMM +// - SP_IMM and MEMIX_MEMIY // - MEMIMMEDIATE // - IMMEDIATE const tZ80Opcode Z80Opcodes[] = { @@ -18,18 +18,25 @@ const tZ80Opcode Z80Opcodes[] = { { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, Z80_GAMEBOY }, { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, 0 }, { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, + { L"ld", 2, 0xF9, Z80_PARAM_REG16_SP, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, + { L"ld", 2, 0x47, Z80_PARAM_IR, Z80_PARAM_A, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ld", 3, 0x46, Z80_PARAM_REG8, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"ld", 3, 0x2A, Z80_PARAM_HL, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 4, 0x4B, Z80_PARAM_REG16_SP, Z80_PARAM_MEMIMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, { L"ld", 3, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, + { L"ld", 4, 0x2A, Z80_PARAM_IX_IY, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U8 }, { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 }, - { L"ld", 2, 0x47, Z80_PARAM_IR, Z80_PARAM_A, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, + { L"ld", 4, 0x21, Z80_PARAM_IX_IY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, + { L"ld", 3, 0x70, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_REG8, -1, 0, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, + { L"ld", 4, 0x36, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE2_U8 }, { L"ld", 3, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_HL, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 4, 0x43, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_REG16_SP, -1, 4, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, { L"ld", 3, 0x32, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, + { L"ld", 4, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_GAMEBOY }, { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, @@ -38,42 +45,63 @@ const tZ80Opcode Z80Opcodes[] = { { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_GAMEBOY }, { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, + { L"push", 2, 0xE5, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, + { L"pop", 2, 0xE1, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, { L"add", 1, 0x09, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, 0 }, { L"add", 1, 0x80, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"add", 3, 0x86, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"add", 2, 0xC6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, + { L"add", 2, 0x09, Z80_PARAM_IX_IY, Z80_PARAM_REG16_IX_IY, -1, 4, Z80_Z80 | Z80_PREFIX_IX_IY }, { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"adc", 3, 0x8E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"adc", 2, 0x4A, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sub", 3, 0x96, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM | Z80_GAMEBOY }, { L"sub", 1, 0x90, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"sub", 3, 0x96, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"sub", 2, 0xD6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"sbc", 3, 0x9E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, { L"sbc", 2, 0x42, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, + { L"sbc", 3, 0x9E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"and", 3, 0xA6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"and", 1, 0xA0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"and", 3, 0xA6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"and", 2, 0xE6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"xor", 3, 0xAE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"xor", 2, 0xEE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"xor", 1, 0xA8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"xor", 3, 0xAE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"xor", 2, 0xEE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"or", 1, 0xB0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"or", 3, 0xB6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"or", 2, 0xF6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"or", 1, 0xB0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"or", 3, 0xB6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"or", 2, 0xF6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"cp", 1, 0xB8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, + { L"cp", 3, 0xBE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"cp", 2, 0xFE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, { L"cp", 1, 0xB8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, + { L"cp", 3, 0xBE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"cp", 2, 0xFE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, { L"inc", 1, 0x04, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, { L"inc", 1, 0x03, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, + { L"inc", 2, 0x23, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, + { L"inc", 3, 0x34, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"dec", 1, 0x05, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, { L"dec", 1, 0x0B, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, + { L"dec", 2, 0x2B, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, + { L"dec", 3, 0x35, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, { L"daa", 1, 0x27, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"cpl", 1, 0x2F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"rlca", 1, 0x07, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, @@ -81,16 +109,26 @@ const tZ80Opcode Z80Opcodes[] = { { L"rrca", 1, 0x0F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"rra", 1, 0x1F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"rlc", 2, 0x00, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rlc", 4, 0x06, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"rrc", 2, 0x08, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rrc", 4, 0x0E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"rl", 2, 0x10, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rl", 4, 0x16, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"rr", 2, 0x18, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"rr", 4, 0x1E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"sla", 2, 0x20, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"sla", 4, 0x26, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"sra", 2, 0x28, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"sra", 4, 0x2E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB | Z80_GAMEBOY }, { L"srl", 2, 0x38, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, + { L"srl", 4, 0x3E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, { L"bit", 2, 0x40, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"bit", 4, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"res", 4, 0x86, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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_PREFIX_CB | Z80_IMMEDIATE_U3 }, + { L"set", 4, 0xC6, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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, 0 }, { L"scf", 1, 0x37, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"halt", 1, 0x76, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, @@ -100,6 +138,8 @@ const tZ80Opcode Z80Opcodes[] = { { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, { L"jp", 1, 0xE9, Z80_PARAM_MEMHL, Z80_PARAM_NONE, -1, -1, 0 }, { L"jp", 1, 0xE9, Z80_PARAM_HL, Z80_PARAM_NONE, -1, -1, 0 }, + { L"jp", 2, 0xE9, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, + { L"jp", 2, 0xE9, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, { L"jp", 3, 0xC3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, { L"jr", 2, 0x20, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, { L"jr", 2, 0x18, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, @@ -108,10 +148,12 @@ const tZ80Opcode Z80Opcodes[] = { { L"ret", 1, 0xC0, Z80_PARAM_CONDITION, Z80_PARAM_NONE, 3, -1, 0 }, { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_GAMEBOY }, + { L"reti", 2, 0x4D, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_RST }, { L"ex", 1, 0x08, Z80_PARAM_AF, Z80_PARAM_AF_PRIME, -1, -1, Z80_Z80 }, { L"ex", 1, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_HL, -1, -1, Z80_Z80 }, { L"ex", 1, 0xEB, Z80_PARAM_DE, Z80_PARAM_HL, -1, -1, Z80_Z80 }, + { L"ex", 2, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, { L"exx", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 }, { L"djnz", 2, 0x10, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, { L"out", 2, 0x41, Z80_PARAM_MEMC, Z80_PARAM_REG8, -1, 3, Z80_Z80 | Z80_PREFIX_ED }, diff --git a/Archs/Z80/Z80Opcodes.h b/Archs/Z80/Z80Opcodes.h index 08bcec81..3945b718 100644 --- a/Archs/Z80/Z80Opcodes.h +++ b/Archs/Z80/Z80Opcodes.h @@ -23,6 +23,9 @@ #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 @@ -42,9 +45,12 @@ #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_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 @@ -82,12 +88,17 @@ #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_HAS_IMMEDIATE ( Z80_IMMEDIATE_U3 | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE_S8 \ | Z80_IMMEDIATE_U16 | Z80_RST | Z80_JUMP_RELATIVE \ - | Z80_INTERRUPT_MODE ) + | 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) +#define Z80_REG_BIT(reg) (1 << (reg & 0xF)) struct tZ80Opcode { diff --git a/Archs/Z80/Z80Parser.cpp b/Archs/Z80/Z80Parser.cpp index e4b3d110..cb745097 100644 --- a/Archs/Z80/Z80Parser.cpp +++ b/Archs/Z80/Z80Parser.cpp @@ -25,6 +25,12 @@ const Z80RegisterDescriptor Z80Regs16AF[] = { // kinda hacky { 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 }, }; @@ -87,7 +93,7 @@ bool Z80Parser::parseRegisterIR(Parser& parser, Z80RegisterValue& dest) return parseRegisterTable(parser, dest, Z80RegsIR, std::size(Z80RegsIR), Z80_REG_IR_ALL); } -bool Z80Parser::parseAFPrime(Parser& parser) +bool Z80Parser::parseRegisterAFShadow(Parser& parser) { const Token &token = parser.nextToken(); CHECK(token.type == TokenType::Identifier); @@ -96,6 +102,11 @@ bool Z80Parser::parseAFPrime(Parser& parser) 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 = Z80_COND_BIT_ALL; @@ -168,7 +179,7 @@ bool Z80Parser::parseMemoryImmediate(Parser& parser, Expression& dest) return true; } -bool Z80Parser::parseFF00PlusC(Parser& parser) +bool Z80Parser::parseFF00PlusC(Parser& parser, Z80RegisterValue& destReg) { CHECK(parser.matchToken(TokenType::LParen)); @@ -182,33 +193,57 @@ bool Z80Parser::parseFF00PlusC(Parser& parser) CHECK(parser.matchToken(TokenType::Plus)); } - Z80RegisterValue tempReg; - CHECK(parseRegister8(parser, tempReg, Z80_REG_BIT(Z80_REG8_C))); + 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, Expression& dest, bool& isNegative) +bool Z80Parser::parseSPImmediate(Parser& parser, Z80RegisterValue& destReg, Expression& destImm, bool& isNegative) { isNegative = false; - Z80RegisterValue tempReg; - CHECK(parseRegister16SP(parser, tempReg, Z80_REG_BIT(Z80_REG16_SP))); + 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 - dest = createConstExpression(0); + destImm = createConstExpression(0); return true; } parser.eatToken(); isNegative = token.type == TokenType::Minus; - dest = parser.parseExpression(); - CHECK(dest.isLoaded()); + destImm = parser.parseExpression(); + CHECK(destImm.isLoaded()); return true; } @@ -259,16 +294,22 @@ bool Z80Parser::parseOpcodeParameter(Parser& parser, unsigned char paramType, Z8 case Z80_PARAM_IR: return parseRegisterIR(parser, destReg); case Z80_PARAM_AF_PRIME: - return parseAFPrime(parser); + 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); + return parseFF00PlusC(parser, destReg); case Z80_PARAM_SP_IMM: - return parseSPImmediate(parser, destImm, isNegative); + return parseSPImmediate(parser, destReg, destImm, isNegative); case Z80_PARAM_CONDITION: return parseCondition(parser, destReg); default: @@ -286,9 +327,32 @@ bool Z80Parser::parseOpcodeParameterList(Parser& parser, const tZ80Opcode opcode if (opcode.rhs) { CHECK(parser.matchToken(TokenType::Comma)); - CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); + + 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; + } + + // Mixing ix and iy not allowed + 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; } diff --git a/Archs/Z80/Z80Parser.h b/Archs/Z80/Z80Parser.h index 7471f81e..fd6e000f 100644 --- a/Archs/Z80/Z80Parser.h +++ b/Archs/Z80/Z80Parser.h @@ -18,14 +18,16 @@ class Z80Parser bool parseRegister16SP(Parser& parser, Z80RegisterValue& dest, int allowed); bool parseRegister16AF(Parser& parser, Z80RegisterValue& dest, int allowed); bool parseRegisterIR(Parser& parser, Z80RegisterValue& dest); - bool parseAFPrime(Parser& parser); + 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); - bool parseSPImmediate(Parser& parser, Expression& dest, bool& isNegative); + 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, const tZ80Opcode, Z80OpcodeVariables& vars); }; From 3187f19d73668ae6ffb5ee096cc1cfbb7873f1d5 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 22:17:47 +0200 Subject: [PATCH 19/23] Add test case for Z80 instructions --- Tests/Z80/Z80 Opcodes/Z80 Opcodes.asm | 714 ++++++++++++++++++++++++++ Tests/Z80/Z80 Opcodes/expected.bin | Bin 0 -> 1416 bytes 2 files changed, 714 insertions(+) create mode 100644 Tests/Z80/Z80 Opcodes/Z80 Opcodes.asm create mode 100644 Tests/Z80/Z80 Opcodes/expected.bin 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 0000000000000000000000000000000000000000..daa12d418b665c456af82a8e6c64fb44e48bed8e GIT binary patch literal 1416 zcmWO5XLych7{+m}y?LlzRkNz-cqnR**s*%WjvYHlh}me1w3OJfQ>(Gs*s){JKe4sO zZ3V4y2W_c!zj}T69mh33oS)8g@A%%k=`&=^B$=~h&6Yh!&Rmi^Pav=FdHM3cS3n9D z3KT9dLR#Y$57)4*rG_qp$V;a90DUselL zui<-N)vQ%p>eQ`Qzd^%BL5-UwS=_q(6LkJE?q;rb??!$SMNSy zef#wvFmTY|@F7EoMMMrC5jAqu=rLo*eG@(Y+wZ=gFmY1M4?j+x^3&95vD0VFoHcvS z+_-u37c5+~cuD-vOP4KQvGSLMRjb#mUH9v6iR(9P+_ZVi)}(FQckJA?dr$J-eftmi z-a+3xvY$kAiRPx#(R-#g`dDRTPE*>k>kUM~3F#Y^)0AD6HA-c`Bgd)MX0&0Dv9 zFZGV^-MuG&{&oL>?>&6vduj6c$3D~CNlykc zl8MY@AuHL)P7ZRCi`?WPFCO{G&wCW0AcZJQ5sFfb_bJW?l;A^3Qi{@)p)BS2i1K_) z1wNr7m8eWLs#Alns7WnqQ-`|LqdpC2NF#!1OcR>YjOMhUCBd|!HEn21JHAE;p*HyVTrVwcE8E((v2ATT z-9l`8+rf6Uoor{_LTxwO-S)6OZC~5Z_O}D<5F245^)t$jwqxvAotD~VcDY?)SKBpq ztzBm~*o}6R-E6np9d@VPWwM&WM$2flji!_-Z?uV~g7L*v5mQyxn(8J`F`;6@ z#Dt3(DrT6N2r-djhKm^?W~7*CF)?Cd#l(q;7n2|+QB0DUWHGl)s<~_Ko0l%DE9^AQ zX__nL$~#SS6`U`wia1Sk)m@OdU~wVhLdAuN3l}$3+%Rzw;v&Tj7dJxONO95PV#LLY zixU?wEQAch}u_FAb7O Date: Sun, 20 Sep 2020 23:30:07 +0200 Subject: [PATCH 20/23] Clean up Z80 opcode flags a bit, add GBA e-Reader architecture. --- Archs/Z80/CZ80Instruction.cpp | 10 + Archs/Z80/Z80.cpp | 1 + Archs/Z80/Z80.h | 4 +- Archs/Z80/Z80Opcodes.cpp | 350 +++++++++++++++++----------------- Archs/Z80/Z80Opcodes.h | 18 +- Archs/Z80/Z80Parser.cpp | 58 ++++-- Archs/Z80/Z80Parser.h | 5 +- Parser/DirectivesParser.cpp | 4 + Parser/DirectivesParser.h | 1 + 9 files changed, 255 insertions(+), 196 deletions(-) diff --git a/Archs/Z80/CZ80Instruction.cpp b/Archs/Z80/CZ80Instruction.cpp index 2a765f7b..4d521082 100644 --- a/Archs/Z80/CZ80Instruction.cpp +++ b/Archs/Z80/CZ80Instruction.cpp @@ -145,14 +145,24 @@ bool CZ80Instruction::Validate(const ValidateState& state) 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; diff --git a/Archs/Z80/Z80.cpp b/Archs/Z80/Z80.cpp index 7bef25d4..a2167435 100644 --- a/Archs/Z80/Z80.cpp +++ b/Archs/Z80/Z80.cpp @@ -7,6 +7,7 @@ CZ80Architecture Z80; CZ80Architecture::CZ80Architecture() { + this->Version = Z80ArchType::Invalid; } std::unique_ptr CZ80Architecture::parseDirective(Parser& parser) diff --git a/Archs/Z80/Z80.h b/Archs/Z80/Z80.h index 785f0793..43e5747c 100644 --- a/Archs/Z80/Z80.h +++ b/Archs/Z80/Z80.h @@ -4,7 +4,7 @@ #include "Core/ELF/ElfRelocator.h" #include "Core/Expression.h" -enum class Z80ArchType { Z80 = 0, Gameboy, Invalid }; +enum class Z80ArchType { Z80 = 0, Gameboy, Ereader, Invalid }; struct Z80RegisterValue { @@ -31,6 +31,8 @@ class CZ80Architecture: public CArchitecture { case Z80ArchType::Gameboy: return L"Gameboy"; + case Z80ArchType::Ereader: + return L"e-Reader"; default: return L"Z80"; } diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 31e6da25..51fda04d 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -7,179 +7,181 @@ // - IMMEDIATE 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, 0 }, - { L"ld", 1, 0x40, Z80_PARAM_REG8_MEMHL, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_LOAD_REG8_REG8 }, - { L"ld", 1, 0x02, Z80_PARAM_MEMBC_MEMDE, Z80_PARAM_A, 4, -1, 0 }, - { L"ld", 1, 0x0A, Z80_PARAM_A, Z80_PARAM_MEMBC_MEMDE, -1, 4, 0 }, - { L"ld", 1, 0x22, Z80_PARAM_HLI_HLD, Z80_PARAM_A, 4, -1, Z80_GAMEBOY }, - { L"ld", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_HLI_HLD, -1, 4, Z80_GAMEBOY }, - { L"ld", 1, 0xE2, Z80_PARAM_FF00_C, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, - { L"ld", 2, 0x57, Z80_PARAM_A, Z80_PARAM_IR, -1, 3, Z80_Z80 | Z80_PREFIX_ED }, - { L"ld", 1, 0xF2, Z80_PARAM_A, Z80_PARAM_FF00_C, -1, -1, Z80_GAMEBOY }, - { L"ld", 1, 0xF9, Z80_PARAM_SP, Z80_PARAM_HL, -1, -1, 0 }, - { L"ld", 2, 0xF8, Z80_PARAM_HL, Z80_PARAM_SP_IMM, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, - { L"ld", 2, 0xF9, Z80_PARAM_REG16_SP, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"ld", 2, 0x47, Z80_PARAM_IR, Z80_PARAM_A, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ld", 3, 0x46, Z80_PARAM_REG8, Z80_PARAM_MEMIX_MEMIY, 3, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"ld", 3, 0x2A, Z80_PARAM_HL, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, - { L"ld", 4, 0x4B, Z80_PARAM_REG16_SP, Z80_PARAM_MEMIMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, - { L"ld", 3, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, - { L"ld", 3, 0xFA, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, - { L"ld", 4, 0x2A, Z80_PARAM_IX_IY, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, - { L"ld", 2, 0x06, Z80_PARAM_REG8_MEMHL, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U8 }, - { L"ld", 3, 0x01, Z80_PARAM_REG16_SP, Z80_PARAM_IMMEDIATE, 4, -1, Z80_IMMEDIATE_U16 }, - { L"ld", 4, 0x21, Z80_PARAM_IX_IY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, - { L"ld", 3, 0x70, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_REG8, -1, 0, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"ld", 4, 0x36, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_IMMEDIATE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 | Z80_IMMEDIATE2_U8 }, - { L"ld", 3, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_HL, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, - { L"ld", 4, 0x43, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_REG16_SP, -1, 4, Z80_IMMEDIATE_U16 | Z80_Z80 | Z80_PREFIX_ED }, - { L"ld", 3, 0x32, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_Z80 }, - { L"ld", 3, 0x08, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_SP, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, - { L"ld", 3, 0xEA, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U16 | Z80_GAMEBOY }, - { L"ld", 4, 0x22, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U16 }, - { L"ldi", 1, 0x22, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, - { L"ldi", 1, 0x2A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_GAMEBOY }, - { L"ldd", 1, 0x32, Z80_PARAM_MEMHL, Z80_PARAM_A, -1, -1, Z80_GAMEBOY }, - { L"ldd", 1, 0x3A, Z80_PARAM_A, Z80_PARAM_MEMHL, -1, -1, Z80_GAMEBOY }, - { L"ldh", 2, 0xE0, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_IMMEDIATE_U8 | Z80_GAMEBOY }, - { L"ldh", 2, 0xF0, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_GAMEBOY }, - { L"ldhl", 2, 0xF8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, - { L"push", 1, 0xC5, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, - { L"push", 2, 0xE5, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"pop", 1, 0xC1, Z80_PARAM_REG16_AF, Z80_PARAM_NONE, 4, -1, 0 }, - { L"pop", 2, 0xE1, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"add", 1, 0x09, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, 0 }, - { L"add", 1, 0x80, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"add", 3, 0x86, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"add", 2, 0xC6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"add", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_GAMEBOY }, - { L"add", 2, 0x09, Z80_PARAM_IX_IY, Z80_PARAM_REG16_IX_IY, -1, 4, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"adc", 1, 0x88, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"adc", 3, 0x8E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"adc", 2, 0xCE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"adc", 2, 0x4A, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, - { L"sub", 1, 0x90, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sub", 3, 0x96, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"sub", 2, 0xD6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"sub", 2, 0xE8, Z80_PARAM_SP, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_S8 | Z80_NEGATE_IMM | Z80_GAMEBOY }, - { L"sub", 1, 0x90, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, - { L"sub", 3, 0x96, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"sub", 2, 0xD6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"sbc", 1, 0x98, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"sbc", 3, 0x9E, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"sbc", 2, 0xDE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 | Z80_ADD_SUB_IMMEDIATE }, - { L"sbc", 2, 0x42, Z80_PARAM_HL, Z80_PARAM_REG16_SP, -1, 4, Z80_Z80 | Z80_PREFIX_ED }, - { L"sbc", 3, 0x9E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"and", 1, 0xA0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"and", 3, 0xA6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"and", 2, 0xE6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"and", 1, 0xA0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, - { L"and", 3, 0xA6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"and", 2, 0xE6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"xor", 1, 0xA8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"xor", 3, 0xAE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"xor", 2, 0xEE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"xor", 1, 0xA8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, - { L"xor", 3, 0xAE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"xor", 2, 0xEE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"or", 1, 0xB0, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"or", 3, 0xB6, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"or", 2, 0xF6, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"or", 1, 0xB0, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, - { L"or", 3, 0xB6, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"or", 2, 0xF6, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"cp", 1, 0xB8, Z80_PARAM_A, Z80_PARAM_REG8_MEMHL, -1, 0, 0 }, - { L"cp", 3, 0xBE, Z80_PARAM_A, Z80_PARAM_MEMIX_MEMIY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"cp", 2, 0xFE, Z80_PARAM_A, Z80_PARAM_IMMEDIATE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"cp", 1, 0xB8, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, 0 }, - { L"cp", 3, 0xBE, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"cp", 2, 0xFE, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U8 }, - { L"inc", 1, 0x04, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, - { L"inc", 1, 0x03, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, - { L"inc", 2, 0x23, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"inc", 3, 0x34, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"dec", 1, 0x05, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 3, -1, 0 }, - { L"dec", 1, 0x0B, Z80_PARAM_REG16_SP, Z80_PARAM_NONE, 4, -1, 0 }, - { L"dec", 2, 0x2B, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"dec", 3, 0x35, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY | Z80_IMMEDIATE_U8 }, - { L"daa", 1, 0x27, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"cpl", 1, 0x2F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"rlca", 1, 0x07, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"rla", 1, 0x17, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"rrca", 1, 0x0F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"rra", 1, 0x1F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"rlc", 2, 0x00, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"rlc", 4, 0x06, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"rrc", 2, 0x08, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"rrc", 4, 0x0E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"rl", 2, 0x10, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"rl", 4, 0x16, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"rr", 2, 0x18, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"rr", 4, 0x1E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"sla", 2, 0x20, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"sla", 4, 0x26, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"sra", 2, 0x28, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"sra", 4, 0x2E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"swap", 2, 0x30, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB | Z80_GAMEBOY }, - { L"srl", 2, 0x38, Z80_PARAM_REG8_MEMHL, Z80_PARAM_NONE, 0, -1, Z80_PREFIX_CB }, - { L"srl", 4, 0x3E, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_PREFIX_CB | Z80_IMMEDIATE_U8 | Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"bit", 2, 0x40, Z80_PARAM_IMMEDIATE, Z80_PARAM_REG8_MEMHL, 3, 0, Z80_PREFIX_CB | Z80_IMMEDIATE_U3 }, - { L"bit", 4, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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_PREFIX_CB | Z80_IMMEDIATE_U3 }, - { L"res", 4, 0x86, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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_PREFIX_CB | Z80_IMMEDIATE_U3 }, - { L"set", 4, 0xC6, Z80_PARAM_IMMEDIATE, Z80_PARAM_MEMIX_MEMIY, 3, -1, 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, 0 }, - { L"scf", 1, 0x37, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"halt", 1, 0x76, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"stop", 2, 0x10, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_STOP | Z80_GAMEBOY }, - { L"di", 1, 0xF3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"ei", 1, 0xFB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"jp", 3, 0xC2, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, - { L"jp", 1, 0xE9, Z80_PARAM_MEMHL, Z80_PARAM_NONE, -1, -1, 0 }, - { L"jp", 1, 0xE9, Z80_PARAM_HL, Z80_PARAM_NONE, -1, -1, 0 }, - { L"jp", 2, 0xE9, Z80_PARAM_MEMIX_MEMIY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"jp", 2, 0xE9, Z80_PARAM_IX_IY, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"jp", 3, 0xC3, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, - { L"jr", 2, 0x20, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, - { L"jr", 2, 0x18, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, - { L"call", 3, 0xC4, Z80_PARAM_CONDITION, Z80_PARAM_IMMEDIATE, 3, -1, Z80_IMMEDIATE_U16 }, - { L"call", 3, 0xCD, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_IMMEDIATE_U16 }, - { L"ret", 1, 0xC0, Z80_PARAM_CONDITION, Z80_PARAM_NONE, 3, -1, 0 }, - { L"ret", 1, 0xC9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, 0 }, - { L"reti", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_GAMEBOY }, - { L"reti", 2, 0x4D, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"rst", 1, 0xC7, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 0, -1, Z80_RST }, - { L"ex", 1, 0x08, Z80_PARAM_AF, Z80_PARAM_AF_PRIME, -1, -1, Z80_Z80 }, - { L"ex", 1, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_HL, -1, -1, Z80_Z80 }, - { L"ex", 1, 0xEB, Z80_PARAM_DE, Z80_PARAM_HL, -1, -1, Z80_Z80 }, - { L"ex", 2, 0xE3, Z80_PARAM_MEMSP, Z80_PARAM_IX_IY, -1, -1, Z80_Z80 | Z80_PREFIX_IX_IY }, - { L"exx", 1, 0xD9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 }, - { L"djnz", 2, 0x10, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_IMMEDIATE_S8 | Z80_JUMP_RELATIVE }, - { L"out", 2, 0x41, Z80_PARAM_MEMC, Z80_PARAM_REG8, -1, 3, Z80_Z80 | Z80_PREFIX_ED }, - { L"out", 2, 0xD3, Z80_PARAM_MEMIMMEDIATE, Z80_PARAM_A, -1, -1, Z80_Z80 | Z80_IMMEDIATE_U8 }, - { L"in", 2, 0x40, Z80_PARAM_REG8, Z80_PARAM_MEMC, 3, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"in", 2, 0xDB, Z80_PARAM_A, Z80_PARAM_MEMIMMEDIATE, -1, -1, Z80_Z80 | Z80_IMMEDIATE_U8 }, - { L"neg", 2, 0x44, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"retn", 2, 0x45, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"im", 2, 0x46, Z80_PARAM_IMMEDIATE, Z80_PARAM_NONE, 3, -1, Z80_Z80 | Z80_PREFIX_ED | Z80_INTERRUPT_MODE }, - { L"rrd", 2, 0x67, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"rld", 2, 0x6F, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ldi", 2, 0xA0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"cpi", 2, 0xA1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ini", 2, 0xA2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"outi", 2, 0xA3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ldd", 2, 0xA8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"cpd", 2, 0xA9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ind", 2, 0xAA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"outd", 2, 0xAB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"ldir", 2, 0xB0, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"cpir", 2, 0xB1, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"inir", 2, 0xB2, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"otir", 2, 0xB3, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"lddr", 2, 0xB8, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"cpdr", 2, 0xB9, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"indr", 2, 0xBA, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, - { L"otdr", 2, 0xBB, Z80_PARAM_NONE, Z80_PARAM_NONE, -1, -1, Z80_Z80 | Z80_PREFIX_ED }, + { 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", 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 index 3945b718..6af56dea 100644 --- a/Archs/Z80/Z80Opcodes.h +++ b/Archs/Z80/Z80Opcodes.h @@ -14,9 +14,9 @@ #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 +#define Z80_PARAM_CONDITION 0x0E // nz, z, nc, c, po, pe, p, m #define Z80_PARAM_AF 0x0F // af -#define Z80_PARAM_AF_PRIME 0x10 // 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 @@ -64,8 +64,15 @@ | 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 @@ -90,6 +97,13 @@ #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 \ diff --git a/Archs/Z80/Z80Parser.cpp b/Archs/Z80/Z80Parser.cpp index cb745097..0289c0ec 100644 --- a/Archs/Z80/Z80Parser.cpp +++ b/Archs/Z80/Z80Parser.cpp @@ -48,6 +48,21 @@ const Z80RegisterDescriptor Z80RegsIR[] = { 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); @@ -109,11 +124,18 @@ bool Z80Parser::parseRegisterIXIY(Parser& parser, Z80RegisterValue& dest, int al bool Z80Parser::parseCondition(Parser& parser, Z80RegisterValue& dest) { - int allowed = Z80_COND_BIT_ALL; + 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); } @@ -293,7 +315,7 @@ bool Z80Parser::parseOpcodeParameter(Parser& parser, unsigned char paramType, Z8 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_PRIME: + 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)); @@ -317,37 +339,37 @@ bool Z80Parser::parseOpcodeParameter(Parser& parser, unsigned char paramType, Z8 } } -bool Z80Parser::parseOpcodeParameterList(Parser& parser, const tZ80Opcode opcode, Z80OpcodeVariables& vars) +bool Z80Parser::parseOpcodeParameterList(Parser& parser, Z80OpcodeVariables& vars) { bool isNegative = false; - if (opcode.lhs) + if (Opcode.lhs) { - CHECK(parseOpcodeParameter(parser, opcode.lhs, vars.LeftParam, vars.ImmediateExpression, isNegative)); + CHECK(parseOpcodeParameter(parser, Opcode.lhs, vars.LeftParam, vars.ImmediateExpression, isNegative)); } - if (opcode.rhs) + if (Opcode.rhs) { CHECK(parser.matchToken(TokenType::Comma)); - if (opcode.flags & Z80_HAS_2_IMMEDIATES) + if (Opcode.flags & Z80_HAS_2_IMMEDIATES) { - CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression2, isNegative)); + CHECK(parseOpcodeParameter(parser, Opcode.rhs, vars.RightParam, vars.ImmediateExpression2, isNegative)); } else { - CHECK(parseOpcodeParameter(parser, opcode.rhs, vars.RightParam, vars.ImmediateExpression, isNegative)); + 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) && + if ((Opcode.flags & Z80_LOAD_REG8_REG8) && vars.LeftParam.num == Z80_REG8_MEMHL && vars.RightParam.num == Z80_REG8_MEMHL) { return false; } - // Mixing ix and iy not allowed - if (Z80_IS_PARAM_IX_IY(opcode.lhs) && Z80_IS_PARAM_IX_IY(opcode.rhs)) + // 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) @@ -370,19 +392,19 @@ std::unique_ptr Z80Parser::parseOpcode(Parser& parser) const std::wstring stringValue = token.getStringValue(); for (int z = 0; Z80Opcodes[z].name != nullptr; z++) { - if ((Z80Opcodes[z].flags & Z80_Z80) && Z80.GetVersion() != Z80ArchType::Z80) - continue; - if ((Z80Opcodes[z].flags & Z80_GAMEBOY) && Z80.GetVersion() != Z80ArchType::Gameboy) + Opcode = Z80Opcodes[z]; + + if (!isArchCompatible(Opcode)) continue; - if (stringValue == Z80Opcodes[z].name) + if (stringValue == Opcode.name) { TokenizerPosition tokenPos = parser.getTokenizer()->getPosition(); - if (parseOpcodeParameterList(parser, Z80Opcodes[z], vars)) + if (parseOpcodeParameterList(parser, vars)) { // success, return opcode - return std::make_unique(Z80Opcodes[z], vars); + return std::make_unique(Opcode, vars); } parser.getTokenizer()->setPosition(tokenPos); diff --git a/Archs/Z80/Z80Parser.h b/Archs/Z80/Z80Parser.h index fd6e000f..c30c8a3d 100644 --- a/Archs/Z80/Z80Parser.h +++ b/Archs/Z80/Z80Parser.h @@ -13,6 +13,7 @@ class Z80Parser 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); @@ -29,5 +30,7 @@ class Z80Parser 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, const tZ80Opcode, Z80OpcodeVariables& vars); + bool parseOpcodeParameterList(Parser& parser, Z80OpcodeVariables& vars); + + tZ80Opcode Opcode; }; diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index 5a0ced77..7e44a375 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -461,6 +461,9 @@ std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int fla case DIRECTIVE_Z80_GBC: Z80.SetVersion(Z80ArchType::Gameboy); return std::make_unique(L".gbc", L""); + case DIRECTIVE_Z80_EREADER: + Z80.SetVersion(Z80ArchType::Ereader); + return std::make_unique(L".ereader", L""); } return nullptr; @@ -800,6 +803,7 @@ const DirectiveMap directives = { { L".z80", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_Z80 } }, { L".gb", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GB } }, { L".gbc", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GBC } }, + { L".ereader", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_EREADER } }, { L".area", { &parseDirectiveArea, 0 } }, { L".autoregion", { &parseDirectiveAutoRegion, 0 } }, diff --git a/Parser/DirectivesParser.h b/Parser/DirectivesParser.h index 55fc9280..514e137e 100644 --- a/Parser/DirectivesParser.h +++ b/Parser/DirectivesParser.h @@ -72,6 +72,7 @@ using DirectiveMap = std::unordered_multimap #define DIRECTIVE_Z80_Z80 0x00000001 #define DIRECTIVE_Z80_GB 0x00000002 #define DIRECTIVE_Z80_GBC 0x00000003 +#define DIRECTIVE_Z80_EREADER 0x00000004 // Area directive flags #define DIRECTIVE_AREA_SHARED 0x00000001 From 2654565984556d9250c6ab9f6b6e8535909407d3 Mon Sep 17 00:00:00 2001 From: "Prof. 9" Date: Sun, 20 Sep 2020 23:30:28 +0200 Subject: [PATCH 21/23] Add tests for e-Reader Z80. --- Tests/Z80/ER Opcodes/ER Opcodes.asm | 238 ++++++++++++++++++++++++++++ Tests/Z80/ER Opcodes/expected.bin | Bin 0 -> 295 bytes 2 files changed, 238 insertions(+) create mode 100644 Tests/Z80/ER Opcodes/ER Opcodes.asm create mode 100644 Tests/Z80/ER Opcodes/expected.bin diff --git a/Tests/Z80/ER Opcodes/ER Opcodes.asm b/Tests/Z80/ER Opcodes/ER Opcodes.asm new file mode 100644 index 00000000..0f8de091 --- /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 + ret z + ret + jp z, 0x1234 + call z, 0x1234 + call 0x1234 + adc a, 0x55 + rst 0x8 + 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 0000000000000000000000000000000000000000..6f68e4cfdb77e9e11ecb7140cf5f4dc5e0bf31d3 GIT binary patch literal 295 zcmV+?0oeWk0W=Z<0|W&IRR;+Q3k(eoRSyvU5i}AK6BHE|RTmik85$cL9UfI5ApRjV z5+XDbBP1mzRVe-`Dl`%+EG;foFEIWwG!im25;HV4HdQw`{y91{5<5ISK2<+JK|(`B zMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>cVPa!sWoBn+X=-b1ZEkOHadLBXb#`}n zd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^?m6n&7nVOrNot~edp`xRtrKYE-sj922 zt*)=Iv9hzYwYImoxw^Z&y}rM|!NN2W!!#1aG!n(eRmaH5$}|$pG!o4;63$i6(9zN~ t64O=GG!oU;RoL1z65KQr-c{k_<>poC>+V(X@$xhh_4ZZx`T8^x{#9L Date: Sun, 20 Sep 2020 23:48:08 +0200 Subject: [PATCH 22/23] Add a shorthand rst n,n opcode for e-Reader API calls. --- Archs/Z80/Z80Opcodes.cpp | 2 ++ Tests/Z80/ER Opcodes/ER Opcodes.asm | 4 ++-- Tests/Z80/ER Opcodes/expected.bin | Bin 295 -> 297 bytes 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Archs/Z80/Z80Opcodes.cpp b/Archs/Z80/Z80Opcodes.cpp index 51fda04d..bab7062e 100644 --- a/Archs/Z80/Z80Opcodes.cpp +++ b/Archs/Z80/Z80Opcodes.cpp @@ -5,6 +5,7 @@ // - 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) }, @@ -149,6 +150,7 @@ const tZ80Opcode Z80Opcodes[] = { { 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) }, diff --git a/Tests/Z80/ER Opcodes/ER Opcodes.asm b/Tests/Z80/ER Opcodes/ER Opcodes.asm index 0f8de091..505d75f8 100644 --- a/Tests/Z80/ER Opcodes/ER Opcodes.asm +++ b/Tests/Z80/ER Opcodes/ER Opcodes.asm @@ -199,14 +199,14 @@ call nz, 0x1234 push bc add a, 0x55 - rst 0x0 + rst 0x0, 0x55 ret z ret jp z, 0x1234 call z, 0x1234 call 0x1234 adc a, 0x55 - rst 0x8 + rst 0x8, 0x55 ret nc pop de jp nc, 0x1234 diff --git a/Tests/Z80/ER Opcodes/expected.bin b/Tests/Z80/ER Opcodes/expected.bin index 6f68e4cfdb77e9e11ecb7140cf5f4dc5e0bf31d3..10489ffd5d2dddee5ef189dd061c6286ab912abe 100644 GIT binary patch delta 26 icmZ3^w32DUYk|-cCr_COoiPzQYa(VpLzhDSqu6A delta 24 gcmZ3 Date: Sun, 20 Sep 2020 23:52:28 +0200 Subject: [PATCH 23/23] Add Z80 stuff to readme; remove redundant .gbc directive --- Archs/Z80/Z80.h | 2 +- Parser/DirectivesParser.cpp | 4 ---- Parser/DirectivesParser.h | 3 +-- Readme.md | 12 +++++++++++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Archs/Z80/Z80.h b/Archs/Z80/Z80.h index 43e5747c..225dab34 100644 --- a/Archs/Z80/Z80.h +++ b/Archs/Z80/Z80.h @@ -9,7 +9,7 @@ enum class Z80ArchType { Z80 = 0, Gameboy, Ereader, Invalid }; struct Z80RegisterValue { std::wstring name; - int num; + int64_t num; }; class CZ80Architecture: public CArchitecture diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index 7e44a375..48aeeea5 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -458,9 +458,6 @@ std::unique_ptr parseDirectiveZ80Arch(Parser& parser, int fla case DIRECTIVE_Z80_GB: Z80.SetVersion(Z80ArchType::Gameboy); return std::make_unique(L".gb", L""); - case DIRECTIVE_Z80_GBC: - Z80.SetVersion(Z80ArchType::Gameboy); - return std::make_unique(L".gbc", L""); case DIRECTIVE_Z80_EREADER: Z80.SetVersion(Z80ArchType::Ereader); return std::make_unique(L".ereader", L""); @@ -802,7 +799,6 @@ const DirectiveMap directives = { { L".z80", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_Z80 } }, { L".gb", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GB } }, - { L".gbc", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_GBC } }, { L".ereader", { &parseDirectiveZ80Arch, DIRECTIVE_Z80_EREADER } }, { L".area", { &parseDirectiveArea, 0 } }, diff --git a/Parser/DirectivesParser.h b/Parser/DirectivesParser.h index 514e137e..76f22f52 100644 --- a/Parser/DirectivesParser.h +++ b/Parser/DirectivesParser.h @@ -71,8 +71,7 @@ using DirectiveMap = std::unordered_multimap // Z80 directive flags #define DIRECTIVE_Z80_Z80 0x00000001 #define DIRECTIVE_Z80_GB 0x00000002 -#define DIRECTIVE_Z80_GBC 0x00000003 -#define DIRECTIVE_Z80_EREADER 0x00000004 +#define DIRECTIVE_Z80_EREADER 0x00000003 // Area directive flags #define DIRECTIVE_AREA_SHARED 0x00000001 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`