GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
disasm.c
Go to the documentation of this file.
1#include "disasm.h"
2#include "opcodes.h"
3#include <stdio.h>
4
5/* Instruction format (operand shape), used only for text formatting here. */
6typedef enum {
7 D_JU, /* PM: "mn addr" */
8 D_BRANCH, /* PM: "mn mask, addr" */
9 D_REG, /* PM: "mn N, addr" */
10 D_IMM, /* PM: "mn K, addr" */
11 D_PER, /* PM: "mn aux, addr" */
12 D_SS1, /* SS: "mn len, A1, A2" */
13 D_SS2 /* SS: "mn l1, l2, A1, A2" */
15
16struct dmnem { uint8_t op; const char *name; dfmt_t fmt; };
17
18/* Opcode bytes come straight from opcodes.h (single source of truth). The P
19 * format (HLT/NOP2 and the 0x02 group) and the 0x53 sense group are decoded by
20 * second char / aux in ge_disasm_one() rather than from this table. */
21static const struct dmnem DTAB[] = {
22 { JC_OPCODE, "JC", D_BRANCH },
23 { JCC_OPCODE, "JCC", D_BRANCH },
24 { JU_OPCODE, "JU", D_JU },
25 { JRT_OPCODE, "JRT", D_PER },
26 { LR_OPCODE, "LR", D_REG },
27 { STR_OPCODE, "STR", D_REG },
28 { LA_OPCODE, "LA", D_REG },
29 { CMR_OPCODE, "CMR", D_REG },
30 { AMR_OPCODE, "AMR", D_REG },
31 { SMR_OPCODE, "SMR", D_REG },
32 { MVI_OPCODE, "MVI", D_IMM },
33 { NI_OPCODE, "NI", D_IMM },
34 { CMI_OPCODE, "CMI", D_IMM },
35 { CI_OPCODE, "CI", D_IMM },
36 { XI_OPCODE, "XI", D_IMM },
37 { TM_OPCODE, "TM", D_IMM },
38 { PER_OPCODE, "PER", D_PER },
39 { PERI_OPCODE, "PERI", D_PER },
40 { RDC_OPCODE, "RDC", D_PER },
41 { LPSR_OPCODE, "LPSR", D_PER },
42 { MVC_OPCODE, "MVC", D_SS1 },
43 { NC_OPCODE, "NC", D_SS1 },
44 { CMC_OPCODE, "CMC", D_SS1 },
45 { OC_OPCODE, "OC", D_SS1 },
46 { XC_OPCODE, "XC", D_SS1 },
47 { TL_OPCODE, "TL", D_SS1 },
48 { MVQ_OPCODE, "MVQ", D_SS1 },
49 { CMQ_OPCODE, "CMQ", D_SS1 },
50 { EDT_OPCODE, "EDT", D_SS1 },
51 { SR_OPCODE, "SR", D_SS1 },
52 { SL_OPCODE, "SL", D_SS1 },
53 { PK_OPCODE, "PK", D_SS2 },
54 { UPK_OPCODE, "UPK", D_SS2 },
55 { PKS_OPCODE, "PKS", D_SS2 },
56 { UPKS_OPCODE, "UPKS", D_SS2 },
57 { MVP_OPCODE, "MVP", D_SS2 },
58 { CMP_OPCODE, "CMP", D_SS2 },
59 { AP_OPCODE, "AP", D_SS2 },
60 { SP_OPCODE, "SP", D_SS2 },
61 { MP_OPCODE, "MP", D_SS2 },
62 { DP_OPCODE, "DP", D_SS2 },
63 { AD_OPCODE, "AD", D_SS2 },
64 { SD_OPCODE, "SD", D_SS2 },
65 { AB_OPCODE, "AB", D_SS2 },
66 { SB_OPCODE, "SB", D_SS2 },
67};
68#define NDTAB ((int)(sizeof(DTAB) / sizeof(DTAB[0])))
69
70static const struct dmnem *dlookup(uint8_t op)
71{
72 for (int i = 0; i < NDTAB; i++)
73 if (DTAB[i].op == op)
74 return &DTAB[i];
75 return NULL;
76}
77
78/* Address field: bit 15 set => modified field disp(N); else raw hex. */
79static void fmt_addr(uint16_t field, char *buf, size_t n)
80{
81 if (field & 0x8000)
82 snprintf(buf, n, "0x%03X(%d)", field & 0x0FFF, (field >> 12) & 7);
83 else
84 snprintf(buf, n, "0x%04X", field);
85}
86
87int ge_disasm_one(const uint8_t *mem, uint16_t addr, char *out, size_t outn)
88{
89 uint8_t op = mem[addr];
90 char a1[24], a2[24];
91
92 /* ---- P format (top two bits 00) ---- */
93 if (op < 0x40) {
94 uint8_t c2 = mem[(uint16_t)(addr + 1)];
95 const char *mn = NULL;
96
97 if (op == HLT_OPCODE)
98 mn = "HLT";
99 else if (op == NOP2_OPCODE)
100 mn = "NOP2";
101 else if (op == LON_OPCODE) { /* the 0x02 sub-function group */
102 if (c2 == ENS_2NDCHAR) mn = "ENS";
103 else if (c2 == INS_2NDCHAR) mn = "INS";
104 else if (c2 == LOFF_2NDCHAR) mn = "LOFF";
105 else if (c2 == LON_2NDCHAR) mn = "LON";
106 else if (c2 == LOLL_2NDCHAR) mn = "LOLL";
107 }
108
109 if (!mn) {
110 snprintf(out, outn, "DB 0x%02X", op);
111 return 1;
112 }
113 snprintf(out, outn, "%s", mn);
114 return 2;
115 }
116
117 /* ---- PM format (4 bytes) ---- */
118 if (op < 0xC0) {
119 uint8_t aux = mem[(uint16_t)(addr + 1)];
120 uint16_t field = (uint16_t)((mem[(uint16_t)(addr + 2)] << 8) |
121 mem[(uint16_t)(addr + 3)]);
122
123 if (op == JS1_OPCODE) { /* 0x53 sense group, by aux */
124 const char *mn = (aux == JS1_2NDCHAR) ? "JS1"
125 : (aux == JS2_2NDCHAR) ? "JS2"
126 : (aux == JIE_2NDCHAR) ? "JIE" : NULL;
127 if (!mn) { snprintf(out, outn, "DB 0x%02X", op); return 1; }
128 fmt_addr(field, a1, sizeof a1);
129 snprintf(out, outn, "%s %s", mn, a1);
130 return 4;
131 }
132
133 const struct dmnem *m = dlookup(op);
134 if (!m) { snprintf(out, outn, "DB 0x%02X", op); return 1; }
135 fmt_addr(field, a1, sizeof a1);
136
137 switch (m->fmt) {
138 case D_JU:
139 snprintf(out, outn, "%s %s", m->name, a1);
140 return 4;
141 case D_BRANCH:
142 snprintf(out, outn, "%s 0x%02X, %s", m->name, aux & 0xF0, a1);
143 return 4;
144 case D_REG:
145 /* Register-code aux char is 1XXX0000: N = bits 4-6. */
146 snprintf(out, outn, "%s %d, %s", m->name, (aux >> 4) & 7, a1);
147 return 4;
148 case D_IMM:
149 case D_PER:
150 snprintf(out, outn, "%s 0x%02X, %s", m->name, aux, a1);
151 return 4;
152 default:
153 snprintf(out, outn, "DB 0x%02X", op);
154 return 1;
155 }
156 }
157
158 /* ---- SS format (6 bytes) ---- */
159 {
160 const struct dmnem *m = dlookup(op);
161 if (!m) { snprintf(out, outn, "DB 0x%02X", op); return 1; }
162 uint8_t ll = mem[(uint16_t)(addr + 1)];
163 uint16_t A1 = (uint16_t)((mem[(uint16_t)(addr + 2)] << 8) |
164 mem[(uint16_t)(addr + 3)]);
165 uint16_t A2 = (uint16_t)((mem[(uint16_t)(addr + 4)] << 8) |
166 mem[(uint16_t)(addr + 5)]);
167 fmt_addr(A1, a1, sizeof a1);
168 fmt_addr(A2, a2, sizeof a2);
169 if (m->fmt == D_SS1)
170 snprintf(out, outn, "%s %d, %s, %s", m->name, ll + 1, a1, a2);
171 else
172 snprintf(out, outn, "%s %d, %d, %s, %s", m->name,
173 ((ll >> 4) & 0xf) + 1, (ll & 0xf) + 1, a1, a2);
174 return 6;
175 }
176}
177
178int ge_disasm_window(const uint8_t *mem, uint16_t pc,
179 int before, int after, char *out, size_t outn)
180{
181 char text[64];
182
183 /* Resync: variable-length instructions can't be decoded backwards, so try
184 * each start from pc-1 .. pc-16 and keep the furthest-back one whose forward
185 * decode chain lands exactly on pc — that gives aligned preceding lines. */
186 long start = pc;
187 for (int lb = 1; lb <= 16; lb++) {
188 long s = (long)pc - lb;
189 if (s < 0)
190 break;
191 long a = s;
192 while (a < pc) {
193 int l = ge_disasm_one(mem, (uint16_t)a, text, sizeof text);
194 a += (l > 0) ? l : 1;
195 }
196 if (a == pc)
197 start = s; /* aligned; prefer the longest such run */
198 }
199
200 /* Collect instruction start addresses from `start` to a bit past pc. */
201 uint16_t addrs[80];
202 int n = 0;
203 long a = start;
204 while (a <= (long)pc + 48 && n < (int)(sizeof addrs / sizeof addrs[0])) {
205 addrs[n++] = (uint16_t)a;
206 int l = ge_disasm_one(mem, (uint16_t)a, text, sizeof text);
207 a += (l > 0) ? l : 1;
208 }
209
210 int cur = -1;
211 for (int i = 0; i < n; i++)
212 if (addrs[i] == pc) { cur = i; break; }
213 if (cur < 0) { addrs[0] = pc; cur = 0; n = 1; } /* fallback: pc only */
214
215 int lo = cur - before; if (lo < 0) lo = 0;
216 int hi = cur + after; if (hi > n - 1) hi = n - 1;
217
218 size_t used = 0;
219 out[0] = '\0';
220 for (int i = lo; i <= hi; i++) {
221 uint16_t ad = addrs[i];
222 int l = ge_disasm_one(mem, ad, text, sizeof text);
223 if (l <= 0) l = 1;
224
225 char hex[24];
226 int hp = 0;
227 for (int k = 0; k < l && hp < (int)sizeof(hex) - 3; k++)
228 hp += snprintf(hex + hp, sizeof(hex) - (size_t)hp, "%02X ",
229 mem[(uint16_t)(ad + k)]);
230
231 int wrote = snprintf(out + used, outn - used, "%c%04X: %-14s %s\n",
232 (i == cur) ? '>' : ' ', ad, hex, text);
233 if (wrote < 0 || (size_t)wrote >= outn - used)
234 break;
235 used += (size_t)wrote;
236 }
237
238 return cur - lo;
239}
#define NDTAB
Definition disasm.c:68
dfmt_t
Definition disasm.c:6
@ D_SS1
Definition disasm.c:12
@ D_IMM
Definition disasm.c:10
@ D_SS2
Definition disasm.c:13
@ D_REG
Definition disasm.c:9
@ D_BRANCH
Definition disasm.c:8
@ D_PER
Definition disasm.c:11
@ D_JU
Definition disasm.c:7
int ge_disasm_window(const uint8_t *mem, uint16_t pc, int before, int after, char *out, size_t outn)
Definition disasm.c:178
int ge_disasm_one(const uint8_t *mem, uint16_t addr, char *out, size_t outn)
Definition disasm.c:87
static const struct dmnem * dlookup(uint8_t op)
Definition disasm.c:70
static const struct dmnem DTAB[]
Definition disasm.c:21
static void fmt_addr(uint16_t field, char *buf, size_t n)
Definition disasm.c:79
#define JCC_OPCODE
Definition opcodes.h:41
#define LON_OPCODE
Definition opcodes.h:18
#define SP_OPCODE
Definition opcodes.h:75
#define AB_OPCODE
Definition opcodes.h:84
#define LPSR_OPCODE
Definition opcodes.h:50
#define XC_OPCODE
Definition opcodes.h:65
#define NC_OPCODE
Definition opcodes.h:62
#define SD_OPCODE
Definition opcodes.h:83
#define LOFF_2NDCHAR
Definition opcodes.h:16
#define SL_OPCODE
Definition opcodes.h:69
#define CMP_OPCODE
Definition opcodes.h:73
#define MP_OPCODE
Definition opcodes.h:76
#define PERI_OPCODE
Definition opcodes.h:49
#define LON_2NDCHAR
Definition opcodes.h:19
#define CMC_OPCODE
Definition opcodes.h:63
#define UPK_OPCODE
Definition opcodes.h:66
#define NI_OPCODE
Definition opcodes.h:45
#define JS1_2NDCHAR
Definition opcodes.h:36
#define INS_2NDCHAR
Definition opcodes.h:13
#define PKS_OPCODE
Definition opcodes.h:78
#define STR_OPCODE
Definition opcodes.h:53
#define AD_OPCODE
Definition opcodes.h:82
#define JRT_OPCODE
Definition opcodes.h:38
#define OC_OPCODE
Definition opcodes.h:64
#define DP_OPCODE
Definition opcodes.h:77
#define SB_OPCODE
Definition opcodes.h:85
#define HLT_OPCODE
Definition opcodes.h:25
#define XI_OPCODE
Definition opcodes.h:48
#define LOLL_2NDCHAR
Definition opcodes.h:22
#define LR_OPCODE
Definition opcodes.h:54
#define JC_OPCODE
Definition opcodes.h:39
#define PER_OPCODE
Definition opcodes.h:51
#define CMI_OPCODE
Definition opcodes.h:46
#define MVC_OPCODE
Definition opcodes.h:61
#define AP_OPCODE
Definition opcodes.h:74
#define EDT_OPCODE
Definition opcodes.h:71
#define MVI_OPCODE
Definition opcodes.h:44
#define SMR_OPCODE
Definition opcodes.h:57
#define PK_OPCODE
Definition opcodes.h:68
#define NOP2_OPCODE
Definition opcodes.h:24
#define JU_OPCODE
Definition opcodes.h:40
#define SR_OPCODE
Definition opcodes.h:67
#define TL_OPCODE
Definition opcodes.h:70
#define MVQ_OPCODE
Definition opcodes.h:80
#define MVP_OPCODE
Definition opcodes.h:72
#define JS1_OPCODE
Definition opcodes.h:35
#define AMR_OPCODE
Definition opcodes.h:56
#define LA_OPCODE
Definition opcodes.h:42
#define CI_OPCODE
Definition opcodes.h:47
#define CMQ_OPCODE
Definition opcodes.h:81
#define ENS_2NDCHAR
Definition opcodes.h:10
#define JS2_2NDCHAR
Definition opcodes.h:33
#define RDC_OPCODE
Definition opcodes.h:52
#define CMR_OPCODE
Definition opcodes.h:55
#define TM_OPCODE
Definition opcodes.h:43
#define JIE_2NDCHAR
Definition opcodes.h:30
#define UPKS_OPCODE
Definition opcodes.h:79
Definition disasm.c:16
dfmt_t fmt
Definition disasm.c:16
uint8_t op
Definition disasm.c:16
const char * name
Definition disasm.c:16