208 lines
6.4 KiB
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;
|
|
}
|
|
}
|