#include "stdafx.h" #include "PPUProgramCompiler.h" using namespace PPU_instr; template InstrBase* GetInstruction(T* list, const std::string& str) { for(int i=0; icount; ++i) { auto instr = list->get_instr_info(i); if(instr) { if(instr->GetName().compare(str) == 0) { return instr; } } } return nullptr; } template InstrBase* GetInstruction(const std::string& str) { if(auto res = GetInstruction(main_list, str)) return res; if(auto res = GetInstruction(g04_list, str)) return res; if(auto res = GetInstruction(g04_0_list, str)) return res; if(auto res = GetInstruction(g13_list, str)) return res; if(auto res = GetInstruction(g1e_list, str)) return res; if(auto res = GetInstruction(g1f_list, str)) return res; if(auto res = GetInstruction(g3a_list, str)) return res; if(auto res = GetInstruction(g3b_list, str)) return res; if(auto res = GetInstruction(g3e_list, str)) return res; if(auto res = GetInstruction(g3f_list, str)) return res; if(auto res = GetInstruction(g3f_0_list, str)) return res; return nullptr; } s64 FindOp(const std::string& text, const std::string& op, s64 from) { if (text.length() < op.length()) return -1; for (s64 i = from; i= text.length() || text[(size_t) i + op.length()] == '\n' || CompilePPUProgram::IsSkip(text[(size_t) i + op.length()])) return i; } } return -1; } ArrayF sections_list; u32 section_name_offs = 0; u32 section_offs = 0; SectionInfo::SectionInfo(const std::string& _name) { name = _name; memset(&shdr, 0, sizeof(Elf64_Shdr)); section_num = sections_list.Add(this); shdr.sh_offset = section_offs; shdr.sh_name = section_name_offs; section_name_offs += name.length() + 1; } void SectionInfo::SetDataSize(u32 size, u32 align) { if(align) shdr.sh_addralign = align; if(shdr.sh_addralign) size = Memory.AlignAddr(size, shdr.sh_addralign); if(code.GetCount()) { for(u32 i=section_num + 1; iWriteText(fmt::FromUTF8(text)); } } void CompilePPUProgram::WriteError(const std::string& error) { if(m_err_list) { m_err_list->WriteText(fmt::FromUTF8(fmt::Format("line %lld: %s\n", m_line, error))); } } bool CompilePPUProgram::IsSkip(const char c) { return c == ' ' || c == '\t'; } bool CompilePPUProgram::IsCommit(const char c) { return c == '#'; } bool CompilePPUProgram::IsEnd() const { return p >= m_asm.length(); } bool CompilePPUProgram::IsEndLn(const char c) const { return c == '\n' || p - 1 >= m_asm.length(); } char CompilePPUProgram::NextChar() { return *m_asm.substr(p++, 1).c_str(); } void CompilePPUProgram::NextLn() { while( !IsEndLn(NextChar()) ); if(!IsEnd()) m_line++; } void CompilePPUProgram::EndLn() { NextLn(); p--; m_line--; } void CompilePPUProgram::FirstChar() { p = 0; m_line = 1; m_branch_pos = 0; } void CompilePPUProgram::PrevArg() { while( --p >= 0 && (IsSkip(m_asm[(size_t)p]) || m_asm[(size_t)p] == ',')); while( --p >= 0 && !IsSkip(m_asm[(size_t)p]) && !IsEndLn(m_asm[(size_t)p]) ); if(IsEndLn(m_asm[(size_t)p])) p++; } bool CompilePPUProgram::GetOp(std::string& result) { s64 from = -1; for(;;) { const char cur_char = NextChar(); const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); if(endln) p--; if(from == -1) { if(endln || commit) return false; if(!skip) from = p - 1; continue; } if(skip || endln || commit) { const s64 to = (endln ? p : p - 1) - from; result = m_asm.substr(from, to); return true; } } return false; } int CompilePPUProgram::GetArg(std::string& result, bool func) { s64 from = -1; for(char cur_char = NextChar(); !m_error; cur_char = NextChar()) { const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); const bool end = cur_char == ',' || (func && cur_char == ']'); if(endln) p--; if(from == -1) { if(endln || commit || end) return 0; if(!skip) from = p - 1; continue; } const bool text = m_asm[(size_t)from] == '"'; const bool end_text = cur_char == '"'; if((text ? end_text : (skip || commit || end)) || endln) { if(text && p > 2 && m_asm[(size_t)p - 2] == '\\' && (p <= 3 || m_asm[(size_t)p - 3] != '\\')) { continue; } if(text && !end_text) { WriteError("'\"' not found."); m_error = true; } const s64 to = ((endln || text) ? p : p - 1) - from; int ret = 1; if(skip || text) { for(char cur_char = NextChar(); !m_error; cur_char = NextChar()) { const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); const bool end = cur_char == ',' || (func && cur_char == ']'); if(skip) continue; if(end) break; if(commit) { EndLn(); ret = -1; break; } if(endln) { p--; break; } WriteError(fmt::Format("Bad symbol '%c'", cur_char)); m_error = true; break; } } result = m_asm.substr(from, to); if(text) { for(u32 pos = 0; (s32)(pos = result.find('\\', pos)) != std::string::npos;) { if(pos + 1 < result.length() && result[pos + 1] == '\\') { pos += 2; continue; } const char v = result[pos + 1]; switch(v) { case 'n': result = result.substr(0, pos) + '\n' + result.substr(pos + 2, result.length() - (pos + 2)); break; case 'r': result = result.substr(0, pos) + '\r' + result.substr(pos + 2, result.length() - (pos + 2)); break; case 't': result = result.substr(0, pos) + '\t' + result.substr(pos + 2, result.length() - (pos + 2)); break; } pos++; } } return ret; } } return 0; } bool CompilePPUProgram::CheckEnd(bool show_err) { if(m_error) { NextLn(); return false; } while(true) { const char cur_char = NextChar(); const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); if(skip) continue; if(commit) { NextLn(); return true; } if(endln) { p--; NextLn(); return true; } WriteError(fmt::Format("Bad symbol '%c'", cur_char)); NextLn(); return false; } return false; } void CompilePPUProgram::DetectArgInfo(Arg& arg) { const std::string str = arg.string; if(str.empty()) { arg.type = ARG_ERR; return; } if(GetInstruction(str)) { arg.type = ARG_INSTR; return; } if(str.length() > 1) { for(u32 i=0; i '9') { arg.type = ARG_ERR; return; } } u32 reg; sscanf(str.substr(1, str.length() - 1).c_str(), "%d", ®); if(reg >= 32) { arg.type = ARG_ERR; return; } switch((char)str[0]) { case 'r': arg.type = ARG_REG_R; break; case 'f': arg.type = ARG_REG_F; break; case 'v': arg.type = ARG_REG_V; break; default: arg.type = ARG_ERR; break; } arg.value = reg; return; case 'c': if(str.length() > 2 && str[1] == 'r') { for(u32 i=2; i '9') { arg.type = ARG_ERR; return; } } u32 reg; sscanf(str.c_str(), "cr%d", ®); if(reg < 8) { arg.type = ARG_REG_CR; arg.value = reg; } else { arg.type = ARG_ERR; } return; } break; case '"': if(str.length() < 2) { arg.type = ARG_ERR; return; } if(str[str.length() - 1] != '"') { arg.type = ARG_ERR; return; } arg.string = str.substr(1, str.length() - 2); arg.type = ARG_TXT; return; } if(str.length() > 2 && str.substr(0, 2).compare("0x") == 0) { for(u32 i=2; i= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'f') || (str[i] >= 'A' && str[i] <= 'F') ) continue; arg.type = ARG_ERR; return; } u32 val; sscanf(str.c_str(), "0x%x", &val); arg.type = ARG_NUM16; arg.value = val; return; } for(u32 i= str[0] == '-' ? 1 : 0; i '9') { arg.type = ARG_ERR; return; } } u32 val; sscanf(str.c_str(), "%d", &val); arg.type = ARG_NUM; arg.value = val; } void CompilePPUProgram::LoadArgs() { m_args.Clear(); m_cur_arg = 0; std::string str; while(int r = GetArg(str)) { Arg* arg = new Arg(str); DetectArgInfo(*arg); m_args.Add(arg); if(r == -1) break; } m_end_args = m_args.GetCount() > 0; } u32 CompilePPUProgram::GetBranchValue(const std::string& branch) { for(u32 i=0; i= 0) return m_text_addr + m_branches[i].m_pos * 4; return m_branches[i].m_addr; } return 0; } bool CompilePPUProgram::SetNextArgType(u32 types, bool show_err) { if(m_error) return false; if(m_cur_arg >= m_args.GetCount()) { if(show_err) { WriteError(fmt::Format("%d arg not found", m_cur_arg + 1)); m_error = true; } return false; } const Arg& arg = m_args[m_cur_arg]; if(arg.type & types) { m_cur_arg++; return true; } if(show_err) { WriteError(fmt::Format("Bad arg '%s'", arg.string.c_str())); m_error = true; } return false; } bool CompilePPUProgram::SetNextArgBranch(u8 aa, bool show_err) { const u32 pos = m_cur_arg; const bool ret = SetNextArgType(ARG_BRANCH | ARG_IMM, show_err); if(!aa && pos < m_args.GetCount()) { switch(m_args[pos].type) { case ARG_NUM: m_args[pos].value += m_text_addr + m_branch_pos * 4; break; case ARG_BRANCH: m_args[pos].value -= m_text_addr + m_branch_pos * 4; break; } } return ret; } bool CompilePPUProgram::IsBranchOp(const std::string& op) { return op.length() > 1 && op[op.length() - 1] == ':'; } bool CompilePPUProgram::IsFuncOp(const std::string& op) { return op.length() >= 1 && op[0] == '['; } CompilePPUProgram::SP_TYPE CompilePPUProgram::GetSpType(const std::string& op) { if (op.compare(".int") == 0) return SP_INT; if (op.compare(".string") == 0) return SP_STRING; if (op.compare(".strlen") == 0) return SP_STRLEN; if (op.compare(".buf") == 0) return SP_BUF; if (op.compare(".srl") == 0) return SP_SRL; if (op.compare(".srr") == 0) return SP_SRR; if (op.compare(".mul") == 0) return SP_MUL; if (op.compare(".div") == 0) return SP_DIV; if (op.compare(".add") == 0) return SP_ADD; if (op.compare(".sub") == 0) return SP_SUB; if (op.compare(".and") == 0) return SP_AND; if (op.compare(".or") == 0) return SP_OR; if (op.compare(".xor") == 0) return SP_XOR; if (op.compare(".not") == 0) return SP_NOT; if (op.compare(".nor") == 0) return SP_NOR; return SP_ERR; } std::string CompilePPUProgram::GetSpStyle(const SP_TYPE sp) { switch(sp) { case SP_INT: case SP_STRING: case SP_STRLEN: case SP_NOT: return "[dst, src]"; case SP_BUF: return "[dst, size]"; case SP_SRL: case SP_SRR: case SP_MUL: case SP_DIV: case SP_ADD: case SP_SUB: case SP_AND: case SP_OR: case SP_XOR: case SP_NOR: return "[dst, src1, src2]"; } return "error"; } bool CompilePPUProgram::IsSpOp(const std::string& op) { return GetSpType(op) != SP_ERR; } CompilePPUProgram::Branch& CompilePPUProgram::GetBranch(const std::string& name) { for(u32 i=0; i 0 && m_asm[(size_t)p] != '[') p--; p++; std::string dst; if(!GetArg(dst)) { if(m_analyze) WriteHex("error\n"); WriteError(fmt::Format("dst not found. style: %s", GetSpStyle(sp).c_str())); m_error = true; NextLn(); return; } Arg a_dst(dst); DetectArgInfo(a_dst); Branch* dst_branch = NULL; switch(a_dst.type) { case ARG_BRANCH: { Branch& b = GetBranch(dst); if(b.m_addr >= 0 && b.m_id < 0 && b.m_pos < 0) dst_branch = &b; } break; case ARG_ERR: { m_branches.Move(new Branch("", -1, 0)); //TODO: allocated with new, deleted with free() dst_branch = &m_branches[m_branches.GetCount() - 1]; } break; } if(!dst_branch) { if(m_analyze) WriteHex("error\n"); WriteError(fmt::Format("bad dst type. style: %s", GetSpStyle(sp).c_str())); m_error = true; NextLn(); return; } switch(sp) { case SP_INT: case SP_STRING: case SP_STRLEN: case SP_BUF: case SP_NOT: { std::string src1; if(!GetArg(src1, true)) { if(m_analyze) WriteHex("error\n"); WriteError(fmt::Format("src not found. style: %s", GetSpStyle(sp).c_str())); m_error = true; NextLn(); return; } Arg a_src1(src1); DetectArgInfo(a_src1); if(sp == SP_STRLEN ? ~(ARG_TXT | ARG_BRANCH) & a_src1.type : sp == SP_STRING ? ~ARG_TXT & a_src1.type : ~(ARG_IMM | ARG_BRANCH) & a_src1.type) { if(m_analyze) WriteHex("error\n"); WriteError(fmt::Format("bad src type. style: %s", GetSpStyle(sp).c_str())); m_error = true; NextLn(); return; } if(m_asm[(size_t)p - 1] != ']') { if(m_analyze) WriteHex("error\n"); WriteError(fmt::Format("']' not found. style: %s", GetSpStyle(sp).c_str())); m_error = true; NextLn(); return; } if(!CheckEnd()) { if(m_analyze) WriteHex("error\n"); return; } if(sp == SP_STRING) { src1 = src1.substr(1, src1.length()-2); bool founded = false; for(u32 i=0; i> a_src2.value); break; case SP_MUL: *dst_branch = Branch(dst, -1, a_src1.value * a_src2.value); break; case SP_DIV: *dst_branch = Branch(dst, -1, a_src1.value / a_src2.value); break; case SP_ADD: *dst_branch = Branch(dst, -1, a_src1.value + a_src2.value); break; case SP_SUB: *dst_branch = Branch(dst, -1, a_src1.value - a_src2.value); break; case SP_AND: *dst_branch = Branch(dst, -1, a_src1.value & a_src2.value); break; case SP_OR: *dst_branch = Branch(dst, -1, a_src1.value | a_src2.value); break; case SP_XOR: *dst_branch = Branch(dst, -1, a_src1.value ^ a_src2.value); break; case SP_NOR: *dst_branch = Branch(dst, -1, ~(a_src1.value | a_src2.value)); break; } } break; } if(m_analyze) WriteHex("\n"); } void CompilePPUProgram::Compile() { if(m_err_list) { m_err_list->Freeze(); m_err_list->Clear(); } if(m_analyze && m_hex_list) { m_hex_list->Freeze(); m_hex_list->Clear(); } m_code.Clear(); for(u32 i=0; i sections_names; u32 section_name_offset = 1; Elf64_Shdr s_text; memset(&s_text, 0, sizeof(Elf64_Shdr)); s_text.sh_type = 1; s_text.sh_offset = section_offset; s_text.sh_addr = section_offset + 0x10000; s_text.sh_size = text_size; s_text.sh_addralign = 4; s_text.sh_flags = 6; s_text.sh_name = section_name_offset; sections_names.push_back(".text"); section_name_offset += std::string(".text").length() + 1; section_offset += s_text.sh_size; m_text_addr = s_text.sh_addr; struct Module { std::string m_name; Array m_imports; Module(const std::string& name, u32 import) : m_name(name) { Add(import); } void Add(u32 import) { m_imports.AddCpy(import); } void Clear() { m_name.clear(); m_imports.Clear(); } }; Array modules; FirstChar(); while(!IsEnd()) { std::string op; if(!GetOp(op) || !IsFuncOp(op)) { NextLn(); continue; } while(p > 0 && m_asm[(size_t)p] != '[') p--; p++; std::string module, name, id; if(!GetArg(module)) { WriteError("module not found. style: [module, name, id]"); m_error = true; NextLn(); continue; } Arg a_module(module); DetectArgInfo(a_module); if(~ARG_ERR & a_module.type) { WriteError("bad module type. style: [module, name, id]"); m_error = true; NextLn(); continue; } if(!GetArg(name)) { WriteError("name not found. style: [module, name, id]"); m_error = true; NextLn(); continue; } Arg a_name(name); DetectArgInfo(a_name); if(~ARG_ERR & a_name.type) { WriteError("bad name type. style: [module, name, id]"); m_error = true; NextLn(); continue; } if(!GetArg(id, true)) { WriteError("id not found. style: [module, name, id]"); m_error = true; NextLn(); continue; } Arg a_id(id); DetectArgInfo(a_id); if(~ARG_IMM & a_id.type) { WriteError("bad id type. style: [module, name, id]"); m_error = true; NextLn(); continue; } if(m_asm[(size_t)p - 1] != ']') { WriteError("']' not found. style: [module, name, id]"); m_error = true; NextLn(); continue; } if(!CheckEnd()) continue; m_branches.Move(new Branch(name, a_id.value, 0)); //TODO: HACK: new and free() mixed const u32 import = m_branches.GetCount() - 1; bool founded = false; for(u32 i=0; i(op); if(instr) { uint type[] = { ARG_IMM, ARG_REG_R, ARG_REG_F, ARG_REG_V, ARG_REG_CR, }; for(uint i=0; iGetArgCount(); ++i) { switch(instr->GetArg(i).m_type) { case FIELD_BRANCH: SetNextArgBranch(0); //TODO break; default: SetNextArgType(type[instr->GetArg(i).m_type]); break; } } } else { WriteError(fmt::Format("unknown instruction '%s'", op.c_str())); EndLn(); m_error = true; } CheckEnd(); if(m_error) { if(m_analyze) { WriteHex("error\n"); m_error = false; continue; } break; } u32 code; { Array args; args.SetCount(m_args.GetCount()); for(uint i=0; i> 16)); Write32(f, LWZ(12, 12, addr)); Write32(f, STD(2, 1, 40)); Write32(f, LWZ(0, 12, 0)); Write32(f, LWZ(2, 12, 4)); Write32(f, MTSPR(0x009, 0)); Write32(f, BCCTR(20, 0, 0, 0)); } f.Seek(s_lib_stub_top.sh_offset); f.Seek(s_lib_stub_top.sh_size, wxFromCurrent); f.Seek(s_lib_stub.sh_offset); for(u32 i=0, nameoffs=4, dataoffs=0; iThaw(); if(m_analyze) { if(m_hex_list) { m_hex_list->Thaw(); } } else { //TODO: doesn't look portable system("make_fself.cmd"); } }