#include "proc.h" #include "../common/event.h" #if defined(DEBUG) #include #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; } }