Skip to content

Commit

Permalink
Added control unit events
Browse files Browse the repository at this point in the history
Now the events log shows events triggered by the control unit.
  • Loading branch information
parraman committed Dec 8, 2017
1 parent e554114 commit 38b695b
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 17 deletions.
172 changes: 159 additions & 13 deletions src/app/cpu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { IORegMapService } from './ioregmap.service';
import { ClockService} from './clock.service';
import { Exception, ExceptionType } from './exceptions';

import { Utils } from './utils';
import { EventsLogService, SystemEvent } from './events-log.service';

import {
CPURegisterIndex, CPURegister, CPUStatusRegister, CPURegisterOperation,
CPUGeneralPurposeRegister, CPUStackPointerRegister
Expand All @@ -23,11 +26,12 @@ const EXCEPTION_VECTOR_ADDRESS = 0x0009;

export enum ControlUnitOperationType {

FETCH_OPCODE = 0,
DECODE = 1,
FETCH_OPERANDS = 2,
RESOLVE_REGADDRESS = 3,
EXECUTE = 4
RESET = 0,
FETCH_OPCODE = 1,
DECODE = 2,
FETCH_OPERANDS = 3,
RESOLVE_REGADDRESS = 4,
EXECUTE = 5

}

Expand All @@ -39,16 +43,16 @@ export interface CUOperationParamsFetchOpCode {

export interface CUOperationParamsDecode {

opCode: number;
opcode: number;

}

export interface CUOperationParamsFetchOperands {

opcode: number;
address: number;
operand1Type: OperandType;
operand1Address: number;
operand2Type: OperandType;
operand2Address: number;

}

Expand All @@ -61,19 +65,102 @@ export interface CUOperationParamsResolveRegAddress {

export interface CUOperationParamsExecute {

opCode: number;
mnemonic: string;
opcode: number;
operand1Type: OperandType;
operand1Value: number;
operand2Type: OperandType;
operand2Value: number;

}

type ControlUnitOperationParams = CUOperationParamsFetchOpCode | CUOperationParamsDecode |
CUOperationParamsFetchOperands | CUOperationParamsResolveRegAddress |
CUOperationParamsExecute;

export class ControlUnitOperation implements SystemEvent {

public operationType: ControlUnitOperationType;
public data: ControlUnitOperationParams;

constructor(operationType: ControlUnitOperationType, data?: ControlUnitOperationParams) {

this.operationType = operationType;
this.data = data;

export class ControlUnitOperation {
}

toString(): string {

let ret, params;

switch (this.operationType) {
case ControlUnitOperationType.RESET:
ret = `CU: Reset control unit`;
break;
case ControlUnitOperationType.FETCH_OPCODE:
params = <CUOperationParamsFetchOpCode>this.data;
ret = `CU: Fetch opcode from [0x${Utils.pad(params.address, 16, 4)}]`;
break;
case ControlUnitOperationType.DECODE:
params = <CUOperationParamsDecode>this.data;
ret = `CU: Decode opcode 0x${Utils.pad(params.opcode, 16, 2)}`;
break;
case ControlUnitOperationType.FETCH_OPERANDS:
params = <CUOperationParamsFetchOperands>this.data;
if (params.operand1Type === undefined && params.operand2Type === undefined) {
ret = `CU: Instruction {0x${Utils.pad(params.opcode, 16, 2)}: ${OpCode[params.opcode]}} has no operands`;
} else if (params.operand2Type === undefined) {
ret = `CU: Fetch operand ${OperandType[params.operand1Type]} from address [0x${Utils.pad(params.address, 16, 4)}]`
} else {
ret = `CU: Fetch operands (${OperandType[params.operand1Type]}, ${OperandType[params.operand2Type]}) from address [0x${Utils.pad(params.address, 16, 4)}]`
}
break;
case ControlUnitOperationType.RESOLVE_REGADDRESS:
params = <CUOperationParamsResolveRegAddress>this.data;
ret = `CU: Resolve indirect address [${CPURegisterIndex[params.register]}`;
ret += (params.offset >= 0) ? `+${params.offset}]` : `${params.offset}]`;
break;
case ControlUnitOperationType.EXECUTE:
params = <CUOperationParamsExecute>this.data;
ret = `CU: Execute {0x${Utils.pad(params.opcode, 16, 2)}: ${OpCode[params.opcode]}}`

if (params.operand1Type !== undefined) {

if (params.operand1Type === OperandType.BYTE) {
ret += ` (0x${Utils.pad(params.operand1Value, 16, 2)}`;
} else if (params.operand1Type === OperandType.REGISTER_8BITS ||
params.operand1Type === OperandType.REGISTER_16BITS) {
ret += ` (0x${Utils.pad(params.operand1Value, 16, 2)}: ${CPURegisterIndex[params.operand1Value]}`;
} else {
ret += ` (0x${Utils.pad(params.operand1Value, 16, 4)}`;
}

} else {
break;
}

if (params.operand2Type !== undefined) {

if (params.operand2Type === OperandType.BYTE) {
ret += `, 0x${Utils.pad(params.operand2Value, 16, 2)})`;
} else if (params.operand2Type === OperandType.REGISTER_8BITS ||
params.operand2Type === OperandType.REGISTER_16BITS) {
ret += `, 0x${Utils.pad(params.operand2Value, 16, 2)}: ${CPURegisterIndex[params.operand2Value]})`;
} else {
ret += `, 0x${Utils.pad(params.operand2Value, 16, 4)})`;
}

} else {
ret += `)`;
}
break;
default:
break;
}

return ret;

}

}

Expand All @@ -89,6 +176,9 @@ export class CPUService {
protected aluOperationSource = new Subject<ALUOperation>();
public aluOperation$: Observable<ALUOperation>;

protected controlUnitOperationSource = new Subject<ControlUnitOperation>();
public controlUnitOperation$: Observable<ControlUnitOperation>;

protected nextIP = 0;

protected userSP: CPURegister;
Expand Down Expand Up @@ -150,7 +240,8 @@ export class CPUService {

constructor(private memoryService: MemoryService,
private clockService: ClockService,
private ioRegMapService: IORegMapService) {
private ioRegMapService: IORegMapService,
private eventsLogService: EventsLogService) {

const registerA = new CPUGeneralPurposeRegister('A', CPURegisterIndex.A,
CPURegisterIndex.AH, CPURegisterIndex.AL, 0,
Expand Down Expand Up @@ -199,6 +290,7 @@ export class CPUService {

this.cpuRegisterOperation$ = this.cpuRegisterOperationSource.asObservable();
this.aluOperation$ = this.aluOperationSource.asObservable();
this.controlUnitOperation$ = this.controlUnitOperationSource.asObservable();

this.alu = new ArithmeticLogicUnit(statusRegister, (op) => this.publishALUOperation(op));

Expand All @@ -216,6 +308,13 @@ export class CPUService {

}

protected publishControlUnitOperation(operation: ControlUnitOperation) {

this.controlUnitOperationSource.next(operation);
this.eventsLogService.log(operation);

}

public getRegistersBank(): Map<CPURegisterIndex, CPURegister> {

return this.registersBank;
Expand Down Expand Up @@ -320,6 +419,8 @@ export class CPUService {

public reset(): void {

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.RESET));

this.registersBank.get(CPURegisterIndex.A).value = this.registersBank.get(CPURegisterIndex.A).resetValue;
this.registersBank.get(CPURegisterIndex.B).value = this.registersBank.get(CPURegisterIndex.B).resetValue;
this.registersBank.get(CPURegisterIndex.C).value = this.registersBank.get(CPURegisterIndex.C).resetValue;
Expand All @@ -337,7 +438,13 @@ export class CPUService {

private fetchAndDecode(args: Array<number>): InstructionSpec {

let opcode;
let opcode, parameters;

parameters = <CUOperationParamsFetchOpCode> {
address: this.nextIP
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.FETCH_OPCODE, parameters));

try {
opcode = this.memoryService.loadByte(this.nextIP);
Expand All @@ -347,13 +454,28 @@ export class CPUService {
}
this.nextIP += 1;

parameters = <CUOperationParamsDecode> {
opcode: opcode
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.DECODE, parameters));

const instruction = instructionSet.getInstructionFromOpCode(opcode);

if (instruction === undefined) {
throw new Exception(ExceptionType.UNKNOWN_OPCODE,
`Invalid opcode: ${opcode}`, this.IP.value, this.SP.value);
}

parameters = <CUOperationParamsFetchOperands> {
opcode: instruction.opcode,
address: this.nextIP,
operand1Type: instruction.operand1,
operand2Type: instruction.operand2
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.FETCH_OPERANDS, parameters));

let byte, word, register, regaddress, offset, address;

switch (instruction.operand1) {
Expand Down Expand Up @@ -404,6 +526,13 @@ export class CPUService {
offset = offset - 256;
}

parameters = <CUOperationParamsResolveRegAddress> {
register: register,
offset: offset
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.RESOLVE_REGADDRESS, parameters));

if (CPUService.is16bitsGPRorSP(register) === false) {
throw new Exception(ExceptionType.ILLEGAL_INSTRUCTION,
`Invalid first operand: register index ${register} out of bounds`,
Expand Down Expand Up @@ -466,6 +595,13 @@ export class CPUService {
offset = offset - 256;
}

parameters = <CUOperationParamsResolveRegAddress> {
register: register,
offset: offset
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.RESOLVE_REGADDRESS, parameters));

if (CPUService.is16bitsGPRorSP(register) === false) {
throw new Exception(ExceptionType.ILLEGAL_INSTRUCTION,
`Invalid first operand: register index ${register} out of bounds`,
Expand Down Expand Up @@ -503,6 +639,16 @@ export class CPUService {
const args: Array<number> = [];
const instruction = this.fetchAndDecode(args);

const parameters: CUOperationParamsExecute = {
opcode: instruction.opcode,
operand1Type: instruction.operand1,
operand1Value: args[0],
operand2Type: instruction.operand2,
operand2Value: args[1]
};

this.publishControlUnitOperation(new ControlUnitOperation(ControlUnitOperationType.EXECUTE, parameters));

if (this[instruction.methodName].apply(this, args) === true) {
this.IP.value = this.nextIP;
}
Expand Down
6 changes: 3 additions & 3 deletions src/app/events-log-viewer/events-log-viewer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ <h5 style="display: inline-block">Events log</h5>
<a *ngIf="enableLogging === true" (click)="enableLogging = false">Disable</a>
</div>
</div>
<div class="list-group events-log-list" appPreventScroll>
<div *ngFor="let i of logLines" class="list-group-item source-code event" [ngClass]="i.style">
<div #logLinesContainer class="list-group events-log-list" appPreventScroll>
<div #logLinesItems *ngFor="let i of logLines" class="list-group-item source-code event" [ngClass]="i.style">
<span>{{i.text}}</span>
</div>
</div>
Expand All @@ -18,7 +18,7 @@ <h5 style="display: inline-block">Events log</h5>
<a *ngIf="enableCPURegisters === true" (click)="enableCPURegisters = false" [attr.disabled]="isRunning === true || enableLogging === false ? '' : null">Disable</a>
<span style="margin-left:10px;" class="event-enabler alu-event" [ngClass]="{'enabled': enableALU === true, 'muted': isRunning === true || enableLogging === false}">Arithmetic Logic Unit: </span>
<a *ngIf="enableALU === false" (click)="enableALU = true" [attr.disabled]="isRunning === true || enableLogging === false ? '' : null">Enable</a>
<a *ngIf="enableALU === true" (click)="enableALU = false" [attr.disabled]="isRunning === true || enableLogging === false ? '' : null">Enable</a>
<a *ngIf="enableALU === true" (click)="enableALU = false" [attr.disabled]="isRunning === true || enableLogging === false ? '' : null">Disable</a>
</div>
<div class="small"><span class="event-enabler memory-operation-event" [ngClass]="{'enabled': enableMemory === true, 'muted': isRunning === true || enableLogging === false}">Memory:</span>
<a *ngIf="enableMemory === false" (click)="enableMemory = true" [attr.disabled]="isRunning === true || enableLogging === false ? '' : null">Enable</a>
Expand Down
25 changes: 24 additions & 1 deletion src/app/events-log-viewer/events-log-viewer.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { AfterViewInit, Component, Input } from '@angular/core';
import {
AfterViewInit, Component, Input,
ViewChild, ViewChildren, ElementRef, QueryList
} from '@angular/core';
import { EventsLogService, LoggedEvent } from '../events-log.service';
import { MemoryOperation, MemoryOperationType } from '../memory.service';
import { ControlUnitOperation } from '../cpu.service';

class LogLine {

Expand All @@ -23,6 +27,8 @@ class LogLine {
export class EventsLogViewerComponent implements AfterViewInit {

@Input() isRunning: boolean;
@ViewChild('logLinesContainer') logLinesContainer: ElementRef;
@ViewChildren('logLinesItems') logLinesItems: QueryList<any>;

public enableLogging = false;

Expand All @@ -42,10 +48,21 @@ export class EventsLogViewerComponent implements AfterViewInit {

ngAfterViewInit() {

this.logLinesItems.changes.subscribe(() => this.scrollToBottom());

this.eventsLogService.eventsLog$.subscribe((loggedEvent) => this.processLoggedEvent(loggedEvent));

}

private scrollToBottom() {

const element = this.logLinesContainer.nativeElement;
try {
element.scrollTop = element.scrollHeight;
} catch (error) {}

}

private processLoggedEvent(loggedEvent: LoggedEvent) {

if (this.isRunning === true || this.enableLogging === false) {
Expand All @@ -64,6 +81,12 @@ export class EventsLogViewerComponent implements AfterViewInit {

}

} else if (loggedEvent.systemEvent instanceof ControlUnitOperation && this.enableControlUnit === true) {

const newLine = new LogLine(`${loggedEvent.time}: ${loggedEvent.systemEvent.toString()}`,
`control-unit-event`);
this.logLines.push(newLine);

}

}
Expand Down

0 comments on commit 38b695b

Please sign in to comment.