-
Notifications
You must be signed in to change notification settings - Fork 8
/
cbin_load.c
96 lines (81 loc) · 2.89 KB
/
cbin_load.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "cpu6.h"
#include "cbin.h"
#include <stdio.h>
#include <stdlib.h>
static uint16_t read_word(struct cbin_record* record, size_t offset) {
uint16_t word = record->data[offset] << 8;
word = word | record->data[offset+1];
return word;
}
// Type CBIN_DATA
static void load_data(uint16_t *load_offset, struct cbin_record* record) {
if (record->addr == 0x004c && record->len > 0x1b) {
// This appears to be a convention for the old table loader.
// Seems it can't do multiple sectors, or fixups. So intended for
// tape loading start with a single sector replacement loader,
// loaded to 0x4c, and jump to it;
// This new second stage loader completes the load.
//
// But newer loaders ignore it and just steal the load offset
*load_offset = read_word(record, 0x1b);
return;
}
// Load len bytes of data to addr
for (int i = 0; i < record->len; i++) {
mem_write8_debug(record->addr + i + *load_offset, record->data[i]);
}
}
// Type CBIN_FIXUPS
static void apply_fixups(uint16_t load_offset, struct cbin_record* record) {
uint16_t offset = load_offset + record->addr;
uint16_t fixup_addr;
uint16_t fixup_val;
for (size_t i = 0; i < record->len; i += 2) {
fixup_addr = read_word(record, i);
fixup_val = mem_read16_debug(fixup_addr + load_offset);
fixup_val += offset;
mem_write16_debug(fixup_addr + load_offset, fixup_val);
}
}
// Loads a Centurion binary file directly into memory
uint16_t cbin_load(const char *name, uint16_t load_offset) {
uint16_t entry_addr = 0;
struct cbin_record* record = NULL;
cbin_state* cbin = cbin_open(name);
while ((record = cbin_next_record(cbin))) {
switch (record->type)
{
case CBIN_DATA:
if (record->len == 0) {
// a zero length data record is the entry address
entry_addr = record->addr + load_offset;
} else {
load_data(&load_offset, record); // Might modify load_offset
}
break;
case CBIN_FIXUPS:
// Apply fixups
if (record->len % 2 == 1){
fprintf(stderr, "FIXUPS record must have even length");
cbin_error(cbin);
}
apply_fixups(load_offset, record);
break;
default:
fprintf(stderr, "unknown type %02x\n", record->type);
cbin_error(cbin);
}
}
// Finished loading
if (entry_addr && cbin_finished(cbin)) {
printf("Centurion Binary %s loaded; entry at %04hx\n\n", name, entry_addr);
cbin_free(cbin);
return entry_addr;
}
if (!cbin_errored(cbin) && entry_addr == 0) {
fprintf(stderr, "Couldn't find entry point\n");
}
printf("Centurion Binary loading of %s failed\n", name);
cbin_free(cbin);
exit(1);
}