GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
alu_bin.c
Go to the documentation of this file.
1
24#include "alu_bin.h"
25#include <string.h>
26
27/* ------------------------------------------------------------------ */
28/* Internal helpers */
29/* ------------------------------------------------------------------ */
30
39static inline uint8_t field_byte(const struct ge *ge,
40 uint16_t base_addr, uint8_t len,
41 uint8_t pos)
42{
43 if (pos >= len)
44 return 0; /* zero-extension */
45 return ge->mem[(uint16_t)(base_addr - pos)];
46}
47
51static inline void field_byte_set(struct ge *ge,
52 uint16_t base_addr, uint8_t len,
53 uint8_t pos, uint8_t val)
54{
55 if (pos < len)
56 ge_mem_store8(ge, (uint16_t)(base_addr - pos), val);
57}
58
59/* ------------------------------------------------------------------ */
60/* AB – Add Binary (opcode 0xFE) */
61/* */
62/* CC (§5.5.2.1, §5.10.2): */
63/* 0 (FA04=0,FA05=0): sum is zero */
64/* 1 (FA04=0,FA05=1): sum is non-zero (no overflow) */
65/* 2 (FA04=1,FA05=0): overflow, partial result is zero */
66/* 3 (FA04=1,FA05=1): overflow, partial result is non-zero */
67/* ------------------------------------------------------------------ */
68void alu_ab(struct ge *ge,
69 uint16_t a_addr, uint8_t a_len,
70 uint16_t b_addr, uint8_t b_len)
71{
72 /* If L2 > L1 truncate to L1 (manual: "executed as if L2 = L1") */
73 if (b_len > a_len)
74 b_len = a_len;
75
76 uint8_t carry = 0;
77 uint8_t result_nonzero = 0;
78 uint8_t overflow = 0;
79
80 for (uint8_t pos = 0; pos < a_len; pos++) {
81 uint16_t sum = (uint16_t)field_byte(ge, a_addr, a_len, pos)
82 + (uint16_t)field_byte(ge, b_addr, b_len, pos)
83 + carry;
84 carry = (uint8_t)(sum >> 8);
85 uint8_t byte = (uint8_t)(sum & 0xFF);
86 field_byte_set(ge, a_addr, a_len, pos, byte);
87 if (byte != 0)
88 result_nonzero = 1;
89 }
90
91 /*
92 * A carry out of the most-significant byte means a carry was lost
93 * beyond the receiving field — this is the overflow condition.
94 * Manual (§5.5.2.1): "the loss of a possible carry-over beyond the
95 * length of the receiving field (overflow)".
96 */
97 if (carry)
98 overflow = 1;
99
100 uint8_t cc;
101 if (overflow)
102 cc = result_nonzero ? 3u : 2u; /* OV+nonzero | OV+zero */
103 else
104 cc = result_nonzero ? 1u : 0u; /* nonzero | zero */
105
106 alu_set_cc(ge, cc);
107}
108
109/* ------------------------------------------------------------------ */
110/* SB – Subtract Binary (opcode 0xFF) */
111/* */
112/* CC (§5.5.2.2): */
113/* 0: not possible (never set by this implementation) */
114/* 1 (FA04=0,FA05=1): result < 0 (stored complemented to 2^8*L1) */
115/* 2 (FA04=1,FA05=0): result == 0 */
116/* 3 (FA04=1,FA05=1): result > 0 */
117/* ------------------------------------------------------------------ */
118void alu_sb(struct ge *ge,
119 uint16_t a_addr, uint8_t a_len,
120 uint16_t b_addr, uint8_t b_len)
121{
122 /* If L2 > L1 truncate to L1 */
123 if (b_len > a_len)
124 b_len = a_len;
125
126 /*
127 * Perform A = A - B using borrow propagation right-to-left.
128 * borrow = 1 means we borrowed from the next higher position.
129 */
130 uint8_t borrow = 0;
131 uint8_t result_nonzero = 0;
132
133 for (uint8_t pos = 0; pos < a_len; pos++) {
134 int16_t diff = (int16_t)field_byte(ge, a_addr, a_len, pos)
135 - (int16_t)field_byte(ge, b_addr, b_len, pos)
136 - borrow;
137 if (diff < 0) {
138 diff += 256;
139 borrow = 1;
140 } else {
141 borrow = 0;
142 }
143 uint8_t byte = (uint8_t)(diff & 0xFF);
144 field_byte_set(ge, a_addr, a_len, pos, byte);
145 if (byte != 0)
146 result_nonzero = 1;
147 }
148
149 /*
150 * If borrow != 0 after the MSB the mathematical result is negative.
151 * The manual (§5.5.2.2) states: "If the result is negative it is stored
152 * in complemented form (complemented to 2^8*(L1+1))".
153 * The two's complement is already naturally produced by the borrow
154 * propagation (borrowing from an implicit position above yields the
155 * correct two's complement representation).
156 *
157 * Note: the manual writes "2^8(L1+1)" where L1 in the encoding is
158 * len-1 (since "L1+1 positions"), so this is 2^(8*a_len) — exactly
159 * what our byte-level subtraction with wrap produces.
160 */
161 uint8_t negative = borrow; /* borrow out = mathematical underflow */
162
163 uint8_t cc;
164 if (negative) {
165 /* result < 0, stored in complemented form; FA04=0, FA05=1 -> cc=1 */
166 cc = 1u;
167 } else if (!result_nonzero) {
168 /* result == 0; FA04=1, FA05=0 -> cc=2 */
169 cc = 2u;
170 } else {
171 /* result > 0; FA04=1, FA05=1 -> cc=3 */
172 cc = 3u;
173 }
174 alu_set_cc(ge, cc);
175}
176
177/* ------------------------------------------------------------------ */
178/* Internal: decimal digit extraction */
179/* */
180/* In unpacked (zoned) format each byte holds one decimal digit in its */
181/* low nibble (0x0..0x9); the high nibble is the zone and is ignored */
182/* during arithmetic (§5.5.1: "The zones of the characters are not */
183/* processed"). */
184/* ------------------------------------------------------------------ */
185static inline uint8_t dec_digit(const struct ge *ge,
186 uint16_t base_addr, uint8_t len,
187 uint8_t pos)
188{
189 return field_byte(ge, base_addr, len, pos) & 0x0F;
190}
191
192/* ------------------------------------------------------------------ */
193/* AD – Add Decimal (opcode 0xFA) */
194/* */
195/* CC (§5.10.2, inferred – confidence medium, see header): */
196/* 0: result is zero */
197/* 1: result is non-zero */
198/* 2/3: not possible (carry beyond field is silently dropped) */
199/* ------------------------------------------------------------------ */
200void alu_ad(struct ge *ge,
201 uint16_t a_addr, uint8_t a_len,
202 uint16_t b_addr, uint8_t b_len)
203{
204 /* If L2 > L1 truncate to L1 */
205 if (b_len > a_len)
206 b_len = a_len;
207
208 uint8_t carry = 0;
209 uint8_t result_nonzero = 0;
210
211 for (uint8_t pos = 0; pos < a_len; pos++) {
212 uint8_t aorig = field_byte(ge, a_addr, a_len, pos);
213 uint8_t sum = (uint8_t)((aorig & 0x0F)
214 + dec_digit(ge, b_addr, b_len, pos)
215 + carry);
216 carry = sum / 10;
217 uint8_t digit = sum % 10;
218 /*
219 * Per the AD microcode (AB-SB-AD-SD-MVQ-CMQ flowchart, box 50-52
220 * "RO2 -> L1.4"): the result's high nibble is the FIRST operand's zone
221 * (RO2), preserved unchanged; only the low nibble (digit) is computed.
222 */
223 field_byte_set(ge, a_addr, a_len, pos, (uint8_t)((aorig & 0xF0) | digit));
224 if (digit != 0)
225 result_nonzero = 1;
226 }
227
228 /*
229 * CC per the flowchart NOTE table (AD-AB column): F104 = overflow (a carry
230 * out of the most-significant digit of the field), F105 = result nonzero.
231 * alu_set_cc maps cc bit1 -> F104, bit0 -> F105.
232 */
233 alu_set_cc(ge, (uint8_t)((carry ? 2u : 0u) | (result_nonzero ? 1u : 0u)));
234}
235
236/* ------------------------------------------------------------------ */
237/* SD – Subtract Decimal (opcode 0xFB) */
238/* */
239/* CC (§5.10.2, inferred – confidence medium, see header): */
240/* 0: result is zero */
241/* 1: result is non-zero (includes underflow case) */
242/* 2/3: not possible */
243/* */
244/* Underflow (borrow beyond the MSP): the manual treats AD/SD operands */
245/* as positive integers and does not explicitly describe the underflow */
246/* case for SD in the available OCR. We wrap modulo 10^a_len and set */
247/* cc=1. Human re-inspection of the page images is recommended. */
248/* ------------------------------------------------------------------ */
249void alu_sd(struct ge *ge,
250 uint16_t a_addr, uint8_t a_len,
251 uint16_t b_addr, uint8_t b_len)
252{
253 /* If L2 > L1 truncate to L1 */
254 if (b_len > a_len)
255 b_len = a_len;
256
257 uint8_t borrow = 0;
258 uint8_t result_nonzero = 0;
259
260 for (uint8_t pos = 0; pos < a_len; pos++) {
261 uint8_t aorig = field_byte(ge, a_addr, a_len, pos);
262 int8_t diff = (int8_t)(aorig & 0x0F)
263 - (int8_t)(dec_digit(ge, b_addr, b_len, pos))
264 - (int8_t)borrow;
265 if (diff < 0) {
266 diff += 10;
267 borrow = 1;
268 } else {
269 borrow = 0;
270 }
271 uint8_t digit = (uint8_t)diff;
272 /* Preserve the first operand's zone (high nibble), like AD. */
273 field_byte_set(ge, a_addr, a_len, pos, (uint8_t)((aorig & 0xF0) | digit));
274 if (digit != 0)
275 result_nonzero = 1;
276 }
277
278 /*
279 * CC per the flowchart NOTE table (SD-SB-CMQ column): result<0 -> F104=0
280 * F105=1 (cc=1); result=0 -> F104=1 F105=0 (cc=2); result>0 -> F104=1
281 * F105=1 (cc=3). A borrow out of the MSP means the true result is negative
282 * (stored ten's-complement).
283 */
284 uint8_t cc;
285 if (borrow)
286 cc = 1u; /* result < 0 */
287 else if (!result_nonzero)
288 cc = 2u; /* result = 0 */
289 else
290 cc = 3u; /* result > 0 */
291 alu_set_cc(ge, cc);
292}
static void field_byte_set(struct ge *ge, uint16_t base_addr, uint8_t len, uint8_t pos, uint8_t val)
Write one byte into a multi-byte big-endian field.
Definition alu_bin.c:51
static uint8_t dec_digit(const struct ge *ge, uint16_t base_addr, uint8_t len, uint8_t pos)
Definition alu_bin.c:185
void alu_ad(struct ge *ge, uint16_t a_addr, uint8_t a_len, uint16_t b_addr, uint8_t b_len)
AD – Add Decimal (opcode 0xFA).
Definition alu_bin.c:200
static uint8_t field_byte(const struct ge *ge, uint16_t base_addr, uint8_t len, uint8_t pos)
Read one byte from a multi-byte big-endian field at position pos (0 = least-significant byte = rightm...
Definition alu_bin.c:39
void alu_sb(struct ge *ge, uint16_t a_addr, uint8_t a_len, uint16_t b_addr, uint8_t b_len)
SB – Subtract Binary (opcode 0xFF).
Definition alu_bin.c:118
void alu_sd(struct ge *ge, uint16_t a_addr, uint8_t a_len, uint16_t b_addr, uint8_t b_len)
SD – Subtract Decimal (opcode 0xFB).
Definition alu_bin.c:249
void alu_ab(struct ge *ge, uint16_t a_addr, uint8_t a_len, uint16_t b_addr, uint8_t b_len)
AB – Add Binary (opcode 0xFE).
Definition alu_bin.c:68
Binary and decimal (unpacked) arithmetic helpers for the GE-120/130.
void alu_set_cc(struct ge *ge, uint8_t cc)
Definition alu_cc.c:4
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