Casio_asm/interpreter/proc.c

208 lines
6.4 KiB
C

#include "proc.h"
#include "../common/event.h"
#if defined(DEBUG)
#include <stdio.h>
#include "../common/opcodeInfo.h"
#endif
//internal functions
void tick(proc_t *proc);
int readOpcode(proc_t *proc, opcode_t *op, opcode_ext_t *opE);
void handleEvent(proc_t *proc, event_t *evt);
void Proc_execute(proc_t *proc, int maxCycles) {
proc->status=PROC_STATUS_running;
int i=0;
do {
//do a single tick
tick(proc);
i++;
//loop until we either run out of cycles or we somehow kill ourselves
if(i==maxCycles&&maxCycles!=-1) return;
if(proc->status&PROC_STATUS_aborted) return;
} while(Platform_running());
}
void Proc_interrupt(proc_t *proc, integral_t code, integral_t data) {
#if defined(DEBUG)
printf("Interrupted with code %.8x\n", code);
printf("PC was %.8x\n", proc->registers[0].i);
#endif
//set the appropriate bits
proc->status|=PROC_STATUS_running|PROC_STATUS_interrupt|PROC_STATUS_interrupted;
//push the PC to the stack, followed by the data and interrupt code
Stack_push(&proc->stack, proc->registers[0]);
Stack_pushInt(&proc->stack, data);
Stack_pushInt(&proc->stack, code);
//jump to interrupt handler
proc->registers[0].i=proc->intHandler;
//check if code can run from there
if((Mmu_checkAccess(&proc->mmu, proc->intHandler)&(MMU_ACCESS_r|MMU_ACCESS_r))!=(MMU_ACCESS_r|MMU_ACCESS_r)) {
//abort the processor, the interrupt handler is wrong!
proc->status|=PROC_STATUS_aborted;
}
}
void interruptIf(proc_t *proc, integral_t code, integral_t data, integral_t type) {
if(proc->subscribed&type) Proc_interrupt(proc, code|INTERRUPT_hw_mask, data);
}
void initProc(proc_t *proc) {
#if defined(DEBUG)
Opcode_registerOpcodes();
#endif
proc->status=(integral_t) 0;
proc->subscribed=(integral_t) 0;
proc->intHandler=(integral_t) -1;
proc->lastAddress=(integral_t) -1;
initMMU(&proc->mmu);
initStack(&proc->stack);
for(int i=0; i<256; i++) {
proc->registers[i].i=(integral_t) 0;
}
proc->opaque=0;
}
//do a single cycle
void tick(proc_t *proc) {
opcode_t op;
opcode_ext_t opE;
opcode_data_t instruction;
//handle interrupts
if(proc->status&PROC_STATUS_interrupt) {
if(!(proc->status&PROC_STATUS_interrupted)) {
//if we have the interrupt bit but the interrupted bit is cleared
//clear the interrupt bit and go back where we were
Stack_pop(&proc->stack, &proc->registers[0]);
proc->status^=PROC_STATUS_interrupt;
}
}
#if defined(DEBUG)&&defined(PC)
//print the top of the stack
if(proc->stack.top) printf("Stack top (%d): %d/%f\n", proc->stack.top, proc->stack.elements[proc->stack.top-1], proc->stack.elements[proc->stack.top-1]);
else printf("Stack is empty\n");
#endif
//read and decode instruction
if(readOpcode(proc, &op, &opE)) {
if(op.op==OP_extend) {
Proc_decodeInstructionExt(proc, &opE, &instruction);
} else {
Proc_decodeInstruction(proc, &op, &instruction);
}
#if defined(DEBUG)&&defined(PC)
#define getInst(field) (instruction.ext?opE.field:op.field)
//find mnemonic
opcode_info_t info;
Opcode_getOpByCode(instruction.op, instruction.ext, &info);
printf("Instruction at %.8x is %s (%d, %s) [%s]\n", proc->registers[0].i, info.name, instruction.op, instruction.ext?"extended":"normal", info.desc);
//print number of explicit arguments
if(instruction.nArgs==0) printf("Arguments taken from stack\n");
else if(instruction.nArgs==1) printf("One argument from register %.2x\n", getInst(arg0));
else if(instruction.nArgs==2) printf("Two arguments from registers %.2x and %.2x\n", getInst(arg0), getInst(arg1));
else if(instruction.nArgs==3) printf("Immediate argument %.4x\n", getInst(imm)&0xffff);
//print runtime arguments
if(instruction.argc==0) printf("No runtime arguments\n");
else if(instruction.argc==1) printf("One runtime argument: %d/%f\n", instruction.arg0.i, instruction.arg0.d);
else if(instruction.argc==2) printf("Two runtime arguments: %d/%f and %d/%f\n", instruction.arg0.i, instruction.arg0.d, instruction.arg1.i, instruction.arg1.d);
#endif
//execute instruction
Proc_executeInstruction(proc, &instruction);
}
#if defined(DEBUG)&&defined(PC)
else printf("Couldn't decode instruction\n");
#endif
//tick and get event
event_t evt;
if(proc->status&PROC_STATUS_running) {
//do a platform tick and read a single event
Platform_tick(0);
if(Event_get(&evt)) handleEvent(proc, &evt);
} else {
//wait until we unlock ourselves
//read all events until then
while(!((proc->status&PROC_STATUS_running)||(proc->status&PROC_STATUS_aborted))) {
if(Event_wait(&evt)) handleEvent(proc, &evt);
//if the platform told us to quit, then kill ourselves
if(!Platform_running()) return;
}
}
}
//read an instruction
int readOpcode(proc_t *proc, opcode_t *op, opcode_ext_t *ext) {
int pc=proc->registers[0].i;
mmu_t *mmu=&proc->mmu;
int access=Mmu_checkAccess(mmu, pc);
int len=1;
if((access&MMU_ACCESS_r)&&(access&MMU_ACCESS_x)) {
//we can read and execute the first byte, this is good
//as we only need execute permission for the first byte
Mmu_readByte(mmu, &op->bytes[0], pc);
if(op->op==OP_extend) {
//we have an extended instruction, read it that way
ext->bytes[0]=op->bytes[0];
Mmu_readByte(mmu, &ext->bytes[1], pc+1);
if(ext->nArgs) {
Mmu_readByte(mmu, &ext->bytes[2], pc+2);
if(ext->nArgs==1) {
len=3;
} else {
Mmu_readByte(mmu, &ext->bytes[3], pc+3);
len=4;
}
} else {
len=2;
}
} else {
//we have a standard instruction
if(op->nArgs) {
Mmu_readByte(mmu, &op->bytes[1], pc+1);
if(op->nArgs==1) {
len=2;
} else {
Mmu_readByte(mmu, &op->bytes[2], pc+2);
len=3;
}
}
}
//increment PC
proc->registers[0].i=pc+len;
return 1;
} else {
#if defined(DEBUG)
printf("Access is not good: %o at %.8x\n", access&0xff, pc);
#endif
//not good, interrupt processor!
Proc_interrupt(proc, INTERRUPT_illegal_jump, proc->registers[0].i);
return 0;
}
}
void handleEvent(proc_t *proc, event_t *evt) {
switch(evt->type) {
case EVENT_TYPE_KEYBOARD:
switch(evt->subtype) {
case EVENT_TYPE_KEYBOARD_PRESS:
interruptIf(proc, evt->keyevent.repeat?INTERRUPT_hw_keyrepeat:INTERRUPT_hw_keydown, evt->keyevent.code, PROC_SUBSCRIPTION_keyboard);
return;
case EVENT_TYPE_KEYBOARD_RELEASE:
if(evt->keyevent.repeat) return;
interruptIf(proc, INTERRUPT_hw_keyup, evt->keyevent.code, PROC_SUBSCRIPTION_keyboard);
return;
}
return;
case EVENT_TYPE_TIMER:
interruptIf(proc, INTERRUPT_hw_timer, evt->timerevent.code, PROC_SUBSCRIPTION_timer);
return;
}
}