Some changes
This commit is contained in:
@@ -48,6 +48,10 @@ set(SOURCE_FILES
|
|||||||
util/texteditor.c util/texteditor.h
|
util/texteditor.c util/texteditor.h
|
||||||
util/hexdump.c util/hexdump.h
|
util/hexdump.c util/hexdump.h
|
||||||
util/cpustatusui.c util/cpustatusui.h
|
util/cpustatusui.c util/cpustatusui.h
|
||||||
|
peripherals/audio.c
|
||||||
|
peripherals/audio.h
|
||||||
|
peripherals/peripheraldata.c
|
||||||
|
peripherals/peripheraldata.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build the target executable
|
# Build the target executable
|
||||||
|
@@ -90,6 +90,9 @@ void toUpperCase(char *string) {
|
|||||||
if (*string > 0x60 && *string < 0x7b) {
|
if (*string > 0x60 && *string < 0x7b) {
|
||||||
(*string) -= 0x20;
|
(*string) -= 0x20;
|
||||||
}
|
}
|
||||||
|
if (*string == '\r') {
|
||||||
|
*string = ' ';
|
||||||
|
}
|
||||||
string++;
|
string++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +120,7 @@ int getOpcode(char *mnemonic) {
|
|||||||
else if (strcmp(mnemonic, "HLT") == 0)
|
else if (strcmp(mnemonic, "HLT") == 0)
|
||||||
return HLT;
|
return HLT;
|
||||||
else if (strcmp(mnemonic, "MOV") == 0)
|
else if (strcmp(mnemonic, "MOV") == 0)
|
||||||
return -2; // Special case: we must decide between MOV_RN_IMM, MOV_RN_RM, MOV_RN_ADDR, MOV_ADDR_RN
|
return -2; // Special case: we must decide between MOV_IMM_RN, MOV_RN_RM, MOV_RN_ADDR, MOV_ADDR_RN
|
||||||
else if (strcmp(mnemonic, "SWAP") == 0)
|
else if (strcmp(mnemonic, "SWAP") == 0)
|
||||||
return SWAP;
|
return SWAP;
|
||||||
else if (strcmp(mnemonic, "SWAPN") == 0)
|
else if (strcmp(mnemonic, "SWAPN") == 0)
|
||||||
@@ -161,9 +164,13 @@ int getOpcode(char *mnemonic) {
|
|||||||
else if (strcmp(mnemonic, "JNE") == 0)
|
else if (strcmp(mnemonic, "JNE") == 0)
|
||||||
return JNE;
|
return JNE;
|
||||||
else if (strcmp(mnemonic, "JMPBS") == 0)
|
else if (strcmp(mnemonic, "JMPBS") == 0)
|
||||||
return -14; //Special: decide between JMP_BIT_SET_RN and JMP_BIT_SET_ADDR
|
return -14; //Special: decide between BIT_TS_RN and BIT_TS_ADDR
|
||||||
else if (strcmp(mnemonic, "JMPBC") == 0)
|
else if (strcmp(mnemonic, "JMPBC") == 0)
|
||||||
return -15; //Special: decide between JMP_BIT_CLEAR_RN and JMP_BIT_CLEAR_ADDR
|
return -15; //Special: decide between BIT_TC_RN and BIT_TC_ADDR
|
||||||
|
else if (strcmp(mnemonic, "BITS") == 0)
|
||||||
|
return -16; //Special: decide between BITS_RN and BITS_ADDR
|
||||||
|
else if (strcmp(mnemonic, "BITC") == 0)
|
||||||
|
return -17; //Special: decide between BITC_RN and BITC_ADDR
|
||||||
else if (strcmp(mnemonic, "JG") == 0)
|
else if (strcmp(mnemonic, "JG") == 0)
|
||||||
return JG;
|
return JG;
|
||||||
else if (strcmp(mnemonic, "JL") == 0)
|
else if (strcmp(mnemonic, "JL") == 0)
|
||||||
@@ -186,20 +193,20 @@ int getOpcode(char *mnemonic) {
|
|||||||
// based on the type of the operand (register vs. immediate vs. memory).
|
// based on the type of the operand (register vs. immediate vs. memory).
|
||||||
// The following helper functions decide that, given two operands (as strings).
|
// The following helper functions decide that, given two operands (as strings).
|
||||||
//
|
//
|
||||||
// For example, "MOV Rn, 42" should choose MOV_RN_IMM, while "MOV Rn, Rm" should choose MOV_RN_RM.
|
// For example, "MOV Rn, 42" should choose MOV_IMM_RN, while "MOV Rn, Rm" should choose MOV_RN_RM.
|
||||||
// We assume that memory addresses are written in square brackets, e.g. "[123]".
|
// We assume that memory addresses are written in square brackets, e.g. "[123]".
|
||||||
//
|
//
|
||||||
int resolveMOV(const char *dest, const char *src) {
|
int resolveMOV(const char *dest, const char *src) {
|
||||||
// If dest starts with '[' then it is a memory destination.
|
// If dest starts with '[' then it is a memory destination.
|
||||||
if (dest[0] == '[') return MOV_ADDR_RN; // actually, MOV [Addr], Rn expects Rn in second operand
|
if (dest[0] == '[') return MOV_RN_ADDR; // actually, MOV [Addr], Rn expects Rn in second operand
|
||||||
// Otherwise, dest is a register.
|
// Otherwise, dest is a register.
|
||||||
// Now, check src:
|
// Now, check src:
|
||||||
if (src[0] == 'R' || src[0] == 'r') {
|
if ((dest[0] == 'R' || dest[0] == 'r') && (src[0] == 'R' || src[0] == 'r')) {
|
||||||
return MOV_RN_RM;
|
return MOV_RN_RM;
|
||||||
} else if (src[0] == '[') {
|
} else if (src[0] == '[') {
|
||||||
return MOV_RN_ADDR;
|
return MOV_ADDR_RN;
|
||||||
} else {
|
} else {
|
||||||
return MOV_RN_IMM;
|
return MOV_IMM_RN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,9 +235,13 @@ int resolveALU(int baseOpcode, const char *src) {
|
|||||||
case -13:
|
case -13:
|
||||||
return DEC_RN;
|
return DEC_RN;
|
||||||
case -14:
|
case -14:
|
||||||
return JMP_BIT_SET_RN;
|
return BIT_TS_RN;
|
||||||
case -15:
|
case -15:
|
||||||
return JMP_BIT_CLEAR_RN;
|
return BIT_TC_RN;
|
||||||
|
case -16:
|
||||||
|
return BITS_RN;
|
||||||
|
case -17:
|
||||||
|
return BITC_RN;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -266,9 +277,13 @@ int resolveALU(int baseOpcode, const char *src) {
|
|||||||
case -13:
|
case -13:
|
||||||
return DEC_ADDR;
|
return DEC_ADDR;
|
||||||
case -14:
|
case -14:
|
||||||
return JMP_BIT_SET_ADDR;
|
return BIT_TS_ADDR;
|
||||||
case -15:
|
case -15:
|
||||||
return JMP_BIT_CLEAR_ADDR;
|
return BIT_TC_ADDR;
|
||||||
|
case -16:
|
||||||
|
return BITS_ADDR;
|
||||||
|
case -17:
|
||||||
|
return BITC_ADDR;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -367,6 +382,17 @@ uint32_t completePass(const char *source, CPU *cpu, bool erase) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop at the first semicolon.
|
||||||
|
char *semicolon = strchr(line, ';');
|
||||||
|
if (semicolon != NULL) {
|
||||||
|
*semicolon = '\0'; // Terminate the string at the semicolon
|
||||||
|
trim(line); // Trim again in case spaces remain
|
||||||
|
if (line[0] == '\0') {
|
||||||
|
lineIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any label definitions (up to the colon).
|
// Remove any label definitions (up to the colon).
|
||||||
char *colon = strchr(line, ':');
|
char *colon = strchr(line, ':');
|
||||||
if (colon != NULL) {
|
if (colon != NULL) {
|
||||||
@@ -398,31 +424,20 @@ uint32_t completePass(const char *source, CPU *cpu, bool erase) {
|
|||||||
fprintf(stderr, "Error: MOV requires two operands.\n");
|
fprintf(stderr, "Error: MOV requires two operands.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int resolvedOpcode = resolveMOV(operand1, operand2);
|
int resolvedOpcode = resolveMOV(operand2, operand1);
|
||||||
cpu->memory[addr++] = resolvedOpcode;
|
cpu->memory[addr++] = resolvedOpcode;
|
||||||
if (resolvedOpcode == MOV_RN_IMM) {
|
if (resolvedOpcode == MOV_IMM_RN) {
|
||||||
int reg = parseRegister(operand1);
|
uint8_t imm = parseImmediate(operand1);
|
||||||
uint8_t imm = parseImmediate(operand2);
|
int reg = parseRegister(operand2);
|
||||||
cpu->memory[addr++] = reg;
|
|
||||||
cpu->memory[addr++] = imm;
|
cpu->memory[addr++] = imm;
|
||||||
} else if (resolvedOpcode == MOV_RN_RM) {
|
|
||||||
int regDest = parseRegister(operand1);
|
|
||||||
int regSrc = parseRegister(operand2);
|
|
||||||
cpu->memory[addr++] = regDest;
|
|
||||||
cpu->memory[addr++] = regSrc;
|
|
||||||
} else if (resolvedOpcode == MOV_RN_ADDR) {
|
|
||||||
int reg = parseRegister(operand1);
|
|
||||||
// Assume source is written as "[address]": remove the brackets.
|
|
||||||
char addrStr[32];
|
|
||||||
strncpy(addrStr, operand2 + 1, strlen(operand2) - 2);
|
|
||||||
addrStr[strlen(operand2) - 2] = '\0';
|
|
||||||
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
|
||||||
cpu->memory[addr++] = reg;
|
cpu->memory[addr++] = reg;
|
||||||
cpu->memory[addr++] = memAddr & 0xFF;
|
} else if (resolvedOpcode == MOV_RN_RM) {
|
||||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
int regSrc = parseRegister(operand1);
|
||||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
int regDest = parseRegister(operand2);
|
||||||
|
cpu->memory[addr++] = regSrc;
|
||||||
|
cpu->memory[addr++] = regDest;
|
||||||
} else if (resolvedOpcode == MOV_ADDR_RN) {
|
} else if (resolvedOpcode == MOV_ADDR_RN) {
|
||||||
// Destination is memory (written as "[address]").
|
// Assume source is written as "[address]": remove the brackets.
|
||||||
char addrStr[32];
|
char addrStr[32];
|
||||||
strncpy(addrStr, operand1 + 1, strlen(operand1) - 2);
|
strncpy(addrStr, operand1 + 1, strlen(operand1) - 2);
|
||||||
addrStr[strlen(operand1) - 2] = '\0';
|
addrStr[strlen(operand1) - 2] = '\0';
|
||||||
@@ -432,6 +447,17 @@ uint32_t completePass(const char *source, CPU *cpu, bool erase) {
|
|||||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||||
cpu->memory[addr++] = reg;
|
cpu->memory[addr++] = reg;
|
||||||
|
} else if (resolvedOpcode == MOV_RN_ADDR) {
|
||||||
|
// Destination is memory (written as "[address]").
|
||||||
|
char addrStr[32];
|
||||||
|
strncpy(addrStr, operand2 + 1, strlen(operand2) - 2);
|
||||||
|
addrStr[strlen(operand2) - 2] = '\0';
|
||||||
|
int reg = parseRegister(operand1);
|
||||||
|
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
||||||
|
cpu->memory[addr++] = reg;
|
||||||
|
cpu->memory[addr++] = memAddr & 0xFF;
|
||||||
|
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||||
|
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// --- INC and DEC (baseOpcode == -12 or -13) ---
|
// --- INC and DEC (baseOpcode == -12 or -13) ---
|
||||||
@@ -465,7 +491,7 @@ uint32_t completePass(const char *source, CPU *cpu, bool erase) {
|
|||||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int resolvedOpcode = resolveALU(baseOpcode, operand2);
|
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||||
cpu->memory[addr++] = resolvedOpcode;
|
cpu->memory[addr++] = resolvedOpcode;
|
||||||
int regDest = parseRegister(operand1);
|
int regDest = parseRegister(operand1);
|
||||||
cpu->memory[addr++] = regDest;
|
cpu->memory[addr++] = regDest;
|
||||||
@@ -617,7 +643,7 @@ uint32_t completePass(const char *source, CPU *cpu, bool erase) {
|
|||||||
}
|
}
|
||||||
const uint32_t remainingBytes = CPU_INSTRUCTION_SIZE - (addr - oldAddr);
|
const uint32_t remainingBytes = CPU_INSTRUCTION_SIZE - (addr - oldAddr);
|
||||||
if (remainingBytes > CPU_INSTRUCTION_SIZE) {
|
if (remainingBytes > CPU_INSTRUCTION_SIZE) {
|
||||||
printf("HELP, INSTRUCTION SIZE SMALLER THAN INSTRUCTION");
|
printf("HELP, INSTRUCTION SIZE SMALLER THAN INSTRUCTION\n");
|
||||||
}
|
}
|
||||||
cpu->addrToLineMapper[(addr - (addr % CPU_INSTRUCTION_SIZE)) / CPU_INSTRUCTION_SIZE] = lineIndex;
|
cpu->addrToLineMapper[(addr - (addr % CPU_INSTRUCTION_SIZE)) / CPU_INSTRUCTION_SIZE] = lineIndex;
|
||||||
addr += remainingBytes;
|
addr += remainingBytes;
|
||||||
|
@@ -65,7 +65,7 @@ int getOpcode(char *mnemonic);
|
|||||||
// based on the type of the operand (register vs. immediate vs. memory).
|
// based on the type of the operand (register vs. immediate vs. memory).
|
||||||
// The following helper functions decide that, given two operands (as strings).
|
// The following helper functions decide that, given two operands (as strings).
|
||||||
//
|
//
|
||||||
// For example, "MOV Rn, 42" should choose MOV_RN_IMM, while "MOV Rn, Rm" should choose MOV_RN_RM.
|
// For example, "MOV Rn, 42" should choose MOV_IMM_RN, while "MOV Rn, Rm" should choose MOV_RN_RM.
|
||||||
// We assume that memory addresses are written in square brackets, e.g. "[123]".
|
// We assume that memory addresses are written in square brackets, e.g. "[123]".
|
||||||
//
|
//
|
||||||
int resolveMOV(const char *dest, const char *src);
|
int resolveMOV(const char *dest, const char *src);
|
||||||
|
79
cpu/core.c
79
cpu/core.c
@@ -93,10 +93,10 @@ void step(CPU *cpu) {
|
|||||||
write_mem(cpu, addrTemp, temp - 1);
|
write_mem(cpu, addrTemp, temp - 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOV_RN_IMM:
|
case MOV_IMM_RN:
|
||||||
//Load from immediate to register
|
//Load from immediate to register
|
||||||
reg1 = read_reg_number(cpu);
|
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
|
reg1 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] = imm;
|
cpu->regs[reg1] = imm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -372,7 +372,43 @@ void step(CPU *cpu) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JMP_BIT_SET_RN:
|
case BITS_RN: {
|
||||||
|
// Jump if bit in register set
|
||||||
|
reg1 = read_reg_number(cpu);
|
||||||
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
|
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
|
if (reg1 >= REG_COUNT) {
|
||||||
|
reg1 = REG_COUNT - 1;
|
||||||
|
}
|
||||||
|
if (bit > 7) {
|
||||||
|
bit = 7;
|
||||||
|
}
|
||||||
|
temp |= (1 << bit);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
cpu->pc += CPU_INSTRUCTION_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BITC_RN: {
|
||||||
|
// Jump if bit in register set
|
||||||
|
reg1 = read_reg_number(cpu);
|
||||||
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
|
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
|
if (reg1 >= REG_COUNT) {
|
||||||
|
reg1 = REG_COUNT - 1;
|
||||||
|
}
|
||||||
|
if (bit > 7) {
|
||||||
|
bit = 7;
|
||||||
|
}
|
||||||
|
temp &= ~(1 << bit);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
cpu->pc += CPU_INSTRUCTION_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BIT_TS_RN: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
@@ -391,13 +427,40 @@ void step(CPU *cpu) {
|
|||||||
}
|
}
|
||||||
cpu->pc += CPU_INSTRUCTION_SIZE;
|
cpu->pc += CPU_INSTRUCTION_SIZE;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case JMP_BIT_SET_ADDR: {
|
case BITS_ADDR: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
addrTemp = read_address_argument(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
if (addrTemp >= MEM_SIZE) {
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
addrTemp = MEM_SIZE - 1;
|
temp = read_mem(cpu, addrTemp);
|
||||||
|
if (bit > 7) {
|
||||||
|
bit = 7;
|
||||||
}
|
}
|
||||||
|
temp |= (1 << bit);
|
||||||
|
write_mem(cpu, addrTemp, temp);
|
||||||
|
cpu->pc += CPU_INSTRUCTION_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BITC_ADDR: {
|
||||||
|
// Jump if bit in register set
|
||||||
|
addrTemp = read_address_argument(cpu);
|
||||||
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
|
|
||||||
|
temp = read_mem(cpu, addrTemp);
|
||||||
|
if (bit > 7) {
|
||||||
|
bit = 7;
|
||||||
|
}
|
||||||
|
temp &= ~(1 << bit);
|
||||||
|
write_mem(cpu, addrTemp, temp);
|
||||||
|
cpu->pc += CPU_INSTRUCTION_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BIT_TS_ADDR: {
|
||||||
|
// Jump if bit in register set
|
||||||
|
addrTemp = read_address_argument(cpu);
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
if (bit > 7) {
|
if (bit > 7) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
@@ -423,7 +486,7 @@ void step(CPU *cpu) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JMP_BIT_CLEAR_RN: {
|
case BIT_TC_RN: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
temp = read_reg(cpu, reg1);
|
temp = read_reg(cpu, reg1);
|
||||||
@@ -440,7 +503,7 @@ void step(CPU *cpu) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JMP_BIT_CLEAR_ADDR: {
|
case BIT_TC_ADDR: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
addrTemp = read_address_argument(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
|
17
cpu/core.h
17
cpu/core.h
@@ -22,6 +22,7 @@
|
|||||||
#define CPU_MODE_LOOP (1 << 3)
|
#define CPU_MODE_LOOP (1 << 3)
|
||||||
#define CPU_MODE_STEP (1 << 4)
|
#define CPU_MODE_STEP (1 << 4)
|
||||||
#define CPU_MODE_SECOND (1 << 5)
|
#define CPU_MODE_SECOND (1 << 5)
|
||||||
|
#define CPU_MODE_FRAME (1 << 6)
|
||||||
|
|
||||||
#define CPU_INSTRUCTION_SIZE 8 //Biggest instruction
|
#define CPU_INSTRUCTION_SIZE 8 //Biggest instruction
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ typedef enum {
|
|||||||
|
|
||||||
HLT,
|
HLT,
|
||||||
|
|
||||||
MOV_RN_IMM, // MOV Rn, Imm - Move immediate to register (Rn = Imm)
|
MOV_IMM_RN, // MOV Rn, Imm - Move immediate to register (Rn = Imm)
|
||||||
MOV_RN_RM, // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
|
MOV_RN_RM, // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
|
||||||
MOV_RN_ADDR, // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
|
MOV_RN_ADDR, // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
|
||||||
MOV_ADDR_RN, // MOV [Addr], Rn - Store register value into memory address ([Addr] = Rn)
|
MOV_ADDR_RN, // MOV [Addr], Rn - Store register value into memory address ([Addr] = Rn)
|
||||||
@@ -114,11 +115,17 @@ typedef enum {
|
|||||||
JE, // JE Addr - Jump if equal (if zero flag set, PC = Addr)
|
JE, // JE Addr - Jump if equal (if zero flag set, PC = Addr)
|
||||||
JNE, // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)
|
JNE, // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)
|
||||||
|
|
||||||
JMP_BIT_SET_RN, // jump relative if a given bit in a register is set
|
BIT_TS_RN, // jump relative if a given bit in a register is set
|
||||||
JMP_BIT_SET_ADDR, // jump relative if a given bit in memory is set
|
BIT_TS_ADDR, // jump relative if a given bit in memory is set
|
||||||
|
|
||||||
JMP_BIT_CLEAR_RN, // jump relative if a given bit in a register is not set
|
BIT_TC_RN, // jump relative if a given bit in a register is not set
|
||||||
JMP_BIT_CLEAR_ADDR, // jump relative if a given bit in memory is not set
|
BIT_TC_ADDR, // jump relative if a given bit in memory is not set
|
||||||
|
|
||||||
|
BITS_RN, // jump relative if a given bit in a register is set
|
||||||
|
BITS_ADDR, // jump relative if a given bit in memory is set
|
||||||
|
|
||||||
|
BITC_RN, // jump relative if a given bit in a register is not set
|
||||||
|
BITC_ADDR, // jump relative if a given bit in memory is not set
|
||||||
|
|
||||||
JG, // JG Addr - Jump if greater (if greater flag set, PC = Addr)
|
JG, // JG Addr - Jump if greater (if greater flag set, PC = Addr)
|
||||||
|
|
||||||
|
53
cpu/memory.c
53
cpu/memory.c
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
|
#define SFR_OFFSET (0xDF00)
|
||||||
|
|
||||||
uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value) {
|
uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value) {
|
||||||
if (addr >= MEM_SIZE) {
|
if (addr >= MEM_SIZE) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -11,10 +13,50 @@ uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value) {
|
|||||||
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x00:
|
||||||
|
pcm_buffer_push(&audioData.pcmVoice, value << 8 | cpu->memory[SFR_OFFSET + 0x01]);
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x02:
|
||||||
|
audioData.synthVoices[0].volume = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x03:
|
||||||
|
audioData.synthVoices[0].waveform = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x04:
|
||||||
|
audioData.synthVoices[0].phase = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x05:
|
||||||
|
audioData.synthVoices[0].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x06];
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x07:
|
||||||
|
audioData.synthVoices[1].volume = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x08:
|
||||||
|
audioData.synthVoices[1].waveform = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x09:
|
||||||
|
audioData.synthVoices[1].phase = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x0A:
|
||||||
|
audioData.synthVoices[1].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x0B];
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x0C:
|
||||||
|
audioData.synthVoices[2].volume = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x0D:
|
||||||
|
audioData.synthVoices[2].waveform = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x0E:
|
||||||
|
audioData.synthVoices[2].phase = value;
|
||||||
|
|
||||||
|
case SFR_OFFSET + 0x0F:
|
||||||
|
audioData.synthVoices[2].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x10];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cpu->memory[addr] = value;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu->memory[addr] = value;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +92,15 @@ uint32_t read_address_argument(CPU *cpu) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t read_register_argument(CPU *cpu) {
|
||||||
|
uint8_t out = read_mem(cpu, cpu->pc);
|
||||||
|
cpu->pc += 1;
|
||||||
|
if (out >= REG_COUNT) {
|
||||||
|
out = REG_COUNT - 1;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Push a 32-bit program counter (PC) onto the stack
|
// Push a 32-bit program counter (PC) onto the stack
|
||||||
void write_stack(CPU *cpu) {
|
void write_stack(CPU *cpu) {
|
||||||
if (cpu->pc >= MEM_SIZE) {
|
if (cpu->pc >= MEM_SIZE) {
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../cpu/core.h"
|
#include "../cpu/core.h"
|
||||||
|
#include "../peripherals/peripheraldata.h"
|
||||||
|
|
||||||
uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value);
|
uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value);
|
||||||
|
|
||||||
@@ -32,5 +33,7 @@ uint8_t read_mem(CPU *cpu, uint32_t addr);
|
|||||||
|
|
||||||
uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value);
|
uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value);
|
||||||
|
|
||||||
|
uint8_t read_register_argument(CPU *cpu);
|
||||||
|
|
||||||
|
|
||||||
#endif //RISCB_MEMORY_H
|
#endif //RISCB_MEMORY_H
|
||||||
|
BIN
distWin/SDL2.dll
Normal file
BIN
distWin/SDL2.dll
Normal file
Binary file not shown.
BIN
distWin/SDL2_ttf.dll
Normal file
BIN
distWin/SDL2_ttf.dll
Normal file
Binary file not shown.
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
`HLT` - Halt CPU execution
|
`HLT` - Halt CPU execution
|
||||||
|
|
||||||
### **MOV_RN_IMM**
|
### **MOV_IMM_RN**
|
||||||
|
|
||||||
`MOV R1, 0x10` - Move immediate value `0x10` to register `R1`
|
`MOV R1, 0x10` - Move immediate value `0x10` to register `R1`
|
||||||
|
|
||||||
@@ -178,18 +178,73 @@
|
|||||||
|
|
||||||
`RET` - Return from subroutine
|
`RET` - Return from subroutine
|
||||||
|
|
||||||
### **JMP_BIT_CLEAR_RN**
|
### **BIT_TC_RN**
|
||||||
|
|
||||||
`JMPBC R1, 3, 0x4000` - Jump to address `0x4000` if bit `3` in register `R1` is **not set**.
|
`JMPBC R1, 3, 0x4000` - Jump to address `0x4000` if bit `3` in register `R1` is **not set**.
|
||||||
|
|
||||||
### **JMP_BIT_CLEAR_ADDR**
|
### **BIT_TC_ADDR**
|
||||||
|
|
||||||
`JMPBC 0x2000, 5, 0x5000` - Jump to address `0x5000` if bit `5` in memory at address `0x2000` is **not set**.
|
`JMPBC 0x2000, 5, 0x5000` - Jump to address `0x5000` if bit `5` in memory at address `0x2000` is **not set**.
|
||||||
|
|
||||||
### **JMP_BIT_SET_RN**
|
### **BIT_TS_RN**
|
||||||
|
|
||||||
`JMPBS R2, 1, 0x6000` - Jump to address `0x6000` if bit `1` in register `R2` **is set**.
|
`JMPBS R2, 1, 0x6000` - Jump to address `0x6000` if bit `1` in register `R2` **is set**.
|
||||||
|
|
||||||
### **JMP_BIT_SET_ADDR**
|
### **BIT_TS_ADDR**
|
||||||
|
|
||||||
`JMPBS 0x3000, 7, 0x7000` - Jump to address `0x7000` if bit `7` in memory at address `0x3000` **is set**.
|
`JMPBS 0x3000, 7, 0x7000` - Jump to address `0x7000` if bit `7` in memory at address `0x3000` **is set**.
|
||||||
|
|
||||||
|
### **BITS_ADDR**
|
||||||
|
|
||||||
|
`BITS [0x2000], 3` - Set bit `3` in memory at address `0x2000`
|
||||||
|
|
||||||
|
- Reads a memory address as an argument.
|
||||||
|
- Reads a bit index from the next byte.
|
||||||
|
- Ensures the bit index is between `0-7`.
|
||||||
|
- Sets the specified bit in the memory address.
|
||||||
|
- Increments the program counter.
|
||||||
|
|
||||||
|
### **BITC_ADDR**
|
||||||
|
|
||||||
|
`BITC [0x2000], 5` - Clear bit `5` in memory at address `0x2000`
|
||||||
|
|
||||||
|
- Reads a memory address as an argument.
|
||||||
|
- Reads a bit index from the next byte.
|
||||||
|
- Ensures the bit index is between `0-7`.
|
||||||
|
- Clears the specified bit in the memory address.
|
||||||
|
- Increments the program counter.
|
||||||
|
|
||||||
|
### **BITS_RN**
|
||||||
|
|
||||||
|
`BITS R1, 2` - Set bit `2` in register `R1`
|
||||||
|
|
||||||
|
- Reads a register number.
|
||||||
|
- Reads a bit index from the next byte.
|
||||||
|
- Ensures the register is valid (`0 - REG_COUNT - 1`).
|
||||||
|
- Ensures the bit index is between `0-7`.
|
||||||
|
- Sets the specified bit in the register.
|
||||||
|
- Increments the program counter.
|
||||||
|
|
||||||
|
### **BITC_RN**
|
||||||
|
|
||||||
|
`BITC R2, 6` - Clear bit `6` in register `R2`
|
||||||
|
|
||||||
|
- Reads a register number.
|
||||||
|
- Reads a bit index from the next byte.
|
||||||
|
- Ensures the register is valid (`0 - REG_COUNT - 1`).
|
||||||
|
- Ensures the bit index is between `0-7`.
|
||||||
|
- Clears the specified bit in the register.
|
||||||
|
- Increments the program counter.
|
||||||
|
|
||||||
|
### **BITS (Special Instruction)**
|
||||||
|
|
||||||
|
- This mnemonic decides between `BITS_RN` and `BITS_ADDR` based on the operand.
|
||||||
|
- If the operand is a register, `BITS_RN` is used.
|
||||||
|
- If the operand is a memory address, `BITS_ADDR` is used.
|
||||||
|
|
||||||
|
### **BITC (Special Instruction)**
|
||||||
|
|
||||||
|
- This mnemonic decides between `BITC_RN` and `BITC_ADDR` based on the operand.
|
||||||
|
- If the operand is a register, `BITC_RN` is used.
|
||||||
|
- If the operand is a memory address, `BITC_ADDR` is used.
|
||||||
|
|
||||||
|
144
main.c
144
main.c
@@ -1,11 +1,13 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include "util/font.h"
|
#include "util/font.h"
|
||||||
#include "assembler/assembler.h"
|
#include "assembler/assembler.h"
|
||||||
#include "util/texteditor.h"
|
#include "util/texteditor.h"
|
||||||
#include "util/hexdump.h"
|
#include "util/hexdump.h"
|
||||||
#include "util/cpustatusui.h"
|
#include "util/cpustatusui.h"
|
||||||
|
#include "peripherals/peripheraldata.h"
|
||||||
|
|
||||||
//Screen dimension constants
|
//Screen dimension constants
|
||||||
const int SCREEN_WIDTH = 1280;
|
const int SCREEN_WIDTH = 1280;
|
||||||
@@ -13,8 +15,11 @@ const int SCREEN_HEIGHT = 720;
|
|||||||
const int targetFPS = 60;
|
const int targetFPS = 60;
|
||||||
const int delayNeeded = 1000 / targetFPS;
|
const int delayNeeded = 1000 / targetFPS;
|
||||||
|
|
||||||
|
#define TARGET_CYCLE_TIME_NS 15625 // 15.625 µs per cycle for 64 kHz
|
||||||
|
|
||||||
//The window we'll be rendering to
|
//The window we'll be rendering to
|
||||||
SDL_Window *window = NULL;
|
SDL_Window *window = NULL;
|
||||||
|
volatile bool running = true;
|
||||||
|
|
||||||
//The surface contained by the window
|
//The surface contained by the window
|
||||||
SDL_Renderer *renderer = NULL;
|
SDL_Renderer *renderer = NULL;
|
||||||
@@ -44,6 +49,71 @@ TextEditor editors[editorCount];
|
|||||||
unsigned long frames = 0;
|
unsigned long frames = 0;
|
||||||
bool cursor = true;
|
bool cursor = true;
|
||||||
|
|
||||||
|
pthread_t cpu_thread;
|
||||||
|
pthread_mutex_t cpu_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void msleep(unsigned int milliseconds) {
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = milliseconds / 1000;
|
||||||
|
ts.tv_nsec = (milliseconds % 1000) * 1000000;
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cpu_loop(void *arg) {
|
||||||
|
uint64_t last_tick = SDL_GetTicks64();
|
||||||
|
uint64_t last_speed_check = last_tick;;
|
||||||
|
uint64_t step_count = 0;
|
||||||
|
struct timespec start, end, sleep_time;
|
||||||
|
while (running) {
|
||||||
|
pthread_mutex_lock(&cpu_mutex);
|
||||||
|
|
||||||
|
if (!(cpu.mode & (CPU_MODE_PAUSED | CPU_MODE_HALTED | CPU_MODE_ERROR))) {
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &start); // Get start time
|
||||||
|
step(&cpu);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &end); // Get end time
|
||||||
|
// Compute step duration in nanoseconds
|
||||||
|
uint64_t step_duration_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL +
|
||||||
|
(end.tv_nsec - start.tv_nsec);
|
||||||
|
|
||||||
|
// Compute remaining time to maintain 64 kHz
|
||||||
|
if (step_duration_ns < TARGET_CYCLE_TIME_NS) {
|
||||||
|
uint64_t remaining_ns = TARGET_CYCLE_TIME_NS - step_duration_ns;
|
||||||
|
sleep_time.tv_sec = 0;
|
||||||
|
sleep_time.tv_nsec = remaining_ns;
|
||||||
|
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_time, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
step_count++;
|
||||||
|
} else {
|
||||||
|
pthread_mutex_unlock(&cpu_mutex);
|
||||||
|
msleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu.mode & CPU_MODE_SECOND) {
|
||||||
|
pthread_mutex_unlock(&cpu_mutex);
|
||||||
|
msleep(1000);
|
||||||
|
continue;
|
||||||
|
} else if (cpu.mode & CPU_MODE_FRAME) {
|
||||||
|
uint64_t frame_duration = SDL_GetTicks64() - last_tick;
|
||||||
|
if (frame_duration < delayNeeded) {
|
||||||
|
pthread_mutex_unlock(&cpu_mutex);
|
||||||
|
msleep(delayNeeded - frame_duration);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_tick = SDL_GetTicks64();
|
||||||
|
}
|
||||||
|
// Print speed every second
|
||||||
|
uint64_t now = SDL_GetTicks64();
|
||||||
|
if (now - last_speed_check >= 1000) {
|
||||||
|
printf("CPU Max Speed: %lu Hz\n", step_count);
|
||||||
|
step_count = 0;
|
||||||
|
last_speed_check = now;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&cpu_mutex);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
char *read_file_as_string(const char *filename) {
|
char *read_file_as_string(const char *filename) {
|
||||||
FILE *file = fopen(filename, "rb"); // Open file in binary mode
|
FILE *file = fopen(filename, "rb"); // Open file in binary mode
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
@@ -91,7 +161,10 @@ void compile(bool erase) {
|
|||||||
|
|
||||||
int init() {
|
int init() {
|
||||||
//Initialize SDL
|
//Initialize SDL
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL);
|
||||||
|
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||||
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -116,6 +189,24 @@ int init() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&audioData, 0, sizeof(AudioData));
|
||||||
|
|
||||||
|
SDL_AudioSpec spec = {0};
|
||||||
|
spec.freq = SAMPLE_RATE;
|
||||||
|
spec.format = AUDIO_F32SYS;
|
||||||
|
spec.channels = 1;
|
||||||
|
spec.samples = 4096;
|
||||||
|
spec.callback = audio_callback;
|
||||||
|
spec.userdata = &audioData;
|
||||||
|
|
||||||
|
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
|
||||||
|
if (dev == 0) {
|
||||||
|
printf("Failed to open audio: %s\n", SDL_GetError());
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(dev, 0);
|
||||||
|
|
||||||
SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
||||||
SDL_RenderSetViewport(renderer, &viewport);
|
SDL_RenderSetViewport(renderer, &viewport);
|
||||||
|
|
||||||
@@ -126,7 +217,6 @@ int init() {
|
|||||||
init_editor(&codeEditor, &smallFont, 10, 80, renderer, 35, 1000, 48, false);
|
init_editor(&codeEditor, &smallFont, 10, 80, renderer, 35, 1000, 48, false);
|
||||||
init_editor(&memoryViewer, &smallerFont, 738, 80, renderer, 59, MEM_SIZE / 16 + 2, 70, true);
|
init_editor(&memoryViewer, &smallerFont, 738, 80, renderer, 59, MEM_SIZE / 16 + 2, 70, true);
|
||||||
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
|
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL);
|
|
||||||
for (int i = 0; i < editorCount; i++) {
|
for (int i = 0; i < editorCount; i++) {
|
||||||
generate_string_display(&codeEditor, renderer);
|
generate_string_display(&codeEditor, renderer);
|
||||||
}
|
}
|
||||||
@@ -150,18 +240,22 @@ int render() {
|
|||||||
rect2.w = 0;
|
rect2.w = 0;
|
||||||
rect2.h = 0;
|
rect2.h = 0;
|
||||||
|
|
||||||
SDL_QueryTexture(cpuStatsTexture, NULL, NULL, &rect2.w, &rect2.h);
|
if (cpuStatsTexture) {
|
||||||
|
SDL_QueryTexture(cpuStatsTexture, NULL, NULL, &rect2.w, &rect2.h);
|
||||||
|
|
||||||
SDL_RenderCopy(renderer, cpuStatsTexture, NULL, &rect2);
|
SDL_RenderCopy(renderer, cpuStatsTexture, NULL, &rect2);
|
||||||
|
}
|
||||||
|
|
||||||
rect2.x = 100;
|
rect2.x = 100;
|
||||||
rect2.y = 46;
|
rect2.y = 46;
|
||||||
rect2.w = 0;
|
rect2.w = 0;
|
||||||
rect2.h = 0;
|
rect2.h = 0;
|
||||||
|
|
||||||
SDL_QueryTexture(cpuStateTexture, NULL, NULL, &rect2.w, &rect2.h);
|
if (cpuStateTexture) {
|
||||||
|
SDL_QueryTexture(cpuStateTexture, NULL, NULL, &rect2.w, &rect2.h);
|
||||||
|
|
||||||
SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2);
|
SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
frames++;
|
frames++;
|
||||||
@@ -289,6 +383,11 @@ int processEvent(SDL_Event e) {
|
|||||||
int newWidth = e.window.data1;
|
int newWidth = e.window.data1;
|
||||||
int newHeight = e.window.data2;
|
int newHeight = e.window.data2;
|
||||||
|
|
||||||
|
for (int editorIndex = 0; editorIndex < editorCount; editorIndex++) {
|
||||||
|
generate_string_display(&editors[editorIndex], renderer);
|
||||||
|
}
|
||||||
|
updateState(true);
|
||||||
|
|
||||||
// Adjust the viewport to match the new window size;
|
// Adjust the viewport to match the new window size;
|
||||||
SDL_Rect viewport = {0, 0, newWidth, newHeight};
|
SDL_Rect viewport = {0, 0, newWidth, newHeight};
|
||||||
SDL_RenderSetViewport(renderer, &viewport);
|
SDL_RenderSetViewport(renderer, &viewport);
|
||||||
@@ -346,11 +445,18 @@ int processEvent(SDL_Event e) {
|
|||||||
updateState(false);
|
updateState(false);
|
||||||
return 1;
|
return 1;
|
||||||
case SDLK_F8:
|
case SDLK_F8:
|
||||||
if (++cpuSpeedTemp == 3) {
|
if (cpu.mode & CPU_MODE_STEP) {
|
||||||
cpuSpeedTemp = 0;
|
cpu.mode &= ~CPU_MODE_STEP;
|
||||||
|
cpu.mode |= CPU_MODE_SECOND;
|
||||||
|
} else if (cpu.mode & CPU_MODE_SECOND) {
|
||||||
|
cpu.mode &= ~CPU_MODE_SECOND;
|
||||||
|
cpu.mode |= CPU_MODE_FRAME;
|
||||||
|
} else if (cpu.mode & CPU_MODE_FRAME) {
|
||||||
|
cpu.mode &= ~CPU_MODE_FRAME;
|
||||||
|
// No specific mode set, defaults to none
|
||||||
|
} else {
|
||||||
|
cpu.mode |= CPU_MODE_STEP; // Restart cycle
|
||||||
}
|
}
|
||||||
cpu.mode &= ~(CPU_MODE_SECOND | CPU_MODE_STEP);
|
|
||||||
cpu.mode |= cpuSpeedTemp << 4;
|
|
||||||
updateState(false);
|
updateState(false);
|
||||||
return 1;
|
return 1;
|
||||||
case SDLK_F5:
|
case SDLK_F5:
|
||||||
@@ -367,6 +473,7 @@ int processEvent(SDL_Event e) {
|
|||||||
cpu.mode |= CPU_MODE_PAUSED;
|
cpu.mode |= CPU_MODE_PAUSED;
|
||||||
if (keyMod & (KMOD_CTRL | KMOD_SHIFT)) {
|
if (keyMod & (KMOD_CTRL | KMOD_SHIFT)) {
|
||||||
cpu.mode |= CPU_MODE_HALTED;
|
cpu.mode |= CPU_MODE_HALTED;
|
||||||
|
cpu.pc = 0;
|
||||||
}
|
}
|
||||||
updateState(false);
|
updateState(false);
|
||||||
break;
|
break;
|
||||||
@@ -376,10 +483,16 @@ int processEvent(SDL_Event e) {
|
|||||||
FILE *fptr;
|
FILE *fptr;
|
||||||
char fname[20];
|
char fname[20];
|
||||||
snprintf(fname, sizeof(fname), "riscb%lu.bsm", time(NULL));
|
snprintf(fname, sizeof(fname), "riscb%lu.bsm", time(NULL));
|
||||||
fptr = fopen(fname, "w");
|
fptr = fopen(fname, "wb");
|
||||||
generate_string(&editors[0]);
|
generate_string(&editors[0]);
|
||||||
fputs(editors[0].outputString, fptr);
|
fputs(editors[0].outputString, fptr);
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
|
if (keyMod & KMOD_SHIFT) {
|
||||||
|
snprintf(fname, sizeof(fname), "riscb%lu.mem", time(NULL));
|
||||||
|
fptr = fopen(fname, "wb");
|
||||||
|
fwrite(cpu.memory, sizeof(cpu.memory[0]), MEM_SIZE, fptr);
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -391,7 +504,7 @@ int processEvent(SDL_Event e) {
|
|||||||
sscanf(editors[0].lines[editors[0].cursor_line].text, "%s", fname);
|
sscanf(editors[0].lines[editors[0].cursor_line].text, "%s", fname);
|
||||||
toLowerCase(fname);
|
toLowerCase(fname);
|
||||||
strcat(fname, ".bsm");
|
strcat(fname, ".bsm");
|
||||||
fptr = fopen(fname, "r");
|
fptr = fopen(fname, "rb");
|
||||||
if (fptr) {
|
if (fptr) {
|
||||||
char *prog = read_file_as_string(fname);
|
char *prog = read_file_as_string(fname);
|
||||||
toUpperCase(prog);
|
toUpperCase(prog);
|
||||||
@@ -465,11 +578,12 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
|||||||
|
|
||||||
//Hack to get window to stay up
|
//Hack to get window to stay up
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
bool running = true;
|
|
||||||
Uint64 start;
|
Uint64 start;
|
||||||
Uint64 end;
|
Uint64 end;
|
||||||
|
pthread_create(&cpu_thread, NULL, cpu_loop, NULL);
|
||||||
while (running) {
|
while (running) {
|
||||||
start = SDL_GetTicks64();
|
start = SDL_GetTicks64();
|
||||||
|
pthread_mutex_lock(&cpu_mutex);
|
||||||
while (SDL_PollEvent(&e)) {
|
while (SDL_PollEvent(&e)) {
|
||||||
running = processEvent(e);
|
running = processEvent(e);
|
||||||
}
|
}
|
||||||
@@ -495,12 +609,11 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
|||||||
updateState(false);
|
updateState(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&cpu_mutex);
|
||||||
end = SDL_GetTicks64();
|
end = SDL_GetTicks64();
|
||||||
const unsigned long timeNeeded = end - start;
|
const unsigned long timeNeeded = end - start;
|
||||||
if (timeNeeded < delayNeeded) {
|
if (timeNeeded < delayNeeded) {
|
||||||
SDL_Delay(delayNeeded - timeNeeded);
|
SDL_Delay(delayNeeded - timeNeeded);
|
||||||
} else {
|
|
||||||
printf("%lu\n", timeNeeded);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +630,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
|||||||
if (cpuStatsTexture) SDL_DestroyTexture(cpuStatsTexture);
|
if (cpuStatsTexture) SDL_DestroyTexture(cpuStatsTexture);
|
||||||
if (cpuStateTexture) SDL_DestroyTexture(cpuStateTexture);
|
if (cpuStateTexture) SDL_DestroyTexture(cpuStateTexture);
|
||||||
|
|
||||||
|
pthread_join(cpu_thread, NULL);
|
||||||
if (renderer) SDL_DestroyRenderer(renderer);
|
if (renderer) SDL_DestroyRenderer(renderer);
|
||||||
if (window) SDL_DestroyWindow(window);
|
if (window) SDL_DestroyWindow(window);
|
||||||
|
|
||||||
|
79
peripherals/audio.c
Normal file
79
peripherals/audio.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
// Created by bruno on 16.2.2025.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "audio.h"
|
||||||
|
|
||||||
|
int pcm_buffer_empty(PcmVoice *pcm) {
|
||||||
|
return pcm->head == pcm->tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcm_buffer_full(PcmVoice *pcm) {
|
||||||
|
return ((pcm->tail + 1) % PCM_BUFFER_SIZE) == pcm->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcm_buffer_push(PcmVoice *pcm, uint16_t sample) {
|
||||||
|
if (!pcm_buffer_full(pcm)) {
|
||||||
|
pcm->buffer[pcm->tail] = sample;
|
||||||
|
pcm->tail = (pcm->tail + 1) % PCM_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t pcm_buffer_pop(PcmVoice *pcm) {
|
||||||
|
if (!pcm_buffer_empty(pcm)) {
|
||||||
|
uint16_t sample = pcm->buffer[pcm->head];
|
||||||
|
pcm->head = (pcm->head + 1) % PCM_BUFFER_SIZE;
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_callback(void *userdata, Uint8 *stream, int len) {
|
||||||
|
AudioData *audio = (AudioData *) userdata;
|
||||||
|
int samples = len / sizeof(float);
|
||||||
|
|
||||||
|
for (int i = 0; i < samples; i++) {
|
||||||
|
float mix = 0.0f;
|
||||||
|
int activeVoices = 0;
|
||||||
|
|
||||||
|
for (int v = 0; v < NUM_SYNTH_VOICES; v++) {
|
||||||
|
SynthVoice *voice = &audio->synthVoices[v];
|
||||||
|
if (voice->volume == 0 || voice->frequency == 0) continue;
|
||||||
|
|
||||||
|
float sample = 0.0f;
|
||||||
|
float t = (float) voice->phase / 255.0f * 2.0f - 1.0f;
|
||||||
|
|
||||||
|
switch (voice->waveform) {
|
||||||
|
default:
|
||||||
|
case WAVE_SINE:
|
||||||
|
sample = sinf(voice->phase * 2.0f * M_PI / 256.0f);
|
||||||
|
break;
|
||||||
|
case WAVE_SQUARE:
|
||||||
|
sample = (t >= 0.0f) ? 1.0f : -1.0f;
|
||||||
|
break;
|
||||||
|
case WAVE_SAWTOOTH:
|
||||||
|
sample = t;
|
||||||
|
break;
|
||||||
|
case WAVE_TRIANGLE:
|
||||||
|
sample = (t < 0) ? -t : t;
|
||||||
|
break;
|
||||||
|
case WAVE_NOISE:
|
||||||
|
sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice->phase += (uint8_t) ((voice->frequency * 256) / SAMPLE_RATE);
|
||||||
|
mix += sample * (voice->volume / 255.0f);
|
||||||
|
activeVoices++;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcmVoice *pcm = &audio->pcmVoice;
|
||||||
|
if (pcm->volume > 0 && !pcm_buffer_empty(pcm)) {
|
||||||
|
float pcmSample = pcm_buffer_pop(pcm) / 65535.0f;
|
||||||
|
mix += pcmSample * (pcm->volume / 255.0f);
|
||||||
|
activeVoices++;
|
||||||
|
}
|
||||||
|
|
||||||
|
((float *) stream)[i] = (activeVoices > 0) ? mix / activeVoices : 0.0f;
|
||||||
|
}
|
||||||
|
}
|
55
peripherals/audio.h
Normal file
55
peripherals/audio.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
// Created by bruno on 16.2.2025.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RISCB_AUDIO_H
|
||||||
|
#define RISCB_AUDIO_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
#define NUM_SYNTH_VOICES 3
|
||||||
|
#define PCM_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WAVE_SINE,
|
||||||
|
WAVE_SQUARE,
|
||||||
|
WAVE_SAWTOOTH,
|
||||||
|
WAVE_TRIANGLE,
|
||||||
|
WAVE_NOISE
|
||||||
|
} Waveform;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t volume;
|
||||||
|
uint16_t frequency;
|
||||||
|
uint8_t phase;
|
||||||
|
Waveform waveform;
|
||||||
|
} SynthVoice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t volume;
|
||||||
|
uint8_t upsample_factor;
|
||||||
|
uint8_t upsample_count;
|
||||||
|
uint16_t buffer[PCM_BUFFER_SIZE];
|
||||||
|
size_t head, tail;
|
||||||
|
} PcmVoice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SynthVoice synthVoices[NUM_SYNTH_VOICES];
|
||||||
|
PcmVoice pcmVoice;
|
||||||
|
} AudioData;
|
||||||
|
|
||||||
|
int pcm_buffer_empty(PcmVoice *pcm);
|
||||||
|
|
||||||
|
int pcm_buffer_full(PcmVoice *pcm);
|
||||||
|
|
||||||
|
void pcm_buffer_push(PcmVoice *pcm, uint16_t sample);
|
||||||
|
|
||||||
|
uint16_t pcm_buffer_pop(PcmVoice *pcm);
|
||||||
|
|
||||||
|
void audio_callback(void *userdata, Uint8 *stream, int len);
|
||||||
|
|
||||||
|
#endif //RISCB_AUDIO_H
|
7
peripherals/peripheraldata.c
Normal file
7
peripherals/peripheraldata.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 16.2.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "peripheraldata.h"
|
||||||
|
|
||||||
|
AudioData audioData;
|
11
peripherals/peripheraldata.h
Normal file
11
peripherals/peripheraldata.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 16.2.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef RISCB_PERIPHERALDATA_H
|
||||||
|
#define RISCB_PERIPHERALDATA_H
|
||||||
|
|
||||||
|
#include "audio.h"
|
||||||
|
|
||||||
|
extern AudioData audioData;
|
||||||
|
#endif //RISCB_PERIPHERALDATA_H
|
@@ -121,7 +121,7 @@ void renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer, SDL_Te
|
|||||||
} else if (cpu->mode & CPU_MODE_HALTED) {
|
} else if (cpu->mode & CPU_MODE_HALTED) {
|
||||||
strcat(valueStr, "HLT ");
|
strcat(valueStr, "HLT ");
|
||||||
} else if (cpu->mode & CPU_MODE_PAUSED) {
|
} else if (cpu->mode & CPU_MODE_PAUSED) {
|
||||||
strcat(valueStr, "PAUS ");
|
strcat(valueStr, "PS ");
|
||||||
} else {
|
} else {
|
||||||
strcat(valueStr, "RUN ");
|
strcat(valueStr, "RUN ");
|
||||||
}
|
}
|
||||||
@@ -136,6 +136,8 @@ void renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer, SDL_Te
|
|||||||
strcat(valueStr, "STP");
|
strcat(valueStr, "STP");
|
||||||
} else if (cpu->mode & CPU_MODE_SECOND) {
|
} else if (cpu->mode & CPU_MODE_SECOND) {
|
||||||
strcat(valueStr, "SEC");
|
strcat(valueStr, "SEC");
|
||||||
|
} else if (cpu->mode & CPU_MODE_FRAME) {
|
||||||
|
strcat(valueStr, "FRM");
|
||||||
} else {
|
} else {
|
||||||
strcat(valueStr, "BRR");
|
strcat(valueStr, "BRR");
|
||||||
}
|
}
|
||||||
|
14
util/font.c
14
util/font.c
@@ -13,10 +13,10 @@ prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t
|
|||||||
unsigned int i = 1;
|
unsigned int i = 1;
|
||||||
do {
|
do {
|
||||||
if (i == 173) { //specifically this char is 0 width (IDK why)
|
if (i == 173) { //specifically this char is 0 width (IDK why)
|
||||||
out.surface[i] = SDL_CreateRGBSurface(0,pxSize,pxSize,32,0,0,0,0);
|
out.surface[i] = SDL_CreateRGBSurface(0, pxSize, pxSize, 32, 0, 0, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
char tmpOut[2] = {i, 0};
|
char tmpOut[2] = {i, 0};
|
||||||
out.surface[i] = TTF_RenderText_Solid(gFont, tmpOut, out.color);
|
out.surface[i] = TTF_RenderText_Solid(gFont, tmpOut, out.color);
|
||||||
}
|
}
|
||||||
out.texture[i] = SDL_CreateTextureFromSurface(renderer, out.surface[i]);
|
out.texture[i] = SDL_CreateTextureFromSurface(renderer, out.surface[i]);
|
||||||
i++;
|
i++;
|
||||||
@@ -51,7 +51,11 @@ void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t
|
|||||||
|
|
||||||
void destroyFont(BitmapFont *font) {
|
void destroyFont(BitmapFont *font) {
|
||||||
for (uint16_t i = 1; i < 256; i++) {
|
for (uint16_t i = 1; i < 256; i++) {
|
||||||
SDL_DestroyTexture(font->texture[i]);
|
if (font->texture[i]) {
|
||||||
SDL_FreeSurface(font->surface[i]);
|
SDL_DestroyTexture(font->texture[i]);
|
||||||
|
}
|
||||||
|
if (font->surface[i]) {
|
||||||
|
SDL_FreeSurface(font->surface[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -49,8 +49,9 @@ void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Rendere
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate output and display strings.
|
// Allocate output and display strings.
|
||||||
editor->outputString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->maxLines + 1));
|
editor->outputString = (char *) malloc(sizeof(char) * ((editor->max_line_width + 1) * editor->maxLines + 1) + 1);
|
||||||
editor->displayString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->displayLineCount + 1));
|
editor->displayString = (char *) malloc(
|
||||||
|
sizeof(char) * ((editor->max_line_width + 1) * editor->displayLineCount + 1) + 1);
|
||||||
if (!editor->outputString || !editor->displayString) {
|
if (!editor->outputString || !editor->displayString) {
|
||||||
fprintf(stderr, "Failed to allocate memory for output/display strings.\n");
|
fprintf(stderr, "Failed to allocate memory for output/display strings.\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -301,9 +302,9 @@ void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) {
|
|||||||
charDstRect.x += charDstRect.w + 1;
|
charDstRect.x += charDstRect.w + 1;
|
||||||
linePTR++;
|
linePTR++;
|
||||||
}
|
}
|
||||||
strcat(editor->displayString, "\n");
|
|
||||||
charDstRect.x = 4;
|
charDstRect.x = 4;
|
||||||
charDstRect.y += charDstRect.h + 1;
|
charDstRect.y += charDstRect.h + 1;
|
||||||
|
strcat(editor->displayString, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_SetRenderTarget(renderer, NULL);
|
SDL_SetRenderTarget(renderer, NULL);
|
||||||
|
Reference in New Issue
Block a user