Created
August 24, 2022 05:49
-
-
Save ilyakurdyukov/6b80c55b17fbd0b77d888088c36b4910 to your computer and use it in GitHub Desktop.
Intel 4004 (mcs-4) emulator written from reading manual (not tested)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// looks correct but not tested at all | |
// temp | |
unsigned i, t, x; | |
// mcs-4 state | |
unsigned short pc, stack[3]; | |
unsigned char r[16], a, c; | |
unsigned char test, ram, src, wpm_idx, sp; | |
unsigned char rom_out[16], rom_in[16], ram_out[4]; | |
// 8 banks x 4 chips x 4 registers x (16 + 4) chars x 4 bits | |
unsigned char data[8][256], status[8][16][4]; | |
#define NEXT t = prog[pc++]; pc &= 0xfff; | |
#define ADD a += t; c = a >> 4; a &= 15; | |
NEXT | |
switch (t & 0xf0) { | |
case 0x00: // 0000-0000 NOP (3.9) | |
if (t) goto illegal; | |
break; | |
case 0x10: // 0010-cccc JCN (3.7.3) | |
i = t & 15; | |
t = !test | c << 1 | !a << 2 | 16; | |
i = (i & t) + 15; | |
NEXT | |
if (i & 16) pc = (pc & ~0xff) | t; | |
break; | |
case 0x20: | |
i = t & 14; | |
if (t & 1) { | |
// 0010-ppp1 SRC (3.10.2) | |
src = r[i] << 4 | r[i + 1]; | |
} else { | |
// 0010-ppp0 FIM (3.6.1) | |
NEXT | |
r[i] = t >> 4; | |
r[i + 1] = t & 15; | |
} | |
break; | |
case 0x30: | |
i = t & 14; | |
x = r[i] << 4 | r[i + 1]; | |
x |= pc & ~0xff; | |
if (t & 1) { | |
// 0011-ppp1 JIN (3.7.2) | |
pc = x; | |
} else { | |
// 0011-ppp0 FIN (3.3.2) | |
t = prog[x]; | |
r[i] = t >> 4; | |
r[i + 1] = t & 15; | |
} | |
break; | |
case 0x40: // 0100-xxxx JUN (3.7.1) | |
i = t & 15; | |
NEXT | |
pc = i << 8 | t; | |
break; | |
case 0x50: // 0101-xxxx JMS (3.8.1) | |
i = t & 15; | |
NEXT | |
stack[sp] = pc; | |
sp = sp < 2 ? sp + 1 : 0; | |
pc = i << 8 | t; | |
break; | |
case 0x60: // 0110-rrrr INC (3.3.1) | |
i = t & 15; | |
r[i] = (r[i] + 1) & 15; | |
break; | |
case 0x70: // 0111-rrrr ISZ (3.7.4) | |
i = t & 15; | |
i = r[i] = (r[i] + 1) & 15; | |
NEXT | |
if (i) pc = (pc & ~0xff) | t; | |
break; | |
case 0x80; // 1000-rrrr ADD (3.4.1) | |
i = t & 15; | |
t = r[i] + c; | |
ADD | |
break; | |
case 0x90; // 1001-rrrr SUB (3.4.2) | |
i = t & 15; | |
t = 16 - r[i] - c; | |
ADD | |
break; | |
case 0xa0: // 1010-rrrr LD (3.4.3) | |
i = t & 15; | |
a = r[i]; | |
break; | |
case 0xb0: // 1011-rrrr XCH (3.4.4) | |
i = t & 15; | |
t = a; | |
a = r[i]; | |
r[i] = t; | |
break; | |
case 0xc0; // 1100-xxxx BBL (3.8.2) | |
a = t & 15; | |
sp = sp ? sp - 1 : 2; | |
pc = stack[sp]; | |
break; | |
case 0xd0: // 1101-xxxx LDM (3.6.2) | |
a = t & 15; | |
break; | |
case 0xe0: // 1101-xxxx IO (3.11) | |
switch (t) { | |
case 0xe0: // 1110-0000 WRM (3.11.4) | |
data[ram][src] = a; break; | |
case 0xe1: // 1110-0001 WMP (3.11.6) | |
ram_out_out[src >> 6] = a; break; | |
case 0xe2: // 1110-0010 WRR (3.11.7) | |
rom_out[src >> 4] = a; break; | |
case 0xe3: // 1110-0011 WPM (3.11.10) | |
wpm_idx ^= 4; | |
i = rom_out[15] << 8 | src; | |
t = prog[i]; | |
if (rom_out[14]) { | |
prog[i] = (t & ~(15 << wpm_idx)) | a << wpm_idx; | |
} else { | |
rom_in[15 - (wpm_idx >> 3)] = t >> wpm_idx & 15; | |
} | |
break; | |
case 0xe4: // 1110-01xx WRn (3.11.5) | |
case 0xe5: case 0xe6: case 0xe7: | |
status[ram][src >> 4][t & 3] = a; break; | |
case 0xe8: // 1110-1000 SBM (3.11.9) | |
t = 16 - data[ram][src] - c; ADD break; | |
case 0xe9: // 1110-1001 RDM (3.11.1) | |
a = data[ram][src]; break; | |
case 0xea: // 1110-1010 RDR (3.11.3) | |
a = rom_in[src >> 4]; break; | |
case 0xeb: // 1110-1011 ADM (3.11.8) | |
t = data[ram][src] + c; ADD break; | |
case 0xec: // 1110-11xx RDn (3.11.2) | |
case 0xed: case 0xee: case 0xef: | |
a = status[src >> 4][t & 3]; break; | |
} | |
break; | |
case 0xf0: | |
switch (t) { | |
case 0xf0: // 1111-0000 CLB (3.5.1) | |
a = 0; c = 0; break; | |
case 0xf1: // 1111-0001 CLC (3.5.2) | |
c = 0; break; | |
case 0xf2: // 1111-0010 IAC (3.5.3) | |
t = 1; ADD break; | |
case 0xf3: // 1111-0011 CMC (3.5.4) | |
c ^= 1; break; | |
case 0xf4: // 1111-0011 CMA (3.5.5) | |
a ^= 15; break; | |
case 0xf5: // 1111-0101 RAL (3.5.6) | |
t = a + c; ADD break; | |
case 0xf6: // 1111-0110 RAR (3.5.7) | |
a |= c << 4; c = a & 1; a >>= 1; break; | |
case 0xf7: // 1111-0111 TCC (3.5.8) | |
a = c; c = 0; break; | |
case 0xf8: // 1111-1000 DAC (3.5.9) | |
t = 15; ADD break; | |
case 0xf9: // 1111-1001 TCS (3.5.10) | |
a = 9 + c; c = 0; break; | |
case 0xfa: // 1111-1010 STC (3.5.11) | |
c = 1; break; | |
case 0xfb: // 1111-1011 DAA (3.5.12) | |
if (a > 9 || c) { | |
a += 6; c = 1; a &= 15; | |
} | |
break; | |
case 0xfc: { // 1111-1100 KBP (3.5.13) | |
// a = a ? a & (a - 1) ? 15 : CTZ(a) + 1 : 0; | |
const unsigned char kbp_tab[16] = | |
{ 0,1,2,15, 3,15,15,15, 4,15,15,15, 15,15,15,15 }; | |
a = kbp_tab[a]; break; | |
} | |
case 0xfd: // 1111-1101 DCL (3.10.1) | |
ram = a & 7; break; | |
default: goto illegal; | |
} | |
break; | |
default: | |
illegal: | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment