GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
alu_reg.c
Go to the documentation of this file.
1
13#include <stdint.h>
14#include <string.h>
15#include "ge.h"
16#include "alu_cc.h"
17#include "alu_reg.h"
18
19/* =========================================================================
20 * Internal helpers
21 * =========================================================================*/
22
27static inline uint16_t mem_read16(struct ge *ge, uint16_t addr)
28{
29 return (uint16_t)(((uint16_t)ge->mem[addr] << 8) |
30 (uint16_t)ge->mem[(uint16_t)(addr + 1)]);
31}
32
36static inline void mem_write16(struct ge *ge, uint16_t addr, uint16_t val)
37{
38 ge_mem_store8(ge, addr, (uint8_t)(val >> 8));
39 ge_mem_store8(ge, (uint16_t)(addr + 1), (uint8_t)(val & 0xFF));
40}
41
42/* =========================================================================
43 * LR — Load Register (0xBC)
44 * §5.6.4.1: "The content of the memory field, addressed by 11, is
45 * transferred to the N index register."
46 * Qualitative result: not interested.
47 * =========================================================================*/
48void alu_lr(struct ge *ge, uint16_t *reg, uint16_t addr)
49{
50 *reg = mem_read16(ge, addr);
51}
52
53/* =========================================================================
54 * STR — Store Register (0x84)
55 * §5.6.4.5: "The content of the N register is transferred to the
56 * memory field with address 11."
57 * Qualitative result: not interested.
58 * =========================================================================*/
59void alu_str(struct ge *ge, uint16_t reg_val, uint16_t addr)
60{
61 mem_write16(ge, addr, reg_val);
62}
63
64/* =========================================================================
65 * CMR — Compare Memory to Register (0xBD)
66 * §5.6.4.4: purely qualitative result, no operand modification.
67 *
68 * CC encoding (FA04=bit4, FA05=bit5 of ffFI; cc=(FA04<<1)|FA05):
69 * FA04 FA05 cc meaning
70 * 0 0 0 "Not possible" — never set
71 * 0 1 1 Register < Memory → ALU_CC_LOW (1)
72 * 1 0 2 Register == Memory → ALU_CC_EQUAL (2)
73 * 1 1 3 Register > Memory → ALU_CC_HIGH (3)
74 *
75 * Confidence: HIGH (direct table from §5.6.4.4 and confirmed in §5.10.2)
76 * =========================================================================*/
77void alu_cmr(struct ge *ge, uint16_t reg_val, uint16_t value)
78{
79 uint8_t cc;
80
81 if (reg_val < value)
82 cc = ALU_CC_LOW; /* 1 */
83 else if (reg_val == value)
84 cc = ALU_CC_EQUAL; /* 2 — "Reg equal to memory" maps to FA04=1,FA05=0 */
85 else
86 cc = ALU_CC_HIGH; /* 3 — "Reg greater than memory" */
87
88 alu_set_cc(ge, cc);
89}
90
91/* =========================================================================
92 * AMR — Add Memory to Register (0xBE)
93 * §5.6.4.2: register = register + memory_value; result left in register.
94 * Performed as 16-bit binary arithmetic; carry beyond 16 bits is lost
95 * ("overflow").
96 *
97 * CC encoding (§5.6.4.2) — ADD-magnitude table:
98 * FA04 FA05 cc meaning
99 * 0 0 0 result == 0, no overflow (raw 0)
100 * 0 1 1 result != 0, no overflow (raw 1)
101 * 1 0 2 overflow, partial==0 (raw 2)
102 * 1 1 3 overflow, partial!=0 (raw 3)
103 *
104 * Carry flip-flops:
105 * URPE = carry out of bit 15 (the "overflow" carry, bit 16 of 32-bit sum)
106 * URPU = carry out of bit 14 (carry into bit 15)
107 *
108 * Confidence: HIGH (direct table §5.6.4.2)
109 * =========================================================================*/
110void alu_amr(struct ge *ge, uint16_t *reg, uint16_t value)
111{
112 uint32_t full = (uint32_t)(*reg) + (uint32_t)value;
113 uint16_t result = (uint16_t)(full & 0xFFFF);
114
115 /* Carry out of bit 15 → overflow */
116 uint8_t carry_out = (full >> 16) & 1;
117
118 /*
119 * Carry out of bit 14 = carry into bit 15.
120 * This is the carry from the low 15 bits into bit 15:
121 * bit-15 of the sum is produced by (a[14] + b[14] + carry_into_14).
122 * We can detect it as: if the top bit of result differs from
123 * what a plain 15-bit addition would give.
124 * Simpler: carry_into_15 = carry out of bit 14 = ((a & 0x7FFF) + (b & 0x7FFF)) >> 15
125 */
126 uint8_t carry_into_15 = (uint8_t)(
127 (((uint32_t)(*reg & 0x7FFF)) + ((uint32_t)(value & 0x7FFF))) >> 15
128 );
129
130 ge->URPE = carry_out;
131 ge->URPU = carry_into_15;
132
133 *reg = result;
134
135 /* CC: overflow flag = FA04 = carry_out; nonzero flag = FA05 = (result!=0) */
136 uint8_t cc = (uint8_t)((carry_out << 1) | (result != 0 ? 1 : 0));
137 alu_set_cc(ge, cc);
138}
139
140/* =========================================================================
141 * SMR — Subtract Memory from Register (0xBF)
142 * §5.6.4.3: register = register - memory_value.
143 * Result is in 2's-complement form; negative stays complemented.
144 *
145 * CC encoding (§5.6.4.3) — SIGNED-result table:
146 * FA04 FA05 cc meaning
147 * 0 0 0 "Not possible" — never set
148 * 0 1 1 result < 0 (complemented) → ALU_CC_NEG (1)
149 * 1 0 2 result == 0 → ALU_CC_ZERO (2)
150 * 1 1 3 result > 0 → ALU_CC_POS (3)
151 *
152 * For signed interpretation we compare as signed 16-bit integers.
153 * The subtraction is implemented as addition of 2's complement of value,
154 * consistent with the GE-130 hardware.
155 *
156 * Carry flip-flops:
157 * URPE = borrow out of bit 15 (set when result wrapped, i.e. reg < value unsigned)
158 * URPU = borrow into bit 15
159 *
160 * Confidence: HIGH (direct table §5.6.4.3)
161 * =========================================================================*/
162void alu_smr(struct ge *ge, uint16_t *reg, uint16_t value)
163{
164 uint16_t result = (uint16_t)(*reg - value);
165
166 /*
167 * Borrow: in subtract-as-add-complement, carry out = NOT borrow.
168 * Unsigned borrow out: reg < value → borrow (URPE=1).
169 */
170 uint8_t borrow_out = (*reg < value) ? 1 : 0;
171
172 /*
173 * Borrow into bit 15: borrow from lower 15 bits.
174 * = 1 if (reg & 0x7FFF) < (value & 0x7FFF)
175 */
176 uint8_t borrow_into_15 = (uint8_t)((*reg & 0x7FFF) < (value & 0x7FFF) ? 1 : 0);
177
178 ge->URPE = borrow_out;
179 ge->URPU = borrow_into_15;
180
181 *reg = result;
182
183 /*
184 * CC based on signed 16-bit interpretation of result:
185 * NOTE: §5.6.4.3 says "result < 0 (complemented)" for FA05=1.
186 * Signed comparison:
187 */
188 uint8_t cc;
189 int16_t sresult = (int16_t)result;
190
191 if (sresult < 0)
192 cc = ALU_CC_NEG; /* 1 */
193 else if (sresult == 0)
194 cc = ALU_CC_ZERO; /* 2 */
195 else
196 cc = ALU_CC_POS; /* 3 */
197
198 alu_set_cc(ge, cc);
199}
200
201/* =========================================================================
202 * LA — Load Address (0x68)
203 * §5.6.4.6: "The address 11 specified in the instruction is stored in the
204 * N register, after it has been modified by possible indexing."
205 * No memory data fetch. Qualitative result: not interested.
206 * =========================================================================*/
207void alu_la(struct ge *ge, uint16_t *reg, uint16_t addr)
208{
209 (void)ge; /* no CC change, no memory access */
210 *reg = addr;
211}
212
213/* =========================================================================
214 * SR — Search Right (0xD9)
215 * §5.5.4.1: scan field left-to-right for model byte; deposit result in
216 * register 7 (r7 pointer supplied by caller).
217 *
218 * Result address:
219 * Found at offset i: r7 = field + i + 1
220 * Not found: r7 = field + len
221 *
222 * Qualitative result: "not interested" per §5.5.4.
223 *
224 * UNCERTAINTY: The manual says "The address 11 is the one to the left"
225 * (i.e. field parameter = leftmost address). Confirmed: field is leftmost.
226 * Confidence: MEDIUM (OCR of §5.5.4.1 truncated; logic is symmetric to SL)
227 * =========================================================================*/
228void alu_sr(struct ge *ge, uint16_t *r7,
229 uint16_t field, uint16_t len, uint8_t model)
230{
231 uint16_t i;
232
233 for (i = 0; i < len; i++) {
234 if (ge->mem[(uint16_t)(field + i)] == model) {
235 *r7 = (uint16_t)(field + i + 1);
236 alu_set_cc(ge, 1u); /* found -> FA05=1 */
237 return;
238 }
239 }
240
241 /* Not found: address following the field */
242 *r7 = (uint16_t)(field + len);
243 alu_set_cc(ge, 0u); /* not found -> FA05=0 */
244}
245
246/* =========================================================================
247 * SL — Search Left (0xDB) — the mirror of SR.
248 * §5.5.4.2: scan field right-to-left for the model byte; deposit result in r7.
249 *
250 * The field's given address (A1) is its RIGHTMOST byte; the field extends LEFT
251 * (lower addresses) for `len` bytes, i.e. [field-len+1 .. field], and is scanned
252 * right-to-left. Result is the address "following" the match in the direction
253 * of search (leftward), i.e. match-1 — the mirror of SR's match+1.
254 *
255 * Found at byte `pos`: r7 = pos - 1
256 * Not found: r7 = field - len (past the left end of the field)
257 *
258 * CC: FA05 = found (1) / not found (0), as for SR.
259 *
260 * Validated against funktionalcpu step 0x22: SL 2,0x0557,0x0558 scans
261 * [0x0556..0x0557] right-to-left, finds 0x44 at 0x0557, returns 0x0556.
262 * =========================================================================*/
263void alu_sl(struct ge *ge, uint16_t *r7,
264 uint16_t field, uint16_t len, uint8_t model)
265{
266 uint16_t i;
267
268 /* Scan right-to-left from the rightmost byte (field) toward lower addresses */
269 for (i = 0; i < len; i++) {
270 uint16_t pos = (uint16_t)(field - i);
271 if (ge->mem[pos] == model) {
272 *r7 = (uint16_t)(pos - 1); /* address following the match (leftward) */
273 alu_set_cc(ge, 1u); /* found -> FA05=1 */
274 return;
275 }
276 }
277
278 /* Not found: past the left end of the field */
279 *r7 = (uint16_t)(field - len);
280 alu_set_cc(ge, 0u); /* not found -> FA05=0 */
281}
282
283/* =========================================================================
284 * MVQ — Move Quartets (0xF8)
285 * §5.5.1.3, §3.084/3.098: Copy digit nibbles (low 4 bits) from src to dst.
286 * Zones (high 4 bits) of destination bytes are preserved.
287 * Both fields addressed from rightmost byte; operation proceeds right-to-left.
288 *
289 * CC result (§5.10.2):
290 * FA04=0 FA05=0 (cc=0) → all digit nibbles of result are zero
291 * FA04=0 FA05=1 (cc=1) → at least one digit nibble nonzero
292 *
293 * NOTE: MVQ uses the ADD-magnitude raw values 0 and 1 directly — these do NOT
294 * map to the COMPARE/SIGNED-result named constants; use raw literals here.
295 *
296 * UNCERTAINTY: "Zones are not processed" (§3.098) could mean:
297 * (a) zones of src are ignored (only digit nibbles copied) — implemented here
298 * (b) zones of dst are zeroed, entire byte overwritten
299 * (c) entire byte is copied (zones "not processed" meaning "not arithmetically
300 * significant")
301 * Interpretation (a) is most consistent with "decimal unpacked" semantics.
302 * Hardware trace recommended.
303 * Confidence: MEDIUM (OCR partially clear; semantics from §3.098 context)
304 * =========================================================================*/
305void alu_mvq(struct ge *ge, uint16_t dst, uint16_t src, uint8_t len)
306{
307 uint8_t any_nonzero = 0;
308 uint8_t i;
309
310 /* Process right-to-left; addresses given are already rightmost */
311 for (i = 0; i < len; i++) {
312 uint16_t sa = (uint16_t)(src - i);
313 uint16_t da = (uint16_t)(dst - i);
314 uint8_t digit = ge->mem[sa] & 0x0F; /* take digit nibble from src */
315 uint8_t zone = ge->mem[da] & 0xF0; /* keep zone nibble from dst */
316 ge_mem_store8(ge, da, zone | digit);
317 if (digit != 0)
318 any_nonzero = 1;
319 }
320
321 /*
322 * CC: 0 = all-zero result, 1 = nonzero result (ADD-magnitude raw values).
323 * FA04=0 always for MVQ (only FA05 encodes the result).
324 * Use raw 0/1 — these are not the COMPARE/SIGNED-result named constants.
325 */
326 alu_set_cc(ge, any_nonzero ? 1u : 0u);
327}
328
329/* =========================================================================
330 * CMQ — Compare Quartets (0xF9)
331 * §5.5.1.4: Compare digit nibbles of two fields. Neither altered.
332 * "Not considering the zones."
333 * Both fields addressed from rightmost byte.
334 * Comparison is left-to-right (most significant digit first),
335 * consistent with treating the field as a multi-digit number.
336 *
337 * CC result (§5.5.1.4) — COMPARE table:
338 * FA04 FA05 cc meaning
339 * 0 0 0 "Not possible" — never set
340 * 0 1 1 OP1 < OP2 → ALU_CC_LOW (1)
341 * 1 0 2 OP1 == OP2 → ALU_CC_EQUAL (2)
342 * 1 1 3 OP1 > OP2 → ALU_CC_HIGH (3)
343 *
344 * UNCERTAINTY: Same as MVQ regarding zone handling.
345 * Confidence: MEDIUM-HIGH (§5.5.1.4 text and §5.10.2 table consistent)
346 * =========================================================================*/
347void alu_cmq(struct ge *ge, uint16_t a, uint16_t b, uint8_t len)
348{
349 uint8_t i;
350
351 /*
352 * Compare left-to-right (most significant digit first).
353 * Addresses a and b are the rightmost bytes, so the leftmost
354 * byte of field 'a' is at (a - len + 1).
355 */
356 for (i = 0; i < len; i++) {
357 uint8_t da = ge->mem[(uint16_t)(a - (len - 1) + i)] & 0x0F;
358 uint8_t db = ge->mem[(uint16_t)(b - (len - 1) + i)] & 0x0F;
359
360 if (da < db) {
361 alu_set_cc(ge, ALU_CC_LOW); /* 1 */
362 return;
363 }
364 if (da > db) {
365 alu_set_cc(ge, ALU_CC_HIGH); /* 3 */
366 return;
367 }
368 }
369
370 /* All digit nibbles equal */
371 alu_set_cc(ge, ALU_CC_EQUAL); /* 2 */
372}
void alu_set_cc(struct ge *ge, uint8_t cc)
Definition alu_cc.c:4
@ ALU_CC_EQUAL
Definition alu_cc.h:52
@ ALU_CC_POS
Definition alu_cc.h:53
@ ALU_CC_ZERO
Definition alu_cc.h:52
@ ALU_CC_NEG
Definition alu_cc.h:51
@ ALU_CC_LOW
Definition alu_cc.h:51
@ ALU_CC_HIGH
Definition alu_cc.h:53
void alu_mvq(struct ge *ge, uint16_t dst, uint16_t src, uint8_t len)
alu_mvq - Move Quartets (MVQ, 0xF8)
Definition alu_reg.c:305
void alu_cmq(struct ge *ge, uint16_t a, uint16_t b, uint8_t len)
alu_cmq - Compare Quartets (CMQ, 0xF9)
Definition alu_reg.c:347
void alu_lr(struct ge *ge, uint16_t *reg, uint16_t addr)
alu_lr - Load Register (LR, 0xBC)
Definition alu_reg.c:48
void alu_cmr(struct ge *ge, uint16_t reg_val, uint16_t value)
alu_cmr - Compare Memory to Register (CMR, 0xBD)
Definition alu_reg.c:77
void alu_sr(struct ge *ge, uint16_t *r7, uint16_t field, uint16_t len, uint8_t model)
alu_sr - Search Right (SR, 0xD9)
Definition alu_reg.c:228
static void mem_write16(struct ge *ge, uint16_t addr, uint16_t val)
mem_write16 - write a big-endian 16-bit word to memory.
Definition alu_reg.c:36
static uint16_t mem_read16(struct ge *ge, uint16_t addr)
mem_read16 - read a big-endian 16-bit word from memory.
Definition alu_reg.c:27
void alu_smr(struct ge *ge, uint16_t *reg, uint16_t value)
alu_smr - Subtract Memory from Register (SMR, 0xBF)
Definition alu_reg.c:162
void alu_la(struct ge *ge, uint16_t *reg, uint16_t addr)
alu_la - Load Address (LA, 0x68)
Definition alu_reg.c:207
void alu_sl(struct ge *ge, uint16_t *r7, uint16_t field, uint16_t len, uint8_t model)
alu_sl - Search Left (SL, 0xDB)
Definition alu_reg.c:263
void alu_str(struct ge *ge, uint16_t reg_val, uint16_t addr)
alu_str - Store Register (STR, 0x84)
Definition alu_reg.c:59
void alu_amr(struct ge *ge, uint16_t *reg, uint16_t value)
alu_amr - Add Memory to Register (AMR, 0xBE)
Definition alu_reg.c:110
ALU helpers for GE-120/GE-130 register and memory-field operations.
void ge_mem_store8(struct ge *ge, uint16_t addr, uint8_t val)
Store a byte with generated odd parity + mark-written (for the hybrid ALU/SS write paths that write g...
Definition ge.c:119
The entire state of the emulated system, including registers, memory, peripherals and timings.
Definition ge.h:96
uint8_t mem[MEM_SIZE]
The memory of the emulated system.
Definition ge.h:566
uint8_t URPU
Definition ge.h:426
uint8_t URPE
Definition ge.h:425