Created
January 4, 2012 16:41
-
-
Save andrewrcollins/1560868 to your computer and use it in GitHub Desktop.
#TJHSST ~ MIC-1 Emulator ~ high school microcode project
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
/* 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 |
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
/* | |
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(); | |
} |
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
/* | |
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(); | |
} |
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
/* 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; | |
{ | |
} |
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
/* | |
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(); | |
} |
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
/* | |
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')); | |
} | |
} |
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
/* | |
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); | |
} |
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
/* | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.