2016-07-07 00:35:40 +00:00
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <regex>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <cctype>
|
|
|
|
|
|
|
|
|
|
struct ASMLine
|
|
|
|
|
{
|
|
|
|
|
enum class Type
|
|
|
|
|
{
|
|
|
|
|
Label,
|
|
|
|
|
Instruction,
|
|
|
|
|
Directive
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ASMLine(Type t, std::string te) : type(t), text(std::move(te)) {}
|
|
|
|
|
|
|
|
|
|
Type type;
|
|
|
|
|
std::string text;
|
|
|
|
|
};
|
|
|
|
|
|
2016-07-16 04:24:59 +00:00
|
|
|
|
int parse_8bit_literal(const std::string &s)
|
|
|
|
|
{
|
|
|
|
|
return std::stoi(std::string(std::next(std::begin(s)), std::end(s)));
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-10 19:10:52 +00:00
|
|
|
|
std::string fixup_8bit_literal(const std::string &s)
|
|
|
|
|
{
|
|
|
|
|
if (s[0] == '$')
|
|
|
|
|
{
|
2016-07-16 04:24:59 +00:00
|
|
|
|
return "#" + std::to_string(static_cast<uint8_t>(parse_8bit_literal(s)));
|
2016-07-10 19:10:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
struct Operand
|
|
|
|
|
{
|
|
|
|
|
enum class Type
|
|
|
|
|
{
|
|
|
|
|
empty,
|
|
|
|
|
literal,
|
|
|
|
|
reg /*ister*/
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Type type = Type::empty;
|
2016-07-07 17:08:28 +00:00
|
|
|
|
int reg_num = 0;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
std::string value;
|
|
|
|
|
|
|
|
|
|
Operand() = default;
|
|
|
|
|
|
2016-08-07 01:58:24 +00:00
|
|
|
|
bool operator==(const Operand &other) const {
|
|
|
|
|
return type == other.type && reg_num == other.reg_num && value == other.value;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
Operand(const Type t, std::string v)
|
2016-07-07 00:35:40 +00:00
|
|
|
|
: type(t), value(std::move(v))
|
|
|
|
|
{
|
|
|
|
|
assert(type == Type::literal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Operand(const Type t, const int num)
|
|
|
|
|
: type(t), reg_num(num)
|
|
|
|
|
{
|
|
|
|
|
assert(type == Type::reg);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-08-07 01:58:24 +00:00
|
|
|
|
Operand get_register(const int reg_num, const int offset = 0) {
|
2016-07-16 04:24:59 +00:00
|
|
|
|
switch (reg_num) {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
// http://sta.c64.org/cbm64mem.html
|
|
|
|
|
case 0x00: return Operand(Operand::Type::literal, "$03"); // unused, fp->int routine pointer
|
|
|
|
|
case 0x01: return Operand(Operand::Type::literal, "$04");
|
|
|
|
|
case 0x02: return Operand(Operand::Type::literal, "$05"); // unused, int->fp routine pointer
|
|
|
|
|
case 0x03: return Operand(Operand::Type::literal, "$06");
|
|
|
|
|
case 0x04: return Operand(Operand::Type::literal, "$fb"); // unused
|
|
|
|
|
case 0x05: return Operand(Operand::Type::literal, "$fc"); // unused
|
|
|
|
|
case 0x06: return Operand(Operand::Type::literal, "$fd"); // unused
|
|
|
|
|
case 0x07: return Operand(Operand::Type::literal, "$fe"); // unused
|
|
|
|
|
case 0x08: return Operand(Operand::Type::literal, "$22"); // unused
|
|
|
|
|
case 0x09: return Operand(Operand::Type::literal, "$23"); // unused
|
2016-08-09 20:02:56 +00:00
|
|
|
|
case 0x0A: return Operand(Operand::Type::literal, "$39"); // Current BASIC line number
|
|
|
|
|
case 0x0B: return Operand(Operand::Type::literal, "$3a"); // Current BASIC line number
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case 0x10: return get_register(0x00 + offset);
|
|
|
|
|
case 0x11: return get_register(0x02 + offset);
|
|
|
|
|
case 0x12: return get_register(0x04 + offset);
|
|
|
|
|
case 0x13: return get_register(0x06 + offset);
|
|
|
|
|
case 0x14: return get_register(0x08 + offset);
|
2016-08-09 20:02:56 +00:00
|
|
|
|
case 0x15: return get_register(0x0A + offset);
|
2016-07-16 04:24:59 +00:00
|
|
|
|
};
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Unhandled register number: " + std::to_string(reg_num));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
struct mos6502 : ASMLine
|
|
|
|
|
{
|
|
|
|
|
enum class OpCode
|
|
|
|
|
{
|
|
|
|
|
unknown,
|
|
|
|
|
lda,
|
2016-07-07 17:20:41 +00:00
|
|
|
|
ldy,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
tay,
|
|
|
|
|
tya,
|
|
|
|
|
cpy,
|
2016-07-07 16:51:54 +00:00
|
|
|
|
eor,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
sta,
|
2016-07-07 17:20:41 +00:00
|
|
|
|
sty,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
pha,
|
|
|
|
|
pla,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
php,
|
|
|
|
|
plp,
|
2016-07-07 16:51:54 +00:00
|
|
|
|
lsr,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
AND,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
inc,
|
|
|
|
|
dec,
|
|
|
|
|
ORA,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
cmp,
|
|
|
|
|
bne,
|
|
|
|
|
beq,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
bmi,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
jmp,
|
|
|
|
|
adc,
|
2016-07-07 20:58:39 +00:00
|
|
|
|
sbc,
|
2016-08-07 01:58:24 +00:00
|
|
|
|
rts,
|
2016-08-08 23:05:16 +00:00
|
|
|
|
clc,
|
|
|
|
|
sec,
|
|
|
|
|
bit
|
2016-07-07 00:35:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool get_is_branch(const OpCode o) {
|
|
|
|
|
switch (o) {
|
|
|
|
|
case OpCode::beq:
|
|
|
|
|
case OpCode::bne:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::bmi:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
return true;
|
|
|
|
|
case OpCode::lda:
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::ldy:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::tay:
|
|
|
|
|
case OpCode::tya:
|
|
|
|
|
case OpCode::cpy:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::eor:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::sta:
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::sty:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::pha:
|
|
|
|
|
case OpCode::pla:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::php:
|
|
|
|
|
case OpCode::plp:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::lsr:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::AND:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::inc:
|
|
|
|
|
case OpCode::dec:
|
|
|
|
|
case OpCode::ORA:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::cmp:
|
|
|
|
|
case OpCode::jmp:
|
|
|
|
|
case OpCode::adc:
|
|
|
|
|
case OpCode::sbc:
|
2016-07-07 20:58:39 +00:00
|
|
|
|
case OpCode::rts:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case OpCode::clc:
|
2016-08-08 23:05:16 +00:00
|
|
|
|
case OpCode::sec:
|
|
|
|
|
case OpCode::bit:
|
2016-07-07 17:08:28 +00:00
|
|
|
|
case OpCode::unknown:
|
|
|
|
|
break;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
2016-07-07 17:08:28 +00:00
|
|
|
|
return false;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool get_is_comparison(const OpCode o) {
|
|
|
|
|
switch (o) {
|
|
|
|
|
case OpCode::cmp:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::cpy:
|
2016-08-08 23:05:16 +00:00
|
|
|
|
case OpCode::bit:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
return true;
|
|
|
|
|
case OpCode::lda:
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::ldy:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::tay:
|
|
|
|
|
case OpCode::tya:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::eor:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::sta:
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::sty:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::pha:
|
|
|
|
|
case OpCode::pla:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::php:
|
|
|
|
|
case OpCode::plp:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::lsr:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::AND:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::inc:
|
|
|
|
|
case OpCode::dec:
|
|
|
|
|
case OpCode::ORA:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::jmp:
|
|
|
|
|
case OpCode::bne:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::bmi:
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::beq:
|
|
|
|
|
case OpCode::adc:
|
|
|
|
|
case OpCode::sbc:
|
2016-07-07 20:58:39 +00:00
|
|
|
|
case OpCode::rts:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case OpCode::clc:
|
2016-08-08 23:05:16 +00:00
|
|
|
|
case OpCode::sec:
|
2016-07-07 17:08:28 +00:00
|
|
|
|
case OpCode::unknown:
|
|
|
|
|
break;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
2016-07-07 17:08:28 +00:00
|
|
|
|
return false;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
explicit mos6502(const OpCode o)
|
2016-07-07 00:35:40 +00:00
|
|
|
|
: ASMLine(Type::Instruction, to_string(o)), opcode(o), is_branch(get_is_branch(o)), is_comparison(get_is_comparison(o))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
mos6502(const Type t, std::string s)
|
2016-07-07 00:35:40 +00:00
|
|
|
|
: ASMLine(t, std::move(s))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
mos6502(const OpCode o, Operand t_o)
|
2016-07-07 00:35:40 +00:00
|
|
|
|
: ASMLine(Type::Instruction, to_string(o)), opcode(o), op(std::move(t_o)), is_branch(get_is_branch(o)), is_comparison(get_is_comparison(o))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::string to_string(const OpCode o)
|
|
|
|
|
{
|
|
|
|
|
switch (o) {
|
|
|
|
|
case OpCode::lda:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "lda";
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::ldy:
|
|
|
|
|
return "ldy";
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::tay:
|
|
|
|
|
return "tay";
|
|
|
|
|
case OpCode::tya:
|
|
|
|
|
return "tya";
|
|
|
|
|
case OpCode::cpy:
|
|
|
|
|
return "cpy";
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::eor:
|
|
|
|
|
return "eor";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::sta:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "sta";
|
2016-07-07 17:20:41 +00:00
|
|
|
|
case OpCode::sty:
|
|
|
|
|
return "sty";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::pha:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "pha";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::pla:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "pla";
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::php:
|
|
|
|
|
return "php";
|
|
|
|
|
case OpCode::plp:
|
|
|
|
|
return "plp";
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case OpCode::lsr:
|
|
|
|
|
return "lsr";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::AND:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "and";
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::inc:
|
|
|
|
|
return "inc";
|
|
|
|
|
case OpCode::dec:
|
|
|
|
|
return "dec";
|
|
|
|
|
case OpCode::ORA:
|
|
|
|
|
return "ora";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::cmp:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "cmp";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::bne:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "bne";
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case OpCode::bmi:
|
|
|
|
|
return "bmi";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::beq:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "beq";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::jmp:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "jmp";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::adc:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "adc";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::sbc:
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return "sbc";
|
2016-07-07 20:58:39 +00:00
|
|
|
|
case OpCode::rts:
|
|
|
|
|
return "rts";
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case OpCode::clc:
|
|
|
|
|
return "clc";
|
2016-08-08 23:05:16 +00:00
|
|
|
|
case OpCode::sec:
|
|
|
|
|
return "sec";
|
|
|
|
|
case OpCode::bit:
|
|
|
|
|
return "bit";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case OpCode::unknown:
|
|
|
|
|
return "";
|
|
|
|
|
};
|
2016-07-07 17:08:28 +00:00
|
|
|
|
|
|
|
|
|
return "";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string to_string() const
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case ASMLine::Type::Label:
|
2016-08-07 03:01:01 +00:00
|
|
|
|
return text; // + ':';
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case ASMLine::Type::Directive:
|
|
|
|
|
case ASMLine::Type::Instruction:
|
2016-08-08 23:05:16 +00:00
|
|
|
|
return '\t' + text + ' ' + op.value + "; " + comment;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
};
|
2016-07-07 17:08:28 +00:00
|
|
|
|
throw std::runtime_error("Unable to render: " + text);
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OpCode opcode = OpCode::unknown;
|
|
|
|
|
Operand op;
|
2016-08-08 23:05:16 +00:00
|
|
|
|
std::string comment;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
bool is_branch = false;
|
|
|
|
|
bool is_comparison = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct i386 : ASMLine
|
|
|
|
|
{
|
|
|
|
|
enum class OpCode
|
|
|
|
|
{
|
|
|
|
|
unknown,
|
|
|
|
|
movzbl,
|
|
|
|
|
movzwl,
|
|
|
|
|
shrb,
|
|
|
|
|
xorl,
|
|
|
|
|
andl,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
andb,
|
2016-08-07 01:58:24 +00:00
|
|
|
|
addb,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
ret,
|
|
|
|
|
movb,
|
2016-07-07 16:51:54 +00:00
|
|
|
|
cmpb,
|
|
|
|
|
movl,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
jmp,
|
|
|
|
|
jne,
|
|
|
|
|
je,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
js,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
testb,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
incl,
|
|
|
|
|
incb,
|
|
|
|
|
decl,
|
|
|
|
|
decb,
|
|
|
|
|
sarl,
|
2016-07-07 00:35:40 +00:00
|
|
|
|
addl,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
subl,
|
2016-08-07 01:58:24 +00:00
|
|
|
|
subb,
|
2016-07-16 04:24:59 +00:00
|
|
|
|
sall,
|
|
|
|
|
orl,
|
|
|
|
|
orb,
|
|
|
|
|
rep,
|
2016-08-07 01:58:24 +00:00
|
|
|
|
pushl,
|
|
|
|
|
sbbb,
|
|
|
|
|
negb,
|
|
|
|
|
notb
|
2016-07-07 00:35:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static OpCode parse_opcode(Type t, const std::string &o)
|
|
|
|
|
{
|
|
|
|
|
switch(t)
|
|
|
|
|
{
|
|
|
|
|
case Type::Label:
|
|
|
|
|
return OpCode::unknown;
|
|
|
|
|
case Type::Directive:
|
|
|
|
|
return OpCode::unknown;
|
|
|
|
|
case Type::Instruction:
|
|
|
|
|
{
|
|
|
|
|
if (o == "movzwl") return OpCode::movzwl;
|
|
|
|
|
if (o == "movzbl") return OpCode::movzbl;
|
|
|
|
|
if (o == "shrb") return OpCode::shrb;
|
|
|
|
|
if (o == "xorl") return OpCode::xorl;
|
|
|
|
|
if (o == "andl") return OpCode::andl;
|
|
|
|
|
if (o == "ret") return OpCode::ret;
|
|
|
|
|
if (o == "movb") return OpCode::movb;
|
2016-07-07 16:51:54 +00:00
|
|
|
|
if (o == "cmpb") return OpCode::cmpb;
|
|
|
|
|
if (o == "movl") return OpCode::movl;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
if (o == "jmp") return OpCode::jmp;
|
|
|
|
|
if (o == "testb") return OpCode::testb;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
if (o == "incl") return OpCode::incl;
|
|
|
|
|
if (o == "sarl") return OpCode::sarl;
|
|
|
|
|
if (o == "decl") return OpCode::decl;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
if (o == "jne") return OpCode::jne;
|
|
|
|
|
if (o == "je") return OpCode::je;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
if (o == "js") return OpCode::js;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
if (o == "subl") return OpCode::subl;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o == "subb") return OpCode::subb;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
if (o == "addl") return OpCode::addl;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o == "addb") return OpCode::addb;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
if (o == "sall") return OpCode::sall;
|
|
|
|
|
if (o == "orl") return OpCode::orl;
|
|
|
|
|
if (o == "andb") return OpCode::andb;
|
|
|
|
|
if (o == "orb") return OpCode::orb;
|
|
|
|
|
if (o == "decb") return OpCode::decb;
|
|
|
|
|
if (o == "incb") return OpCode::incb;
|
|
|
|
|
if (o == "rep") return OpCode::rep;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o == "notb") return OpCode::notb;
|
|
|
|
|
if (o == "negb") return OpCode::negb;
|
|
|
|
|
if (o == "sbbb") return OpCode::sbbb;
|
|
|
|
|
if (o == "pushl") return OpCode::pushl;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-07 17:08:28 +00:00
|
|
|
|
throw std::runtime_error("Unknown opcode: " + o);
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Operand parse_operand(std::string o)
|
|
|
|
|
{
|
|
|
|
|
if (o.empty()) {
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return Operand();
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (o[0] == '%') {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o == "%al") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x00);
|
|
|
|
|
} else if (o == "%ah") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x01);
|
|
|
|
|
} else if (o == "%bl") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x02);
|
|
|
|
|
} else if (o == "%bh") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x03);
|
|
|
|
|
} else if (o == "%cl") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x04);
|
|
|
|
|
} else if (o == "%ch") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x05);
|
|
|
|
|
} else if (o == "%dl") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x06);
|
|
|
|
|
} else if (o == "%dh") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x07);
|
|
|
|
|
} else if (o == "%sil") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x08);
|
2016-08-09 20:02:56 +00:00
|
|
|
|
} else if (o == "%dil") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x0A);
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o == "%ax" || o == "%eax") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x10);
|
|
|
|
|
} else if (o == "%bx" || o == "%ebx") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x11);
|
|
|
|
|
} else if (o == "%cx" || o == "%ecx") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x12);
|
|
|
|
|
} else if (o == "%dx" || o == "%edx") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x13);
|
|
|
|
|
} else if (o == "%si" || o == "%esi") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x14);
|
2016-08-09 20:02:56 +00:00
|
|
|
|
} else if (o == "%di" || o == "%edi") {
|
|
|
|
|
return Operand(Operand::Type::reg, 0x15);
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Unknown register operand: '" + o + "'");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2016-07-10 19:10:52 +00:00
|
|
|
|
return Operand(Operand::Type::literal, std::move(o));
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 21:48:27 +00:00
|
|
|
|
i386(const int t_line_num, std::string t_line_text, Type t, std::string t_opcode, std::string o1="", std::string o2="")
|
|
|
|
|
: ASMLine(t, t_opcode), line_num(t_line_num), line_text(std::move(t_line_text)),
|
2016-07-10 19:10:52 +00:00
|
|
|
|
opcode(parse_opcode(t, t_opcode)), operand1(parse_operand(o1)), operand2(parse_operand(o2))
|
2016-07-07 00:35:40 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 16:51:54 +00:00
|
|
|
|
int line_num;
|
|
|
|
|
std::string line_text;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
OpCode opcode;
|
|
|
|
|
Operand operand1;
|
|
|
|
|
Operand operand2;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void translate_instruction(std::vector<mos6502> &instructions, const i386::OpCode op, const Operand &o1, const Operand &o2)
|
|
|
|
|
{
|
|
|
|
|
switch(op)
|
|
|
|
|
{
|
2016-07-07 20:58:39 +00:00
|
|
|
|
case i386::OpCode::ret:
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::rts);
|
|
|
|
|
break;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case i386::OpCode::movl:
|
|
|
|
|
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num, 1));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1));
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate movl instruction");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::xorl:
|
|
|
|
|
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg
|
|
|
|
|
&& o1.reg_num == o2.reg_num) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#$00"));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1));
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate movl instruction");
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case i386::OpCode::movb:
|
2016-07-07 17:08:28 +00:00
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
2016-07-16 04:24:59 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
|
|
|
|
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
|
|
|
|
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate movb instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case i386::OpCode::orb:
|
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::ORA, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::ORA, get_register(o2.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate orb instruction");
|
2016-07-16 04:24:59 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case i386::OpCode::movzbl:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
2016-07-07 00:35:40 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, o1);
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
2016-07-07 16:51:54 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate movzbl instruction");
|
2016-07-07 16:51:54 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::shrb:
|
2016-07-16 04:24:59 +00:00
|
|
|
|
if (o1.type == Operand::Type::reg || o2.type == Operand::Type::reg) {
|
|
|
|
|
const auto do_shift = [&instructions](const int reg_num) {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lsr, get_register(reg_num));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (o1.type == Operand::Type::literal) {
|
|
|
|
|
const auto count = parse_8bit_literal(o1.value);
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
|
do_shift(o2.reg_num);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
do_shift(o1.reg_num);
|
|
|
|
|
}
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate shrb instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::testb:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg && o1.reg_num == o2.reg_num) {
|
|
|
|
|
// this just tests the register for 0
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
2016-08-08 23:05:16 +00:00
|
|
|
|
// instructions.emplace_back(mos6502::OpCode::bit, Operand(Operand::Type::literal, "#$00"));
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
|
|
|
|
// ands the values
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
2016-08-08 23:05:16 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::bit, get_register(o2.reg_num));
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
|
|
|
|
// ands the values
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
2016-08-08 23:05:16 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::bit, get_register(o2.reg_num));
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate testb instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case i386::OpCode::decb:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::reg) {
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::dec, get_register(o1.reg_num));
|
|
|
|
|
} else {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::dec, o1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::incb:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::reg) {
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::inc, get_register(o1.reg_num));
|
|
|
|
|
} else {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::inc, o1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case i386::OpCode::jne:
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::bne, o1);
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::je:
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::beq, o1);
|
|
|
|
|
break;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case i386::OpCode::js:
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::bmi, o1);
|
|
|
|
|
break;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
case i386::OpCode::jmp:
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::jmp, o1);
|
|
|
|
|
break;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case i386::OpCode::addb:
|
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o2.reg_num));
|
2016-08-07 01:58:24 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::clc);
|
2016-07-10 19:10:52 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::adc, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
2016-07-16 04:24:59 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
2016-08-09 20:02:56 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::clc);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::adc, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::clc);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::adc, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
2016-07-07 16:51:54 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate addb instruction");
|
2016-07-07 16:51:54 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::cmpb:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
2016-08-08 23:05:16 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::cmp, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
2016-08-08 23:05:16 +00:00
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o2.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::cmp, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
2016-07-07 16:51:54 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate cmb instruction");
|
2016-07-07 16:51:54 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2016-07-16 04:24:59 +00:00
|
|
|
|
case i386::OpCode::andb:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
2016-07-16 04:24:59 +00:00
|
|
|
|
const auto reg = get_register(o2.reg_num);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, reg);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, reg);
|
2016-08-07 01:58:24 +00:00
|
|
|
|
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
|
|
|
|
const auto reg = get_register(o2.reg_num);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate andb instruction");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::negb:
|
|
|
|
|
if (o1.type == Operand::Type::reg) {
|
|
|
|
|
// perform a two's complement of the register location
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff"));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::inc, get_register(o1.reg_num));
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate negb instruction");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case i386::OpCode::notb:
|
|
|
|
|
if (o1.type == Operand::Type::reg) {
|
|
|
|
|
// exclusive or against 0xff to perform a logical not
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff"));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o1.reg_num));
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate notb instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case i386::OpCode::subb:
|
2016-08-08 23:05:16 +00:00
|
|
|
|
// DEST <- DEST - SRC
|
|
|
|
|
// o2 = o2 - o1
|
2016-08-07 01:58:24 +00:00
|
|
|
|
// Ensure that we set the carry flag before performing the subtraction
|
2016-08-08 23:05:16 +00:00
|
|
|
|
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, o2);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sec);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sbc, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate subb instruction");
|
|
|
|
|
}
|
2016-08-07 01:58:24 +00:00
|
|
|
|
break;
|
2016-08-09 20:02:56 +00:00
|
|
|
|
case i386::OpCode::pushl:
|
|
|
|
|
if (o1.type == Operand::Type::reg) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::pha);
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num, 1));
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::pha);
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Cannot translate sbb instruction");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2016-08-07 01:58:24 +00:00
|
|
|
|
case i386::OpCode::sbbb:
|
|
|
|
|
// DEST <- (DEST – (SRC + CF))
|
|
|
|
|
// o2 <- (o2 - (o1 + cf))
|
|
|
|
|
// if o1 and o2 are the same we get
|
|
|
|
|
// o2 <- (o2 - (o2 + cf))
|
|
|
|
|
// o2 <- -cf
|
|
|
|
|
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg
|
|
|
|
|
&& o1.reg_num == o2.reg_num) {
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#$00")); // reset a
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sbc, Operand(Operand::Type::literal, "#$00")); // subtract out the carry flag
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff")); // invert the bits
|
|
|
|
|
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); // place the value
|
2016-07-07 00:35:40 +00:00
|
|
|
|
} else {
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate sbb instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2016-08-07 01:58:24 +00:00
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
default:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
throw std::runtime_error("Cannot translate unhandled instruction");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 16:51:54 +00:00
|
|
|
|
enum class LogLevel
|
|
|
|
|
{
|
|
|
|
|
Warning,
|
|
|
|
|
Error
|
|
|
|
|
};
|
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
std::string to_string(const LogLevel ll)
|
2016-07-07 16:51:54 +00:00
|
|
|
|
{
|
2016-07-07 17:08:28 +00:00
|
|
|
|
switch (ll)
|
|
|
|
|
{
|
|
|
|
|
case LogLevel::Warning:
|
|
|
|
|
return "warning";
|
|
|
|
|
case LogLevel::Error:
|
|
|
|
|
return "error";
|
|
|
|
|
}
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
2016-07-07 16:51:54 +00:00
|
|
|
|
|
2016-07-07 17:08:28 +00:00
|
|
|
|
void log(LogLevel ll, const i386 &i, const std::string &message)
|
|
|
|
|
{
|
2016-08-06 19:08:24 +00:00
|
|
|
|
std::cout << to_string(ll) << ": " << i.line_num << ": " << message << ": `" << i.line_text << "`\n";
|
2016-07-07 16:51:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void log(LogLevel ll, const int line_no, const std::string &line, const std::string &message)
|
|
|
|
|
{
|
2016-08-06 19:08:24 +00:00
|
|
|
|
std::cout << to_string(ll) << ": " << line_no << ": " << message << ": `" << line << "`\n";
|
2016-07-07 16:51:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
void to_mos6502(const i386 &i, std::vector<mos6502> &instructions)
|
|
|
|
|
{
|
2016-07-07 16:51:54 +00:00
|
|
|
|
try {
|
|
|
|
|
switch(i.type)
|
|
|
|
|
{
|
|
|
|
|
case ASMLine::Type::Label:
|
|
|
|
|
instructions.emplace_back(i.type, i.text);
|
|
|
|
|
return;
|
|
|
|
|
case ASMLine::Type::Directive:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
// instructions.emplace_back(i.type, i.text);
|
2016-08-06 19:08:24 +00:00
|
|
|
|
return;
|
2016-07-07 16:51:54 +00:00
|
|
|
|
case ASMLine::Type::Instruction:
|
2016-08-07 01:58:24 +00:00
|
|
|
|
// instructions.emplace_back(ASMLine::Type::Directive, "; " + i.line_text);
|
2016-08-08 23:05:16 +00:00
|
|
|
|
|
|
|
|
|
const auto head = instructions.size();
|
2016-07-07 16:51:54 +00:00
|
|
|
|
translate_instruction(instructions, i.opcode, i.operand1, i.operand2);
|
2016-08-08 23:05:16 +00:00
|
|
|
|
for_each(std::next(instructions.begin(), head), instructions.end(), [ text = i.line_text ](auto &ins){ ins.comment = text; });
|
2016-07-07 16:51:54 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
log(LogLevel::Error, i, e.what());
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 20:58:39 +00:00
|
|
|
|
bool optimize(std::vector<mos6502> &instructions)
|
|
|
|
|
{
|
|
|
|
|
if (instructions.size() < 2) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t op = 0; op < instructions.size() - 1; ++op)
|
|
|
|
|
{
|
2016-08-06 19:08:24 +00:00
|
|
|
|
if (instructions[op].opcode == mos6502::OpCode::tya
|
|
|
|
|
&& instructions[op+1].opcode == mos6502::OpCode::tay)
|
2016-07-07 20:58:39 +00:00
|
|
|
|
{
|
2016-08-06 19:08:24 +00:00
|
|
|
|
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
|
2016-07-07 20:58:39 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-08-07 01:58:24 +00:00
|
|
|
|
if (instructions[op].opcode == mos6502::OpCode::sta
|
|
|
|
|
&& instructions[op+1].opcode == mos6502::OpCode::lda
|
|
|
|
|
&& instructions[op].op == instructions[op+1].op)
|
|
|
|
|
{
|
|
|
|
|
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t op = 0; op < instructions.size() - 1; ++op)
|
|
|
|
|
{
|
|
|
|
|
if (instructions[op].opcode == mos6502::OpCode::lda
|
|
|
|
|
&& instructions[op].op.type == Operand::Type::literal)
|
|
|
|
|
{
|
|
|
|
|
const auto operand = instructions[op].op;
|
|
|
|
|
auto op2 = op+1;
|
|
|
|
|
while (op2 < instructions.size() && instructions[op2].opcode == mos6502::OpCode::sta) {
|
|
|
|
|
++op2;
|
|
|
|
|
}
|
|
|
|
|
if (instructions[op2].opcode == mos6502::OpCode::lda
|
|
|
|
|
&& operand == instructions[op2].op)
|
|
|
|
|
{
|
|
|
|
|
instructions.erase(std::next(std::begin(instructions), op2), std::next(std::begin(instructions), op2+1));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2016-07-07 20:58:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-07 01:58:24 +00:00
|
|
|
|
|
2016-07-07 20:58:39 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-07 03:01:01 +00:00
|
|
|
|
bool fix_long_branches(std::vector<mos6502> &instructions, int &branch_patch_count)
|
|
|
|
|
{
|
|
|
|
|
std::map<std::string, size_t> labels;
|
|
|
|
|
for (size_t op = 0; op < instructions.size(); ++op)
|
|
|
|
|
{
|
|
|
|
|
if (instructions[op].type == ASMLine::Type::Label) {
|
|
|
|
|
labels[instructions[op].text] = op;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t op = 0; op < instructions.size(); ++op)
|
|
|
|
|
{
|
|
|
|
|
if (instructions[op].is_branch && std::abs(static_cast<int>(labels[instructions[op].op.value]) - static_cast<int>(op)) * 3 > 255)
|
|
|
|
|
{
|
|
|
|
|
++branch_patch_count;
|
|
|
|
|
const auto going_to = instructions[op].op.value;
|
|
|
|
|
const auto new_pos = "branch_patch_" + std::to_string(branch_patch_count);
|
|
|
|
|
// uh-oh too long of a branch, have to convert this to a jump...
|
|
|
|
|
if (instructions[op].opcode == mos6502::OpCode::bne) {
|
|
|
|
|
instructions[op] = mos6502(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_pos));
|
|
|
|
|
instructions.insert(std::next(std::begin(instructions), op + 1), mos6502(mos6502::OpCode::jmp, Operand(Operand::Type::literal, going_to)));
|
|
|
|
|
instructions.insert(std::next(std::begin(instructions), op + 2), mos6502(ASMLine::Type::Label, new_pos));
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Don't know how to reorg this branch");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 20:58:39 +00:00
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
bool fix_overwritten_flags(std::vector<mos6502> &instructions)
|
|
|
|
|
{
|
|
|
|
|
if (instructions.size() < 3) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-08 23:05:16 +00:00
|
|
|
|
for (size_t op = 0; op < instructions.size(); ++op)
|
2016-07-07 00:35:40 +00:00
|
|
|
|
{
|
2016-08-08 23:05:16 +00:00
|
|
|
|
if (instructions[op].is_comparison) {
|
|
|
|
|
auto op2 = op + 1;
|
|
|
|
|
while (op2 < instructions.size()
|
|
|
|
|
&& !instructions[op2].is_comparison
|
|
|
|
|
&& !instructions[op2].is_branch)
|
|
|
|
|
{
|
|
|
|
|
++op2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (op2 < instructions.size()
|
|
|
|
|
&& (op2 - op) > 1
|
|
|
|
|
&& instructions[op2-1].opcode != mos6502::OpCode::plp) {
|
|
|
|
|
if (instructions[op2].is_comparison) {
|
|
|
|
|
continue;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-08 23:05:16 +00:00
|
|
|
|
if (instructions[op2].is_branch) {
|
|
|
|
|
// insert a pull of processor status before the branch
|
|
|
|
|
instructions.insert(std::next(std::begin(instructions), op2), mos6502(mos6502::OpCode::plp));
|
|
|
|
|
// insert a push of processor status after the comparison
|
|
|
|
|
instructions.insert(std::next(std::begin(instructions), op+1), mos6502(mos6502::OpCode::php));
|
2016-07-07 00:35:40 +00:00
|
|
|
|
|
2016-08-08 23:05:16 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
|
{
|
2016-07-16 04:24:59 +00:00
|
|
|
|
std::regex Comment(R"(\s*\#.*)");
|
|
|
|
|
std::regex Label(R"(^(\S+):.*)");
|
2016-07-07 00:35:40 +00:00
|
|
|
|
std::regex Directive(R"(^\t(\..+))");
|
|
|
|
|
std::regex UnaryInstruction(R"(^\t(\S+)\s+(\S+))");
|
|
|
|
|
std::regex BinaryInstruction(R"(^\t(\S+)\s+(\S+),\s+(\S+))");
|
|
|
|
|
std::regex Instruction(R"(^\t(\S+))");
|
|
|
|
|
|
|
|
|
|
int lineno = 0;
|
|
|
|
|
|
|
|
|
|
std::vector<i386> instructions;
|
|
|
|
|
|
|
|
|
|
while (std::cin.good())
|
|
|
|
|
{
|
|
|
|
|
std::string line;
|
|
|
|
|
getline(std::cin, line);
|
|
|
|
|
|
2016-07-07 16:51:54 +00:00
|
|
|
|
try {
|
|
|
|
|
std::smatch match;
|
|
|
|
|
if (std::regex_match(line, match, Label))
|
|
|
|
|
{
|
|
|
|
|
instructions.emplace_back(lineno, line, ASMLine::Type::Label, match[1]);
|
2016-07-16 04:24:59 +00:00
|
|
|
|
} else if (std::regex_match(line, match, Comment)) {
|
|
|
|
|
// don't care about comments
|
2016-07-07 16:51:54 +00:00
|
|
|
|
} else if (std::regex_match(line, match, Directive)) {
|
|
|
|
|
instructions.emplace_back(lineno, line, ASMLine::Type::Directive, match[1]);
|
|
|
|
|
} else if (std::regex_match(line, match, UnaryInstruction)) {
|
|
|
|
|
instructions.emplace_back(lineno, line, ASMLine::Type::Instruction, match[1], match[2]);
|
|
|
|
|
} else if (std::regex_match(line, match, BinaryInstruction)) {
|
|
|
|
|
instructions.emplace_back(lineno, line, ASMLine::Type::Instruction, match[1], match[2], match[3]);
|
|
|
|
|
} else if (std::regex_match(line, match, Instruction)) {
|
|
|
|
|
instructions.emplace_back(lineno, line, ASMLine::Type::Instruction, match[1]);
|
|
|
|
|
} else if (line == "") {
|
|
|
|
|
//std::cout << "EmptyLine\n";
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Unparsed Input, Line: " + std::to_string(lineno));
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
log(LogLevel::Error, lineno, line, e.what());
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++lineno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<std::string> labels;
|
|
|
|
|
|
|
|
|
|
for (const auto i : instructions)
|
|
|
|
|
{
|
|
|
|
|
if (i.type == ASMLine::Type::Label) {
|
|
|
|
|
labels.insert(i.text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<std::string> used_labels{"main"};
|
|
|
|
|
|
|
|
|
|
for (const auto i : instructions)
|
|
|
|
|
{
|
|
|
|
|
if (i.type == ASMLine::Type::Instruction)
|
|
|
|
|
{
|
|
|
|
|
if (labels.count(i.operand1.value) != 0) {
|
|
|
|
|
used_labels.insert(i.operand1.value);
|
|
|
|
|
used_labels.insert(i.operand2.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove all labels and directives that we don't need
|
|
|
|
|
instructions.erase(
|
|
|
|
|
std::remove_if(std::begin(instructions), std::end(instructions),
|
|
|
|
|
[&used_labels](const auto &i){
|
2016-08-06 19:08:24 +00:00
|
|
|
|
// if (i.type == ASMLine::Type::Directive) return true;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
if (i.type == ASMLine::Type::Label) {
|
|
|
|
|
if (used_labels.count(i.text) == 0) {
|
|
|
|
|
// remove all unused labels that aren't 'main'
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
std::end(instructions)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// remove everything up to the first label
|
|
|
|
|
// this will probably leave some dead code around at some point
|
|
|
|
|
// but it's a start
|
2016-08-06 19:08:24 +00:00
|
|
|
|
/*
|
2016-07-07 00:35:40 +00:00
|
|
|
|
instructions.erase(
|
|
|
|
|
std::begin(instructions),
|
|
|
|
|
std::find_if(std::begin(instructions),
|
|
|
|
|
std::end(instructions),
|
|
|
|
|
[](const auto &i){ return i.type == ASMLine::Type::Label; }
|
|
|
|
|
)
|
|
|
|
|
);
|
2016-08-06 19:08:24 +00:00
|
|
|
|
*/
|
2016-07-07 00:35:40 +00:00
|
|
|
|
|
|
|
|
|
const auto new_labels =
|
|
|
|
|
[&used_labels](){
|
|
|
|
|
std::map<std::string, std::string> result;
|
|
|
|
|
for (const auto &l : used_labels) {
|
|
|
|
|
auto newl = l;
|
|
|
|
|
std::transform(newl.begin(), newl.end(), newl.begin(), [](const auto c) { return std::tolower(c); });
|
|
|
|
|
newl.erase(std::remove_if(newl.begin(), newl.end(), [](const auto c){ return !std::isalnum(c); }), std::end(newl));
|
2016-08-06 19:08:24 +00:00
|
|
|
|
// std::cout << "Old label: '" << l << "' new label: '" << newl << "'\n";
|
2016-07-07 00:35:40 +00:00
|
|
|
|
result.emplace(std::make_pair(l, newl));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}();
|
|
|
|
|
|
2016-07-10 19:10:52 +00:00
|
|
|
|
|
|
|
|
|
// for (const auto &i : instructions)
|
|
|
|
|
// {
|
|
|
|
|
// std::cout << "'" << i.text << "' '" << i.operand1.value << "' '" << i.operand2.value << "'\n";
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
for (auto &i : instructions)
|
|
|
|
|
{
|
|
|
|
|
if (i.type == ASMLine::Type::Label)
|
|
|
|
|
{
|
2016-08-06 19:08:24 +00:00
|
|
|
|
// std::cout << "Updating label " << i.text << '\n';;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
i.text = new_labels.at(i.text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto itr1 = new_labels.find(i.operand1.value);
|
|
|
|
|
if (itr1 != new_labels.end()) {
|
|
|
|
|
i.operand1.value = itr1->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto itr2 = new_labels.find(i.operand2.value);
|
|
|
|
|
if (itr2 != new_labels.end()) {
|
2016-07-10 19:10:52 +00:00
|
|
|
|
i.operand2.value = itr2->second;
|
2016-07-07 00:35:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-10 19:10:52 +00:00
|
|
|
|
// for (const auto &i : instructions)
|
|
|
|
|
// {
|
|
|
|
|
// std::cout << "'" << i.text << "' '" << i.operand1.value << "' '" << i.operand2.value << "'\n";
|
|
|
|
|
// }
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
std::vector<mos6502> new_instructions;
|
|
|
|
|
|
|
|
|
|
for (const auto &i : instructions)
|
|
|
|
|
{
|
|
|
|
|
to_mos6502(i, new_instructions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (fix_overwritten_flags(new_instructions))
|
|
|
|
|
{
|
|
|
|
|
// do it however many times it takes
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 20:58:39 +00:00
|
|
|
|
while (optimize(new_instructions))
|
|
|
|
|
{
|
|
|
|
|
// do it however many times it takes
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-07 03:01:01 +00:00
|
|
|
|
int branch_patch_count = 0;
|
|
|
|
|
while (fix_long_branches(new_instructions, branch_patch_count))
|
|
|
|
|
{
|
|
|
|
|
// do it however many times it takes
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 00:35:40 +00:00
|
|
|
|
|
|
|
|
|
for (const auto i : new_instructions)
|
|
|
|
|
{
|
|
|
|
|
std::cout << i.to_string() << '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|