Do some stuff

This commit is contained in:
2025-02-18 14:44:08 +01:00
parent 4d3755e2ce
commit 8f46a76fd4
21 changed files with 649 additions and 150 deletions

View File

@@ -52,6 +52,10 @@ set(SOURCE_FILES
peripherals/audio.h peripherals/audio.h
peripherals/peripheraldata.c peripherals/peripheraldata.c
peripherals/peripheraldata.h peripherals/peripheraldata.h
peripherals/sevenseg.c
peripherals/sevenseg.h
peripherals/switches.c
peripherals/switches.h
) )
# Build the target executable # Build the target executable

View File

@@ -7,9 +7,10 @@
#include "string.h" #include "string.h"
// Initialize CPU // Initialize CPU
void init_cpu(CPU *cpu) { void init_cpu(CPU *cpu, SDL_Renderer *renderer) {
memset(cpu, 0, sizeof(CPU)); memset(cpu, 0, sizeof(CPU));
cpu->mode = CPU_MODE_HALTED | CPU_MODE_SECOND; cpu->mode = CPU_MODE_HALTED | CPU_MODE_SECOND;
cpu->renderer = renderer;
} }
// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag, // Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
@@ -41,15 +42,15 @@ void step(CPU *cpu) {
oldPC = cpu->pc; oldPC = cpu->pc;
newPC = oldPC; newPC = oldPC;
uint8_t opcode = read_mem(cpu, cpu->pc++);
const uint32_t differenceAlignment = oldPC % CPU_INSTRUCTION_SIZE; const uint32_t differenceAlignment = oldPC % CPU_INSTRUCTION_SIZE;
if (differenceAlignment) { if (differenceAlignment) {
cpu->pc += differenceAlignment; cpu->pc += differenceAlignment;
} }
uint8_t opcode = read_mem(cpu, cpu->pc++);
switch (opcode) { switch (opcode) {
case NOP: case NOP:
//Don't do anything //Don't do anything
@@ -386,7 +387,7 @@ void step(CPU *cpu) {
} }
temp |= (1 << bit); temp |= (1 << bit);
write_reg(cpu, reg1, temp); write_reg(cpu, reg1, temp);
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -402,9 +403,10 @@ void step(CPU *cpu) {
if (bit > 7) { if (bit > 7) {
bit = 7; bit = 7;
} }
temp &= ~(1 << bit); temp &= ~(1 << bit);
write_reg(cpu, reg1, temp); write_reg(cpu, reg1, temp);
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -425,7 +427,8 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
cpu->pc -= 2;
break; break;
} }
@@ -439,7 +442,7 @@ void step(CPU *cpu) {
} }
temp |= (1 << bit); temp |= (1 << bit);
write_mem(cpu, addrTemp, temp); write_mem(cpu, addrTemp, temp);
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -454,7 +457,7 @@ void step(CPU *cpu) {
} }
temp &= ~(1 << bit); temp &= ~(1 << bit);
write_mem(cpu, addrTemp, temp); write_mem(cpu, addrTemp, temp);
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -470,7 +473,8 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
cpu->pc -= 2;
break; break;
} }
@@ -482,7 +486,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -499,7 +503,8 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
cpu->pc -= 2;
break; break;
} }
@@ -515,7 +520,8 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
cpu->pc -= 2;
break; break;
} }
@@ -526,7 +532,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -537,7 +543,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -548,7 +554,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -559,7 +565,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }
@@ -570,7 +576,7 @@ void step(CPU *cpu) {
cpu->pc = newPC; cpu->pc = newPC;
break; break;
} }
cpu->pc += CPU_INSTRUCTION_SIZE; //cpu->pc += CPU_INSTRUCTION_SIZE;
break; break;
} }

View File

@@ -6,9 +6,10 @@
#define RISCB_CORE_H #define RISCB_CORE_H
#include <stdint.h> #include <stdint.h>
#include <SDL_render.h>
#include "stdio.h" #include "stdio.h"
#define MEM_SIZE 65535 #define MEM_SIZE 2097120
// Register count (register names R0 to R7) // Register count (register names R0 to R7)
#define REG_COUNT 64 #define REG_COUNT 64
#define STACK_SIZE 255 #define STACK_SIZE 255
@@ -39,11 +40,12 @@ typedef struct {
uint32_t programEnd; uint32_t programEnd;
uint8_t mode; uint8_t mode;
uint32_t cycle; uint32_t cycle;
SDL_Renderer *renderer;
} CPU; } CPU;
void step(CPU *cpu); void step(CPU *cpu);
void init_cpu(CPU *cpu); void init_cpu(CPU *cpu, SDL_Renderer *renderer);
// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag, // Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
// and bit1 is the Negative flag). // and bit1 is the Negative flag).

View File

@@ -6,6 +6,12 @@
#define SFR_OFFSET (0xDF00) #define SFR_OFFSET (0xDF00)
#define GPU_OFFSET (0xEF00)
#define GPU_SIZE (160 * 160)
#define GPU_END (GPU_OFFSET + GPU_SIZE)
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;
@@ -15,47 +21,110 @@ uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value) {
case SFR_OFFSET + 0x00: case SFR_OFFSET + 0x00:
pcm_buffer_push(&audioData.pcmVoice, value << 8 | cpu->memory[SFR_OFFSET + 0x01]); pcm_buffer_push(&audioData.pcmVoice, value << 8 | cpu->memory[SFR_OFFSET + 0x01]);
break;
case SFR_OFFSET + 0x02: case SFR_OFFSET + 0x02:
audioData.synthVoices[0].volume = value; audioData.synthVoices[0].volume = value;
break;
case SFR_OFFSET + 0x03: case SFR_OFFSET + 0x03:
audioData.synthVoices[0].waveform = value; audioData.synthVoices[0].waveform = value;
break;
case SFR_OFFSET + 0x04: case SFR_OFFSET + 0x04:
audioData.synthVoices[0].phase = value; audioData.synthVoices[0].phase = value;
break;
case SFR_OFFSET + 0x05: case SFR_OFFSET + 0x05:
audioData.synthVoices[0].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x06]; audioData.synthVoices[0].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x06];
break;
case SFR_OFFSET + 0x07: case SFR_OFFSET + 0x07:
audioData.synthVoices[1].volume = value; audioData.synthVoices[1].volume = value;
break;
case SFR_OFFSET + 0x08: case SFR_OFFSET + 0x08:
audioData.synthVoices[1].waveform = value; audioData.synthVoices[1].waveform = value;
break;
case SFR_OFFSET + 0x09: case SFR_OFFSET + 0x09:
audioData.synthVoices[1].phase = value; audioData.synthVoices[1].phase = value;
break;
case SFR_OFFSET + 0x0A: case SFR_OFFSET + 0x0A:
audioData.synthVoices[1].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x0B]; audioData.synthVoices[1].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x0B];
break;
case SFR_OFFSET + 0x0C: case SFR_OFFSET + 0x0C:
audioData.synthVoices[2].volume = value; audioData.synthVoices[2].volume = value;
break;
case SFR_OFFSET + 0x0D: case SFR_OFFSET + 0x0D:
audioData.synthVoices[2].waveform = value; audioData.synthVoices[2].waveform = value;
break;
case SFR_OFFSET + 0x0E: case SFR_OFFSET + 0x0E:
audioData.synthVoices[2].phase = value; audioData.synthVoices[2].phase = value;
break;
case SFR_OFFSET + 0x0F: case SFR_OFFSET + 0x0F:
audioData.synthVoices[2].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x10]; audioData.synthVoices[2].frequency = value << 8 | cpu->memory[SFR_OFFSET + 0x10];
break;
case SFR_OFFSET + 0x12:
displayA.value = value;
break;
case SFR_OFFSET + 0x13:
displayB.value = value;
break;
case SFR_OFFSET + 0x14:
displayC.value = value;
break;
case SFR_OFFSET + 0x15:
displayD.value = value;
break;
case SFR_OFFSET + 0x16:
displayE.value = value;
break;
case SFR_OFFSET + 0x17:
displayF.value = value;
break;
case SFR_OFFSET + 0x18:
displayG.value = value;
break;
case SFR_OFFSET + 0x19:
displayH.value = value;
break;
default: default:
break; break;
} }
if (addr >= GPU_OFFSET && addr < GPU_END) {
const int index = addr - GPU_OFFSET;
int x = index % 160;
int y = index / 160;
// Proper color scaling from 3-bit and 2-bit channels
Uint8 r = ((value & 0xE0) >> 5) * 36; // 07 mapped to 0255
Uint8 g = ((value & 0x1C) >> 2) * 36;
Uint8 b = ((value & 0x03)) * 85; // 03 mapped to 0255
// Ensure texture format matches RGBA8888
Uint32 color = (255 << 24) | (r << 16) | (g << 8) | b;
Uint32 *pixels = (Uint32 *) gpuSurf->pixels;
pixels[(y * gpuSurf->w) + x] = color;
gpuSurfDirty = true;
}
cpu->memory[addr] = value; cpu->memory[addr] = value;
return 0; return 0;
} }
@@ -66,6 +135,30 @@ uint8_t read_mem(CPU *cpu, uint32_t addr) {
} }
switch (addr) { switch (addr) {
case SFR_OFFSET + 0x20:
return (switchesA.value >> 8) & 0xFF;
case SFR_OFFSET + 0x21:
return switchesA.value & 0xFF;
case SFR_OFFSET + 0x22:
return (switchesB.value >> 8) & 0xFF;
case SFR_OFFSET + 0x23:
return switchesB.value & 0xFF;
case SFR_OFFSET + 0x24:
return (switchesC.value >> 8) & 0xFF;
case SFR_OFFSET + 0x25:
return switchesC.value & 0xFF;
case SFR_OFFSET + 0x26:
return (switchesD.value >> 8) & 0xFF;
case SFR_OFFSET + 0x27:
return switchesD.value & 0xFF;
default: default:
return cpu->memory[addr]; return cpu->memory[addr];
} }

View File

@@ -0,0 +1,3 @@
MOV 0XFF R1
MOV R1 [0XDF12]
HLT

View File

@@ -1,53 +0,0 @@
; Test Program using all opcodes
START:
NOP ; No operation (does nothing, advances to the next instruction)
MOV R1, 0x10 ; Move immediate value 0x10 to register R1
MOV R2, R1 ; Move value from R1 to R2
MOV [0x2000], R3 ; Load value from memory address 0x2000 into R3
MOV R1, [0x2000] ; Store R1 value to memory address 0x2000
SWAP R1, R2 ; Swap values between R1 and R2
SWAPN R1 ; Swap nibbles within R1
ADD R1, R2 ; Add R2 to R1
ADD R1, 0x10 ; Add immediate 0x10 to R1
SUB R1, R2 ; Subtract R2 from R1
SUB R1, 0x10 ; Subtract immediate 0x10 from R1
MUL R1, R2 ; Multiply R1 by R2
MUL R1, 0x10 ; Multiply R1 by immediate 0x10
DIV R1, R2 ; Divide R1 by R2
DIV R1, 0x10 ; Divide R1 by immediate 0x10
MOD R1, R2 ; Compute remainder of R1 / R2
MOD R1, 0x10 ; Compute remainder of R1 / 0x10
NEG R1 ; Negate R1
AND R1, R2 ; Bitwise AND of R1 and R2
AND R1, 0x10 ; Bitwise AND R1 with immediate 0x10
OR R1, R2 ; Bitwise OR of R1 and R2
OR R1, 0x10 ; Bitwise OR R1 with immediate 0x10
XOR R1, R2 ; Bitwise XOR of R1 and R2
XOR R1, 0x10 ; Bitwise XOR R1 with immediate 0x10
NOT R1 ; Bitwise NOT of R1
SHL R1, 2 ; Logical shift left R1 by 2 bits
SHR R1, 2 ; Logical shift right R1 by 2 bits
SAR R1, 2 ; Arithmetic shift right R1 by 2 bits (sign extended)
JMP 0x3000 ; Jump to address 0x3000
JMP +5 ; Jump 5 bytes forward
INC R1 ; Increment R1
INC [0x2000] ; Increment value at memory address 0x2000
DEC R1 ; Decrement R1
DEC [0x2000] ; Decrement value at memory address 0x2000
CMP R1, R2 ; Compare R1 and R2 (sets flags based on R1 - R2)
JE 0x4000 ; Jump to 0x4000 if equal (zero flag set)
JNE 0x4000 ; Jump to 0x4000 if not equal (zero flag not set)
JG 0x4000 ; Jump to 0x4000 if greater
JL 0x4000 ; Jump to 0x4000 if less
JGE 0x4000 ; Jump to 0x4000 if greater or equal
JLE 0x4000 ; Jump to 0x4000 if less or equal
CALL 0x5000 ; Call subroutine at 0x5000
RET ; Return from subroutine
JMPBC R1, 3, 0x4000 ; Jump to address 0x4000 if bit 3 in register R1 is not set
JMPBC 0x2000, 5, 0x5000 ; Jump to address 0x5000 if bit 5 in memory at address 0x2000 is not set
JMPBS R2, 1, 0x6000 ; Jump to address 0x6000 if bit 1 in register R2 is set
JMPBS 0x3000, 7, 0x7000 ; Jump to address 0x7000 if bit 7 in memory at address 0x3000 is set
; Halt the program
HLT

3
docs/examples/gputst.bsm Normal file
View File

@@ -0,0 +1,3 @@
MOV 255 R1
MOV R1 [0XEF00]
HLT

View File

@@ -0,0 +1,3 @@
LP:
INC R0
JMP LP

View File

@@ -0,0 +1,18 @@
LP:
MOV [0XDF20] R0
MOV [0XDF21] R1
MOV [0XDF22] R2
MOV [0XDF23] R3
MOV [0XDF24] R4
MOV [0XDF25] R5
MOV [0XDF26] R6
MOV [0XDF27] R7
MOV R0 [0XDF02]
MOV R1 [0XDF03]
MOV R2 [0XDF04]
MOV R3 [0XDF05]
MOV R4 [0XDF06]
MOV R5 [0XDF12]
MOV R6 [0XDF13]
MOV R7 [0XDF14]
JMP LP

10
docs/examples/snd.bsm Normal file
View File

@@ -0,0 +1,10 @@
MOV 255 R1
MOV R1 [0XDF02]
MOV 0 R1
MOV R1 [0XDF03]
MOV R1 [0XDF04]
MOV 0X03 R1
MOV R1 [0XDF06]
MOV 0XE8 R1
MOV R1 [0XDF05]
HLT

View File

@@ -12,19 +12,19 @@
### **MOV_IMM_RN** ### **MOV_IMM_RN**
`MOV R1, 0x10` - Move immediate value `0x10` to register `R1` `MOV 0x10, R1` - Move immediate value `0x10` to register `R1`
### **MOV_RN_RM** ### **MOV_RN_RM**
`MOV R2, R1` - Move value from `R1` to `R2` `MOV R1, R2` - Move value from `R1` to `R2`
### **MOV_RN_ADDR** ### **MOV_RN_ADDR**
`MOV R3, [0x2000]` - Load value from memory address `0x2000` into `R3` `MOV [0x2000], R3` - Load value from memory address `0x2000` into `R3`
### **MOV_ADDR_RN** ### **MOV_ADDR_RN**
`MOV [0x2000], R1` - Store `R1` value to memory address `0x2000` `MOV R1, [0x2000]` - Store `R1` value to memory address `0x2000`
### **SWAP** ### **SWAP**
@@ -198,53 +198,14 @@
`BITS [0x2000], 3` - Set bit `3` in memory at address `0x2000` `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_ADDR**
`BITC [0x2000], 5` - Clear bit `5` in memory at address `0x2000` `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_RN**
`BITS R1, 2` - Set bit `2` in register `R1` `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_RN**
`BITC R2, 6` - Clear bit `6` in register `R2` `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.

53
docs/memory.md Normal file
View File

@@ -0,0 +1,53 @@
# Memory Map Documentation
#### PCM Buffer
| Function | Address |
|----------------------------------|----------|
| **PCM Voice Buffer (High Byte)** | `0xDF00` |
| **PCM Voice Buffer (Low Byte)** | `0xDF01` |
If you want to write to both, write low byte first
#### Voice Generators
| Voice | Volume | Waveform | Phase | Frequency (High Byte, write last) | Frequency (Low Byte) |
|-------------|----------|----------|----------|-----------------------------------|----------------------|
| **Voice 0** | `0xDF02` | `0xDF03` | `0xDF04` | `0xDF05` | `0xDF06` |
| **Voice 1** | `0xDF07` | `0xDF08` | `0xDF09` | `0xDF0A` | `0xDF0B` |
| **Voice 2** | `0xDF0C` | `0xDF0D` | `0xDF0E` | `0xDF0F` | `0xDF10` |
#### Display Controls
| Display | Address |
|---------------|----------|
| **Display A** | `0xDF12` |
| **Display B** | `0xDF13` |
| **Display C** | `0xDF14` |
| **Display D** | `0xDF15` |
| **Display E** | `0xDF16` |
| **Display F** | `0xDF17` |
| **Display G** | `0xDF18` |
| **Display H** | `0xDF19` |
#### Switch Inputs
| Switch Bank | First 2 rows Address | Last 2 rows Address |
|----------------|----------------------|---------------------|
| **Switches A** | `0xDF20` | `0xDF21` |
| **Switches B** | `0xDF22` | `0xDF23` |
| **Switches C** | `0xDF24` | `0xDF25` |
| **Switches D** | `0xDF26` | `0xDF27` |
### GPU Memory
- **Base Address:** `0xEF00`
- **Size:** `160 × 160` pixels
- **End Address:** `0xEF00 + (160 × 160) = GPU_END`
- **Color Encoding:**
- Red: Bits `[7:5]` scaled (0-7 → 0-255)
- Green: Bits `[4:2]` scaled (0-7 → 0-255)
- Blue: Bits `[1:0]` scaled (0-3 → 0-255)
- Pixels are stored in a texture-compatible `RGBA8888` format.
## Memory Operations

131
main.c
View File

@@ -61,29 +61,11 @@ void msleep(unsigned int milliseconds) {
void *cpu_loop(void *arg) { void *cpu_loop(void *arg) {
uint64_t last_tick = SDL_GetTicks64(); 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) { while (running) {
pthread_mutex_lock(&cpu_mutex); pthread_mutex_lock(&cpu_mutex);
if (!(cpu.mode & (CPU_MODE_PAUSED | CPU_MODE_HALTED | CPU_MODE_ERROR))) { if (!(cpu.mode & (CPU_MODE_PAUSED | CPU_MODE_HALTED | CPU_MODE_ERROR))) {
clock_gettime(CLOCK_MONOTONIC, &start); // Get start time
step(&cpu); 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 { } else {
pthread_mutex_unlock(&cpu_mutex); pthread_mutex_unlock(&cpu_mutex);
msleep(10); msleep(10);
@@ -102,13 +84,6 @@ void *cpu_loop(void *arg) {
} }
last_tick = SDL_GetTicks64(); 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); pthread_mutex_unlock(&cpu_mutex);
} }
return NULL; return NULL;
@@ -189,6 +164,16 @@ int init() {
return 1; return 1;
} }
// Create OpenGL context
SDL_GLContext glContext = SDL_GL_CreateContext(window);
if (!glContext) {
fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
exit(1);
}
// Use OpenGL context
SDL_GL_MakeCurrent(window, glContext); // Make sure OpenGL context is current before any OpenGL rendering
memset(&audioData, 0, sizeof(AudioData)); memset(&audioData, 0, sizeof(AudioData));
SDL_AudioSpec spec = {0}; SDL_AudioSpec spec = {0};
@@ -207,6 +192,30 @@ int init() {
SDL_PauseAudioDevice(dev, 0); SDL_PauseAudioDevice(dev, 0);
init_seven_segment(&displayA, renderer, 480, 240, 60, 120);
init_seven_segment(&displayB, renderer, 545, 240, 60, 120);
init_seven_segment(&displayC, renderer, 610, 240, 60, 120);
init_seven_segment(&displayD, renderer, 675, 240, 60, 120);
init_seven_segment(&displayE, renderer, 480, 375, 60, 120);
init_seven_segment(&displayF, renderer, 545, 375, 60, 120);
init_seven_segment(&displayG, renderer, 610, 375, 60, 120);
init_seven_segment(&displayH, renderer, 675, 375, 60, 120);
init_switches(&switchesA, renderer, 500, 500, 100, 100);
init_switches(&switchesB, renderer, 625, 500, 100, 100);
init_switches(&switchesC, renderer, 500, 610, 100, 100);
init_switches(&switchesD, renderer, 625, 610, 100, 100);
gpuSurf = SDL_CreateRGBSurfaceWithFormat(0, 160, 160, 32, SDL_PIXELFORMAT_RGBA8888);
gpuTex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 160, 160);
SDL_SetRenderTarget(renderer, gpuTex);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, NULL);
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);
@@ -220,7 +229,7 @@ int init() {
for (int i = 0; i < editorCount; i++) { for (int i = 0; i < editorCount; i++) {
generate_string_display(&codeEditor, renderer); generate_string_display(&codeEditor, renderer);
} }
init_cpu(&cpu); init_cpu(&cpu, renderer);
compile(true); compile(true);
return 0; return 0;
@@ -257,6 +266,37 @@ int render() {
SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2); SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2);
} }
rect2.x = 530;
rect2.y = 50;
rect2.w = 0;
rect2.h = 0;
if (gpuSurfDirty) {
SDL_UpdateTexture(gpuTex, NULL, gpuSurf->pixels, gpuSurf->pitch);
gpuSurfDirty = false;
}
if (gpuTex) {
SDL_QueryTexture(gpuTex, NULL, NULL, &rect2.w, &rect2.h);
SDL_RenderCopy(renderer, gpuTex, NULL, &rect2);
}
render_seven_segment(&displayA, renderer);
render_seven_segment(&displayB, renderer);
render_seven_segment(&displayC, renderer);
render_seven_segment(&displayD, renderer);
render_seven_segment(&displayE, renderer);
render_seven_segment(&displayF, renderer);
render_seven_segment(&displayG, renderer);
render_seven_segment(&displayH, renderer);
render_switches_segment(&switchesA, renderer);
render_switches_segment(&switchesB, renderer);
render_switches_segment(&switchesC, renderer);
render_switches_segment(&switchesD, renderer);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
frames++; frames++;
if (!(frames % 60)) { if (!(frames % 60)) {
@@ -440,6 +480,17 @@ int processEvent(SDL_Event e) {
move_cursor(&activeEditor, activeEditor.cursor_line, lineLen, false, renderer); move_cursor(&activeEditor, activeEditor.cursor_line, lineLen, false, renderer);
moved = true; moved = true;
break; break;
case SDLK_d:
if (keyMod & KMOD_CTRL && !activeEditor.readOnly) {
insert_line_rel(&activeEditor, renderer);
memset(activeEditor.lines[activeEditor.cursor_line].text, 0, activeEditor.max_line_width);
strcpy(activeEditor.lines[activeEditor.cursor_line].text,
activeEditor.lines[activeEditor.cursor_line - 1].text);
activeEditor.lines[activeEditor.cursor_line].active = 1;
move_cursor_relative(&activeEditor, 0, activeEditor.max_line_width, false, renderer);
generate_string_display(&activeEditor, renderer);
}
break;
case SDLK_F9: case SDLK_F9:
cpu.mode ^= CPU_MODE_LOOP; cpu.mode ^= CPU_MODE_LOOP;
updateState(false); updateState(false);
@@ -452,8 +503,9 @@ int processEvent(SDL_Event e) {
cpu.mode &= ~CPU_MODE_SECOND; cpu.mode &= ~CPU_MODE_SECOND;
cpu.mode |= CPU_MODE_FRAME; cpu.mode |= CPU_MODE_FRAME;
} else if (cpu.mode & CPU_MODE_FRAME) { } else if (cpu.mode & CPU_MODE_FRAME) {
cpu.mode &= ~CPU_MODE_FRAME; //cpu.mode &= ~CPU_MODE_FRAME;
// No specific mode set, defaults to none // No specific mode set, defaults to none
cpu.mode |= CPU_MODE_STEP; // Restart cycle
} else { } else {
cpu.mode |= CPU_MODE_STEP; // Restart cycle cpu.mode |= CPU_MODE_STEP; // Restart cycle
} }
@@ -474,6 +526,7 @@ int processEvent(SDL_Event e) {
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; cpu.pc = 0;
init_cpu(&cpu, renderer);
} }
updateState(false); updateState(false);
break; break;
@@ -566,6 +619,15 @@ int processEvent(SDL_Event e) {
} }
} }
} }
} else if (e.type == SDL_MOUSEBUTTONDOWN) {
SDL_Rect mouse;
mouse.w = 1;
mouse.h = 1;
SDL_GetMouseState(&mouse.x, &mouse.y);
toggle_switch(mouse, &switchesA);
toggle_switch(mouse, &switchesB);
toggle_switch(mouse, &switchesC);
toggle_switch(mouse, &switchesD);
} }
return 1; return 1;
} }
@@ -630,6 +692,21 @@ 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);
destroy_switches_segment(&switchesA);
destroy_switches_segment(&switchesB);
destroy_switches_segment(&switchesC);
destroy_switches_segment(&switchesD);
destroy_seven_segment(&displayA);
destroy_seven_segment(&displayB);
destroy_seven_segment(&displayC);
destroy_seven_segment(&displayD);
destroy_seven_segment(&displayE);
destroy_seven_segment(&displayF);
destroy_seven_segment(&displayG);
destroy_seven_segment(&displayH);
pthread_join(cpu_thread, NULL); 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);

View File

@@ -3,5 +3,37 @@
// //
#include "peripheraldata.h" #include "peripheraldata.h"
#include "sevenseg.h"
#include "switches.h"
AudioData audioData; AudioData audioData;
SevenSegment displayA;
SevenSegment displayB;
SevenSegment displayC;
SevenSegment displayD;
SevenSegment displayE;
SevenSegment displayF;
SevenSegment displayG;
SevenSegment displayH;
Switches switchesA;
Switches switchesB;
Switches switchesC;
Switches switchesD;
SDL_Texture *gpuTex;
SDL_Surface *gpuSurf;
bool gpuSurfDirty = false;

View File

@@ -6,6 +6,39 @@
#define RISCB_PERIPHERALDATA_H #define RISCB_PERIPHERALDATA_H
#include "audio.h" #include "audio.h"
#include "sevenseg.h"
#include "switches.h"
extern AudioData audioData; extern AudioData audioData;
extern SevenSegment displayA;
extern SevenSegment displayB;
extern SevenSegment displayC;
extern SevenSegment displayD;
extern SevenSegment displayE;
extern SevenSegment displayF;
extern SevenSegment displayG;
extern SevenSegment displayH;
extern Switches switchesA;
extern Switches switchesB;
extern Switches switchesC;
extern Switches switchesD;
extern SDL_Texture *gpuTex;
extern SDL_Surface *gpuSurf;
extern bool gpuSurfDirty;
#endif //RISCB_PERIPHERALDATA_H #endif //RISCB_PERIPHERALDATA_H

84
peripherals/sevenseg.c Normal file
View File

@@ -0,0 +1,84 @@
//
// Created by bruno on 17.2.2025.
//
#include "sevenseg.h"
#include <SDL2/SDL_render.h>
void render_segment(SDL_Renderer *renderer, SevenSegment *display) {
int seg_width = display->rect->w / 4;
int seg_height = display->rect->h / 10;
// Define segment positions
SDL_Rect segments[9] = {
{seg_width, display->rect->y, seg_width * 2, seg_height}, // Top
{3 * seg_width + 4, seg_height, seg_height, seg_height * 3.5}, // Top Right
{3 * seg_width + 4, 5.5 * seg_height, seg_height, seg_height * 3.5}, // Bottom Right
{seg_width, 9 * seg_height, seg_width * 2, seg_height}, // Bottom
{display->rect->x, 5.5 * seg_height, seg_height, seg_height * 3.5}, // Bottom Left
{display->rect->x, seg_height, seg_height, seg_height * 3.5}, // Top Left
{seg_width, 4.5 * seg_height, seg_width * 2, seg_height}, // Middle
};
if (display->value & (1 << 7)) {
SDL_SetRenderDrawColor(renderer, 0, 64, 64, 255);
} else {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
for (int i = 0; i < 7; i++) {
if (display->value & (1 << i)) {
SDL_RenderFillRect(renderer, &segments[i]);
}
}
}
void update_display_texture(SDL_Renderer *renderer, SevenSegment *display) {
SDL_SetRenderTarget(renderer, display->texture);
render_segment(renderer, display);
SDL_SetRenderTarget(renderer, NULL);
}
void init_seven_segment(SevenSegment *display, SDL_Renderer *renderer, int x, int y, int width, int height) {
display->rect = malloc(sizeof(SDL_Rect));
display->outRect = malloc(sizeof(SDL_Rect));
display->value = 0;
display->rect->x = 0;
display->rect->y = 0;
display->rect->w = width;
display->rect->h = height;
display->outRect->x = x;
display->outRect->y = y;
display->outRect->w = width;
display->outRect->h = height;
display->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
if (!display->texture) {
fprintf(stderr, "Failed to create texture: %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
update_display_texture(renderer, display);
}
void render_seven_segment(SevenSegment *display, SDL_Renderer *renderer) {
if (display->oldValue != display->value) {
display->oldValue = display->value;
update_display_texture(renderer, display);
}
if (display->texture) {
SDL_RenderCopy(renderer, display->texture, NULL, display->outRect);
}
}
void destroy_seven_segment(SevenSegment *display) {
SDL_DestroyTexture(display->texture);
free(display->rect);
free(display->outRect);
}

34
peripherals/sevenseg.h Normal file
View File

@@ -0,0 +1,34 @@
//
// Created by bruno on 17.2.2025.
//
#ifndef RISCB_SEVENSEG_H
#define RISCB_SEVENSEG_H
#include <SDL_rect.h>
#include <SDL2/SDL_render.h>
typedef struct {
unsigned char value;
unsigned char oldValue;
SDL_Rect *rect;
SDL_Rect *outRect;
SDL_Texture *texture;
} SevenSegment;
//
// Created by bruno on 17.2.2025.
//
void render_segment(SDL_Renderer *renderer, SevenSegment *display);
void update_display_texture(SDL_Renderer *renderer, SevenSegment *display);
void init_seven_segment(SevenSegment *display, SDL_Renderer *renderer, int x, int y, int width, int height);
void render_seven_segment(SevenSegment *display, SDL_Renderer *renderer);
void destroy_seven_segment(SevenSegment *display);
#endif //RISCB_SEVENSEG_H

105
peripherals/switches.c Normal file
View File

@@ -0,0 +1,105 @@
//
// Created by bruno on 17.2.2025.
//
#include "switches.h"
void generate_switch_rects(Switches *switches) {
int switch_width = switches->rect->w / 4;
int switch_height = switches->rect->h / 4;
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
int idx = row * 4 + col;
// Base rect inside switches->rect
switches->rects[idx] = (SDL_Rect) {
col * switch_width + 2,
row * switch_height + 2,
switch_width - 2,
switch_height - 2
};
// Rect relative to outRect
switches->outRects[idx] = (SDL_Rect) {
switches->outRect->x + col * switch_width + 2,
switches->outRect->y + row * switch_height + 2,
switch_width - 2,
switch_height - 2
};
}
}
}
void render_switch_matrix(SDL_Renderer *renderer, Switches *switches) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
for (int i = 0; i < 16; i++) {
if (switches->value & (1 << i)) {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
} else {
// Render black for "off" switches
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
}
SDL_RenderFillRect(renderer, &switches->rects[i]);
}
}
void toggle_switch(SDL_Rect rect, Switches *switches) {
for (int i = 0; i < 16; i++) {
if (SDL_HasIntersection(&rect, &switches->outRects[i])) {
switches->value ^= (1 << i);
}
}
}
void update_switches_texture(SDL_Renderer *renderer, Switches *display) {
SDL_SetRenderTarget(renderer, display->texture);
render_switch_matrix(renderer, display); // Render the 4x4 switch matrix
SDL_SetRenderTarget(renderer, NULL);
}
void init_switches(Switches *switches, SDL_Renderer *renderer, int x, int y, int width, int height) {
switches->rect = malloc(sizeof(SDL_Rect));
switches->outRect = malloc(sizeof(SDL_Rect));
switches->value = 0;
switches->rect->x = 0;
switches->rect->y = 0;
switches->rect->w = width;
switches->rect->h = height;
switches->outRect->x = x;
switches->outRect->y = y;
switches->outRect->w = width;
switches->outRect->h = height;
switches->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
if (!switches->texture) {
fprintf(stderr, "Failed to create texture: %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
switches->value = (1 << 0) | (1 << 1);
generate_switch_rects(switches);
update_switches_texture(renderer, switches);
}
void render_switches_segment(Switches *display, SDL_Renderer *renderer) {
if (display->oldValue != display->value) {
display->oldValue = display->value;
update_switches_texture(renderer, display);
}
if (display->texture) {
SDL_RenderCopy(renderer, display->texture, NULL, display->outRect);
}
}
void destroy_switches_segment(Switches *switches) {
SDL_DestroyTexture(switches->texture);
free(switches->rect);
free(switches->outRect);
}

33
peripherals/switches.h Normal file
View File

@@ -0,0 +1,33 @@
//
// Created by bruno on 17.2.2025.
//
#ifndef RISCB_SWITCHES_H
#define RISCB_SWITCHES_H
#include <SDL_rect.h>
#include <SDL_render.h>
typedef struct {
uint16_t value;
uint16_t oldValue;
SDL_Rect *rect;
SDL_Rect *outRect;
SDL_Texture *texture;
SDL_Rect rects[16];
SDL_Rect outRects[16];
} Switches;
void render_switch_matrix(SDL_Renderer *renderer, Switches *switches);
void update_switches_texture(SDL_Renderer *renderer, Switches *display);
void toggle_switch(SDL_Rect rect, Switches *switches);
void init_switches(Switches *switches, SDL_Renderer *renderer, int x, int y, int width, int height);
void render_switches_segment(Switches *display, SDL_Renderer *renderer);
void destroy_switches_segment(Switches *switches);
#endif //RISCB_SWITCHES_H

View File

@@ -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, "PS "); strcat(valueStr, "PAU ");
} else { } else {
strcat(valueStr, "RUN "); strcat(valueStr, "RUN ");
} }

View File

@@ -371,12 +371,10 @@ void generate_string(TextEditor *editor) {
for (int i = 0; i < editor->maxLines; i++) { for (int i = 0; i < editor->maxLines; i++) {
if (editor->lines[i].active) { if (editor->lines[i].active) {
if (strlen(editor->lines[i].text)) {
strcat(editor->outputString, editor->lines[i].text); strcat(editor->outputString, editor->lines[i].text);
strcat(editor->outputString, "\n"); strcat(editor->outputString, "\n");
} }
} }
}
} }
void fill_editor_from_string(TextEditor *editor, const char *content, int lineStart, bool isComplete, void fill_editor_from_string(TextEditor *editor, const char *content, int lineStart, bool isComplete,