Initial fork

This commit is contained in:
Ricardo Mazeto 2020-01-15 12:18:22 -07:00
commit 9d2a4795c0
5 changed files with 416 additions and 0 deletions

91
README.md Normal file
View File

@ -0,0 +1,91 @@
## MINISC - Minimal Instruction Set Computing
.3v data
| ||||||||
##########
# minisc #
##########
| ||||||||
gnd addr
The MINISC architecture is an emulator written in C, of a fictional minimal Von Neumann Turing Machine. A MINISC CPU have:
- 4 internal registers.
- 8 data pins.
- 8 address pins.
- one pin for .3 Volts input.
- ground pin.
It can address up to 256 bytes, with no bank switching. The MINISC CPU don't have I/O ports, but it can interface with external devices via shared RAM.
### Instruction Layout
o s t
0000.0000
o = opcode
t = target
s = source
The following table shows the encoding of the high nibble for each instruction.
add sub mul div | 0 1 2 3
mod inc dec lur | 4 5 6 7
and or xor cmp | 8 9 A B
biz bis biq big | C D E F
### Registers
op rg pc st
op = instruction
rg = accumulator, register
pc = program counter
st = status
The MINISC architecture gives you full access to the status and program counter registers, both as source and target. You can manually clear the status registers, or jump by loading the address on the program counter.
When the source and/or target of the current instruction is OP, it refers to the next instruction. That's how the CPU writes to RAM.
The following table shows how the instruction is encoded for the target and source lower nibble.
t\s op rg pc st
op | 0 1 2 3
rg | 4 5 6 7
pc | 8 9 A B
st | C D E F
### Status bits
zer sml eql gtr
neg car skp? int?
The skp flag tells the MINISC cpu that the next byte should not be interpreted as a instruction, but as data. The 'int' status flag sets the OP register to 0xf0, making the CPU jump to the last "page". But I'm not sure if they're really necessary yet. So let they be placeholders.
Here's a list of all the operators and which bits they may set on the status register. The values marked with a question mark need to be checked.
add -> zero | equal | carry
sub -> zero | equal | negative
mul -> zero | equal | carry
div -> equal
mod -> zero
inc -> zero (overflow or carry)
dec -> zero (underflow or negative)
lur -> none
and -> zero | equal | greater? | smaller?
or -> zero | equal | greater? | smaller?
xor -> zero | equal? | greater? | smaller?
cmp -> smaller | equal | greater
biz -> none
bis -> none
biq -> none
big -> none
### Macros
The MINISC CPU don't have a jmp instruction, But who needs it? you can load the desired address directly into the PC register.
jmp $AD = ldr PC
$AD

11
cookbook Normal file
View File

@ -0,0 +1,11 @@
: ${CC=gcc}
CFLAGS="-W -Wall -Werror -Wextra -O0 -s"
contains tcc $@ && CC=tcc
contains clang $@ && CC=clang
contains clean $@ && rm -v bin/minisc && exit
check bin/minisc src/minisc.c \
&& $CC $CFLAGS -o bin/minisc $DEPS

12
mas/mas.sh Executable file
View File

@ -0,0 +1,12 @@
# Macro assembler
[[ $# -eq 1 ]] && [[ -f $1 ]] && {
cat $1 | sed -E -f sed.txt > $1.tmp
while read line; do
echo -en "\x`echo -n $line`";
done < $1.tmp > $1.bin
rm $1.tmp
echo done
exit 0
} || echo -e "\tusage: $0 src.asm"

36
mas/sed.txt Normal file
View File

@ -0,0 +1,36 @@
s/[0-9]+:[ ]+//g
s/jmp/78/g
s/\$/\n/g
s/add/0/g
s/sub/1/g
s/mul/2/g
s/div/3/g
s/mod/4/g
s/inc/5/g
s/dec/6/g
s/lur/7/g
s/and/8/g
s/or/9/g
s/xor/a/g
s/cmp/b/g
s/biz/c/g
s/bis/d/g
s/biq/e/g
s/big/f/g
s/op op/0/g
s/op rg/1/g
s/op pc/2/g
s/op st/3/g
s/rg op/4/g
s/rg rg/5/g
s/rg pc/6/g
s/rg st/7/g
s/pc op/8/g
s/pc rg/9/g
s/pc pc/a/g
s/pc st/b/g
s/st op/c/g
s/st rg/d/g
s/st pc/e/g
s/st st/f/g
s/ //g

266
src/minisc.c Normal file
View File

@ -0,0 +1,266 @@
#include <stdio.h>
#include <inttypes.h>
#include <time.h>
#define u8 uint8_t
#define s32 int32_t
#define O_MASK 0xF0 /* 1111.0000 */
#define S_MASK 0x03 /* 0000.0011 */
#define T_MASK 0x0C /* 0000.1100 */
#define T_MSK2 0x0F /* 0000.1111 */
#define OP (reg[0]) /* OP code */
#define RG (reg[1]) /* Register (accumulator) */
#define PC (reg[2]) /* Program Counter */
#define ST (reg[3]) /* Status */
/* If source == source, get the next word.
Same goes for target */
#define SOURCE ( OP & S_MASK ? \
&reg[OP & S_MASK] : \
&ram[PC + 1] )
#define TARGET ( OP & T_MSK2 ?\
&reg[(OP & T_MSK2)/4] :\
&ram[PC + 1] )
#ifdef GCC
/* enums for the goto jump table */
enum { add, sub, mul, div, /* 0 1 2 3 */
mod, inc, dec, lur, /* 4 5 6 7 */
and, or, xor, cmp, /* 8 9 a b */
biz, bis, biq, big /* c d e f */
};
#endif
/* Status flag masks */
enum {
zr=0x01, /* zero */
lt=0x02, /* less than */
eq=0x04, /* equal */
gt=0x08, /* greater than */
cr=0x10, /* carry, overflow */
ng=0x20, /* negative */
_x=0x40, /* unused... */
_y=0x80 /* unused... */
};
int main(int argc, char ** argv){
u8 reg[4] = {0, 0, 0, 0};
u8 *s=0, *t=0, _t=0, ram[256];
#ifndef GCC
u8 _op=0;
#endif
s32 c;
FILE *fp;
struct timespec nap;
/** 125.000.000 for 8hz
** 3.906.250 for 256hz
nap.tv_nsec = (long) 3906250;
**/
nap.tv_sec = 0;
nap.tv_nsec = (long) 125000000;
#ifdef GCC
/* jump table for gotos */
void * jmps[16] = {
&&add, &&sub, &&mul, &&div,
&&mod, &&inc, &&dec, &&lur,
&&and, &&or, &&xor, &&cmp,
&&biz, &&bis, &&biq, &&big
};
#endif
/* exits if we don't have arguments */
if(argc < 2) return 1;
/* exits if we fail reading the file */
fp = fopen(argv[1], "r");
if (!fp) return 2;
/* zeros the ram */
PC=0;do{ram[PC]=0;PC++;}while(PC);
/* reads file to ram */
while((c = fgetc(fp)) != EOF){
ram[PC] = c;
printf("%02x ", ram[PC]);
PC++;
if(!(PC%16))puts("");
/*nanosleep(&nap, NULL);*/
}; puts("");
PC=255;
cycle: /* CPU cycle */
PC++;
OP = ram[PC];
s = SOURCE;
t = TARGET;
_t = *t; /* set the latch target register */
printf("OP=%02x, RG=%02x, PC=%02x, "
"ST=%02x, s=%02x, t=%02x, ",
OP, RG, PC,
ST, *s, *t);
nanosleep(&nap, NULL);
#ifdef GCC
/* gcc dynamic jump */
goto *(jmps[(OP&O_MASK)>>4]);
#endif
#ifndef GCC
_op = (OP&O_MASK)>>4;
/* ternary operator binary search */
_op < 8 ? ({goto lt8;}) : ({goto ge8;});
lt8:
_op < 4 ? ({goto lt4;}) : ({goto ge4;});
lt4:
_op < 2 ? ({goto lt2;}) : ({goto ge2;});
lt2:
_op ? ({goto sub;}) : ({goto add;});
ge2:
_op == 2 ? ({goto mul;}) : ({goto div;});
ge4:
_op < 6 ? ({goto lt6;}) : ({goto ge6;});
lt6:
_op == 4 ? ({goto mod;}) : ({goto inc;});
ge6:
_op == 6 ? ({goto dec;}) : ({goto lur;});
ge8:
_op < 12 ? ({goto ltc;}) : ({goto gec;});
ltc:
_op < 10 ? ({goto lta;}) : ({goto gea;});
lta:
_op == 8 ? ({goto and;}) : ({goto or;});
gea:
_op == 10 ? ({goto xor;}) : ({goto cmp;});
gec:
_op < 14 ? ({goto lte;}) : ({goto gee;});
lte:
_op == 12 ? ({goto biz;}) : ({goto bis;});
gee:
_op == 14 ? ({goto biq;}) : ({goto big;});
#endif
add: /* add */
puts("add'ing");
*t += *s;
if(!*t) ST |= zr;
if(*t == _t) ST |= eq;
if(*t < _t) ST |= cr;
goto cycle;
sub: /* subtract */
puts("sub'ing");
*t -= *s;
if (!*t) ST |= zr;
if (*t == _t) ST |= eq;
if(*t > _t) ST |= ng;
goto cycle;
mul: /* multiply */
puts("mul'ing");
*t *= *s;
if (!*t) ST |= zr;
if (*t < _t) ST |= cr;
goto cycle;
div: /* divide */
puts("div'ing");
/* avoid dividing by zero */
if (*s != 0) *t /= *s;
else ST |= zr;
goto cycle;
mod: /* modulus */
puts("mod'ing");
/* avoid dividing by zero */
if (*s != 0) *t %= *s;
else ST |= zr; /* ? */
goto cycle;
inc: /* increment */
puts("inc'ing");
(*t)++;
if (!*t) ST |= cr;
if (*t == 0) ST |= (zr & cr);
goto cycle;
dec: /* decrement */
puts("dec'ing");
(*t)--;
if (!*t) ST |= zr;
if (*t == 255) ST |= (zr & ng);
goto cycle;
lur: /* load/unload registers (LOAD/STORE) */
puts("lur'ing");
*t = *s;
goto cycle;
and: /* and */
puts("and'ing");
*t &= *s;
if (!*t) ST |= zr;
if (*t < _t) ST |= lt;
if (*t > _t) ST |= gt;
if (*t == _t) ST |= eq;
else ST ^= eq;
goto cycle;
or: /* or */
puts(" or'ing");
*t |= *s;
if (!*t) ST |= zr;
if (*t < _t) ST |= lt;
if (*t > _t) ST |= gt;
if (*t == _t) ST |= eq;
else ST ^= eq;
goto cycle;
xor: /* exclusive or */
puts("xor'ing");
*t ^= *s;
if (!*t) ST |= zr;
if (*t < _t) ST |= lt;
if (*t > _t) ST |= gt;
if (*t == _t) ST |= eq;
else ST ^= eq;
goto cycle;
cmp: /* compare */
puts("cmp'ing");
if (*t < *s) ST |= lt;
if (*t > *s) ST |= gt;
if (*t == *s) ST |= eq;
else ST ^= eq;
goto cycle;
biz: /* branch if zero */
puts("biz'ing");
/* OP+1 to get the next word,
* -1 'cause the 1st thing the cycle does
* is increment the OP */
if (ST & zr) OP = ram[OP+1]-1;
goto cycle;
bis: /* branch if smaller */
puts("bis'ing");
if (ST & lt) OP = ram[OP+1]-1;
goto cycle;
biq: /* branch if eQual */
puts("biq'ing");
if (ST & eq) OP = ram[OP+1]-1;
goto cycle;
big: /* branch if greater */
puts("big'ing");
if (ST & gt) OP = ram[OP+1]-1;
goto cycle;
return 0; /* unreachable */
}