start of a 4004 emulator
This commit is contained in:
parent
80e1fe69c1
commit
e29180ba01
184
3mu/Intel4004.c
Normal file
184
3mu/Intel4004.c
Normal 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 */
|
||||
}
|
Loading…
Reference in New Issue
Block a user