1
0
Fork 0

start of a 4004 emulator

This commit is contained in:
dtb 2024-04-03 15:11:55 -06:00
parent 80e1fe69c1
commit e29180ba01
1 changed files with 184 additions and 0 deletions

184
3mu/Intel4004.c Normal file
View File

@ -0,0 +1,184 @@
/* Intel 4004 emulator. 2024, DTB. Incomplete.
* I didn't know anything about the i4004 before starting to write this file. I
* know slightly more now. */
/* Name - Bits 11_10__9__8__7__6__5__4__3__2__1__0
* Bit - 1 | | | bit -> ||
* Nybble - 4 | | nybble -> |_________|
* Byte - 8 | byte -> |_____________________|
* Word - 12 word -> |_________________________________| */
/* 24---------------------|
*
* words 12---------|12---------|
* bytes 8------|8------|8------|
* nybbles 4--|4--|4--|4--|4--|4--|
* bits 111111111111111111111111 */
#if __STDC_VERSION__ >= 199901L
/* C99 type definitions */
# include <stdint.h>
typedef _Bool Bit;
typedef uint8_t Nybble;
typedef uint8_t Byte;
typedef uint16_t Word;
#else
/* C89 type definitions - probably fine but may need per-platform tweaks. */
/* Must fit at least 1 bit. */ typedef unsigned char Bit;
/* Must fit at least 4 bits. */ typedef unsigned char Nybble;
/* Must fit at least 8 bits. */ typedef unsigned char Byte;
/* Must fit at least 12 bits. */ typedef unsigned int Word;
#endif
#define LOW_NYBBLE(b) ((b) & 0x0F) /* 0b 0000 1111 */
#define HIGH_NYBBLE(b) ((b) & 0xF0) /* 0b 1111 0000 */
/* Intel 4004 datasheet */
/* <https://datasheets.chipdb.org/Intel/MCS-4/datashts/intel-4004.pdf> */
struct Intel4004_State {
Bit carry;
Bit test; /* A pin on the chip. */
Nybble acc; /* Accumulator */
Nybble reg[16]; /* R0 through RF */
Word pc[4]; /* PC0 through PC3 */
Byte *rom;
Byte *ram;
};
/* Intel 4004 instruction set */
/* <http://e4004.szyc.org/iset.html> */
enum Intel4004_Instruction {
/* Summary Mnemonic Hex Binary opcode */
/* No-op */ NOP = 0x0, /* 0b 0000 0000 */
/* INVALID 0b 0000 0001 through 0b 0000 1111 */
/* Jump conditional */ JCN = 0x10, /* 0b 0001 CCCC */
/* Fetch immediate */ FIM = 0x20, /* 0b 0010 RRR0 */
/* Send register control */ SRC = 0x21, /* 0b 0010 RRR1 */
/* Fetch indirect */ FIN = 0x30, /* 0b 0011 RRR0 */
/* Jump indirect */ JIN = 0x31, /* 0b 0011 RRR1 */
/* Jump unconditional */ JUN = 0x40, /* 0b 0100 AAAA */
/* Jump to subroutine */ JMS = 0x50, /* 0b 0101 AAAA */
/* Increment */ INC = 0x60, /* 0b 0110 RRRR */
/* Increment and skip */ ISZ = 0x70, /* 0b 0111 RRRR */
/* Add */ ADD = 0x80, /* 0b 1000 RRRR */
/* Subtract */ SUB = 0x90, /* 0b 1001 RRRR */
/* Load */ LD = 0xA0, /* 0b 1010 RRRR */
/* Exchange */ XCH = 0xB0, /* 0b 1011 RRRR */
/* Branch back and load */ BBL = 0xC0, /* 0b 1100 DDDD */
/* Load immediate */ LDM = 0xD0, /* 0b 1101 DDDD */
/* Write main memory */ WRM = 0xE0, /* 0b 1110 0000 */
/* Write RAM port */ WMP = 0xE1, /* 0b 1110 0001 */
/* Write ROM port */ WRR = 0xE2, /* 0b 1110 0010 */
/* INVALID 0xE3 0b 1110 0011 */
/* Write status char 0 */ WR0 = 0xE4, /* 0b 1110 0100 */
/* Write status char 1 */ WR1 = 0xE5, /* 0b 1110 0101 */
/* Write status char 2 */ WR2 = 0xE6, /* 0b 1110 0110 */
/* Write status char 3 */ WR3 = 0xE7, /* 0b 1110 0111 */
/* Subtract main memory */ SBM = 0xE8, /* 0b 1110 1000 */
/* Read main memory */ RDM = 0xE9, /* 0b 1110 1001 */
/* Read ROM port */ RDR = 0xEA, /* 0b 1110 1010 */
/* Add main memory */ ADM = 0xEB, /* 0b 1110 1011 */
/* Read status char 0 */ RD0 = 0xEC, /* 0b 1110 1100 */
/* Read status char 1 */ RD1 = 0xED, /* 0b 1110 1101 */
/* Read status char 2 */ RD2 = 0xEE, /* 0b 1110 1110 */
/* Read status char 3 */ RD3 = 0xEF, /* 0b 1110 1111 */
/* Clear both */ CLB = 0xF0, /* 0b 1111 0000 */
/* Clear carry */ CLC = 0xF1, /* 0b 1111 0001 */
/* Increment accumulator */ IAC = 0xF2, /* 0b 1111 0010 */
/* Complement carry */ CMC = 0xF3, /* 0b 1111 0011 */
/* Complement */ CMA = 0xF4, /* 0b 1111 0100 */
/* Rotate left */ RAL = 0xF5, /* 0b 1111 0101 */
/* Rotate right */ RAR = 0xF6, /* 0b 1111 0110 */
/* Transfer carry and clear */ TCC = 0xF7, /* 0b 1111 0111 */
/* Decrement accumulator */ DAC = 0xF8, /* 0b 1111 1000 */
/* Transfer carry subtract */ TCS = 0xF9, /* 0b 1111 1001 */
/* Set carry */ STC = 0xFA, /* 0b 1111 1010 */
/* Decimal adjust accumulator */ DAA = 0xFB, /* 0b 1111 1011 */
/* Keyboard process */ KBP = 0xFC, /* 0b 1111 1100 */
/* Designate command line */ DCL = 0xFD /* 0b 1111 1101 */
/* INVALID 0xFE 0b 1111 1110 */
/* INVALID 0xFF 0b 1111 1111 */
};
/* Does nothing. */
void Intel4004_NOP(struct Intel4004_State *state){ /* 0b 0000 0000 */
++*state->pc;
return;
}
#define INTEL4004_JCN_COND_INVERT 0x01 /* 0b 0000 0001 */
#define INTEL4004_JCN_COND_ACCZERO 0x02 /* 0b 0000 0010 */
#define INTEL4004_JCN_COND_CARRYON 0x04 /* 0b 0000 0100 */
#define INTEL4004_JCN_COND_TESTON 0x08 /* 0b 0000 1000 */
#define INTEL4004_JCN_COND_ISINVERT(b) ((b) & INTEL4004_JCN_COND_INVERT)
#define INTEL4004_JCN_COND_ISACCZERO(b) ((b) & INTEL4004_JCN_COND_ACCZERO)
#define INTEL4004_JCN_COND_ISCARRYON(b) ((b) & INTEL4004_JCN_COND_CARRYON)
#define INTEL4004_JCN_COND_ISTESTON(b) ((b) & INTEL4004_JCN_COND_TESTON)
/* Jumps conditionally to the address in the following byte.
* If *state->pc & 0x01, the condition is inverted.
* If *state->pc & 0x02, a zero accumulator satisfies the condition.
* If *state->pc & 0x04, a one carry flag satisfies the condition.
* If *state->pc & 0x08, a one test flag satisfies the condition. */
void Intel4004_JCN(struct Intel4004_State *state){ /* 0b 0001 CCCC */
*state->pc =
!INTEL4004_JCN_COND_ISINVERT(state->rom[*state->pc])
&& ( (INTEL4004_JCN_COND_ISACCZERO(state->rom[*state->pc])
&& state->acc == 0)
|| (INTEL4004_JCN_COND_ISCARRYON(state->rom[*state->pc])
&& state->carry == 1)
|| (INTEL4004_JCN_COND_ISTESTON(state->rom[*state->pc])
&& state->test == 1))
? state->rom[*state->pc + 1]
: *state->pc + 2;
return;
}
/* Macros to get the register pair from FIM, SRC, FIN, and JIN.
* INTEL4004_REGPAIR(opcode) returns the first register, the second register is
* the increment.
* High nybbles tend to be stored in the first register and low nybbles in the
* second. */
#define INTEL4004_REGPAIR_BYTEMASK 0x0E /* 0b 0000 1110 */
#define INTEL4004_REGPAIR(b) ((b) & INTEL4004_REGPAIR_BYTEMASK)
/* Fetches an immediate byte from ROM. */
void Intel4004_FIM(struct Intel4004_State *state){ /* 0b 0010 RRR0 */
state->reg[INTEL4004_REGPAIR(state->rom[*state->pc])]
= HIGH_NYBBLE(state->rom[*state->pc + 1]);
state->reg[INTEL4004_REGPAIR(state->rom[*state->pc]) + 1]
= LOW_NYBBLE(state->rom[*state->pc + 1]);
*state->pc += 2;
return;
}
void Intel4004_SRC(struct Intel4004_State *state){ /* 0b 0010 RRR1 */
}
void Intel4004_FIN(struct Intel4004_State *state){ /* 0b 0011 RRR0 */
}