Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ba54919
Start Gameboy architecture, implement basic loads.
Prof9 Sep 19, 2020
315bf72
Add the funky load types
Prof9 Sep 19, 2020
dfae289
push, pop
Prof9 Sep 20, 2020
170da32
add, adc, sub, sbc, inc, dec
Prof9 Sep 20, 2020
30a3a44
Remove conversion of add a,1 -> inc a and sub a,1 -> dec a because th…
Prof9 Sep 20, 2020
4789036
and, xor, or, cp, rlc, rrc, rl, rr, sla, sra, swap, srl, bit, res, set
Prof9 Sep 20, 2020
e4775fb
jp, jr, call, ret, reti
Prof9 Sep 20, 2020
e848bab
rst
Prof9 Sep 20, 2020
e1857cb
"Implement" writeTempData
Prof9 Sep 20, 2020
c475a62
Support ld (c),a as alias for ld (0xFF00+c),a; support ldh and ldhl a…
Prof9 Sep 20, 2020
263d5ee
Rename Gameboy to Z80
Prof9 Sep 20, 2020
9d95df9
Support some more RST and LDH syntaxes
Prof9 Sep 20, 2020
e5ad7d7
Check for Gameboy-specific opcodes, add .z80 directive
Prof9 Sep 20, 2020
4912348
Add test cases for Gameboy
Prof9 Sep 20, 2020
71f39ec
Support Z80-specific main opcodes and ED-prefixed opcodes.
Prof9 Sep 20, 2020
c8b58ff
Add single-operand sub, and, xor, or & cp, which are the canonical fo…
Prof9 Sep 20, 2020
5068f0e
Add jp (hl), the canonical (but stupid) representation of jp hl.
Prof9 Sep 20, 2020
5de352e
Implement IX/IY instructions
Prof9 Sep 20, 2020
3187f19
Add test case for Z80 instructions
Prof9 Sep 20, 2020
2f94295
Clean up Z80 opcode flags a bit, add GBA e-Reader architecture.
Prof9 Sep 20, 2020
2654565
Add tests for e-Reader Z80.
Prof9 Sep 20, 2020
6f877d4
Add a shorthand rst n,n opcode for e-Reader API calls.
Prof9 Sep 20, 2020
9f5977a
Add Z80 stuff to readme; remove redundant .gbc directive
Prof9 Sep 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 269 additions & 0 deletions Archs/Z80/CZ80Instruction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
#include "Archs/Z80/CZ80Instruction.h"
#include "Archs/Z80/Z80Opcodes.h"
#include "Core/Common.h"
#include "Core/Expression.h"
#include "Core/FileManager.h"
#include "Core/Misc.h"
#include "Util/Util.h"

CZ80Instruction::CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars)
{
this->Opcode = sourceOpcode;
this->Vars = vars;
this->RamPos = 0;
}

bool CZ80Instruction::Validate(const ValidateState& state)
{
RamPos = g_fileManager->getVirtualAddress();

Vars.Length = Opcode.length;
Vars.Encoding = Opcode.encoding;
Vars.WriteImmediate8 = false;
Vars.WriteImmediate16 = false;

// Evaluate immediate
if (Opcode.flags & Z80_HAS_IMMEDIATE)
{
if (!Vars.ImmediateExpression.evaluateInteger(Vars.Immediate))
{
Logger::queueError(Logger::Error, L"Invalid expression");
return false;
}
if ((Opcode.flags & Z80_HAS_2_IMMEDIATES) && !Vars.ImmediateExpression2.evaluateInteger(Vars.Immediate2))
{
Logger::queueError(Logger::Error, L"Invalid expression");
return false;
}
if (Vars.IsNegative)
{
Vars.Immediate = -Vars.Immediate;
}
if (Opcode.flags & Z80_JUMP_RELATIVE)
{
Vars.Immediate = (Vars.Immediate - RamPos - 2);
}

int64_t min = INT64_MIN;
int64_t max = INT64_MAX;
int64_t min2 = INT64_MIN;
int64_t max2 = INT64_MAX;
Vars.WriteImmediate8 = false;
Vars.WriteImmediate16 = false;
if (Opcode.flags & Z80_INTERRUPT_MODE)
{
min = 0;
max = 2;
}
else if (Opcode.flags & Z80_IMMEDIATE_U3)
{
min = 0;
max = 7;
}
else if (Opcode.flags & Z80_IMMEDIATE_U8)
{
min = 0;
max = 255;
Vars.WriteImmediate8 = true;
}
else if (Opcode.flags & Z80_IMMEDIATE_S8)
{
min = -128;
max = 127;
Vars.WriteImmediate8 = true;
}
else if (Opcode.flags & Z80_IMMEDIATE_U16)
{
min = 0;
max = 65535;
Vars.WriteImmediate16 = true;
}
if (Opcode.flags & Z80_IMMEDIATE2_U8)
{
min2 = 0;
max2 = 255;
}

// add <-> sub
if ((Opcode.flags & Z80_ADD_SUB_IMMEDIATE) && Vars.Immediate < 0)
{
// Change opcode
Vars.Encoding ^= 0x10;
Vars.Immediate = -Vars.Immediate;
}
if (Opcode.flags & Z80_NEGATE_IMM)
{
Vars.Immediate = -Vars.Immediate;
}

if (Z80.GetVersion() == Z80ArchType::Gameboy)
{
// Special loads in range 0xFF00 - 0xFFFF
if (Vars.RightParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00 && !(Opcode.flags & Z80_IMMEDIATE_U3))
{
// ld (0xFF00+u8),a can be encoded as E0 XX instead
Vars.Encoding = 0xE0;
Vars.Length = 2;
Vars.Immediate &= 0xFF;
Vars.RightParam.num = 0;
Vars.WriteImmediate8 = true;
Vars.WriteImmediate16 = false;
}
else if (Vars.LeftParam.num == Z80_REG8_A && Vars.Immediate >= 0xFF00)
{
// ld a,(0xFF00+u8) can be encoded as F0 XX instead
Vars.Encoding = 0xF0;
Vars.Length = 2;
Vars.Immediate &= 0xFF;
Vars.LeftParam.num = 0;
Vars.WriteImmediate8 = true;
Vars.WriteImmediate16 = false;
}
}

if (Vars.Immediate < min || Vars.Immediate > max)
{
if (Opcode.flags & Z80_JUMP_RELATIVE)
{
Logger::queueError(Logger::Error, L"Jump target %04X out of range", Vars.Immediate);
}
else if (Opcode.flags & Z80_INTERRUPT_MODE)
{
Logger::queueError(Logger::Error, L"Interrupt mode %i out of range", Vars.Immediate);
}
else
{
Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate);
}
return false;
}
if ((Opcode.flags & Z80_HAS_2_IMMEDIATES) && (Vars.Immediate2 < min2 || Vars.Immediate2 > max2))
{
Logger::queueError(Logger::Error, L"Immediate %i out of range", Vars.Immediate2);
return false;
}

if (Opcode.flags & Z80_RST)
{
bool invalidRST = false;
if (Z80.GetVersion() == Z80ArchType::Gameboy && Vars.Immediate >= 0x00 && Vars.Immediate <= 0x7)
{
// Nintendo syntax
// 1 -> 8, 2 -> 16, 3 -> 24, etc
Vars.Immediate <<= 3;
}
else if (Z80.GetVersion() == Z80ArchType::Ereader && Vars.Immediate != 0x00 && Vars.Immediate != 0x08)
{
invalidRST = true;
}
else if (Vars.Immediate != 0x00 && Vars.Immediate != 0x08 && Vars.Immediate != 0x10 && Vars.Immediate != 0x18 &&
Vars.Immediate != 0x20 && Vars.Immediate != 0x28 && Vars.Immediate != 0x30 && Vars.Immediate != 0x38)
{
invalidRST = true;
}

if (invalidRST)
{
Logger::queueError(Logger::Error, L"Invalid RST target %i", Vars.Immediate);
return false;
}
}

// Move immediate to lhs
if (Opcode.flags & (Z80_IMMEDIATE_U3 | Z80_RST))
{
Vars.LeftParam.name = L"imm";
Vars.LeftParam.num = Vars.Immediate;
}
else if (Opcode.flags & Z80_INTERRUPT_MODE)
{
Vars.LeftParam.name = L"imm";
Vars.LeftParam.num = Vars.Immediate + (Vars.Immediate != 0 ? 1 : 0);
}
}

g_fileManager->advanceMemory(Vars.Length);

return false;
}

void CZ80Instruction::Encode() const
{
unsigned char encoding = Vars.Encoding;
int prefixes = 0;

if (Z80_IS_PARAM_IX_IY(Opcode.lhs) && Vars.LeftParam.num == Z80_REG16_IX ||
Z80_IS_PARAM_IX_IY(Opcode.rhs) && Vars.RightParam.num == Z80_REG16_IX)
{
g_fileManager->writeU8(0xDD);
prefixes++;
}
else if (Z80_IS_PARAM_IX_IY(Opcode.lhs) && Vars.LeftParam.num == Z80_REG16_IY ||
Z80_IS_PARAM_IX_IY(Opcode.rhs) && Vars.RightParam.num == Z80_REG16_IY)
{
g_fileManager->writeU8(0xFD);
prefixes++;
}

if (Opcode.flags & Z80_PREFIX_CB)
{
g_fileManager->writeU8(0xCB);
prefixes++;
}
else if (Opcode.flags & Z80_PREFIX_ED)
{
g_fileManager->writeU8(0xED);
prefixes++;
}

if (Opcode.lhs && Opcode.lhsShift >= 0)
{
encoding |= (Vars.LeftParam.num & 0x7F) << Opcode.lhsShift;
}
if (Opcode.rhs && Opcode.rhsShift >= 0)
{
encoding |= (Vars.RightParam.num & 0x7F) << Opcode.rhsShift;
}

// If there are 2 prefixes, opcode follows immediate
if (prefixes < 2)
{
g_fileManager->writeU8(encoding);
}

// Write immediates
if (Vars.WriteImmediate16)
{
g_fileManager->writeU16((uint16_t)(Vars.Immediate & 0xFFFF));
}
else if (Vars.WriteImmediate8)
{
g_fileManager->writeU8((uint8_t)(Vars.Immediate & 0xFF));
}
if (Opcode.flags & Z80_IMMEDIATE2_U8)
{
g_fileManager->writeU8((uint8_t)(Vars.Immediate2 & 0xFF));
}

// If there are 2 prefixes, opcode follows immediate
if (prefixes >= 2)
{
g_fileManager->writeU8(encoding);
}

if (Opcode.flags & Z80_STOP)
{
g_fileManager->writeU8(0x00);
}
}

void CZ80Instruction::writeTempData(TempData& tempData) const
{
char str[256];

int pos = sprintf(str, " %S", Opcode.name);
while (pos < 11) str[pos++] = ' ';
str[pos] = 0;

tempData.writeLine(RamPos, convertUtf8ToWString(str));
}
33 changes: 33 additions & 0 deletions Archs/Z80/CZ80Instruction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "Archs/Z80/Z80.h"
#include "Archs/Z80/Z80Opcodes.h"
#include "Commands/CAssemblerCommand.h"

struct Z80OpcodeVariables
{
Z80RegisterValue LeftParam;
Z80RegisterValue RightParam;
Expression ImmediateExpression;
Expression ImmediateExpression2;
int64_t Immediate;
int64_t Immediate2;
unsigned char Length;
unsigned char Encoding;
bool IsNegative : 1;
bool WriteImmediate8 : 1;
bool WriteImmediate16 : 1;
};

class CZ80Instruction: public CAssemblerCommand
{
public:
CZ80Instruction(const tZ80Opcode& sourceOpcode, Z80OpcodeVariables& vars);
bool Validate(const ValidateState& state) override;
void Encode() const override;
void writeTempData(TempData& tempData) const override;
private:
Z80OpcodeVariables Vars;
tZ80Opcode Opcode;
int64_t RamPos;
};
37 changes: 37 additions & 0 deletions Archs/Z80/Z80.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "Archs/Z80/CZ80Instruction.h"
#include "Archs/Z80/Z80.h"
#include "Archs/Z80/Z80Parser.h"
#include "Parser/Parser.h"

CZ80Architecture Z80;

CZ80Architecture::CZ80Architecture()
{
this->Version = Z80ArchType::Invalid;
}

std::unique_ptr<CAssemblerCommand> CZ80Architecture::parseDirective(Parser& parser)
{
Z80Parser Z80Parser;

return Z80Parser.parseDirective(parser);
}

std::unique_ptr<CAssemblerCommand> CZ80Architecture::parseOpcode(Parser& parser)
{
Z80Parser Z80Parser;

return Z80Parser.parseOpcode(parser);
}

void CZ80Architecture::NextSection()
{
}

void CZ80Architecture::Pass2()
{
}

void CZ80Architecture::Revalidate()
{
}
44 changes: 44 additions & 0 deletions Archs/Z80/Z80.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include "Archs/Architecture.h"
#include "Core/ELF/ElfRelocator.h"
#include "Core/Expression.h"

enum class Z80ArchType { Z80 = 0, Gameboy, Ereader, Invalid };

struct Z80RegisterValue
{
std::wstring name;
int64_t num;
};

class CZ80Architecture: public CArchitecture
{
public:
CZ80Architecture();

virtual std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser);
virtual std::unique_ptr<CAssemblerCommand> parseOpcode(Parser& parser);
virtual void NextSection();
virtual void Pass2();
virtual void Revalidate();
virtual std::unique_ptr<IElfRelocator> getElfRelocator() { return 0; };
virtual Endianness getEndianness() { return Endianness::Little; };
void SetVersion(Z80ArchType v) { Version = v; };
Z80ArchType GetVersion() { return Version; };
const std::wstring GetName() {
switch (Version)
{
case Z80ArchType::Gameboy:
return L"Gameboy";
case Z80ArchType::Ereader:
return L"e-Reader";
default:
return L"Z80";
}
}
private:
Z80ArchType Version;
};

extern CZ80Architecture Z80;
Loading