Basic C interpreter environment - retrotruestory/M1DEV GitHub Wiki
Here's how to implement a basic C interpreter environment for Magic-1:
.cseg
.global _init_interpreter
.global _execute_instruction
.global _push_stack
.global _pop_stack
// Initialize interpreter environment
_init_interpreter:
enter 8
push a
push b
// Setup interpreter stack
ld.16 b,#INTERP_STACK_TOP
ld.16 a,#INTERP_STACK_SIZE
st.16 _interp_sp,b
// Initialize symbol table
call _init_symtab
pop b
pop a
leave
ret
// Execute single instruction
_execute_instruction:
enter 4
push b
ld.16 b,6(sp) ; Load instruction pointer
ld.8 a,(b) ; Get opcode
// Decode and execute instruction
cmp.8 a,#OP_PUSH
br.eq .do_push
cmp.8 a,#OP_ADD
br.eq .do_add
// ... other opcodes ...
pop b
leave
ret
Create the interpreter interface:
#ifndef INTERPRETER_H
#define INTERPRETER_H
#define INTERP_STACK_SIZE 1024
#define INTERP_STACK_TOP 0x18000
#define MAX_SYMBOLS 256
// Instruction opcodes
#define OP_PUSH 0x01
#define OP_POP 0x02
#define OP_ADD 0x03
#define OP_SUB 0x04
#define OP_MUL 0x05
#define OP_DIV 0x06
#define OP_CALL 0x07
#define OP_RET 0x08
typedef struct {
uint16_t sp; // Stack pointer
uint16_t bp; // Base pointer
uint16_t pc; // Program counter
uint8_t *code; // Code segment
uint16_t code_size; // Code size
} interp_context;
// Function declarations
extern void init_interpreter(void);
extern int execute_instruction(interp_context *ctx);
extern int run_program(uint8_t *code, uint16_t size);
#endif
Implement the core interpreter:
#include "interpreter.h"
static uint16_t interp_sp;
static uint16_t interp_stack[INTERP_STACK_SIZE];
// Simple expression evaluator
int evaluate_expression(interp_context *ctx) {
uint8_t opcode = ctx->code[ctx->pc];
int val1, val2, result;
switch(opcode) {
case OP_PUSH:
ctx->pc++;
val1 = ctx->code[ctx->pc];
push_stack(val1);
break;
case OP_ADD:
val2 = pop_stack();
val1 = pop_stack();
result = val1 + val2;
push_stack(result);
break;
case OP_MUL:
val2 = pop_stack();
val1 = pop_stack();
result = val1 * val2;
push_stack(result);
break;
// Add other operations
}
ctx->pc++;
return 0;
}
// Main interpreter loop
int run_program(uint8_t *code, uint16_t size) {
interp_context ctx;
// Initialize context
ctx.pc = 0;
ctx.code = code;
ctx.code_size = size;
ctx.sp = interp_sp;
ctx.bp = interp_sp;
// Main interpretation loop
while(ctx.pc < ctx.code_size) {
if(evaluate_expression(&ctx) < 0) {
return -1; // Error
}
}
return pop_stack(); // Return final result
}
Example usage:
#include <stdio.h>
#include "interpreter.h"
int main() {
// Simple program: 5 + (3 * 2)
uint8_t program[] = {
OP_PUSH, 5, // Push 5
OP_PUSH, 3, // Push 3
OP_PUSH, 2, // Push 2
OP_MUL, // Multiply
OP_ADD, // Add
};
// Initialize interpreter
init_interpreter();
// Run program
int result = run_program(program, sizeof(program));
printf("Result: %d\n", result); // Should print 11
return 0;
}
Update Makefile:
CC = clcc
AS = m1_as
CFLAGS = -O2
OBJECTS = interp_core.o interpreter.o test_interp.o
test_interp: $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $(OBJECTS)
%.o: %.s
$(AS) -o $@ $<
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f test_interp *.o
Key features:
- Simple stack-based interpreter
- Support for basic arithmetic operations
- Expression evaluation
- Error handling
- Memory safety through VM system
This implementation provides:
- Basic arithmetic operations
- Stack management
- Program flow control
- Error checking
- Integration with Magic-1's memory system
You can extend it by adding:
- More complex operations
- Function calls
- Variables and symbols
- Conditional execution
- Loops and control structures