aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cpu.ts43
-rw-r--r--src/index.spec.ts7
-rw-r--r--src/index.ts3
-rw-r--r--src/instruction.spec.ts78
-rw-r--r--src/instruction.ts501
-rw-r--r--src/types.ts2
-rw-r--r--tslint.json2
7 files changed, 628 insertions, 8 deletions
diff --git a/src/cpu.ts b/src/cpu.ts
new file mode 100644
index 0000000..a6e0ab6
--- /dev/null
+++ b/src/cpu.ts
@@ -0,0 +1,43 @@
+import { u16, u8 } from './types';
+
+export interface ICPU {
+ readonly data: Uint8Array;
+ readonly dataView: DataView;
+ readonly progMem: Uint16Array;
+ readonly progBytes: Uint8Array;
+ pc: u16;
+ cycles: number;
+
+ readData(addr: u16): u8;
+ writeData(addr: u16, value: u8): void;
+}
+
+export type ICPUMemoryHook = (value: u8, oldValue: u8, addr: u16) => void;
+export interface ICPUMemoryHooks {
+ [key: number]: ICPUMemoryHook;
+}
+
+export class CPU implements ICPU {
+ readonly data = new Uint8Array(16384);
+ readonly data16 = new Uint16Array(this.data.buffer);
+ readonly dataView = new DataView(this.data.buffer);
+ readonly progBytes = new Uint8Array(this.progMem.buffer);
+ readonly writeHooks: ICPUMemoryHooks = [];
+
+ pc = 0;
+ cycles = 0;
+
+ constructor(public progMem: Uint16Array) {}
+
+ readData(addr: number) {
+ return this.data[addr];
+ }
+
+ writeData(addr: number, value: number) {
+ const hook = this.writeHooks[addr];
+ if (hook) {
+ hook(value, this.data[addr], addr);
+ }
+ this.data[addr] = value;
+ }
+}
diff --git a/src/index.spec.ts b/src/index.spec.ts
deleted file mode 100644
index 9f3fef3..0000000
--- a/src/index.spec.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { x } from './index';
-
-describe('index', () => {
- it('should export x = 1', () => {
- expect(x).toEqual(1);
- });
-});
diff --git a/src/index.ts b/src/index.ts
index ad1d380..038ff16 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1 +1,2 @@
-export const x = 1;
+export { CPU, ICPU, ICPUMemoryHook, ICPUMemoryHooks } from './cpu';
+export { avrInstruction } from './instruction';
diff --git a/src/instruction.spec.ts b/src/instruction.spec.ts
new file mode 100644
index 0000000..9ea4c0f
--- /dev/null
+++ b/src/instruction.spec.ts
@@ -0,0 +1,78 @@
+import { CPU, ICPU } from './cpu';
+import { avrInstruction } from './instruction';
+
+describe('avrInstruction', () => {
+ let cpu: CPU;
+
+ beforeEach(() => {
+ cpu = new CPU(new Uint16Array(0x8000));
+ });
+
+ function loadProgram(bytes: string) {
+ const progBuf = cpu.progBytes;
+ for (let i = 0; i < bytes.length; i += 2) {
+ progBuf[i / 2] = parseInt(bytes.substr(i, 2), 16);
+ }
+ }
+
+ it('should execute `JMP 0xb8` instruction', () => {
+ loadProgram('0c945c00');
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x5c);
+ expect(cpu.cycles).toEqual(3);
+ });
+
+ it('should execute `OUT 0x3f, r1` instruction', () => {
+ loadProgram('1fbe');
+ cpu.data[1] = 0x5a; // put the value 5a in r1
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x1);
+ expect(cpu.cycles).toEqual(1);
+ expect(cpu.data[0x5f]).toEqual(0x5a);
+ });
+
+ it('should execute `LDI r28, 0xff` instruction', () => {
+ loadProgram('cfef');
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x1);
+ expect(cpu.cycles).toEqual(1);
+ expect(cpu.data[28]).toEqual(0xff);
+ });
+
+ it('should execute `RJMP 2` instruction', () => {
+ loadProgram('01c0');
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(2);
+ expect(cpu.cycles).toEqual(2);
+ });
+
+ it('should execute `ST X+, r1` instruction', () => {
+ loadProgram('1d92');
+ cpu.data[1] = 0x5a; // put the value 5a in r1
+ cpu.data[26] = 0x9a; // X <- 0x19
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(1);
+ expect(cpu.cycles).toEqual(1);
+ expect(cpu.data[0x9a]).toEqual(0x5a);
+ expect(cpu.data[26]).toEqual(0x9b); // verify that X was incremented
+ });
+
+ it('should execute `CPI r26, 0x9` instruction', () => {
+ loadProgram('a930');
+ cpu.data[26] = 0x8;
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(1);
+ expect(cpu.cycles).toEqual(1);
+ expect(cpu.data[95]).toEqual(53); // SREG 00110101 - HSNC
+ });
+
+ it('should execute `CPC r27, r18` instruction', () => {
+ loadProgram('b207');
+ cpu.data[18] = 0x1;
+ cpu.data[27] = 0x1;
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(1);
+ expect(cpu.cycles).toEqual(1);
+ expect(cpu.data[95]).toEqual(0); // SREG 00000000
+ });
+});
diff --git a/src/instruction.ts b/src/instruction.ts
new file mode 100644
index 0000000..2b56def
--- /dev/null
+++ b/src/instruction.ts
@@ -0,0 +1,501 @@
+import { ICPU } from './cpu';
+
+export function avrInstruction(cpu: ICPU) {
+ const opcode = cpu.progMem[cpu.pc];
+
+ /* ADC */
+ if ((opcode & 0xfc00) === 0x1c00) {
+ /* not implemented */
+ }
+
+ /* ADD */
+ if ((opcode & 0xfc00) === 0xc00) {
+ /* not implemented */
+ }
+
+ /* ADIW */
+ if ((opcode & 0xff00) === 0x9600) {
+ /* not implemented */
+ }
+
+ /* AND */
+ if ((opcode & 0xfc00) === 0x2000) {
+ /* not implemented */
+ }
+
+ /* ANDI */
+ if ((opcode & 0xf000) === 0x7000) {
+ /* not implemented */
+ }
+
+ /* ASR */
+ if ((opcode & 0xfe0f) === 0x9405) {
+ /* not implemented */
+ }
+
+ /* BCLR */
+ if ((opcode & 0xff8f) === 0x9488) {
+ /* not implemented */
+ }
+
+ /* BLD */
+ if ((opcode & 0xfe08) === 0xf800) {
+ /* not implemented */
+ }
+
+ /* BRBC */
+ if ((opcode & 0xfc00) === 0xf400) {
+ /* not implemented */
+ }
+
+ /* BRBS */
+ if ((opcode & 0xfc00) === 0xf000) {
+ /* not implemented */
+ }
+
+ /* BSET */
+ if ((opcode & 0xff8f) === 0x9408) {
+ /* not implemented */
+ }
+
+ /* BST */
+ if ((opcode & 0xfe08) === 0xfa00) {
+ /* not implemented */
+ }
+
+ /* CALL */
+ if ((opcode & 0xfe0e) === 0x940e) {
+ /* not implemented */
+ }
+
+ /* CBI */
+ if ((opcode & 0xff00) === 0x9800) {
+ /* not implemented */
+ }
+
+ /* COM */
+ if ((opcode & 0xfe0f) === 0x9400) {
+ /* not implemented */
+ }
+
+ /* CP */
+ if ((opcode & 0xfc00) === 0x1400) {
+ /* not implemented */
+ }
+
+ /* CPC */
+ if ((opcode & 0xfc00) === 0x400) {
+ const arg1 = cpu.data[(opcode & 0x1f0) >> 4];
+ const arg2 = cpu.data[(opcode & 0xf) | ((opcode & 0x200) >> 5)];
+ let sreg = cpu.data[95];
+ const r = arg1 - arg2 - (sreg & 1);
+ sreg = (sreg & 0xfd) | (0 === r && (sreg >> 1) & 1 ? 2 : 0);
+ sreg = (sreg & 0xfb) | (128 & r ? 4 : 0);
+ sreg = (sreg & 0xf7) | ((arg1 ^ arg2) & (arg1 ^ r) & 128 ? 8 : 0);
+ sreg = (sreg & 0xef) | (((sreg >> 2) & 1) ^ ((sreg >> 3) & 1) ? 0x10 : 0);
+ sreg = (sreg & 0xfe) | (arg2 + (sreg & 1) > arg1 ? 1 : 0);
+ sreg = (sreg & 0xdf) | (1 & ((~arg1 & arg2) | (arg2 & r) | (r & ~arg1)) ? 0x20 : 0);
+ cpu.data[95] = sreg;
+ }
+
+ /* CPI */
+ if ((opcode & 0xf000) === 0x3000) {
+ const arg1 = cpu.data[((opcode & 0xf0) >> 4) + 16];
+ const arg2 = (opcode & 0xf) | ((opcode & 0xf00) >> 4);
+ const r = arg1 - arg2;
+ let sreg = cpu.data[95];
+ sreg = (sreg & 0xfd) | (0 === r ? 2 : 0);
+ sreg = (sreg & 0xfb) | (128 & r ? 4 : 0);
+ sreg = (sreg & 0xf7) | ((arg1 ^ arg2) & (arg1 ^ r) & 128 ? 8 : 0);
+ sreg = (sreg & 0xef) | (((sreg >> 2) & 1) ^ ((sreg >> 3) & 1) ? 0x10 : 0);
+ sreg = (sreg & 0xfe) | (arg2 > arg1 ? 1 : 0);
+ sreg = (sreg & 0xdf) | (1 & ((~arg1 & arg2) | (arg2 & r) | (r & ~arg1)) ? 0x20 : 0);
+ cpu.data[95] = sreg;
+ }
+
+ /* CPSE */
+ if ((opcode & 0xfc00) === 0x1000) {
+ /* not implemented */
+ }
+
+ /* DEC */
+ if ((opcode & 0xfe0f) === 0x940a) {
+ /* not implemented */
+ }
+
+ /* EOR */
+ if ((opcode & 0xfc00) === 0x2400) {
+ /* not implemented */
+ }
+
+ /* FMUL */
+ if ((opcode & 0xff88) === 0x308) {
+ /* not implemented */
+ }
+
+ /* FMULS */
+ if ((opcode & 0xff88) === 0x380) {
+ /* not implemented */
+ }
+
+ /* FMULSU */
+ if ((opcode & 0xff88) === 0x388) {
+ /* not implemented */
+ }
+
+ /* ICALL */
+ if (opcode === 0x9509) {
+ /* not implemented */
+ }
+
+ /* IJMP */
+ if (opcode === 0x9409) {
+ /* not implemented */
+ }
+
+ /* IN */
+ if ((opcode & 0xf800) === 0xb000) {
+ /* not implemented */
+ }
+
+ /* INC */
+ if ((opcode & 0xfe0f) === 0x9403) {
+ /* not implemented */
+ }
+
+ /* JMP */
+ if ((opcode & 0xfe0e) === 0x940c) {
+ cpu.pc = (cpu.progMem[cpu.pc + 1] | ((opcode & 1) << 16) | ((opcode & 0x1f0) << 13)) - 1;
+ cpu.cycles += 2;
+ }
+
+ /* LAC */
+ if ((opcode & 0xfe0f) === 0x9206) {
+ /* not implemented */
+ }
+
+ /* LAS */
+ if ((opcode & 0xfe0f) === 0x9205) {
+ /* not implemented */
+ }
+
+ /* LAT */
+ if ((opcode & 0xfe0f) === 0x9207) {
+ /* not implemented */
+ }
+
+ /* LDI */
+ if ((opcode & 0xf000) === 0xe000) {
+ cpu.data[((opcode & 0xf0) >> 4) + 16] = (opcode & 0xf) | ((opcode & 0xf00) >> 4);
+ }
+
+ /* LDS */
+ if ((opcode & 0xfe0f) === 0x9000) {
+ /* not implemented */
+ }
+
+ /* LDX */
+ if ((opcode & 0xfe0f) === 0x900c) {
+ /* not implemented */
+ }
+
+ /* LDX */
+ if ((opcode & 0xfe0f) === 0x900d) {
+ /* not implemented */
+ }
+
+ /* LDX */
+ if ((opcode & 0xfe0f) === 0x900e) {
+ /* not implemented */
+ }
+
+ /* LDY */
+ if ((opcode & 0xfe0f) === 0x8008) {
+ /* not implemented */
+ }
+
+ /* LDY */
+ if ((opcode & 0xfe0f) === 0x9009) {
+ /* not implemented */
+ }
+
+ /* LDY */
+ if ((opcode & 0xfe0f) === 0x900a) {
+ /* not implemented */
+ }
+
+ /* LDY */
+ if ((opcode & 0xd208) === 0x8008) {
+ /* not implemented */
+ }
+
+ /* LDZ */
+ if ((opcode & 0xfe0f) === 0x8000) {
+ /* not implemented */
+ }
+
+ /* LDZ */
+ if ((opcode & 0xfe0f) === 0x9001) {
+ /* not implemented */
+ }
+
+ /* LDZ */
+ if ((opcode & 0xfe0f) === 0x9002) {
+ /* not implemented */
+ }
+
+ /* LDZ */
+ if ((opcode & 0xd208) === 0x8000) {
+ /* not implemented */
+ }
+
+ /* LPM */
+ if (opcode === 0x95c8) {
+ /* not implemented */
+ }
+
+ /* LPM */
+ if ((opcode & 0xfe0f) === 0x9004) {
+ /* not implemented */
+ }
+
+ /* LPM */
+ if ((opcode & 0xfe0f) === 0x9005) {
+ /* not implemented */
+ }
+
+ /* LSR */
+ if ((opcode & 0xfe0f) === 0x9406) {
+ /* not implemented */
+ }
+
+ /* MOV */
+ if ((opcode & 0xfc00) === 0x2c00) {
+ /* not implemented */
+ }
+
+ /* MOVW */
+ if ((opcode & 0xff00) === 0x100) {
+ /* not implemented */
+ }
+
+ /* MUL */
+ if ((opcode & 0xfc00) === 0x9c00) {
+ /* not implemented */
+ }
+
+ /* MULS */
+ if ((opcode & 0xff00) === 0x200) {
+ /* not implemented */
+ }
+
+ /* MULSU */
+ if ((opcode & 0xff88) === 0x300) {
+ /* not implemented */
+ }
+
+ /* NEG */
+ if ((opcode & 0xfe0f) === 0x9401) {
+ /* not implemented */
+ }
+
+ /* NOP */
+ if (opcode === 0) {
+ /* NOP */
+ }
+
+ /* OR */
+ if ((opcode & 0xfc00) === 0x2800) {
+ /* not implemented */
+ }
+
+ /* SBR */
+ if ((opcode & 0xf000) === 0x6000) {
+ /* not implemented */
+ }
+
+ /* OUT */
+ if ((opcode & 0xf800) === 0xb800) {
+ cpu.writeData(((opcode & 0xf) | ((opcode & 0x600) >> 5)) + 32, cpu.data[(opcode & 0x1f0) >> 4]);
+ }
+
+ /* POP */
+ if ((opcode & 0xfe0f) === 0x900f) {
+ /* not implemented */
+ }
+
+ /* PUSH */
+ if ((opcode & 0xfe0f) === 0x920f) {
+ /* not implemented */
+ }
+
+ /* RCALL */
+ if ((opcode & 0xf000) === 0xd000) {
+ /* not implemented */
+ }
+
+ /* RET */
+ if (opcode === 0x9508) {
+ /* not implemented */
+ }
+
+ /* RETI */
+ if (opcode === 0x9518) {
+ /* not implemented */
+ }
+
+ /* RJMP */
+ if ((opcode & 0xf000) === 0xc000) {
+ cpu.pc = cpu.pc + ((opcode & 0x7ff) - (opcode & 0x800 ? 0x800 : 0));
+ cpu.cycles++;
+ }
+
+ /* ROR */
+ if ((opcode & 0xfe0f) === 0x9407) {
+ /* not implemented */
+ }
+
+ /* SBC */
+ if ((opcode & 0xfc00) === 0x800) {
+ /* not implemented */
+ }
+
+ /* SBCI */
+ if ((opcode & 0xf000) === 0x4000) {
+ /* not implemented */
+ }
+
+ /* SBI */
+ if ((opcode & 0xff00) === 0x9a00) {
+ /* not implemented */
+ }
+
+ /* SBIC */
+ if ((opcode & 0xff00) === 0x9900) {
+ /* not implemented */
+ }
+
+ /* SBIS */
+ if ((opcode & 0xff00) === 0x9b00) {
+ /* not implemented */
+ }
+
+ /* SBIW */
+ if ((opcode & 0xff00) === 0x9700) {
+ /* not implemented */
+ }
+
+ /* SBRC */
+ if ((opcode & 0xfe08) === 0xfc00) {
+ /* not implemented */
+ }
+
+ /* SBRS */
+ if ((opcode & 0xfe08) === 0xfe00) {
+ /* not implemented */
+ }
+
+ /* SLEEP */
+ if (opcode === 0x9588) {
+ /* not implemented */
+ }
+
+ /* SPM */
+ if (opcode === 0x95e8) {
+ /* not implemented */
+ }
+
+ /* SPM */
+ if (opcode === 0x95f8) {
+ /* not implemented */
+ }
+
+ /* STS */
+ if ((opcode & 0xfe0f) === 0x9200) {
+ /* not implemented */
+ }
+
+ /* STX */
+ if ((opcode & 0xfe0f) === 0x920c) {
+ /* not implemented */
+ }
+
+ /* STX */
+ if ((opcode & 0xfe0f) === 0x920d) {
+ cpu.writeData(cpu.dataView.getUint16(26, true), cpu.data[(opcode & 0x1f0) >> 4]);
+ cpu.dataView.setUint16(26, cpu.dataView.getUint16(26, true) + 1, true);
+ }
+
+ /* STX */
+ if ((opcode & 0xfe0f) === 0x920e) {
+ /* not implemented */
+ }
+
+ /* STY */
+ if ((opcode & 0xfe0f) === 0x8208) {
+ /* not implemented */
+ }
+
+ /* STY */
+ if ((opcode & 0xfe0f) === 0x9209) {
+ /* not implemented */
+ }
+
+ /* STY */
+ if ((opcode & 0xfe0f) === 0x920a) {
+ /* not implemented */
+ }
+
+ /* STY */
+ if ((opcode & 0xd208) === 0x8208) {
+ /* not implemented */
+ }
+
+ /* STZ */
+ if ((opcode & 0xfe0f) === 0x8200) {
+ /* not implemented */
+ }
+
+ /* STZ */
+ if ((opcode & 0xfe0f) === 0x9201) {
+ /* not implemented */
+ }
+
+ /* STZ */
+ if ((opcode & 0xfe0f) === 0x9202) {
+ /* not implemented */
+ }
+
+ /* STZ */
+ if ((opcode & 0xd208) === 0x8200) {
+ /* not implemented */
+ }
+
+ /* SUB */
+ if ((opcode & 0xfc00) === 0x1800) {
+ /* not implemented */
+ }
+
+ /* SUBI */
+ if ((opcode & 0xf000) === 0x5000) {
+ /* not implemented */
+ }
+
+ /* SWAP */
+ if ((opcode & 0xfe0f) === 0x9402) {
+ /* not implemented */
+ }
+
+ /* WDR */
+ if (opcode === 0x95a8) {
+ /* not implemented */
+ }
+
+ /* XCH */
+ if ((opcode & 0xfe0f) === 0x9204) {
+ /* not implemented */
+ }
+
+ cpu.pc = (cpu.pc + 1) % cpu.progMem.length;
+ cpu.cycles++;
+}
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..8a13fe6
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,2 @@
+export type u8 = number;
+export type u16 = number;
diff --git a/tslint.json b/tslint.json
index d28ddd5..50ab37a 100644
--- a/tslint.json
+++ b/tslint.json
@@ -5,6 +5,8 @@
"no-console": false,
"object-literal-sort-keys": false,
"ordered-imports": false,
+ "no-bitwise": false,
+ "member-access": false,
"quotemark": [true, "single"],
"trailing-comma": false
}