regs/src/omf.cc

417 lines
12 KiB
C++
Raw Permalink Normal View History

2020-02-15 00:08:27 +00:00
/** @copyright 2020 Sean Kasun */
#include "omf.h"
2020-02-18 06:05:37 +00:00
#include <algorithm>
2020-02-15 00:08:27 +00:00
2020-02-17 23:58:22 +00:00
enum SegOp {
DONE = 0x00,
RELOC = 0xe2,
INTERSEG = 0xe3,
DS = 0xf1,
LCONST = 0xf2,
cRELOC = 0xf5,
cINTERSEG = 0xf6,
SUPER = 0xf7,
};
2020-02-19 00:03:29 +00:00
OMF::OMF() {
2020-02-15 00:08:27 +00:00
}
2020-02-18 06:05:37 +00:00
static bool compareSegments(const Segment &a, const Segment &b) {
return a.mapped < b.mapped;
}
2024-05-22 02:19:11 +00:00
void Segment::initDPS() {
kind = 0x12;
length = 1024;
resspc = 1024;
bytecnt = 0;
name = "Direct";
}
bool Segment::isDPS() {
return (kind & 0x1f) == 0x12;
}
2024-05-22 04:26:08 +00:00
bool Segment::isJump() {
return (kind & 0x1f) == 0x02;
}
2024-05-22 02:19:11 +00:00
2020-02-19 00:03:29 +00:00
bool OMF::load(const char *filename, uint32_t org) {
2020-02-15 00:08:27 +00:00
handle = TheHandle::createFromFile(filename);
if (!handle->isOpen()) {
return false;
}
2020-02-15 00:08:27 +00:00
if (!isOMF()) {
Segment seg;
seg.bytecnt = handle->length;
2020-02-17 05:18:50 +00:00
seg.kind = 0; // code
2020-02-19 00:03:29 +00:00
seg.entry = 0;
seg.mapped = org;
2020-02-18 06:05:37 +00:00
seg.data = handle;
2020-02-21 23:57:11 +00:00
seg.length = seg.bytecnt;
seg.segnum = 1;
2020-02-15 00:08:27 +00:00
segments.push_back(seg);
} else {
if (!loadSegments()) {
return false;
}
if (!mapSegments()) {
return false;
}
if (!relocSegments()) {
return false;
}
}
2020-02-18 06:05:37 +00:00
std::sort(segments.begin(), segments.end(), compareSegments);
2020-02-15 00:08:27 +00:00
return true;
}
bool OMF::isOMF() {
if (handle->length < 32) {
return false;
}
uint64_t ofs = 0;
for (ofs = 0; ofs < handle->length - 16;) {
handle->seek(ofs);
uint32_t bytecnt = handle->r32();
handle->skip(11);
uint8_t version = handle->r8();
if (version == 1) {
2020-02-17 23:58:22 +00:00
ofs += bytecnt * 512;
2020-02-15 00:08:27 +00:00
} else if (version == 2) {
2020-02-17 23:58:22 +00:00
ofs += bytecnt;
2020-02-15 00:08:27 +00:00
} else {
return false;
}
}
return ofs == handle->length;
}
bool OMF::loadSegments() {
handle->seek(0);
for (uint64_t ofs = 0; ofs < handle->length;) {
Segment seg;
handle->seek(ofs);
seg.bytecnt = handle->r32();
seg.resspc = handle->r32();
seg.length = handle->r32();
auto kind = handle->r8();
seg.lablen = handle->r8();
seg.numlen = handle->r8();
auto version = handle->r8();
seg.banksize = handle->r32();
seg.kind = handle->r16();
handle->skip(2); // undefined
seg.org = handle->r32();
seg.align = handle->r32();
handle->skip(2); // byte ordering
seg.segnum = handle->r16();
seg.entry = handle->r32();
auto dispname = handle->r16();
auto dispdata = handle->r16();
2024-05-21 10:59:34 +00:00
auto skip = 0;
2020-02-15 00:08:27 +00:00
if (seg.lablen == 0) {
2024-05-21 10:59:34 +00:00
skip = 1;
2020-02-15 00:08:27 +00:00
handle->seek(ofs + dispname + 0xa);
seg.lablen = handle->r8();
if (seg.lablen == 0) {
seg.lablen = 0xa;
}
}
2024-05-21 10:59:34 +00:00
// there are 2 names, one is always 10 bytes at dispname,
// this is the "loadname" and it specifies the name by the linker
// it is followed by the segname, which is the actual segment name.
2020-02-28 19:11:56 +00:00
// check if load name is valid
2024-05-21 10:59:34 +00:00
handle->seek(ofs + dispname + 0xa + skip);
2020-02-15 00:08:27 +00:00
seg.name = handle->read(seg.lablen);
seg.offset = ofs + dispdata;
if (version == 1) { // convert to v2
seg.bytecnt *= 512;
seg.kind = (kind & 0xf1f) | ((kind & 0xe0) << 8);
}
seg.mapped = 0;
ofs += seg.bytecnt;
segments.push_back(seg);
}
2020-02-17 23:58:22 +00:00
return true;
2020-02-15 00:08:27 +00:00
}
bool OMF::mapSegments() {
// use a memory map that denotes runs of available ram
2020-02-18 06:05:37 +00:00
std::vector<uint32_t> memory;
2024-05-22 02:19:11 +00:00
memory.push_back(0x300); // min
2020-02-18 06:05:37 +00:00
memory.push_back(0x7f0000); // max
2020-02-15 00:08:27 +00:00
// first map any segments that know where they belong
for (auto &seg : segments) {
2024-05-22 02:19:11 +00:00
seg.map(memory, false);
std::sort(memory.begin(), memory.end());
2020-02-15 00:08:27 +00:00
}
// now map everything else by first fit
2024-05-22 02:19:11 +00:00
for (auto &seg : segments) { // map.true
seg.map(memory, true);
std::sort(memory.begin(), memory.end());
}
// create direct-page if it doesn't exist
bool found = false;
2020-02-15 00:08:27 +00:00
for (auto &seg : segments) {
2024-05-22 02:19:11 +00:00
if (seg.isDPS()) {
found = true;
}
}
if (!found) {
Segment seg;
seg.initDPS();
seg.map(memory, true);
segments.push_back(seg);
}
return true;
}
bool Segment::map(std::vector<uint32_t> &memory, bool force) {
if (mapped) {
return false;
}
if (org && ((org & 0xffff) != 0 || (kind & 0x800) == 0)) {
mapped = org;
// verif we're not overlapping
for (auto m : memory) {
if (mapped < m && mapped + length > m) {
fprintf(stderr, "Segment %d (%s) collides with another segment!\n", segnum, name.c_str());
2020-02-15 00:08:27 +00:00
return false;
}
}
2024-05-22 02:19:11 +00:00
memory.push_back(mapped);
memory.push_back(mapped + length);
if (mapped < memory[0]) { // below the min?
memory[0] = mapped;
}
return true;
2020-02-15 00:08:27 +00:00
}
2024-05-22 02:19:11 +00:00
if (force) {
for (auto it = memory.begin(); it != memory.end(); it++) {
auto base = *it;
// skip special?
if ((kind & 0x1f) != 0x12 && (base & 0xff0000) == 0) {
base += 0x10000; // not in bank 0
}
// are we aligned?
if (align != 0 && (base & (align - 1)) != 0) {
base += align;
base &= ~(align - 1);
}
// does it cross bank boundaries?
if (banksize != 0 &&
(((base & (banksize - 1)) + length) & ~(banksize - 1)) != 0) {
base += banksize;
base &= ~(banksize - 1);
}
// does it fit?!
it++;
uint32_t end = *it;
if (base < end && end - base >= length) {
mapped = base;
memory.push_back(mapped);
memory.push_back(mapped + length);
return true;
}
}
fprintf(stderr, "Segment %d (%s) doesn't fit in memory\n", segnum, name.c_str());
}
return false;
2020-02-15 00:08:27 +00:00
}
bool OMF::relocSegments() {
for (auto &seg : segments) {
auto done = false;
auto pc = seg.mapped;
auto data = std::vector<uint8_t>(seg.length);
handle->seek(seg.offset);
while (!done) {
auto opcode = handle->r8();
switch (opcode) {
case DONE:
done = true;
break;
case RELOC:
{
auto numBytes = handle->r8();
auto bitShift = static_cast<int8_t>(handle->r8());
auto offset = handle->r32();
auto subOffset = handle->r32() + seg.mapped;
if (bitShift < 0) {
subOffset >>= -bitShift;
} else {
subOffset <<= bitShift;
}
patch(data, offset, numBytes, subOffset);
}
break;
case INTERSEG:
{
auto numBytes = handle->r8();
auto bitShift = static_cast<int8_t>(handle->r8());
auto offset = handle->r32();
handle->skip(2); // filenum
auto segnum = handle->r16();
auto subOffset = handle->r32();
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
if (bitShift < 0) {
subOffset >>= -bitShift;
} else {
subOffset <<= bitShift;
}
patch(data, offset, numBytes, subOffset);
}
break;
case DS:
pc += handle->r32(); // filled with zeros
break;
case LCONST:
{
auto count = handle->r32();
2020-02-17 23:58:22 +00:00
auto v = handle->readBytes(count);
std::copy(v.begin(), v.end(), data.begin() + (pc - seg.mapped));
2020-02-15 00:08:27 +00:00
pc += count;
}
break;
case cRELOC:
{
auto numBytes = handle->r8();
auto bitShift = static_cast<int8_t>(handle->r8());
auto offset = handle->r16();
auto subOffset = handle->r16() + seg.mapped;
if (bitShift < 0) {
subOffset >>= -bitShift;
} else {
subOffset <<= bitShift;
}
patch(data, offset, numBytes, subOffset);
}
break;
case cINTERSEG:
{
auto numBytes = handle->r8();
auto bitShift = static_cast<int8_t>(handle->r8());
auto offset = handle->r16();
auto segnum = handle->r8();
int32_t subOffset = handle->r16();
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
if (bitShift < 0) {
subOffset >>= -bitShift;
} else {
subOffset <<= bitShift;
}
patch(data, offset, numBytes, subOffset);
}
break;
case SUPER:
{
auto superLen = handle->r32();
superLen += handle->tell();
auto superType = handle->r8();
int32_t superPage = 0;
while (handle->tell() < superLen) {
auto numOfs = handle->r8();
if (numOfs & 0x80) {
2020-02-17 23:58:22 +00:00
superPage += 256 * (numOfs & 0x7f);
2020-02-15 00:08:27 +00:00
continue;
}
auto subHandle = TheHandle::createFromArray(data);
for (auto o = 0; o <= numOfs; o++) {
auto offset = superPage | handle->r8();
uint8_t numBytes = 0;
subHandle->seek(offset);
2020-02-17 23:58:22 +00:00
int32_t subOffset = subHandle->r16();
2020-02-15 00:08:27 +00:00
if (superType == 0 || superType == 1) {
subOffset += seg.mapped;
numBytes = superType + 2;
} else if (superType < 14) { // INTERSEG1-12
auto segnum = subHandle->r8();
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
numBytes = 3;
} else if (superType < 26) { // INTERSEG13-14
auto segnum = superType - 13;
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
numBytes = 2;
} else { // INTERSEG25-36
auto segnum = superType - 25;
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
subOffset >>= 16;
numBytes = 2;
}
patch(data, offset, numBytes, subOffset);
}
superPage += 256;
}
}
break;
default:
if (opcode < 0xe0) {
2020-02-17 23:58:22 +00:00
auto v = handle->readBytes(opcode);
std::copy(v.begin(), v.end(), data.begin() + (pc - seg.mapped));
2020-02-15 00:08:27 +00:00
pc += opcode;
} else {
fprintf(stderr, "Unknown segment opcode: $%02x\n", opcode);
return false;
}
break;
}
}
2024-05-22 04:26:08 +00:00
if (seg.isJump()) { // patch jumptable
for (int i = 8; i < seg.length; i += 14) {
uint16_t segnum = data[i + 4] | (data[i + 5] << 8);
int32_t subOffset = data[i + 6] | (data[i + 7] << 8) |
(data[i + 8] << 16) | (data[i + 9] << 24);
for (auto &sub : segments) {
if (sub.segnum == segnum) {
subOffset += sub.mapped;
break;
}
}
patch(data, i + 11, 3, subOffset);
}
}
2020-02-15 00:08:27 +00:00
seg.data = TheHandle::createFromArray(data);
}
return true;
}
void OMF::patch(std::vector<uint8_t> &array, uint32_t offset, uint8_t numBytes,
uint32_t value) {
for (int i = 0; i < numBytes; i++, value >>= 8) {
array[offset + i] = value & 0xff;
}
}
2020-02-18 06:05:37 +00:00
std::vector<Segment> OMF::get() const {
return segments;
}