Skip to content

Instantly share code, notes, and snippets.

@andrewrcollins
Created January 4, 2012 16:41
Show Gist options
  • Save andrewrcollins/1560868 to your computer and use it in GitHub Desktop.
Save andrewrcollins/1560868 to your computer and use it in GitHub Desktop.
#TJHSST ~ MIC-1 Emulator ~ high school microcode project
/* M I C . H
Header file for the type declarations used in the various
MIC??? programs. */
/* defines for accessing the registers with these names. */
#define PC 0
#define AC 1
#define SP 2
#define IR 3
#define TIR 4
#define ZERO 5
#define ONE 6
#define NEGONE 7
#define AMASK 8
#define SMASK 9
/* define for loading in the program file. */
#define ONEK 1000
/* since the following are in the 'machine' type, why not put them into the
registers array, it makes my life easier. */
#define ALATCH 16
#define BLATCH 17
#define MAR 18
#define MBR 19
#define AMUX 20
#define ALU 21
#define SHIFTER 22
#define MMUX 23
#define MPC 24
#define NEGATIVE 25
#define ZEROFLAG 26
#define INCREMENT 27
#define READY 28
/* this define is for accessing the 257th microinstruction, which is
considered the MIR by the emulator. The reason for squeezing all the
variables and such into tight arrays is simply for the reason that
it makes the programming easier, there are hardly any exceptions
to programming, no extra varibles to be dealt with, just indices into
arrays, much easier. */
#define MIR 256
/* the micro-instruction format for the micro-code in the
micro-store */
typedef struct {
unsigned amux : 1; /* controls the A multiplexer */
unsigned cond : 2; /* jump control */
unsigned alu : 2; /* ALU control */
unsigned sh : 2; /* shifter control */
unsigned mbr : 1; /* Memory Buffer Register enable */
unsigned mar : 1; /* Memory Address Register enable */
unsigned rd : 1; /* Read enable */
unsigned wr : 1; /* Write enable */
unsigned enc : 1; /* enable the C address line */
unsigned C : 4; /* C address */
unsigned B : 4; /* B address */
unsigned A : 4; /* A address */
unsigned addr : 8; /* ADDRess for the next micro-code instruction */
} microins_type;
/* a union so that the micro-code can be entered in easily as
hexadecimal sequeneces. */
typedef union {
microins_type instruction;
unsigned long int integer;
} microins;
#ifdef EMULATOR
/* the following are the variables found in the machinetype structure and
a short description of their purposes :
the Memory Buffer Register and the Memory Address Register
which contain the address in main memory and the data to be written
or the data that was written in main memory.
alatch and blatch are the latches used to hold the data loaded from
the registers and used by the ALU and MAR.
alu is the temporary output of the ALU accessed by the shifter.
shifter is the output of the shifting function part.
mpc is the micro-code instruction pointer, or program counter.
amux is the output of the A multiplexer which has inputs from the
mbr and alatch.
mmux is the micro-code multiplexer which is the output of
multiplexed inputs of the addr field in mir and the incremented mpc.
negative and zero are the flags ascerted by the ALU.
increment is the increment of mpc, simply.
ready is a variable used in accessing memory, read and writes.
this is the Micro-Instruction Register used to hold the next
micro-code instruction.
there are 16 16 bit registers available only to the micro-code programmer.
only ac,pc, and sp are available to the macro-code programmer in
this version.
the other 9 variables in the array are the preceding mpc, mmux, etc.
create the micro-code array. this stores the micro-code which is
executed by the emulator. the micro-code it is going to be running
has only 78 lines, but room for 256 is made for later additions.
the memory that the Mic-1 uses takes up 4096 16-bit words of
space. this is it. The Mic-1 programmer/user is not to assume that
the data stored in the memory is set to 0 at power-up, it is not. */
typedef struct {
microins microcode[257];
int registers[29],memory[4096];
} machinetype;
int powerup(char *,char *,machinetype *),
showmicroins(microins,int),printb(int,int),
viewregs(machinetype *,int),
subclock1(machinetype *),
subclock2(machinetype *),
subclock3(machinetype *),
subclock4(machinetype *);
#endif
#ifdef ASSEMBLER
typedef struct {
char *mnemonic;
unsigned int opcode;
} mnemonictype;
mnemonictype mnemonics[23] =
{ {"LODD",0x0000}, {"STOD",0x1000}, {"ADDD",0x2000}, {"SUBD",0x3000},
{"JPOS",0x4000}, {"JZER",0x5000}, {"JUMP",0x6000}, {"LOCO",0x7000},
{"LODL",0x8000}, {"STOL",0x9000}, {"ADDL",0xa000}, {"SUBL",0xb000},
{"JNEG",0xc000}, {"JNZE",0xd000}, {"CALL",0xe000}, {"PSHI",0xf000},
{"POPI",0xf200}, {"PUSH",0xf400}, { "POP",0xf600}, {"RETN",0xf800},
{"SWAP",0xfa00}, {"INSP",0xfc00}, {"DESP",0xfe00} };
#endif
/*
MIC-1 Emulator
MicroCode Section
*/
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
#include <string.h>
/* contains prototypes for the cursor hide() and show() functions. */
#include "cursor.h"
/* this definition must be here, or none of the prototyping and
type declarations are registered. */
#define EMULATOR
/* contains all the function prototypes and type declarations. */
#include "mic.h"
/* these are the string names of the 16 registers and some others. */
struct {
char name[8];
int x,y;
} regname[25] = { {"PC\0",5,1},{"AC\0",14,1},{"SP\0",23,1},{"IR\0",31,1},
{"TIR\0",40,1},{"0\0",49,1},{"+1\0",58,1},{"-1\0",67,1},
{"AMK\0",5,2},{"SMK\0",14,2},{"A\0",23,2},{"B\0",31,2},
{"C\0",40,2},{"D\0",49,2},{"E\0",58,2},{"F\0",67,2},
{"ALATCH\0",9,3},{"BLATCH\0",22,3},{"MAR\0",9,4},
{"MBR\0",22,4},{"AMUX\0",9,5},{"ALU\0",22,5},
{"SHIFTER\0",9,6},{"MMUX\0",22,6},{"MPC\0",9,7} };
/* the powerup function must be called before anything can be
done on the Mic-1 Emulator, it is just like the power up of
a real machine except that on a real machine the micro-code
would be burned into a ROM, instead of that it is stored
in a seperate data file, so different micro-codes can be
loaded into the Emulator, giving access to different level
2 assembly/machine languages. */
int powerup(microname,macroname,machine)
char *microname,*macroname;
machinetype *machine;
{
FILE *progfile;
int cnt;
/* during power-up the micro-code to be used by the Mic-1 Emulator
is loaded into the control store. */
if((progfile=fopen(microname,"rb"))==NULL) {
puts("error opening the micro-code program file");
exit(1);
}
/* read in the micro-code program file. It should be a binary
file composed of 256 unsigned long ints. These are the
actual bit patterns that make up the micro-code, to
create a micro-code use the auxillary program, MICMAKe. */
puts("reading micro-code...");
if((fread(machine->microcode,sizeof(microins),256,progfile))!=256) {
puts("error in reading the micro-code program file");
exit(1);
}
fclose(progfile);
/* now a program that runs on the Mic-1, written in the language
begin interpreted by the micro-code running in the control store
is loaded into the memory of the Mic-1 starting at address 0. */
if((progfile=fopen(macroname,"rb"))==NULL) {
puts("error opening the macro-code program file\n");
exit(1);
}
/* memory is cleared at power-up, but again this is not to be assumed
by the level 2 programmer. all memory locations set to zero. */
memset(machine->memory,0,4096);
/* read the file's contents into memory directly, until the
file is empty, or memory is full. */
puts("reading macro-code...");
cnt=0;
while(!feof(progfile)&&(cnt<4096))
cnt+=fread(&machine->memory[cnt],sizeof(unsigned int),ONEK,progfile);
/* all micro-code programs start at address 0 in the micro-code.
all macro-code programs start at address 0. */
machine->registers[READY]=0;
/* all registers start at zero, except for special registers,
but this should not be assumed by the
Mic-1 Emulator user, other emulators may not do this. */
for(cnt=0;cnt<25;cnt++)
machine->registers[cnt]=0;
machine->registers[PC]=0; /* start program at 0. */
machine->registers[SP]=0x0fc0;
/* the stack goes downward from high memory starting at address 4032,
the addresses above this are used for memory-mapped I/O and other
system stuff that will be added later on. */
machine->registers[ONE]=1; /* for incrementing things fast. */
machine->registers[NEGONE]=0xffff; /* -1 for decrementing things fast. */
machine->registers[AMASK]=0x0fff; /* address mask in Mac-1 interpreter. */
machine->registers[SMASK]=0x00ff; /* stack pointer mask */
}
/* all the operations in this function are those executed during
the first clock pulse of the Mic-1. */
int subclock1(machine)
machinetype *machine;
{
/* get the new micro-code program counter. */
machine->registers[MPC]=machine->registers[MMUX];
/* get the next micro-code instruction. */
machine->microcode[MIR].instruction=
machine->microcode[machine->registers[MPC]].instruction;
/* now for all the memory gorp, a memory read/write takes two
clock cycles, so ready is the clock count for a read or a
write, the following code could easily be modified for
faster or slower memory access times. */
switch(machine->registers[READY]) {
/* if ready equals zero then no memory read/writes are begin done. */
case 0 :
/* check if there is a want to read/write to/from memory. */
if(machine->microcode[MIR].instruction.rd)
/* initiate a read process. */
machine->registers[READY]=1;
if(machine->microcode[MIR].instruction.wr)
/* initiate a write process. */
machine->registers[READY]=4;
break;
/* a memory read/write is in progress, stay in progress for one
more cycle. */
case 1 :
case 4 :
machine->registers[READY]++;
break;
/* act on the values in the mar or mbr and do the read/write
operation now. */
case 2 :
/* the % is used to get a valid address, 0..4095 are all that
are allowed. */
machine->registers[MBR]=machine->memory[machine->registers[MAR]];
machine->registers[READY]=0;
break;
case 5 :
machine->memory[machine->registers[MAR]]=machine->registers[MBR];
machine->registers[READY]=0;
break;
}
}
int subclock2(machine)
machinetype *machine;
{
/* get the information from the various registers to their
respective latches. */
machine->registers[ALATCH]=machine->registers
[machine->microcode[MIR].instruction.A];
machine->registers[BLATCH]=machine->registers
[machine->microcode[MIR].instruction.B];
/* amux is the multiplexor controlling which line, A or MBR, goes to the
ALU. */
machine->registers[AMUX]=
(machine->microcode[MIR].instruction.amux)
?machine->registers[MBR]:machine->registers[ALATCH];
/* increment is the next micro-code instruction index into the control
store. */
machine->registers[INCREMENT]=machine->registers[MPC]+1;
}
int subclock3(machine)
machinetype *machine;
{
/* the following is the operation of the ALU. */
switch(machine->microcode[MIR].instruction.alu) {
case 0 :
machine->registers[ALU]=
machine->registers[AMUX]+machine->registers[BLATCH]; /* Addition */
break;
case 1 :
machine->registers[ALU]=
machine->registers[AMUX]&machine->registers[BLATCH]; /* ANDdition */
break;
case 2 :
machine->registers[ALU]=
machine->registers[AMUX]; /* Pass through */
break;
case 3 :
machine->registers[ALU]=
~machine->registers[AMUX]; /* Invert it. */
break;
}
machine->registers[NEGATIVE]=
(machine->registers[ALU]&0x8000)&&1; /* Ascert the flags. */
machine->registers[ZEROFLAG]=(machine->registers[ALU]==0);
/* the following mimics the operation of the shifter on
the output of the ALU. */
switch(machine->microcode[MIR].instruction.sh) {
case 0 :
case 3 :
machine->registers[SHIFTER]=
machine->registers[ALU]; /* Pass through. */
break;
case 1 :
machine->registers[SHIFTER]=
machine->registers[ALU]>>1; /* Right shift. */
break;
case 2 :
machine->registers[SHIFTER]=
machine->registers[ALU]<<1; /* Left shift. */
break;
}
/* now we load the MAR. */
if(machine->microcode[MIR].instruction.mar)
machine->registers[MAR]=machine->registers[BLATCH]%4096;
}
int subclock4(machine)
machinetype *machine;
{
/* Save our results, or not. */
if(machine->microcode[MIR].instruction.enc)
machine->registers[machine->microcode[MIR].instruction.C]=
machine->registers[SHIFTER];
/* Get the MBR filled with whatever. */
if(machine->microcode[MIR].instruction.mbr)
machine->registers[MBR]=machine->registers[SHIFTER]%4096;
/* Determine the value of MMUX which is what
MPC will be on the next time step. */
switch(machine->microcode[MIR].instruction.cond) {
case 1 :
if(!machine->registers[NEGATIVE]) {
machine->registers[MMUX]=machine->registers[INCREMENT];
break;
}
case 3 :
machine->registers[MMUX]=machine->microcode[MIR].instruction.addr;
break;
case 2 :
if(machine->registers[ZEROFLAG]) {
machine->registers[MMUX]=machine->microcode[MIR].instruction.addr;
break;
}
case 0 :
machine->registers[MMUX]=machine->registers[INCREMENT];
break;
}
}
int showmicroins(mir,mcnt)
microins mir;
int mcnt;
{
static int cnt=0;
gotoxy(1,9+cnt);
cnt=(cnt>7)?0:++cnt;
clreol();
printf("%-3d ",mcnt);
if(mir.instruction.mar)
printf("mar := %s; ",strlwr(regname[mir.instruction.B].name));
if(mir.instruction.enc||mir.instruction.mbr||
(!mir.instruction.enc&&mir.instruction.alu==2)) {
if(mir.instruction.mbr)
printf("mbr := ");
if(mir.instruction.enc)
printf("%s := ",strlwr(regname[mir.instruction.C].name));
else
if(mir.instruction.alu==2)
printf("alu := ");
switch(mir.instruction.sh) {
case 1 :
printf("rshift(");
break;
case 2 :
printf("lshift(");
break;
}
switch(mir.instruction.alu) {
case 0 :
printf("%s + %s",strlwr(regname[mir.instruction.A].name),
strlwr(regname[mir.instruction.B].name));
break;
case 1 :
printf("band(%s,%s)",strlwr(regname[mir.instruction.A].name),
strlwr(regname[mir.instruction.B].name));
break;
case 2 :
if(!mir.instruction.amux)
printf("%s",strlwr(regname[mir.instruction.A].name));
else printf("mbr");
break;
case 3 :
printf("inv(%s)",strlwr(regname[mir.instruction.A].name));
break;
}
switch(mir.instruction.sh) {
case 1 :
case 2 :
putch(')');
break;
}
printf("; ");
}
if(mir.instruction.rd)
printf("rd; ");
if(mir.instruction.wr)
printf("wr; ");
switch(mir.instruction.cond) {
case 0 :
break;
case 1 :
printf("if n then goto %d;",mir.instruction.addr);
break;
case 2 :
printf("if z then goto %d;",mir.instruction.addr);
break;
case 3 :
printf("goto %d;",mir.instruction.addr);
break;
}
}
int viewregs(machine,current)
machinetype *machine;
int current;
{
static int cnt=-1;
if(cnt==(-1)) {
for(cnt=0;cnt<16;cnt++) {
gotoxy(regname[cnt].x-4,regname[cnt].y);
printf("%3s",strupr(regname[cnt].name));
}
for(;cnt<25;cnt++) {
gotoxy(regname[cnt].x-8,regname[cnt].y);
printf("%-7s",strupr(regname[cnt].name));
}
}
for(cnt=0;cnt<25;cnt++) {
gotoxy(regname[cnt].x,regname[cnt].y);
printf("%04X",machine->registers[cnt]);
}
gotoxy(22,7);
putch(machine->registers[NEGATIVE]?'N':' ');
gotoxy(24,7);
putch(machine->registers[ZEROFLAG]?'Z':' ');
gotoxy(27,7);
clreol();
gotoxy(27+current*2,7);
putch(current+'0');
gotoxy(1,8);
showmicroins(machine->microcode[MIR],machine->registers[MPC]);
}
int process(cycle,machine)
int (*cycle)();
machinetype *machine;
{
(*cycle)(machine);
}
main(argc,argv)
int argc;
char *argv[];
{
machinetype machine;
char key=' ',filename[14];
int current=0,quit=0,continuous=0;
/* an array of function pointers, used to step through a micro-code
instruction sequence. */
int *clocks[] = {
(int *) subclock1,
(int *) subclock2,
(int *) subclock3,
(int *) subclock4
};
if(argc<2) {
puts("MIC <micro-filename> <macro-filename>");
exit(1);
}
powerup(argv[1],argv[2],&machine);
hide();
clrscr();
viewregs(&machine,current);
while(!quit) {
switch(tolower(getch())) {
case 'c' :
/* Continuous Running,Tracing, Stepping will stop when a key is pressed.
When c is pressed the last stepping method is repeated continuously,
let's say we stepped and then pressed c, we would then continue
stepping until a key was pressed. */
continuous=1;
ungetch(key);
break;
case 'r' :
/* Run, no stepping, just one macro-instruction is run. Or until MPC==0,
which means the instruction is done. */
do {
do {
while(!machine.registers[MPC]) {
process(clocks[current],&machine);
current++;
current&=3;
}
process(clocks[current],&machine);
current++;
current&=3;
} while(machine.registers[MPC]);
viewregs(&machine,current);
} while (!kbhit()&continuous);
continuous=0;
key='r';
break;
case 't' :
/* Trace though each clock-cycle, all four sub-cycles. Or until current==0,
which means the clocks are done. */
do {
do {
process(clocks[current],&machine);
current++;
current&=3;
} while(current);
viewregs(&machine,current);
} while (!kbhit()&continuous);
continuous=0;
key='t';
break;
case 's' : /* Step through each sub-clock cycle seperately. */
do {
process(clocks[current],&machine);
current++;
current&=3;
viewregs(&machine,current);
} while (!kbhit()&continuous);
continuous=0;
key='s';
break;
case 'q' :
/* Quit the emulator. It will ask if you want to save later on. */
gotoxy(1,23);
puts("Do you want to Quit?");
if(tolower(getch())=='y')
quit=1;
gotoxy(1,23);
clreol();
break;
case 'w' :
/* Write the current state of this emulation to disk, memory, registers,
MPC, everything. */
do {
gotoxy(1,24);
clreol();
gotoxy(1,23);
clreol();
puts("Enter filename to save into.");
gets(filename);
strupr(filename);
filename[8]=0;
strcat(filename,".SAV");
gotoxy(1,24);
clreol();
gotoxy(1,23);
clreol();
puts("Is this correct?");
puts(filename);
} while(tolower(getch())!='y');
gotoxy(1,24);
clreol();
gotoxy(1,23);
clreol();
break;
case 'l' :
/* Load a previously save state of the emulation, micro-store, TIR,
memory, everything. */
break;
}
}
show();
}
/*
MIC-1 Emulator
MicroCode Section
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <graphics.h>
/* this definition must be here, or none of the prototyping and
type declarations are registered. */
#define EMULATOR
/* contains all the function prototypes and type declarations. */
#include "mic.h"
/* these are the string names of the 16 registers and some others. */
char regname[25][10] = { "PC\0","AC\0","SP\0","IR\0","TIR\0","0\0",
"+1\0","-1\0","AMK\0","SMK\0","A\0",
"B\0","C\0","D\0","E\0","F\0",
"ALATCH\0","BLATCH\0","MAR\0","MBR\0",
"AMUX\0","ALU\0","SHIFTER\0","MMUX\0",
"MPC\0" };
/* the powerup function must be called before anything can be
done on the Mic-1 Emulator, it is just like the power up of
a real machine except that on a real machine the micro-code
would be burned into a ROM, instead of that it is stored
in a seperate data file, so different micro-codes can be
loaded into the Emulator, giving access to different level
2 assembly/machine languages. */
int powerup(microname,macroname,machine)
char *microname,*macroname;
machinetype *machine;
{
FILE *progfile;
int cnt;
/* during power-up the micro-code to be used by the Mic-1 Emulator
is loaded into the control store. */
if((progfile=fopen(microname,"rb"))==NULL) {
printf("error opening the micro-code program file\n");
exit(1);
}
/* read in the micro-code program file. It should be a binary
file composed of 256 unsigned long ints. These are the
actual bit patterns that make up the micro-code, to
create a micro-code use the auxillary program, MICMAKe. */
sputs("reading micro-code");
if((fread(machine->microcode,sizeof(microins),256,progfile))!=256) {
printf("error in reading the micro-code program file\n");
exit(1);
}
fclose(progfile);
/* now a program that runs on the Mic-1, written in the language
begin interpreted by the micro-code running in the control store
is loaded into the memory of the Mic-1 starting at address 0. */
if((progfile=fopen(macroname,"rb"))==NULL) {
printf("error opening the macro-code program file\n");
exit(1);
}
cnt=0;
/* read the file's contents into memory directly, until the
file is empty, or memory is full. */
sputs("reading macro-code");
while(!feof(progfile)&&(cnt<4096))
cnt+=fread(&machine->memory[cnt],sizeof(unsigned int),ONEK,progfile);
/* all micro-code programs start at address 0 in the micro-code.
all macro-code programs start at address 0. */
machine->mbr=machine->mar=machine->mmux=machine->ready=0;
/* all registers start at zero, except for special registers,
but this should not be assumed by the
Mic-1 Emulator user, other emulators may not do this. */
for(cnt=0;cnt<16;cnt++)
machine->registers[cnt]=0;
machine->registers[PC]=0; /* start program at 0. */
machine->registers[SP]=0x0fc0;
/* the stack goes downward from high memory starting at address 4032,
the addresses above this are used for memory-mapped I/O and other
system stuff that will be added later on. */
machine->registers[ONE]=1; /* for incrementing things fast. */
machine->registers[NEGONE]=0xffff; /* -1 for decrementing things fast. */
machine->registers[AMASK]=0x0fff; /* address mask in Mac-1 interpreter. */
machine->registers[SMASK]=0x00ff; /* stack pointer mask */
}
/* all the operations in this function are those executed during
the first clock pulse of the Mic-1. */
int subclock1(machine)
machinetype *machine;
{
/* get the new micro-code program counter. */
machine->mpc=machine->mmux;
/* get the next micro-code instruction. */
machine->mir.instruction=machine->microcode[machine->mpc].instruction;
/* now for all the memory gorp, a memory read/write takes two
clock cycles, so ready is the clock count for a read or a
write, the following code could easily be modified for
faster or slower memory access times. */
switch(machine->ready) {
/* if ready equals zero then no memory read/writes are begin done. */
case 0 :
/* check if there is a want to read/write to/from memory. */
if(machine->mir.instruction.rd)
/* initiate a read process. */
machine->ready=1;
if(machine->mir.instruction.wr)
/* initiate a write process. */
machine->ready=4;
break;
/* a memory read/write is in progress, stay in progress for one
more cycle. */
case 1 :
case 4 :
machine->ready++;
break;
/* act on the values in the mar or mbr and do the read/write
operation now. */
case 2 :
/* the % is used to get a valid address, 0..4095 are all that
are allowed. */
machine->mbr=machine->memory[machine->mar];
machine->ready=0;
break;
case 5 :
machine->memory[machine->mar]=machine->mbr;
machine->ready=0;
break;
}
}
int subclock2(machine)
machinetype *machine;
{
/* get the information from the various registers to their
respective latches. */
machine->alatch=machine->registers[machine->mir.instruction.A];
machine->blatch=machine->registers[machine->mir.instruction.B];
/* amux is the multiplexor controlling which line, A or MBR, goes to the
ALU. */
machine->amux=(machine->mir.instruction.amux)?machine->mbr:machine->alatch;
/* increment is the next micro-code instruction index into the control
store. */
machine->increment=machine->mpc+1;
}
int subclock3(machine)
machinetype *machine;
{
/* the following is the operation of the ALU. */
switch(machine->mir.instruction.alu) {
case 0 :
machine->alu=machine->amux+machine->blatch; /* Addition */
break;
case 1 :
machine->alu=machine->amux&machine->blatch; /* ANDdition */
break;
case 2 :
machine->alu=machine->amux; /* Pass through */
break;
case 3 :
machine->alu=~machine->amux; /* Invert it. */
break;
}
machine->negative=(machine->alu&0x8000)&&1; /* Ascert the flags. */
machine->zero=(machine->alu==0);
/* the following mimics the operation of the shifter on
the output of the ALU. */
switch(machine->mir.instruction.sh) {
case 0 :
case 3 :
machine->shifter=machine->alu; /* Pass through. */
break;
case 1 :
machine->shifter=machine->alu>>1; /* Right shift. */
break;
case 2 :
machine->shifter=machine->alu<<1; /* Left shift. */
break;
}
/* now we load the MAR. */
if(machine->mir.instruction.mar)
machine->mar=machine->blatch%4096;
}
int subclock4(machine)
machinetype *machine;
{
/* Save our results, or not. */
if(machine->mir.instruction.enc)
machine->registers[machine->mir.instruction.C]=machine->shifter;
/* Get the MBR filled with whatever. */
if(machine->mir.instruction.mbr)
machine->mbr=machine->shifter%4096;
/* Determine the value of MMUX which is what
MPC will be on the next time step. */
switch(machine->mir.instruction.cond) {
case 0 :
machine->mmux=machine->increment;
break;
case 1 :
if(machine->negative)
machine->mmux=machine->mir.instruction.addr;
else
machine->mmux=machine->increment;
break;
case 2 :
if(machine->zero)
machine->mmux=machine->mir.instruction.addr;
else
machine->mmux=machine->increment;
break;
case 3 :
machine->mmux=machine->mir.instruction.addr;
break;
}
}
int showmicroins(mir)
microins mir;
{
static microins instruct;
static int called=0;
int ycnt;
if(!called) {
called=1;
ycnt=10;
outtextxy(10,ycnt+=10,"A C");
outtextxy(10,ycnt+=10,"M O A M M E");
outtextxy(10,ycnt+=10,"U N L S B A R W N");
outtextxy(10,ycnt+=10,"X D U H R R D R C C B A ADDR");
}
ycnt=60;
if(instruct.instruction.amux!=mir.instruction.amux) {
instruct.instruction.amux=mir.instruction.amux;
outtextxy(10,ycnt,printb(mir.instruction.amux,1));
}
ycnt+=10;
if(instruct.instruction.cond!=mir.instruction.cond) {
instruct.instruction.cond=mir.instruction.cond;
if(instruct.instruction.alu!=mir.instruction.alu) {
instruct.instruction.alu=mir.instruction.alu;
if(instruct.instruction.sh!=mir.instruction.sh) {
instruct.instruction.sh=mir.instruction.sh;
if(instruct.instruction.mbr!=mir.instruction.mbr) {
instruct.instruction.mbr=mir.instruction.mbr;
if(instruct.instruction.mar!=mir.instruction.mar) {
instruct.instruction.mar=mir.instruction.mar;
if(instruct.instruction.rd!=mir.instruction.rd) {
instruct.instruction.rd=mir.instruction.rd;
if(instruct.instruction.wr!=mir.instruction.wr) {
instruct.instruction.wr=mir.instruction.wr;
if(instruct.instruction.enc!=mir.instruction.enc) {
instruct.instruction.enc=mir.instruction.enc;
if(instruct.instruction.C!=mir.instruction.C) {
instruct.instruction.C=mir.instruction.C;
if(instruct.instruction.B!=mir.instruction.B) {
instruct.instruction.B=mir.instruction.B;
if(instruct.instruction.A!=mir.instruction.A) {
instruct.instruction.A=mir.instruction.A;
if(instruct.instruction.addr!=mir.instruction.addr) {
instruct.instruction.addr=mir.instruction.addr;
printb(mir.instruction.cond,2);
printb(mir.instruction.alu,2);
printb(mir.instruction.sh,2);
printb(mir.instruction.mbr,1);
printb(mir.instruction.mar,1);
printb(mir.instruction.rd,1);
printb(mir.instruction.wr,1);
printb(mir.instruction.enc,1);
printb(mir.instruction.C,4);
printb(mir.instruction.B,4);
printb(mir.instruction.A,4);
printb(mir.instruction.addr,8);
puts("");
}
char *printb(num,len)
int num,len;
{
char *tmp,tmp2;
tmp=(char *)malloc(sizeof(char)*len);
while(len>0) {
len--;
sprintf(&tmp2,"%c",((num&(1<<len))&&1)?'1':'0');
strcat(tmp,&tmp2);
}
strcat(tmp,' ');
return tmp;
}
int sputs(string)
char *string;
{
while(*string) {
putch(*string);
string++;
}
}
int viewregs(machine)
machinetype *machine;
{
int cnt;
char tmp[80];
gotoxy(1,1);
for(cnt=0;cnt<8;cnt++) {
sprintf(tmp,"%3s %04X ",regname[cnt],machine->registers[cnt]);
sputs(tmp);
}
puts("");
for(;cnt<16;cnt++) {
sprintf(tmp,"%3s %04X ",regname[cnt],machine->registers[cnt]);
sputs(tmp);
}
puts("");
sprintf(tmp,"%7s %04X %7s %04X",regname[16],machine->alatch,
regname[17],machine->blatch);
sputs(tmp);
puts("");
sprintf(tmp,"%7s %04X %7s %04X",regname[18],machine->mar,
regname[19],machine->mbr);
sputs(tmp);
puts("");
sprintf(tmp,"%7s %04X %7s %04X",regname[20],machine->amux,
regname[21],machine->alu);
sputs(tmp);
puts("");
sprintf(tmp,"%7s %04X %7s %04X",regname[22],machine->shifter,
regname[23],machine->mmux);
sputs(tmp);
puts("");
sprintf(tmp,"%7s %04X %3c %3c m[x] %04X",regname[24],machine->mpc,
(machine->negative)?'N':' ',(machine->zero)?'Z':' ',
machine->memory[machine->registers[3]&0x00ff]);
sputs(tmp);
puts("");
showmicroins(machine->mir);
}
int process(cycle,machine)
int (*cycle)();
machinetype *machine;
{
(*cycle)(machine);
}
main(argc,argv)
int argc;
char *argv[];
{
machinetype machine;
char key;
int current=0,quit=0;
/* an array of function pointers, used to step through a micro-code
instruction sequence. */
int *clocks[] = {
(int *) subclock1,(int *) subclock2,(int *) subclock3,(int *) subclock4
};
if(argc<2) {
printf("MIC <micro-filename> <macro-filename>\n");
exit(1);
}
powerup(argv[1],argv[2],&machine);
hide();
textcolor(LIGHTGREEN);
textbackground(BLACK);
clrscr();
viewregs(&machine);
while(!quit) {
switch(tolower(getch())) {
case 'r' :
/* Run, no stepping, just one macro-instruction is run. Or until MPC==0,
which means the instruction is done. */
do {
while(!machine.mpc) {
process(clocks[current],&machine);
current++;
current&=3;
}
process(clocks[current],&machine);
current++;
current&=3;
} while(machine.mpc);
viewregs(&machine);
break;
case 't' :
/* Trace though each clock-cycle, all four sub-cycles. Or until current==0,
which means the clocks are done. */
do {
process(clocks[current],&machine);
current++;
current&=3;
} while(current);
viewregs(&machine);
break;
case 's' : /* Step through each sub-clock cycle seperately. */
process(clocks[current],&machine);
current++;
current&=3;
viewregs(&machine);
break;
case 'q' :
/* Quit the emulator. It will ask if you want to save later on. */
gotoxy(1,15);
sputs("Do you want to Quit?");
if(tolower(getch())=='y')
quit=1;
gotoxy(1,15);
sputs(" ");
break;
case 'w' :
/* Write the current state of this emulation to disk, memory, registers,
MPC, everything. */
break;
case 'l' :
/* Load a previously save state of the emulation, micro-store, TIR,
memory, everything. */
break;
}
}
show();
}
/* Assembler for the MAC-1 assembly language
; A sample program that does nothing but show what this assembler can and
; cannot assemble
B: EQU 25 ; literal B is the value 25
A: 20 ; symbol A is the address of the value 20
HERE: STOD A ; AC := memory[symbol A], so AC := 20,
; not AC := memory[20]
; symbol HERE is the address of this instruction
LOCO B ; AC := literal B, so AC := 25
LOOP: LOCO A ; AC := symbol A, is not AC := 20,
; but really AC := address containing 20
; LOOP is the symbol representing the current address
JUMP LOOP ; PC := symbol LOOP, so PC := addressing containing
; the instruction LOCO A.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* mnemonics stuff are defined if */
#define ASSEMBLER
/* is defined before including it */
#include "mic.h"
/* number of slots in the hash table. */
#define k 15
struct symboltype {
char *string;
int value,type;
struct symboltype *next;
};
main(argc,argv)
int argc;
char *argv[];
{
struct symboltype *symbols[k],*literal[k];
printf("blah");
}
/* frees all the hash nodes in the hash linked list pointed to by symbol.
returns number of nodes freed. */
int clearout(symbol)
struct symboltype *symbol;
{
int freed=0;
switch((long unsigned int)symbol) {
case NULL :
break;
default :
freed=1+clearout(symbol->next);
free(symbol);
break;
}
return freed;
}
/* remove all the symbols in the hash table. returns number of symbols in the
table, all hash code slots. */
int hashclear(table)
struct symboltype *table[k];
{
int cnt;
static int chk=(-1);
struct symboltype *tmp;
switch(chk) {
case -1 :
for(cnt=0;cnt<k;cnt++)
table[cnt]=NULL;
chk=0;
break;
default :
chk=0;
for(cnt=0;cnt<k;cnt++)
chk+=clearout(table[cnt]);
break;
}
return chk;
}
/* hashcode takes a string and returns the hash code for the string
that is used in storing the symbol in a hash table. */
int hashcode(string)
char *string;
{
char *tmp;
int sum=0;
tmp=string;
while(*tmp)
sum+=(int)(*tmp),tmp++;
return sum%k;
}
/* puts the given symbol in the given hash table using the given
hash code. code should be generated by hashcode(). */
int hashput(table,symbol,code)
struct symboltype *table[k];
struct symboltype *symbol;
int code;
{
struct symboltype *tmp;
tmp=table[code];
if(tmp!=NULL)
while(tmp->next!=NULL)
tmp=tmp->next;
(*tmp)=malloc(sizeof(struct symboltype));
*(*tmp)=(*symbol);
}
/* returns the truth that the symbol containing string
is in the table by returning a pointer to the symbol, or NULL. */
struct symboltype *hashcheck(table,string,code)
struct symboltype *table[k];
char *string;
int code;
{
}
/*
Mic-1 Emulator
Micro-Code Generator
*/
#include <stdio.h>
#include <conio.h>
#include "cursor.h"
typedef struct {
unsigned amux : 1; /* controls the A multiplexer */
unsigned cond : 2; /* jump control */
unsigned alu : 2; /* ALU control */
unsigned sh : 2; /* shifter control */
unsigned mbr : 1; /* Memory Buffer Register enable */
unsigned mar : 1; /* Memory Address Register enable */
unsigned rd : 1; /* Read enable */
unsigned wr : 1; /* Write enable */
unsigned enc : 1; /* enable the C address line */
unsigned C : 4; /* C address */
unsigned B : 4; /* B address */
unsigned A : 4; /* A address */
unsigned addr : 8; /* ADDRess for the next micro-code instruction */
} micro_type;
typedef union {
micro_type instruction;
unsigned long int integer;
} microinstruct;
microinstruct code[128];
main(argc,argv)
int argc;
char *argv[];
{
FILE *insource,*outcode;
char codestr[80],*copy1,*first,copy2[80],*copy3,*second,*tmp;
int icnt;
if(argc<3) {
printf("MICMAKE <source-code filename> <out-code filename>\n");
exit(1);
}
if((insource=fopen(argv[1],"r"))==NULL) {
printf("Error opening source-code file\n");
exit(1);
}
if((outcode=fopen(argv[2],"wb"))==NULL) {
printf("Error opening out-code file\n");
exit(1);
}
for(icnt=0;icnt<128;icnt++)
code[icnt].integer=0;
while(!feof(insource)) {
fgets(codestr,80,insource);
copy1=codestr;
copy1[strlen(copy1)+1]=0;
first=strtok(copy1,";");
do {
strcpy(copy2,first);
second=strtok(copy2," (),");
do {
if(!stricmp(second,"rd")) {
code[icnt].instruction.rd=1;
printf("RD\n");
}
if(!stricmp(second,"wr")) {
code[icnt].instruction.wr=1;
printf("WR\n");
}
if(!stricmp(second,"goto")) {
code[icnt].instruction.cond=3;
second=strtok(NULL," (),");
code[icnt].instruction.addr=atoi(second);
printf("GOTO %d\n",atoi(second));
}
if(!stricmp(second,"if")) {
second=strtok(NULL," (),");
if(!stricmp(second,"n"))
code[icnt].instruction.cond=1;
if(!stricmp(second,"z"))
code[icnt].instruction.cond=2;
second=strtok(NULL," (),");
second=strtok(NULL," (),");
second=strtok(NULL," (),");
code[icnt].instruction.addr=atoi(second);
printf("IF %d THEN GOTO %d\n",
code[icnt].instruction.cond,code[icnt].instruction.addr);
}
} while((second=strtok(NULL," (),")));
copy3=strchr(copy1,'\0')+1;
copy1=copy3;
if(*copy1=='\n') {
icnt++;
copy1=NULL;
}
} while((first=strtok(copy1,";")));
}
fwrite(code,sizeof(unsigned long int),128,outcode);
fcloseall();
}
/*
Mic-1 Emulator
Micro-Code Generator
*/
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "cursor.h"
#include "mic.h"
#define boxx 11
#define boxy 8
/* the micro-code storage area for the program. */
microins code[256];
char bitbox[][50]={
{"\x11\x04\x10\x01\13A\13A\13A\13A\12A\0"},
{"\x11\x04\x10\x0f\0"},
{"\x9~~HLP\1LP\1LP\1LPLPLPLPLPLP\3LP\3LP\3LP\7L:\1~\0"},
{"\x9~~9~2\1~2\1~2\1~2~2~2~2~2~2\3~2\3~2\3~2\7~9\1~\0"},
{"\x9~~GPNPLNPLNPLNPNPNPNPNPNLP\1LNLP\1LNLP\1LN\2LP\3L;\1~\0"},
{"\x9~\1CX~2~~2~~2~~2~2~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\3CX~~2~~2~~2~2~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\6CX~~2~~2~2~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\11CX~~2~2~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\14CX~2~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\16CX~2~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\15C\2CX~2~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\15C\4CX~2~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\15C\6CX~~2\3~2\3~2\5~2\6~\0"},
{"\x9~\15C\11CX\3~2\3~2\5~2\6~\0"},
{"\x9~\15C\15CCX\3~2\5~2\6~\0"},
{"\x9~\15C\15C\5CX\5~2\6~\0"},
{"\x9~\15C\15C\13CCX\6~\0"},
{"\x10\1\x11\7\13A\13A\13A\13A\12A\0"},
{"\x10\8\x11\7\15 Andy Collins' Micro-Code Editor\15 \0"},
{"\14 Computer Architecture First Period\13 \0"}
} ;
struct {
char *name;
int position,length;
} select[13]={ {"\1 AMUX\1 \0",23,1},
{"\1 COND\1 \0",25,2},
{"\2 ALU\1 \0",28,2},
{"\3 SH\1 \0",31,2},
{"\2 MBR\1 \0",34,1},
{"\2 MAR\1 \0",36,1},
{"\3 RD\1 \0",38,1},
{"\3 WR\1 \0",40,1},
{"\2 ENC\1 \0",42,1},
{"\4 C\1 \0",44,4},
{"\4 B\1 \0",49,4},
{"\4 A\1 \0",54,4},
{"\1 ADDR\1 \0",59,8} };
char *icntstr="\x10\xa\x11\8\6 MPC\13 \13 \13 \13 \5 \0";
int icnt=0;
main(argc,argv)
int argc;
char *argv[];
{
FILE *outcode;
int quit,current,ct,copy=0;
char key;
if(argc<2) {
printf("MICMAKE <out-code filename>\n");
exit(1);
}
if((outcode=fopen(argv[1],"rb"))==NULL) {
printf("Error opening out-code file\n");
exit(1);
}
hide();
screen();
textcolor(BLACK);
textbackground(BLACK);
fread(code,sizeof(microins),256,outcode);
fclose(outcode);
if((outcode=fopen(argv[1],"wb"))==NULL) {
printf("Error opening out-code file\n");
exit(1);
}
for(icnt=0;icnt<256;icnt++)
ibar(0);
quit=0;
showins();
current=0;
icnt=0;
ibar(1);
ibar(-1);
setaway(current);
while(!quit) {
if(kbhit()) {
key=getch();
if(!key)
key=getch();
switch(key) {
case 'q' :
case 27 :
quit=1;
break;
case 72 :
clearaway(current);
current--;
if(current<0)
current=12;
setaway(current);
break;
case 80 :
clearaway(current);
current++;
if(current>12)
current=0;
setaway(current);
break;
case 77 :
incit(current,1);
break;
case 75 :
incit(current,-1);
break;
case 73 :
ibar(1);
break;
case 81 :
ibar(-1);
break;
case 71 :
for(ct=0;ct<16;ct++)
ibar(1);
break;
case 79 :
for(ct=0;ct<16;ct++)
ibar(-1);
break;
case 'c' :
code[icnt].integer=0;
ibar(1);
ibar(-1);
break;
case 'r' :
copy=icnt;
break;
case 'w' :
code[icnt]=code[copy];
ibar(1);
ibar(-1);
break;
}
}
}
show();
fwrite(code,sizeof(unsigned long int),256,outcode);
fcloseall();
}
int incit(whichone,way)
int whichone,way;
{
gotoxy(select[whichone].position,boxy+2);
switch(whichone) {
case 0 :
if(way)
code[icnt].instruction.amux^=1;
printb(code[icnt].instruction.amux,select[whichone].length);
break;
case 1 :
code[icnt].instruction.cond+=way;
printb(code[icnt].instruction.cond,select[whichone].length);
break;
case 2 :
code[icnt].instruction.alu+=way;
printb(code[icnt].instruction.alu,select[whichone].length);
break;
case 3 :
code[icnt].instruction.sh+=way;
printb(code[icnt].instruction.sh,select[whichone].length);
break;
case 4 :
if(way)
code[icnt].instruction.mbr^=1;
printb(code[icnt].instruction.mbr,select[whichone].length);
break;
case 5 :
if(way)
code[icnt].instruction.mar^=1;
printb(code[icnt].instruction.mar,select[whichone].length);
break;
case 6 :
if(way)
code[icnt].instruction.rd^=1;
printb(code[icnt].instruction.rd,select[whichone].length);
break;
case 7 :
if(way)
code[icnt].instruction.wr^=1;
printb(code[icnt].instruction.wr,select[whichone].length);
break;
case 8 :
if(way)
code[icnt].instruction.enc^=1;
printb(code[icnt].instruction.enc,select[whichone].length);
break;
case 9 :
code[icnt].instruction.C+=way;
printb(code[icnt].instruction.C,select[whichone].length);
break;
case 10 :
code[icnt].instruction.B+=way;
printb(code[icnt].instruction.B,select[whichone].length);
break;
case 11 :
code[icnt].instruction.A+=way;
printb(code[icnt].instruction.A,select[whichone].length);
break;
case 12 :
code[icnt].instruction.addr+=way;
printb(code[icnt].instruction.addr,select[whichone].length);
break;
}
}
int ibar(way)
int way;
{
textcolor(LIGHTGREEN);
textbackground(DARKGRAY);
gotoxy((icnt%128)/2+9,5+(icnt/128));
putch(' ');
icnt+=way;
if(icnt>255)
icnt=0;
if(icnt<0)
icnt=255;
if(way) {
textcolor(LIGHTGREEN);
textbackground(DARKGRAY);
gotoxy((icnt%128)/2+9,5+(icnt/128));
putch((char)(222-((icnt&1)==0)));
gotoxy(21,7);
printb(icnt,8);
showins();
}
}
int showins(void)
{
int cnt;
for(cnt=0;cnt<13;cnt++) {
textcolor(WHITE);
textbackground(RED);
incit(cnt,0);
}
}
int clearaway(whichone)
int whichone;
{
textcolor(WHITE);
textbackground(RED);
incit(whichone,0);
textcolor(LIGHTMAGENTA);
textbackground(BLACK);
gotoxy(13,boxy+4+whichone);
putstr(select[whichone].name,0);
gotoxy(13,boxy+4+whichone);
putch(' ');
gotoxy(20,boxy+4+whichone);
putch(' ');
}
int setaway(whichone)
int whichone;
{
textbackground(CYAN);
textcolor(YELLOW);
gotoxy(13,boxy+4+whichone);
putstr(select[whichone].name,0);
textcolor(LIGHTCYAN);
gotoxy(13,boxy+4+whichone);
putch('>');
gotoxy(20,boxy+4+whichone);
putch('<');
textcolor(YELLOW);
incit(whichone,0);
}
int screen(void)
{
int cnt;
textcolor(YELLOW);
textbackground(BLUE);
clrscr();
gotoxy(9,7);
putstr(icntstr,0);
gotoxy(boxx,boxy);
putstr(bitbox[0],158);
cnt=1;
while(cnt<=18) {
gotoxy(boxx,cnt+boxy-1);
putstr(bitbox[cnt],129);
cnt++;
}
gotoxy(boxx,25);
putstr(bitbox[0],155);
gotoxy(11,1);
putstr(bitbox[18],158);
cnt=19;
while(cnt<21) {
gotoxy(11,cnt-17);
putstr(bitbox[cnt],0);
cnt++;
}
gotoxy(11,4);
putstr(bitbox[18],155);
textcolor(LIGHTMAGENTA);
textbackground(BLACK);
for(cnt=0;cnt<13;cnt++) {
gotoxy(13,cnt+boxy+4);
putstr(select[cnt].name,0);
}
}
/* this function is special, it takes a run-length encoded string
and puts it to the screen using putch. the escape code is anything
in the range 0x0X -> 0x1X. all but the lower nibble + 1 bit
is the number of times to repeat the next char, a maximum of 64. */
int putstr(string,offset)
char *string;
int offset;
{
int cnt;
char *tmp;
tmp=string;
while(*tmp) {
while((*tmp)&&((*tmp)>=32)) {
putch((*tmp)+offset); /* the offset variable is for encryption. */
tmp++;
}
if(*tmp) {
cnt=(*tmp);
tmp++;
switch(cnt) {
case 16 :
textcolor(*tmp);
tmp++;
break;
case 17 :
textbackground(*tmp);
tmp++;
break;
default :
while(cnt) {
putch((*tmp)+offset);
cnt--;
}
break;
}
}
}
}
int printb(num,len)
int num,len;
{
while(len>0) {
len--;
putch((((num&(1<<len))&&1)?'1':'0'));
}
}
/*
Mic-1 Emulator
Micro-Code Generator
*/
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
/* defines for accessing the registers with these names. */
#define PC 0
#define AC 1
#define SP 2
#define IR 3
#define TIR 4
#define ZERO 5
#define ONE 6
#define NEGONE 7
#define AMASK 8
#define SMASK 9
#define Areg 10
#define Breg 11
#define Creg 12
#define Dreg 13
#define Ereg 14
#define Freg 15
/* defines for reading the input strings an interpretting them. */
#define PCstr "pc"
#define ACstr "ac"
#define SPstr "sp"
#define IRstr "ir"
#define TIRstr "tir"
#define ZEROstr "0"
#define ONEstr "1"
#define NEG1str "(-1)"
#define AMSKstr "amask"
#define SMSKstr "smask"
#define Astr "a"
#define Bstr "b"
#define Cstr "c"
#define Dstr "d"
#define Estr "e"
#define Fstr "f"
#define MARstr "mar"
#define MBRstr "mbr"
#define RDstr "rd"
#define WRstr "wr"
#define ALUstr "alu"
#define Zstr "z"
#define Nstr "n"
#define IFstr "if"
#define THENstr "then"
#define GOTOstr "goto"
#define LSHstr "lshift"
#define RSHstr "rshift"
#define INVstr "inv"
#define ANDstr "band"
#define EQUstr ":="
#define ADDstr '+'
char regstrs[16][5] = {PCstr,ACstr,SPstr,IRstr,TIRstr,ZEROstr,ONEstr,NEG1str,
AMSKstr,SMSKstr,Astr,Bstr,Cstr,Dstr,Estr,Fstr};
typedef struct {
unsigned amux : 1; /* controls the A multiplexer */
unsigned cond : 2; /* jump control */
unsigned alu : 2; /* ALU control */
unsigned sh : 2; /* shifter control */
unsigned mbr : 1; /* Memory Buffer Register enable */
unsigned mar : 1; /* Memory Address Register enable */
unsigned rd : 1; /* Read enable */
unsigned wr : 1; /* Write enable */
unsigned enc : 1; /* enable the C address line */
unsigned C : 4; /* C address */
unsigned B : 4; /* B address */
unsigned A : 4; /* A address */
unsigned addr : 8; /* ADDRess for the next micro-code instruction */
} microins_type;
typedef union {
microins_type instruction;
unsigned long int integer;
} microins;
main(argc,argv)
int argc;
char *argv[];
{
FILE *source,*codefile;
char instr[80],*second,*third,*fourth,*tmp;
microins code;
int cnt;
if(argc<3) {
printf("MICMAKE <source-file> <micro-code-file>\n");
exit(1);
}
if((source=fopen(argv[1],"r"))==NULL) {
printf("error opening the source file.\n");
exit(1);
}
if((codefile=fopen(argv[1],"wb"))==NULL) {
printf("error opening the code file.\n");
exit(1);
}
while(!feof(source)) {
fgets(instr,80,source);
code.integer=0;
if((second=strstr(instr,MARstr))!=NULL) {
code.instruction.mar=1;
if((third=strstr(second,EQUstr))!=NULL) {
fourth=strchr(third,';');
for(cnt=0;cnt<16;cnt++) {
tmp=strstr(third,regstrs[cnt]);
if((tmp!=NULL)&&(tmp<fourth))
break;
}
if(cnt==16) {
tmp=strstr(third,MBRstr);
if((tmp!=NULL)&&(tmp<fourth))
code.instruction.amux=1;
else {
printf("Unknown register name in MAR :=\n");
leave();
}
}
else
code.instruction.B=cnt;
}
else {
printf("MAR can't be used as a variable!\n");
leave();
}
}
if((second=strstr(instr,RDstr))!=NULL)
code.instruction.rd=1;
if((second=strstr(instr,WRstr))!=NULL)
code.instruction.wr=1;
if((second=strstr(instr,ALUstr))!=NULL) {
code.instruction.enc=0;
code.instruction.alu=2;
}
if((second=strstr(instr,IFstr))!=NULL) {
if((third=strstr(second,Zstr))!=NULL)
code.instruction.cond=2;
else
if((third=strstr(second,Nstr))!=NULL)
code.instruction.cond=1;
else {
printf("no other choices but N or Z in IF THEN GOTO\n");
leave();
}
if((third=strstr(third,THENstr))==NULL) {
printf("missing THEN in IF THEN GOTO\n");
leave();
}
if((third=strstr(third,GOTOstr))!=NULL)
code.instruction.addr=atoi(strchr(third,' '));
else {
printf("missing GOTO in IF THEN GOTO\n");
leave();
}
}
else
if((second=strstr(instr,GOTOstr))!=NULL) {
third=strchr(second,';');
tmp=strchr(second,' ');
if((third!=NULL)&&(tmp<third))
code.instruction.addr=atoi(tmp);
else {
printf("error in GOTO statement\n");
leave();
}
code.instruction.cond=3;
}
if((second=strstr(instr,LSHstr))!=NULL) {
code.instruction.sh=2;
third=strchr(second,'(');
fourth=strcht(secod,')');
tmp=strchr(second,ADDstr);
if((tmp>third)&&(tmp<fourth))
}
if((second=strstr(instr,RSHstr))!=NULL) {
code.instruction.sh=1;
}
fwrite(&code,sizeof(microins),1,codefile);
}
flcoseall();
}
int leave(void)
{
fcloseall();
exit(1);
}
/*
Mic-1 Emulator
Macro-Code Compiler
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXINSTRUCT 23
typedef struct {
char *mnemonic;
unsigned int binary;
} macroinstruction;
macroinstruction set[MAXINSTRUCT] =
{ {"LODD\0",0x0000}, {"STOD\0",0x1000}, {"ADDD\0",0x2000},
{"SUBD\0",0x3000}, {"JPOS\0",0x4000}, {"JZER\0",0x5000},
{"JUMP\0",0x6000}, {"LOCO\0",0x7000}, {"LODL\0",0x8000},
{"STOL\0",0x9000}, {"ADDL\0",0xA000}, {"SUBL\0",0xB000},
{"JNEG\0",0xC000}, {"JNZE\0",0xD000}, {"CALL\0",0xE000},
{"PSHI\0",0xF000}, {"POPI\0",0xF200}, {"PUSH\0",0xF400},
{ "POP\0",0xF600}, {"RETN\0",0xF800}, {"SWAP\0",0xFA00},
{"INSP\0",0xFC00}, {"DESP\0",0xFE00} };
main(argc,argv)
int argc;
char *argv[];
{
FILE *source,*object;
char instring[80],*instructstr;
int inscnt,linecnt=0,instruction,variable;
if(argc<2) {
printf("MACMAKE <source filename> <object filename>\n");
exit(1);
}
if((source=fopen(argv[1],"r"))==NULL) {
printf("error opening the source code file\n");
exit(1);
}
if((object=fopen(argv[2],"wb"))==NULL) {
printf("error opening the object code file\n");
exit(1);
}
while(!feof(source)) {
if(fgets(instring,80,source)) {
linecnt++;
instructstr=strtok(instring," ");
for(inscnt=0;inscnt<MAXINSTRUCT;inscnt++)
if(!strcmp(set[inscnt].mnemonic,instructstr))
break;
variable=atoi(strtok(NULL," "));
switch(inscnt) {
case 15 :
case 16 :
case 17 :
case 18 :
case 19 :
case 20 :
instruction=set[inscnt].binary;
break;
case 21 :
case 22 :
if((variable>255)||(variable<0)) {
printf("variable out of range at line #%4d\n",linecnt);
fcloseall();
exit(1);
}
instruction=set[inscnt].binary|variable;
break;
case MAXINSTRUCT :
printf("unknown mnemonic at line #%4d\n",linecnt);
fcloseall();
exit(1);
break;
default :
if((variable>4095)||(variable<0)) {
printf("variable out of range at line #%4d\n",linecnt);
fcloseall();
exit(1);
}
instruction=set[inscnt].binary|variable;
break;
}
printf("%5s ",set[inscnt].mnemonic);
printb(instruction,16);
printf("\n");
fwrite(&instruction,sizeof(unsigned int),1,object);
}
}
fcloseall();
}
int printb(num,len)
int num,len;
{
while(len>0) {
len--;
putch((((num&(1<<len))&&1)?'1':'0'));
}
return 0;
}
@andrewrcollins
Copy link
Author

This is a set of C programs found on old 5.25 floppy disks from when I was a huge nerd at Thomas Jefferson High School for Science and Technology in the computer systems lab between 1988 and 1992.

It appears to be a microcode assembler and emulator for my "Computer Architecture" class.

Anyone can do whatever they'd like to with them--if anything.

And, yes, I remain a crushingly huge nerd.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment