# SIC Simulator實作
因系統程式的需求,使用C語言實作SIC Simulator
## 初始值、變數宣告
### 檔案、輸入宣告
```
FILE* f;
//filename
char fname[20];
//
char tname[20];
//command line
char c_line[30];
//object code line
char o_line[80];
// command load,show,unload,exit,run
char cmd[8];
//program length
int prog_len = 0;
//start address
int start_add = 0;
//record first address
int first_add = 0;
//cursor address
int curr_add = 0;
```
### load、run部分宣告
```
//virtual memory block
char* memory;
int mem_size = 0;
int loaded = 0;
int op = 0;
int indexed = 0;
int operand = 0;
int running = 0;
//register A,register X,register L,reister PC,register SW
int reg_A, reg_X, reg_L, reg_PC, reg_SW;
const char s_command[5][7] = { "load", "show", "unload", "exit", "run" };
const int MAXADD = 0x7FFF;
const int IKEY = 0x8000;
const char optab[26][3] = { "18", "40", "28", "24", "3C", "30", "34", "38", "48", "00", "50", "08", "04", "20", "44", "D8", "4C", "0C", "54", "14", "E8", "10", "1C", "E0", "2C", "DC" };
```
除了SIC指令在初始實都先給上數字之外,還設定SW的值,**未設定** = 0、**相等(eq)** = 1、**大於(gt)** = 2及**小於(lt)** = 3。
## Load
### Read Header Record
```
void rd_header() {
char tmp[7];
int i, j, s;
for (i = 7, j = 0; i < 13; i++, j++) tmp[j] = o_line[i];
tmp[j] = '\0';
sscanf(tmp, "%x", &start_add);
for (i = 13, j = 0; i < 19; i++, j++) tmp[j] = o_line[i];
tmp[j] = '\0';
sscanf(tmp, "%x", &prog_len);
s = prog_len * 2 + 1;
memory = (char*)malloc(sizeof(char) * s);
if (memory) {
for (i = 0; i < s; i++) memory[i] = 'X';
memory[s - 1] = '\0';
loaded = 1;
mem_size = s;
}
else {
printf("Loading Failed! (Memory allocation error)\n");
}
}
```
### Read Text Record
```
void rd_text() {
char tmp[7];
int i, j, l, s;
for (i = 1, j = 0; i < 7; i++, j++) tmp[j] = o_line[i];
tmp[j] = '\0';
sscanf(tmp, "%x", &s);
//printf("%X",s);
for (i = 7, j = 0; i < 9; i++, j++) tmp[j] = o_line[i];
tmp[j] = '\0';
sscanf(tmp, "%x", &l);
l = 9 + l * 2;
for (i = 9, j = (s - start_add) * 2; i < l; i++, j++)
memory[j] = o_line[i];
}
```
### Read End Record
```
void rd_end() {
char tmp[7];
int i, j;
for (i = 1, j = 0; i < 7; i++, j++) tmp[j] = o_line[i];
tmp[j] = '\0';
sscanf(tmp, "%x", &first_add);
}
```
這部分的程式碼為老師提供範例碼就有提供,就仔細看過理解後用於load的操作上,在讀record的部分本來就有完成**Relocation**的部分,因此在Absolute Loader的演算法上,很多部分已經在讀Record時完成,也算是一個很大的幫助。
接下來就是將剩下的部分依照演算法完成。
### Load
```
void s_load() {
int i;
if(loaded) printf("ERROR : There is already an object program loaded in memory !\n");
else{
printf(" file name > ");
fgets(fname, 20, stdin);
i = strlen(fname);
fname[i - 1] = '\0';
f = fopen(fname, "r");
if(f == NULL) printf("ERROR : Failed to open dile!\n");
else{
fgets(o_line, 80, f);
i = strlen(o_line);
o_line[i - 1] = '\0';
char prog_name[7];
if (o_line[0] == 'H') {
int i, j;
for (i = 1, j = 0; i < 7; i++, j++) prog_name[j] = o_line[i];
prog_name[j] = '\0';
rd_header();
}
fgets(o_line, 80, f);
//verify program name and length
printf("Program name = [%s], Program Length = [%x]\n", prog_name, prog_len);
while (o_line[0] != 'E') {
int i;
i = strlen(o_line);
o_line[i - 1] = '\0';
//printf("%s\n", o_line);
rd_text();
fgets(o_line, 80, f);
}
rd_end();
loaded = 1;
o_line[0] = '\0';
printf("Load Successful!\n");
}
fclose(f);
}
}
```
看到其他人的程式碼都是將開檔案部分直接就寫在load的command之後,但是由於當初所想就沒想那麼多,就在load的部分上加上一個輸入的部分。
**loaded**用於紀錄是否已經有程式在內部,如果已經有城市在內部就沒有辦法load並且回報錯誤資訊。
由於每個Record的第一個字元會提示這個Record是哪種Record,可用於判斷是否將程式讀完,並且在讀完程式時回報Load Successful!。
這個部份在課本上有提供演算法,就在這邊也順便附上。

其實簡單來說就是把每個Record讀完之後跳到結束位址。
## Show
```
void s_show() {
int i, j = 0;
for(curr_add = first_add;curr_add < first_add + prog_len;curr_add += 16){
printf("%x ",curr_add);
for(i = 0 + j * 32;i < 8 + j * 32 && memory[i] != '\0';i++) printf("%c",memory[i]);
printf(" ");
for(;i < 16 + j * 32 && memory[i] != '\0';i++) printf("%c",memory[i]);
printf(" ");
for(;i < 24 + j * 32 && memory[i] != '\0';i++) printf("%c",memory[i]);
printf(" ");
for(;i < 32 + j * 32 && memory[i] != '\0';i++) printf("%c",memory[i]);
printf("\n");
j++;
}
}
```
顯示目前Memory內所包含的資料,由於前便是使用Dynamic Allocated來進行陣列配置,因此會依照程式的大小配置陣列大小。在未配置的空間會以'X'裝入。
## Unload
主要目的就是將已經讀入的部分清除。
```
void s_unload() {
if(loaded){
fclose(f);
fname[0] = '\0';
o_line[0] = '\0';
free(memory);
prog_len = 0;
start_add = 0;
first_add = 0;
curr_add = 0;
mem_size = 0;
loaded = 0;
op = 0;
indexed = 0;
operand = 0;
}
}
```
這部分主要就是將已經讀入的東西清除,因此這部分就十分直覺。
## Run
### initial_run
```
void init_run() {
reg_A = 0;
reg_X = 0;
reg_L = 0;
reg_PC = first_add;
reg_SW = 0;
curr_add = (first_add - start_add) * 2;
running = 1;
}
```
初始執行前的狀態,將暫存器歸零,Program Counter調到目前即將執行的位址。
### get_op
```
void get_op() {
char s[3];
char t[5];
s[0] = memory[curr_add];
s[1] = memory[curr_add + 1];
s[2] = '\0';
op = lookup(s);
t[0] = memory[curr_add + 2];
t[1] = memory[curr_add + 3];
t[2] = memory[curr_add + 4];
t[3] = memory[curr_add + 5];
t[4] = '\0';
sscanf(t, "%X", &operand);
if (operand >= IKEY) {
indexed = 1;
operand -= IKEY;
}
else {
indexed = 0;
}
curr_add += 6;
//printf("*%X*", curr_add );
reg_PC += 3;
}
```
針對每一個object code進行分析,將opode讀出,operand讀出,並進行轉換。
由於前面有對每一個opcode進行數字編號,這個部分主要就是轉換,並且進行位置的調控。
### get_value
```
int get_value(int r, int x) {
int tmp = 0;
char s[7];
int i, j;
if (x) r += reg_X;
i = (r - start_add) * 2;
for (j = 0; j < 6; j++) s[j] = memory[i++];
s[6] = '\0';
sscanf(s, "%X", &tmp);
return tmp;
}
```
### get_byte
```
int get_byte(int r, int x) {
int tmp = 0;
char s[3];
int i, j;
if (x) r += reg_X;
i = (r - start_add) * 2;
j = 0;
s[j++] = memory[i++];
s[j++] = memory[i++];
s[2] = '\0';
sscanf(s, "%X", &tmp);
printf("LDCH: r = [%X], x = [%d], tmp = [%c]\n", r, x, tmp);
return tmp;
}
```
### put_byte
```
void put_byte(int k, int r, int x) {
int tmp = 0;
char s[3];
int i, j;
if (x) r += reg_X;
sprintf(s, "%02X", k);
i = (r - start_add) * 2;
j = 0;
memory[i++] = s[j++];;
memory[i++] = s[j++];;
}
```
### pute_value
```
void put_value(int k, int r, int x) {
int tmp = 0;
char s[7];
int i, j;
printf("put_value ( %X, %X, %X ) starts.\n", k, r, x);
if (x) r += reg_X;
sprintf(s, "%06X", k);
i = (r - start_add) * 2;
for (j = 0; j < 6; j++) memory[i++] = s[j];
printf("put_value ( %X, %X, %X ) finishes.\n", k, r, x);
}
```
在進行LD及ST暫存器時會使用到,並且在進行PC relative的指令取值時也會使用到。
### show_reg
```
void show_reg() {
printf("Register A = [%06X];\n", reg_A);
printf("Register X = [%06X];\n", reg_X);
printf("Register L = [%06X];\n", reg_L);
printf("Register SW = [%06X];\n", reg_SW);
printf("Register PC = [%06X];\n", reg_PC);
}
```
展示目前reg的狀況,用於執行完後展示。
### s_run
```
void s_run() {
init_run();
printf("*%X*", curr_add );
while (running) {
get_op();
//printf("%X", operand );
//printf("%d\n", op);
switch(op){
case 0:
reg_A += get_value(operand,indexed);
break;
case 1:
reg_A &= get_value(operand,indexed);
break;
case 2:
if(reg_A > get_value(operand,indexed)) reg_SW = gt;
else if(reg_A < get_value(operand,indexed)) reg_SW = lt;
else reg_SW = eq;
break;
case 3:
reg_A /= get_value(operand,indexed);
break;
case 4:
reg_PC = operand;
curr_add = (reg_PC - start_add)*2;
break;
case 5:
if(reg_SW == eq) reg_PC = operand,curr_add = (reg_PC - start_add)*2;
break;
```
```
case 6:
if(reg_SW == gt) reg_PC = operand,curr_add = (reg_PC - start_add)*2;
break;
case 7:
if(reg_SW == lt) reg_PC = operand,curr_add = (reg_PC - start_add)*2;
break;
```
```
case 8:
reg_L = reg_PC,reg_PC = operand, curr_add = (reg_PC - start_add)*2;
break;
case 9:
reg_A = get_value(operand,indexed);
break;
case 10:
reg_A = (reg_A & 16776960) | get_byte(operand,indexed);
break;
```
case 11:
reg_L = get_value(operand,indexed);
break;
case 12:
reg_X = get_value(operand,indexed);
case 13:
reg_A *= get_value(operand,indexed);
break;
case 14:
reg_A |= get_value(operand,indexed);
break;
case 15:
reg_A = reg_A;
break;
```
case 16:
if(reg_L == 0) running = 0;
else reg_PC = reg_L,curr_add = (reg_PC - start_add) * 2;
break;
case 17:
put_value(reg_A,operand,indexed);
break;
case 18:
put_byte(reg_A,operand,indexed);
break;
case 19:
put_value(reg_L,operand,indexed);
break;
case 20 :
put_value(reg_SW,operand,indexed);
break;
```
```
case 21:
put_value(reg_X,operand,indexed);
break;
case 22:
reg_A -= get_value(operand,indexed);
break;
case 23:
reg_SW = gt;
break;
case 24:
reg_X += 1;
if(reg_X > get_value(operand,indexed)) reg_SW = gt;
else if(reg_X < get_value(operand,indexed)) reg_SW = lt;
else reg_SW = eq;
break;
case 25:
reg_A = reg_A;
break;
default:
break;
}
}
show_reg();
}
```
進行程式碼的執行,這個部分可以參考[SIC指令集](http://solomon.ipv6.club.tw/Course/SP.941/sic-instruction.html)來進行,由於程式碼較多就不一一解釋,大部分的函式都可以從前面的介紹中找到,比較特別的部分是在測試Device的部分,由於無法真正進行,只有進行準備或者跳過。
## 主程式
```
int main() {
int comm = 0;
comm = readline();
while (comm != cEXIT) {
switch (comm) {
case cLOAD: s_load();
break;
case cSHOW: s_show();
break;
case cUNLOAD: s_unload();
break;
case cRUN: s_run();
break;
default: printf("Unknown Command!\n");
break;
}
comm = cMIN;
comm = readline();
}
if (loaded) s_unload();
}
```
當輸入exit時會結束程式並進行歸零,其餘就按照上面指令碼名稱去呼叫函式。
至此,SIC Simulator的介紹及實作也告一個段落。
## Sample Run



此為將程式碼在精簡後的結果(將一些檢查部分去除)