program SISC_Computer; (***************** Simple Instruction Set Computer *********************) (** by Wen-Nung Tsai, tsaiwn@csie.nctu.edu.tw, written in Turbo Pascal**) (* CopyLeft not reserved:-) COPY/Distribute it as long as you like it.*) (* Last Modified@1995-12-23 @ Hsinchu, Taiwan, R.O.C. *) (***********************************************************************) (*********************************************************************** This Simple Instruction Set Computer (SISC) has the Architecture of the computer described in the Appendix B of our text book with some extensions: 1. The Memory has 1024 bytes. But you can toggle it between 256/1024. 2. The Program Counter has 10 bits and thus can access 1024 bytes of the memory. However, it will use only 8 bits if the memory size is toggled to 256-byte mode. 3. There is an internal STACK of 200 words which used for CALL ??? and RETURN instructions. 4. I/O instructions are supported so that you can really write a program that its work can be watched:-) 5. Integer comparisom instruction can be used to compare two integers in registers and the conditional jump instructions JLT/JEQ/JGE will jump to a location (up to 3ff in hex) if it matchs the status that affected by last CMP instruction. Note that the integers are treated as signed integer, though the PUTi will print out their value in 0..255. -------------------------------------------------------------------------- This program emulates the simple computer that described in Appendix B of the text book that written by J.Glenn Brookshear: "Introduction to Computer Science: An Overview, fourth edition." However, the instruction set has been expanded. The additional instructions are listed below:(refer to pp.455-456) ****** Note that "?" means don't care 0??? NOP (No OPeration) D0?? GETC : Get a Char into R0 D1?? PUTC : Output a Char from R0 D2?? GETi : Get an integer into R0 D3?? PUTi : Output integer from R0 D5XY GETS : Get a String into Memory starts from location XY D6XY PUTS : Output String from Memory location XY till a '$' the followin instructions can have address that up to 3ffh (1023 decimal): (Note that those "zzz xxxx yyyy" below are in binaryform.) EZXY : determined by right 2 bits in Z (see below) Ezz00 xxxx yyyy: LOAD F,zzxxxxyyyy Ezz01 xxxx yyyy:STORE F,zzxxxxyyyy Ezz10 xxxx yyyy: CALL zzxxxxyyyy E??11 ???? ????: RETurn FZXY : determined by right 2 bits in Z zzzz xxxx yyyy are all in binary form: ??00 xxxx yyyy: CMP RX to RY (will affect status:LT/EQ/GT) zz01 xxxx yyyy: JLT zzxxxxyyyy (Jump if the status is LT) zz10 xxxx yyyy: JEQ zzxxxxyyyy zz11 xxxx yyyy: JGT zzxxxxyyyy Only CMP instruction can affect LT/EQ/GT status ***********************************************************************) (***********************************************************************) (** by Wen-Nung Tsai, tsaiwn@csie.nctu.edu.tw, written in Turbo Pascal**) (* CopyLeft not reserved:-) COPY/Distribute it as long as you like it.*) (* @1995-12-19 *) (***********************************************************************) uses Crt,Dos; (* Not standard! *) (* Crt unit: keypressed,readkey,window,text... *) (* Dos unit: for GetTime() *) (* I didn't use Strings unit which deal C-like strings *) Const ESC=27; version='3.10'; INITDelayTime=1; (*Default delay time in milli seconds *) DFLOGName:string[15]='SISC.LOG'; MaxStrLen=28; (* we only allowed 28 chars per line*) w1len=18; w1width=35; (* for cmdwindow on left screen *) w2len=5; w2width=37; (* for memaddr window for instructions dealed*) w3len=5; (* 5 rows for the memdata window *) X1:byte=3; Y1:byte=4; (* cmdwindow's up-left corner = (3,4) *) x2:byte=42; y2:byte=4; x3:byte=42; y3:byte=17; (* memdata window *) pcx:byte=42; pcy:byte=10; (* for the window to show CPU status *) pcwidth:byte=37; pclen:byte=5; w1color=lightGreen; (* text color for cmdwindow *) MSLarge=1024; (* Large Size of MEMory *) MSSmall=256; (* Memory size small mode *) STACKSize=200; (* STACK has 200 words for CALL/RETURN *) MNEMONIC: array[0..15] of string[5]=( 'NOP ','LOAD ', 'LDI ', 'STORE', 'MOVE ', 'ADD ','FADD ', 'OR ', 'AND ','XOR ','ROTR ', 'JUMP ', 'Halt ', 'PUTS ','LOADF', 'CMP '); ioinst: array[0..6] of string[8]=( 'GETC ', 'PUTC ', 'GETi ', 'PUTi ', '???', 'GetS ', 'PutS '); type cputype= record PC:integer; REG: array[0..15] of integer; IR:integer; TEMP:integer; Carry: byte; BP,numBP: integer; STACK: array [0..STACKSize] of integer; SP: integer; end; ALUtype= record opcode,reg1,reg2,reg3,reg4:byte; addr:word; (* reg4 is the right-2 bit of reg1 *) end; var cx1,cx2,cx3, cy1,cy2,cy3,xone,xten:Byte; (* for cursor of windows *) xtmp,ytmp:Byte; (* tmpint:integer; *) CMD,CMD2,OldCMD,tmpCMD:char; (* store the CoMmanD *) FLOG: text; LOGFILENAME: string[15]; tmpstr:string[33]; ps:integer; tmp4char,tmp2char,strinteger:string[4]; str80,strBlank:string[80]; (* used for input STRING *) str40:string[40]; HehitESCape,HehitCTRLC: boolean; Delaytime: Word; sold:Word; (* SecondOLD for saving time/second *) MEMSize: integer; (* store the Size of MEMory *) MEM: array[0..MSLarge-1+1] of integer; (* the MEMory cells *) Upc: integer; (* pc(program counter) for Unassemble command*) strinst: string[20]; (* STRing for INStruction that decoded *) CPU: cputype; ALU: ALUtype; (*============================================================*) procedure cmdwindow(t:string; crlf:integer); forward; procedure cmdwin2(t:string; crlf:integer); forward; (****** cmdwindow(t,crlf) and cmdwin2(t,crlf) both display the string t on the command window. the flag crlf is used to determined if CR/LF are rewuired after t is displayed: 0 means NO while 1 means YES. cmdwindow(t,crlf) will also append the text onto the LOG file so that you can have a running script ******) procedure showmem(nextinst:integer;mode:integer); forward; procedure memdatawin(loc:integer); forward; (***=========================================================***) procedure beep; begin sound(500); delay(50); nosound; end; procedure beebee; const f1=890; f2=890; f3=880; f4=880; begin sound(f1); delay(30); nosound; delay(20); sound(f2); delay(30); nosound; sound(f3); delay(30); nosound; delay(20); sound(f4); delay(30); nosound; end; procedure showtime; (* show time on the Right-Upper corner *) var h,m,s,hund: Word; (* Word==integer in Standard Pascal*) begin GetTime(h,m,s,hund); (* In Turbo Pascal IDE, press CTRL_F1 for HELP*) if(s <> sold)then begin window(72,1,80,1); sold := s; TextColor(LightMagenta); TextBackground(white); write(h:02,':'); if(m<10)then write('0'); write(m:0,':'); if(s<10)then write('0'); write(s:0); if(m=00) then if (s<2) then beebee; end; end; (***=========================================================***) function myReadKey(var SPCKEY:char): char; begin While(not KeyPressed) do begin delay(100); showtime; cmdwin2('',0); (* required so that cursor in cmdwindow *) end; SPCKEY:= ReadKey; MyReadKey:=SPCKEY; if( ord(SPCKEY)=0)then SPCKEY:=ReadKey; (* special KEY has two bytes*) end; (***=========================================================***) function mycheckESC:boolean; (* return TRUE if ESCape key pressed*) var c,c2:char; begin mycheckESC:=false; if(KeyPressed) then begin c:=myReadKey(c2); if(ord(c)=ESC) then mycheckESC:=true; end; end; (***=========================================================***) function FileExists(FileName: String): Boolean; { Boolean function that returns True if the file exists;otherwise, it returns False. Closes the file if it exists. } var F: file; begin {$I-} (* turn off I/O checking *) Assign(F, FileName); FileMode := 0; (* Set file access to read only *) Reset(F); Close(F); {$I+} FileExists := (IOResult = 0) and (FileName <> ''); end; { FileExists } (*******************) function LeadingZero(w : Word) : String; var s : String; begin Str(w:0,s); (* convert integer w into a string in s *) if Length(s) = 1 then s := '0' + s; LeadingZero := s; end; (**************************************) procedure openLOG(LGFileN:string); (* LOG the running script into the file 'SISC.LOG' *) const days : array [0..6] of String[9] = ('Sunday','Monday','Tuesday', 'Wednesday','Thursday','Friday', 'Saturday'); var h, m, s, hund : Word; y, mm, d, dow : Word; begin (* open LOG file and write down time and date *) assign(FLOG,LGFileN); if(FileExists(LGFileN)) then Append(FLOG) else Rewrite(FLOG); Writeln(FLOG); Write(FLOG, '****** SISC LOG at '); GetTime(h,m,s,hund); Write(FLOG,LeadingZero(h),':', LeadingZero(m),':',LeadingZero(s)); GetDate(y,mm,d,dow); Write(FLOG,' on ', days[dow],', ', mm:0, '/', d:0, '/', y:0); writeln(FLOG,' ******'); end; (***=========================================================***) procedure hello; begin Window(1, 1, 80, 1 ); TextBackground(Black); sound(888); delay(88); sound(777); delay(77); sound(888); delay(99); clrscr; nosound; TextBackground(White); TextColor(LightRed); GotoXY(19,1); Write('Simple Instruction Set Computer ver ',version); (******) Window(27,2, 77, 2); TextBackground(Green); textcolor(Yellow); Write('J.Glenn Brookshear'); TextBackground(white); textcolor(Black); gotoXY(25,1); Write('tsaiwn@csie.nctu.edu.tw'); tmp4char:='0000'; end; (***=========================================================***) procedure showhint(txt:string;mode:Byte); begin (* mode=0 : normal, mode=blink *) Window(1, 2, 26, 2 ); TextBackground(Black); TextColor(White+mode); write(txt); end; (*******************) procedure clearhint; begin showhint(' ',0); (**) end; (***=========================================================***) function mygetline(xp,yp:Byte;mode:integer):integer; (* input a line and return number of char we got as function value *) var x,y,nch: Byte; c1,c2: char; begin x:=xp; y:=yp; (* Cursor for cmdwindow or cmdwin2 *) nch:=0; (* assume 0 char input *) str80[0]:= chr(0); (* assume null string *) c1 := myReadKey(c2); while True do begin case ord(c1) of 3: begin (* Control_C *) cx1:=x; cy1:=y; if(nch>0)then cmdwindow(str80,0); cmdwindow('^C',1); nch:=0; mygetline:= -999; str80[0]:= chr(0); Break; end; 8: begin (* backSpace *) if nch > 0 then begin cmdwin2('',0); textcolor(Black); cx1:=x; cy1:=y; (* cursor back to original position*) (* cmdwin2('',0); clreol; *) (* I do it by myself *) strblank[0]:= chr(nch); (* string length *) cmdwin2(strblank,0); (* clear old string *) textcolor(w1color); dec(nch); str80[0]:= chr(nch); cx1:=x; cy1:=y; cmdwin2(str80,0); end else beep; end; 27: begin (* ESCape *) if nch=0 then mygetline:= -888; break; (* done *) end; 13: begin (* chr(13)= Ctrl_M = RETURN key *) if nch=0 then mygetline := -999 else begin cx1:=x; cy1:=y; cmdwindow(str80,0); (* show and LOG it *) end; cmdwindow('',1); (* CR/LF *) break; (* done, leave the While Loop *) end; 32..126: begin if(nch >= MaxStrLen ) then Beep else begin (* got one char *) inc(nch); cmdwin2(c1,0); (* echo the character *) str80[nch] := c1; str80[0] := chr(nch); end; end; else Beep; end; (*case*) c1:=myReadKey(c2); end;(*While*) (* done: *) if(nch>0)then begin str80[0]:= chr(nch); mygetline:= nch; end; end; (******====================******) function getint(mode:integer):integer; (* get an Integer *) (* mode=0 means decimal, mode=16 means hex *) var nch,xpos,ypos: Byte; data,errcode:integer; begin xpos:=cx1; ypos:=cy1; if(mode = 0) or (mode=10) then showhint('Decimal or $??? for Hex',0) else showhint('enter Hexadecimal value',0); cmdwin2('',0); (* move CURSOR *) nch:=mygetline(xpos,ypos,1); (* get line into str80 *) getint:= nch; (* assume error input, -999 or -888 *) if nch > 0 then begin errcode:=0; if(mode=0) or (mode=10) or (str80[1]='$') then val(str80, data, errcode) else val('$'+str80, data, errcode); (* Hexadecimal mode *) getint:= data; (* suppose got data *) if errcode <> 0 then getint:= -777; (* not integer *) end; (* got something *) end; (***=========================================================***) function hex2ch(n:integer):char; begin (* return the Right most Nibble in character form*) n:= (n and 15)+ ord('0'); if n > ord('9') then n:= n-ord('9')+ord('A')-1; hex2ch:= chr(n); end; (***=========================================================***) procedure int2hex(n:integer); (* integer to Hexdecimal *) (* result will be placed in tmp4char:string[4] *) var k,hex:word; begin for k:=4 downto 1 do begin tmp4char[k]:= hex2ch(n); n := n shr 4; end; end; (***=========================================================***) procedure int2ch(n:integer); (* integer to 2 char *) (* result will be placed in tmp2char:string *) var k,bb:word; begin for k:=2 downto 1 do begin tmp2char[k]:= '.'; bb := n and $7f; if(bb > 31) and (bb < 127) then tmp2char[k]:= chr(bb); n := n shr 8; end; tmp2char[0]:=chr(2); end; (***=========================================================***) procedure unasm(data:integer); (* Un Assembly the instruction *) begin (*unasm *) with ALU do begin strinst:=' '; opcode:= data shr 12; reg1:= (data shr 8) and $0f; addr:= data and 255; reg2:=addr div 16; reg3:= addr mod 16; case opcode of 0,12: strinst:= MNEMONIC[opcode]; (* NOP, Halt *) 1,2,3,11: begin (* 1=LOAD, 2=LDI, 3=STORE, 11=JUMP *) strinst:= concat(MNEMONIC[opcode],' .,. '); strinst[7]:=hex2ch(reg1); strinst[9]:=hex2ch(reg2); strinst[10]:=hex2ch(reg3); end; 4: begin strinst:='MOVE ., '; strinst[7]:=hex2ch(reg2); strinst[9]:=hex2ch(reg3); end; 5..9: begin (* 5=ADD, 6=FADD, 7=OR, 8=AND, 9=XOR *) strinst:= concat(MNEMONIC[opcode],' .,.,. '); strinst[7]:=hex2ch(reg1); strinst[9]:=hex2ch(reg2); strinst[11]:=hex2ch(reg3); end; 10: begin strinst:='ROTR ., '; strinst[7]:=hex2ch(reg1); strinst[9]:=hex2ch(reg3); end; 13: begin (* I/O *) strinst:='? '; case reg1 of 0,1,2,3,5,6: begin strinst:= ioinst[reg1]; (* 'PUTS XY' *) if(reg1=5)or (reg1=6) then begin strinst[7]:=hex2ch(reg2); strinst[8]:=hex2ch(reg3); end; end; end;(*case*) end; (*13: *) 14: begin reg4:= reg1 and 3; reg1:= reg1 shr 2; case reg4 of 0: strinst:='LOAD F,000 '; 1: strinst:='Store F,000 '; 2: strinst:='CALL 000 '; 3: strinst:='RETURN '; end; if(reg4<3)then begin strinst[9]:=hex2ch(reg1); strinst[10]:=hex2ch(reg2); strinst[11]:=hex2ch(reg3); addr:= ( integer(reg1) SHL 8 ) or addr; addr:= addr mod MEMSize; end; end; (* 15: *) 15: begin reg4:= reg1 and 3; reg1:= reg1 shr 2; case reg4 of 0: strinst:='CMP .,. '; 1: strinst:='JLT ... '; 2: strinst:='JEQ ... '; 3: strinst:='JGT ... '; end; strinst[9]:=hex2ch(reg3); if(reg4=0)then strinst[7]:=hex2ch(reg2) else begin (* JLT/JEQ/JGT zXY *) strinst[7]:=hex2ch(reg1); strinst[8]:=hex2ch(reg2); addr:= ( integer(reg1) SHL 8 ) or addr; addr:= addr mod MEMSize; end; end; (* 15: *) end; (* case*) end; (* with *) end; (* unasm *) (***=========================================================***) procedure showCPU; (* show CPU status *) const textCY:array [0..1] of string[3]=('No ', 'Yes'); textLTEQGT:array [-1..1] of string[8]=( '< LT', 'EQ =', 'GT >'); var flagrelation,pc,nextinst,data,k:integer; begin (*showCPU*) pc:=CPU.PC; (* show Program Counter first *) window(pcx,pcy,pcx+pcwidth,pcy+pclen); TextBackground(black); textcolor(LightGreen); Write('PC: '); textcolor(LightRED); int2hex(pc); Write(copy(tmp4char,2,3)); textcolor(LightGreen); write('h'); (* then show the BP *) GotoXY(13,1); Write('Break Pointer(hex): '); int2hex(CPU.BP); if(CPU.numBP>0)then write(copy(tmp4char,2,3):4)else write('none'); writeln; (* then the MEM[pc] and MEM[pc+1] *) nextinst:= (integer(MEM[pc]) SHL 8) or (MEM[pc+1] and $00ff); int2hex(nextinst); Write('MEM[PC]= ',copy(tmp4char,1,2)); Write(' ', copy(tmp4char,3,2)); textcolor(Yellow); unasm(nextinst); write(' ',strinst:15); Writeln; textcolor(LightGreen); (* int2hex(CPU.IR); Write('IR: ',tmp4char,'h '); *) (* show MEMory Size *) str(MEMSize,strinteger); textcolor(yellow); write(MEMSize:4,' bytes'); textcolor(LightGreen); (* then the Status/Flag *) Write(',Carry: '); if(CPU.carry<>0)then textcolor(LightRED) else textcolor(White); write(textCY[CPU.carry]); textcolor(LightGreen); flagrelation:=0; if(CPU.TEMP<0)then flagrelation:= -1 else if(CPU.TEMP>0)then flagrelation:=1; Writeln('<=>status: ', textLTEQGT[flagrelation]); for k:=0 to 15 do begin data:=CPU.REG[k]; int2hex(data); write('R',hex2ch(k),'='); if(k=0)then textbackground(LightGray); write(copy(tmp4char,3,2)); textbackground(Black); write(' '); if(k=5) or (k=11) then writeln; end; showmem(0,0); (* write memdatawin if refer to a memory cell *) end; (***=========================================================***) procedure showmem(nextinst:integer;mode:integer); begin if(mode<>0) then unasm(nextinst); with ALU do begin case opcode of 1,3,11: begin memdatawin(addr); end; 14, (* LOAD F,zXY / STORE / CALL /RETURN *) 15:begin (* CMP, JLT/JEQ/JGT *) if(reg4<>0) then memdatawin(addr); end; else end; (*case*) end; (* with *) end; (***=========================================================***) procedure cmdwin2(t:string;crlf:integer); begin Window(X1, Y1, X1 +w1width, Y1 + w1len ); TextBackground(Black); TextColor(w1color); gotoXY(cx1,cy1); write(t); if(crlf<>0) then writeln; cx1:=whereX; cy1:=whereY; (*backup current X,Y for latter use*) end; (***=========================================================***) procedure cmdwindow(t:string;crlf:integer); (* the only difference between cmdwindow and cmdwin2 is that the latter won't LOG the output text *) begin cmdwin2(t,crlf); (* then, write to LOG file *) write(FLOG,t); if(crlf<>0) then writeln(FLOG); end; (***=========================================================***) procedure memaddrwin(where:integer); (* decode an instruction and show on memaddr window *) (* where is the memory location to decode *) var data,pc:integer; begin Window(X2, Y2, X2 + 35, Y2 + w2len); TextBackground(Black); TextColor(Yellow); pc:=where; if(pc<0) then pc:=CPU.pc; pc:=pc mod MEMSize; gotoxy(cx2,cy2); int2hex(pc); write(copy(tmp4char,2,3),': '); data:= (MEM[pc] SHL 8) or (MEM[pc+1] and $00ff); int2hex(data); write(tmp4char,' '); unasm(data); write(strinst); writeln; cx2:=whereX; cy2:=whereY; end; (***=========================================================***) procedure memdatawin(loc:integer); var data:integer; begin Window(X3, Y3, X3 + 35, Y3 + w3len); TextBackground(Black); TextColor(LightRED); gotoxy(cx3,cy3); int2hex(loc); write(copy(tmp4char,2,3),': '); data:= MEM[loc] and $00ff; int2hex(data); write(copy(tmp4char,3,2),'h == ',data:3); if(data > 127) then write(' == -', 256-data:0); writeln; cx3:=whereX; cy3:=whereY; end; (***=========================================================***) procedure showhelp; (* print out the additional instructions *) begin cmdwindow(' ',1); cmdwindow('Command can be either Upper case',1); cmdwindow(' or lower case:',1); cmdwindow(' Q Quit this system',1); cmdwindow(' H/I Help message/Instructions',1); cmdwindow(' A Assemble assembly program',1); cmdwindow(' B set Break pointer',1); cmdwindow(' E show/Enter memory data',1); cmdwindow(' L/S Load/Save the Machine Code',1); cmdwindow(' P set Program counter',1); cmdwindow(' R? modify content of Register ?',1); cmdwindow(' G Go (RUN) start from PC',1); cmdwindow(' T Trace the program one step',1); cmdwindow(' U Unassemble (4 instructions)',1); cmdwindow(' M Memory size toggle',1); cmdwindow(' =+- show/change RUNNING speed',1); cmdwindow(' other cmd: C K F',1); cmdwindow('Only CMP affects LT/EQ/GT status',1); end; (***=========================================================***) procedure showinst; (* print out the additional instructions *) begin cmdwindow(' ',1); cmdwindow('In addition to instructions on',1); cmdwindow('our text book page 455-456,',1); cmdwindow('("?" means don''t care)',1); cmdwindow(' D0??/D1?? Get/Put Char to/from R0',1); cmdwindow(' D2??/D3?? Get/Put int to/from R0',1); cmdwindow(' D5XY/D6XY In/Put string to/from XY',1); cmdwindow('EZXY (zzzz xxxx yyyy in binary):',1); cmdwindow(' Ezz00 xxxx yyyy: LOAD F,zzxxxxyyyy',1); cmdwindow(' Ezz01 xxxx yyyy:STORE F,zzxxxxyyyy',1); cmdwindow(' Ezz10 xxxx yyyy: CALL zzxxxxyyyy',1); cmdwindow(' E??11 ???? ????: RETurn',1); cmdwindow(' FZXY Input/Output String',1); cmdwindow(' zzzz in binary:',1); cmdwindow(' ??00 xxxx yyyy: CMP RX to RY',1); cmdwindow(' zz01 xxxx yyyy: JLT zzxxxxyyyy',1); cmdwindow(' zz10 xxxx yyyy: JEQ zzxxxxyyyy',1); cmdwindow(' zz11 xxxx yyyy: JGT zzxxxxyyyy',1); cmdwindow('Only CMP affects LT/EQ/GT status',1); end; (***=========================================================***) procedure SetBack; (* set New Background *) var k:integer; begin TextBackground(Black); ClrScr; window(1,3,80,23); (* change only (1,3)-(80,23) BG color *) (** TextBackground(Random(16)); ClrScr; **) TextBackground(3); ClrScr; for k:=1 to 30 do cmdwin2(' ',1); end; (***=========================================================***) function ishexdigit(c:char):boolean; begin ishexdigit:= false; if( UpCase(C) in ['0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'] ) then ishexdigit:=TRUE; end; (*************) procedure waitreturnkey; var c2:char; begin repeat until myReadKey(c2) = char(13); end; (***=========================================================***) procedure showSTACK; var tmpstr: string[6]; k,cnt:integer; begin k:=CPU.SP; str(k:4,tmpstr); cmdwindow(' '+tmpstr+' elements in STACK',1); if(k>0)then begin int2hex(CPU.STACK[k]); (* into tmp4char *) cmdwindow(' '+tmp4char+' <--- TOP',1); k:=k-1; cnt:=1; while (K>0) do begin if(cnt mod 10) = 0 then begin cmdwin2('Hit Return to continue...',0); waitreturnkey; cmdwin2('',1); end;(*if cnt *) int2hex(CPU.STACK[k]); (* into tmp4char *) cmdwindow(' '+tmp4char,1); k:=k-1; cnt:=cnt+1; end; (*while*) end; (* if k *) end; (* showSTACK *) (***=========================================================***) procedure doUnassemble; var Upc1,data,k:integer; begin if(OldCMD <> 'u') and (OldCMD<>'U') (* previous CMD is not "U" *) then Upc:=CPU.pc mod MEMSize; (* unassemble from Program Counter *) for k:=1 to 10 do begin int2hex(Upc); cmdwindow(copy(tmp4char,2,3)+': ',0); Upc1:= (Upc+1) mod MEMSize; data:= (MEM[Upc] SHL 8) or (MEM[(Upc1)] and $00ff); int2hex(data); cmdwindow(tmp4char+' ',0); int2ch(data); (* data ==> tmp2char *) cmdwindow(tmp2char+' ',0); unasm(data); cmdwindow(strinst,1); Upc:= (Upc + 2 ) mod MEMSize; end; end; (***=========================================================***) procedure zapinp2str40(s:string); (* remove blank/tab/ "," s ---> str40[] *) var k,i,p:integer; c:char; begin i:=1; p:=0; k:= ord(s[0]); while k > 0 do begin (* more chars *) c := s[i]; if(ord(c) > 32) and (c<>',') then begin (* 32= $20 = ' ' *) inc(p); str40[p]:= c; end; (*if*) dec(k); inc(i); (* next char *) end; str40[0] := chr(p); (* number of char in the string *) end; (******************) procedure LoadMachineCode; (* Load instruction codes from file into memory *) var k,nch,loc:integer; c1:char; v1,v2,err1,err2,inplen:integer; F: text; strinp:string[28]; begin showhint('default EXTention is .MC',0); cmdwindow('Filename: ',0); nch:=mygetline(cx1,cy1,1); if(nch>0)then begin if(pos('.',str80)=0)then str80 := str80+'.MC'; for k:=1 to nch do str80[k]:=UpCase(str80[k]); end; (* if nch > 0 *) (* cmdwindow(str80,1); *) (* echo *) if (nch>0 ) and (fileexists(str80))then begin Assign(F, str80); cmdwindow('=== Loading file '+str80+' ===',1); reset(F); loc:=0; repeat readln(F,strinp); zapinp2str40(strinp); (* zap out blank *) c1:= str40[1]; inplen:=ord(str40[0]); if(c1=';') then cmdwindow(strinp,1) else if (inplen<4) or(NOT(ishexdigit(c1)) )then begin (*error*) if(inplen=0) then cmdwindow(' ',0) else cmdwindow(' ??? ',0); cmdwindow(strinp,1); end else begin val('$0'+copy(str40,1,2), v1, err1); val('$0'+copy(str40,3,2), v2, err2); if(err1+err2) = 0 then begin (* no error *) int2hex(loc); (* to HEX in tmp4char *) cmdwindow(tmp4char+': ',0); cmdwindow(strinp,1); MEM[loc]:= v1 and $00ff; MEM[loc+1] := v2 and $00ff; loc := loc + 2 ; if(loc > MEMSize) then Break; end;(*if err..*) end(*if c1 *) until eof(F); Close(F); { Close file, save changes } cpu.PC:=0; (* reset the Program_Counter *) for k:=0 to 4 do begin (* show the first 4 instructions *) memaddrwin(2*k); end; end else begin (* file error *) if nch>0 then cmdwindow(str80+' not found',1); end; clearhint; showCPU; end; (***=========================================================***) procedure InitCPU; (* Initialize CPU *) begin CPU.PC:=0; CPU.carry:=0; CPU.numBP:=0; CPU.SP:=0; MEMSize:=MSLarge; (* default 1024 bytes *) OldCMD:='*'; DelayTime:= INITDelayTime; nosound; end; (***=========================================================***) procedure setbreak; (* set BREAK pointer *) var newbp:integer; x,y:byte; begin CPU.numBP:=0; (* call mygetlin *) cmdwindow('Enter BP(RETURN to clear):',0); x:=cx1; y:=cy1; newbp := getint(16); (* gotoXY(x,y); cmdwindow(str80,1); *) if( newbp >= 0 ) then begin CPU.BP := newbp mod MEMSize; CPU.numBP:=1; end; clearhint; showCPU; end; (***=========================================================***) procedure setPC; (* set Program Counter *) var newpc:integer; x,y:byte; begin (* call mygetlin *) cmdwindow('Enter new Program Counter:',0); x:=cx1; y:=cy1; newpc := getint(16); (* gotoXY(x,y); cmdwindow(str80,1); *) if( newpc >= 0 ) then begin CPU.PC := newpc mod MEMSize; end; clearhint; showCPU; end; (***=========================================================***) procedure RegisterModify; var tmpx,tmpy:Byte; c1,c2:char; v,errCode,data:integer; begin clearhint; cmdwindow(UpCase(CMD),0); (* showhint('Enter register number ',0); (***) showhint('Enter register# or = x',0); sound(20); cmdwin2('',0); tmpx:=cx1; tmpy:=cy1; c1:= myReadKey(c2); c1:= UpCase(c1); cmdwindow(c1,1); nosound; if(c1='X') then begin (* clear all registers *) for v:=0 to 15 do cpu.REG[v]:=0; cmdwindow('all Registers cleared',1); showCPU; end else if(c1='=') then begin (* show all registers *) for v:=0 to 15 do begin int2hex( cpu.REG[v] and $00ff); (* to tmp4char *) cmdwindow('R'+hex2ch(v)+'= '+copy(tmp4char,3,2)+' ',0); if( v and 3) = 3 then cmdwindow('',1); (* next line *) end; end else if(ishexdigit(c1)) then begin Val( '$0'+c1, v, errCode); if(errCode=0)then begin int2hex( cpu.REG[v] and $00ff); (* to tmp4char *) cmdwindow('R'+c1+'= '+copy(tmp4char,3,2)+': ',0); data:=getint(16); (* gotoXY(tmpx,tmpy); cmdwindow(str80,1); *) if data <= -777 then (* -777,-888,-999 means no change *) data:=data else begin cpu.REG[v] := data AND $00ff; showCPU; end; end;(* if errCode *) end;(*if c1*) clearhint; end; (******====================******) procedure entermemory; (* command E *) (* Examine memory cell/Enter Hexa value into memory *) var loc,data:integer; label done; begin cmdwindow('Location(hex)?',0); loc:=getint(16); (* loc < 0 means : He hit RETURN only *) While (loc >=0 ) do begin loc:=loc mod MEMSize; (* make sure in the RANGE of legal memory *) (* cmdwindow('',1); *) (* CR/LF *) int2hex(loc); cmdwindow(' MEM['+copy(tmp4char,2,3)+'h] = ',0); str( (MEM[loc] and $00ff):3,strinteger); cmdwindow('dec '+strinteger+' = ',0); memdatawin(loc); (* tmp4char will get the mem[loc] data *) cmdwindow(copy(tmp4char,3,2)+'h : ',0); showhint(' Enter new value in HEX',0); data:=getint(16); if data <= -777 then break; (* -999 means no change *) MEM[loc] := data AND $00ff; memdatawin(loc); loc := loc+1; (* next MEMory cell *) end; (* while loc *) done: clearhint; (* cmdwindow('',1); (**) showCPU; end; (***=========================================================***) function floatvalue(data:integer):real; const wet:array [0..7] of integer=(1,2,4,8,16,32,64,128); var exponent,sign,fraction: integer; ans:real; begin sign:=1; (* assume positive *) if(data and $80) <> 0 then sign:= -1; (* negative *) exponent:= (data shr 4) and 7; (* bit-6, bit-5, bit-4 *) fraction:= data and $0f; ans:= (fraction* wet[exponent])/256.0; if sign < 0 then ans:= -ans; floatvalue:=ans; (*** cmdwin2('',0); writeln(ans);writeln; ***) (* test *) end; function float2bin(fdata:real):integer; const expbit:array [-4..3] of integer= (0, $10, $20, $30, $40, $50, $60, $70); var signbit,exponent,ans:integer; begin signbit:=0; (* assume positive *) if fdata < 0 then begin signbit:= 1; (* negative *) fdata := -fdata; end; exponent:=0; if fdata = 0 then begin ans:=0; exponent:= -4; end else if fdata = 0.5 then ans:= 8 else if fdata < 0.5 then begin while fdata < 0.5 do begin fdata := fdata + fdata; dec(exponent); end; (* while*) if(exponent< -4) then exponent:= -4; ans := ans or expbit[exponent]; end else begin (* fdata > 0.5 *) while fdata > 0.93555 do begin fdata := fdata / 2.0; inc(exponent); end; (* while*) if(exponent>3) then exponent:= 3; ans := ans or expbit[exponent]; end; if signbit <> 0 then ans:=ans or $80; (* turn sign-bit on *) float2bin:=ans; (* that is the bit pattern we want *) end; procedure doFADD(rans, r1, r2 : integer); var v1,v2,ans: real; ansint:integer; begin v1:=floatvalue(cpu.REG[r1 mod $0f]); v2:=floatvalue(cpu.REG[r2 mod $0f]); ans:= v1+v2; if ans > 7.5 then ans:=7.5; (* overflow *) if ans < -7.5 then ans:= -7.5; (* overflow *) ansint:= float2bin(ans); (* convert to binary form *) cpu.REG[rans mod $0f] := ansint and $0ff; end; (***=========================================================***) procedure execoneinst; (* EXECute ONE INSTruction *) var tmpword,k,m: word; firstget: boolean; (* flag for prompt *) tmpx,tmpy:Byte; (* for old cursor position *) sint1,sint2: shortint; tmpint,pc1,nch,errcode:integer; tmpstr6: string[6]; tmpch,tmpc1,tmpc2:char; begin memaddrwin(-1); (* direct CURSOR to MEMADDR window *) pc1:= (CPU.PC+1) mod MEMSize; (*Fetch the instruction, big endian format*) CPU.IR := (MEM[CPU.PC] SHL 8) OR (MEM[pc1] and $00ff); unasm(CPU.IR); (* Decode the inst. in IR *) (* and then, advance PC to point to NEXT Instruction *) with CPU do begin PC:=PC+2; if PC >=MEMSize then PC:=PC mod MEMSize; end; (******* now, EXECute the instruction ******) with ALU do begin sint1:=CPU.REG[reg2] and $00ff; sint2:=CPU.REG[reg3] and $00ff; case opcode of 0: ; (* NOP *) 1: CPU.REG[reg1] := MEM[addr] and $00FF; (* Load r,[mem] *) 2: CPU.REG[reg1] := addr and $00ff; (* LDI *) 3: begin (* Store *) MEM[addr] := CPU.REG[reg1] and $00ff; memdatawin(addr); (* show on MEMD Data Window *) end; 4: begin (* move reg2, reg3 *) CPU.REG[reg3]:=CPU.REG[reg2]; end; 5: begin (* ADD reg1,reg2,reg3 *) tmpint:= integer(sint1 and $00ff) + integer(sint2 and $00ff); (*** cmdwindow('',1); writeln(tmpint); (**debug purpose*) CPU.Carry:=0; (* assume no carry *) if(tmpint and $0100) <> 0 then CPU.Carry:= 1; CPU.REG[reg1]:= tmpint and $00ff; end; 6: begin (* FADD reg1,reg2,reg3 *) doFADD(reg1,reg2,reg3); end; 7: begin (* OR reg1,reg2,reg3 *) tmpint:= integer(sint1) OR integer(sint2); CPU.REG[reg1]:= tmpint and $00ff; end; 8: begin (* AND reg1,reg2,reg3 *) tmpint:= integer(sint1) AND integer(sint2); CPU.REG[reg1]:= tmpint and $00ff; end; 9: begin (* XOR reg1,reg2,reg3 *) tmpint:= integer(sint1) XOR integer(sint2); CPU.REG[reg1]:= tmpint and $00ff; end; 10: begin (* ROTate the reg1 Right reg3 bits *) tmpword:= CPU.REG[reg1]; for k:=1 to reg3 do begin m:= tmpword and 1; (* right most bit *) tmpword:= tmpword shr 1; if(m=1) then tmpword:=tmpword or $80; (* high bit on *) CPU.REG[reg1] := tmpword and $00ff; end; end; 11: begin (* Branch to XY if Rr = R0 *) if(CPU.REG[reg1]=CPU.REG[0]) then CPU.PC:= addr mod MEMSize; end; 12: CPU.SP:=0; (* Halt, clear the STACK *) 13: begin (* Input/Output *) HehitCTRLC:=false; case reg1 of 0: begin (*GETC *) showhint('waiting for one KEY',blink); showCPU; (* so that the "PC" looks correct *) cmdwin2('',0); (* move cursor to cmd window *) tmpc1:= myreadkey(tmpc2); CPU.REG[0] := ord(tmpc1); clearhint; end; 1: begin (*PUTC *) cmdwindow( chr(CPU.REG[0] and $0ff), 0); end; 2: begin (*GETi *) firstget:=true; showCPU; repeat showhint('Waiting decimal integer',blink); if not firstget then beep; cmdwin2('',0); tmpx:=cx1; tmpy:=cy1; nch:= mygetline(cx1,cy1,1); firstget:=false; until nch > 0; val(str80,tmpint,errcode); cpu.REG[0] := tmpint and $0ff; clearhint; (* cmdwin2('',0); gotoXY(tmpx,tmpy); cmdwindow(str80,1); *) end; 3: begin (*PUTi *) str(integer(CPU.REG[0]), tmpstr6); cmdwindow(' '+tmpstr6,0); end; 5: begin (*GETS *) k:= addr mod MEMSize; nch:=mygetline(cx1,cy1,1); nch:=nch+1; m:=1; while nch > 1 do begin MEM[k] := ord(str80[m]) and $0ff; k:= (k+1) mod MEMSize; inc(m); dec(nch); (* m++, nch-- *) end; if nch=1 then MEM[k] := ord('$'); end; 6: begin (*PutS *) k:= addr mod MEMSize; tmpch:= chr( MEM[k] and $0ff); while tmpch <> '$' do begin cmdwindow(tmpch,0); k:= (k+1) mod MEMSize; tmpch:= chr( MEM[k] and $0ff); end; (* while *) end; end; end; 14: begin (* LOAD F,zzXY STORE F,zzXY, CALL xxXY, RETURN *) (* addr = reg1*256+ reg2*16 + reg3 *) case reg4 of 0: begin (* LOAD F,zzXY *); CPU.REG[15] := MEM[addr mod MEMSize] and $00ff; end; 1: begin (*Store F,zzXY *) MEM[addr mod MEMSize] := CPU.REG[15] and $00ff; end; 2: if(CPU.SP < STACKSize)then begin (* CALL zzXY *) inc(CPU.sp); CPU.STACK[CPU.sp]:=CPU.PC; (*save PC *) CPU.PC:= addr mod MEMSize; (* goto addr *) end; 3: if(CPU.SP>0)then begin (*RETurn *) CPU.PC := CPU.STACK[CPU.SP]; dec(CPU.SP); CPU.PC:= CPU.PC mod MEMSize; end; end; end; (* 15: *) 15: begin (* CMP/ JLT/JEQ/JGT *) (* addr = reg1*256+ reg2*16 + reg3 *) case reg4 of (* sub-opcode is in reg4 *) 0: begin (* CMP *); sint1:= CPU.REG[reg2] and $00ff; sint2:= CPU.REG[reg3] and $00ff; CPU.temp := integer( sint1 - sint2 ); end; 1: begin (*JLT*); if CPU.temp < 0 then CPU.PC:= addr mod MEMSize; end; 2: begin (*JEQ *); if CPU.temp = 0 then CPU.PC:= addr mod MEMSize; end; 3: begin (*JGT *) if CPU.temp > 0 then CPU.PC:= addr mod MEMSize; end; end; end; (* 15: *) end; (* case*) end; (* with *) showCPU; end; (***=========================================================***) procedure execute(mode:integer); begin HehitCTRLC:=false; if(UpCase(OldCMD)<> 'T') then showCPU; if(mode <> 1) then (*not Trace mode *) showhint('Hit ESCape to STOP... ',Blink); While TRUE do begin execoneinst; showtime; HeHitESCape:=mycheckESC; (* check Keyboard and process time *) if(HehitESCape)then break; (* leave for loop *) if(HehitCTRLC)then begin (* this happen at input instructions *) HehitCTRLC:=false; break; (* leave for loop *) end; if(mode=1) then Break; (* Trace mode *) if(CPU.IR and $F000) = $C000 then break; (* $C000 = Halt *) if(CPU.numbp>0)then begin (* use array if more Break Points required *) if(CPU.PC=CPU.BP) then break; end; delay(DelayTime); end; clearhint; if(mode<>1)then begin (* Break, but not Trace mode *) int2hex(CPU.PC); (* hex char will be in tmp4char *) showhint('Break at '+copy(tmp4char,2,3)+'h',0); end; end; (***=========================================================***) procedure doAssemble; (* Read Assembly program and Assemble it *) begin beep; showhint('Not implemented yet!',0); end; (***=========================================================***) procedure SaveMCimage; (* Save Machine Code core image *) begin beep; showhint('Not implemented yet!',0); end; (***=========================================================***) procedure changeLogFile; (* change the FileName of LOG File *) var nch,k:integer; begin (*** beep; showhint('Not implemented yet!',0); (**) showhint('Default extention is .LOG',0); (**) cmdwindow('new LOG_FileName:',0); nch:=mygetline(cx1,cy1,1); (* get line into str80 *) if(nch>0)then begin if(pos('.',str80)=0)then str80 := str80+'.LOG'; for k:=1 to nch do str80[k]:=UpCase(str80[k]); end; (* if nch > 0 *) (* cmdwindow(str80,1); *) (* echo *) if(nch<=0) or not(str80[1] in ['A'..'Z','0'..'9']) then begin cmdwindow('Log Filename not changed',1); if(nch>0)then cmdwindow('Filename must begin with letter or digit',1); end else begin (* seems be a good filenamr *) LOGFILENAME := str80; openLOG(LOGFILENAME); (* new OPEN will automatically CLOSE file opened*) end; end; (* changeLogFile *) (***=========================================================***) begin (* MAIN Main main *) sold:= 0; (* for showtime *) SetBack; (* set BackGround color*) strBlank:= ' '; (* blank*) cx1:=1; cy1:=1; cx2:=1;cy2:=1; cx3:=1; cy3:=1; LOGFILENAME:=DFLOGName; openLOG(LOGFILENAME); InitCPU; hello; showCPU; (* Initialize and Show status *) (*** test hex2ch(date) for tmpint:=0 to 15 do cmdwindow(hex2ch(tmpint),0); readln; cmdwindow('',1); ***) showhint('H = Help I=Instructions',blink); While (TRUE) do begin (* loop forever till Quit *) xtmp:=cx1; ytmp:=cy1; (* used when illegal CMD *) tmpCMD:= OldCMD; OldCMD:=CMD; cmdwindow('Yes> ',0); CMD:=myReadKey(CMD2); if(CMD in ['?','H','h','i','I','G','g','T','t','L','l', 'P','p','B','b','C','c','F','f', 'E','e','U','u','a','A','S','s', 'K','k' ]) then begin clearhint; cmdwindow(UpCase(CMD),1); end; (*if CMD *) case CMD of 'Q','q': begin cmdwindow('Q',0); cmdwin2('uit',0); (* not in LOG file *) cmdwindow('',1); (* CR/LF *) break; (* Quit the system *) end; 'H','h','?': showhelp; 'I','i': showinst; 'K','k': showSTACK; 'F','f': changeLogFile; 'G','g': execute(0); 'T','t': execute(1); (* trace mode *) 'A','a': doassemble; 'B','b': setbreak; 'C','c': begin cmdwindow('',0); clrscr; cx1:=1; cy1:=1; end; 'E','e': entermemory; 'L','l': LoadMachineCode; (* Load program in Machine Code form *) 'S','s': SaveMCimage; (* save Machine Code *) 'P','p': setPC; 'R','r': RegisterModify; 'U','u': doUnassemble; '+','-','=','\': begin clearhint; if (CMD='+') or (CMD='\') then begin if DelayTime > 90 then DelayTime:=DelayTime-50 else if DelayTime > 1 then DelayTime:=DelayTime-10; end; if CMD='-'then begin if DelayTime < 50 then DelayTime:=DelayTime+10 else if DelayTime < 888 then DelayTime:=DelayTime+50; end; str(DelayTime,strinteger); cmdwindow(CMD+' Delay='+strinteger, 0); if CMD='=' then begin str(MEMSize,strinteger); cmdwindow(', MemorySize='+strinteger, 0); end; cmdwindow('',1); (* CR/LF *) end; 'M','m': begin (* toggle Memory Size *) MEMSize:= MSLarge+MSSmall - MEMSize; str(MEMSize,strinteger); clearhint; showCPU; cmdwindow(CMD+' now Memory Size='+strinteger, 1); end; else begin sound(444); showhint('H for Help, Q to Quit',Blink); delay(49); nosound; cx1:=xtmp; cy1:=ytmp; CMD:= tmpCMD; (* ignore err cmd *) end; end; (* case CMD *) end; (* while TRUE *) cmdwindow('',1); (* move cursor to command window *) textcolor(LightRED); (* tell him LOG Filename in RED color *) writeln('Log file is '+LogFileName); (* donot call cmdwin... *) cmdwin2('',1); textcolor(w1color); (* change color back to color for this window *) cmdwindow('Thank you for using SISC!',0); sound(660); Delay(200); sound(550); delay(200); cmdwindow('',1); nosound; Close(FLOG); (* close LOG file *) cmdwin2('Hit Return Key...',0); waitRETURNKEY; window(1,1,80,25); TextBackground(Black); TextColor(2); clrScr; Writeln('Your running LOG is in ',LOGFILENAME); end.