Skip to content

Commit

Permalink
Merge pull request #4 from kostDev/dev
Browse files Browse the repository at this point in the history
impl: 0x29, 0x09, 0x49, 0x85, 0x86, 0x84, 0x90
  • Loading branch information
kostDev authored Aug 15, 2024
2 parents 1e50425 + 273e5cf commit cefef97
Show file tree
Hide file tree
Showing 7 changed files with 426 additions and 51 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@

A minimalist and educational 6502 CPU emulator written in JavaScript. This project is designed for learning and experimentation, focusing on the core functionality of the 6502 processor.

Features
#### Features

• Basic Instruction Set: Implements a subset of the 6502 instruction set, allowing for the execution of simple programs.
• 16-bit Addressing: Supports 16-bit memory addressing with 8-bit data registers.
• Memory Management: Includes basic read/write operations for memory handling.
• Customizable: Designed with simplicity in mind, making it easy to extend and modify.
• Testing: Covered with tests 11/151 OPCODES, which are official and documented guidelines. (in progress)

Installation
#### Installation

Clone the repository and navigate to the project directory:

```bash
git clone https://github.com/kostDev/6502-emulator-UA.git
cd 6502-emulator-UA
```
Usage

#### Usage

To start using the emulator, simply include the js folder files in your project and create an instance of the CPU class, as example:

Expand All @@ -36,14 +37,14 @@ const cpu = new CPU(new Memory(0x10000), params); // 64kb -> 0x10000
cpu.run();
```

Roadmap
#### Roadmap

• Implement full 6502 instruction set. Current 11/151 (256)
• Implement full 6502 instruction set. Current 22/151 (256)
• Add debugging tools.
• Create example programs for testing and learning.
• Develop a simple UI for interactive use.

License
#### License

This project is licensed under the MIT License - see the LICENSE file for details.

Expand Down
28 changes: 0 additions & 28 deletions actions/testing.yml

This file was deleted.

4 changes: 2 additions & 2 deletions js/cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class CPU {
this.running = running;

this.memory = memory;
// this.cycles = 0;
this.cycles = 0;
this._registers = {
PC: 0x0600, //(16bit) Program Counter, 0x0600 start address
SP: 0xFF, // (8bit) Stack Pointer
Expand Down Expand Up @@ -165,7 +165,7 @@ class CPU {
decode(opcode) {
const instruction = OPCODES[opcode];
if (instruction) {
// this.cycles += instruction.t; // for future game console emulation processes
this.cycles += instruction.t; // for future console emulation processes
instruction.run && instruction.run(this);
} else throw new Error(`Unknown opcode: 0x${opcode.toString(16).toUpperCase()}`);
}
Expand Down
2 changes: 1 addition & 1 deletion js/memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Memory {

// write in memory 16bit value (2byte)
writeWord(address, value) {
// devide 16bit value on 2parts in two memory 8bit cells as result store 16bit value in two 8bit memory addresses
// divide 16bit value on 2parts in two memory 8bit cells as result store 16bit value in two 8bit memory addresses
this.memory[address] = value & 0xFF; // low byte
this.memory[address+1] = (value >> 8) & 0xFF; // high byte
}
Expand Down
129 changes: 118 additions & 11 deletions js/opcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ const OPCODES = {
0x06: "", // asl("d")
0x07: "", // slo("d") // illegal
0x08: "", // php_implied()
0x09: "", // ora("#i")
0x09: {
name: "ORA #immediate", // AND Memory with Accumulator
t: 2,
code: 0x09,
run: (cpu) => {
cpu.ACC |= cpu.fetch();
cpu.negativeFlag = cpu.ACC;
cpu.zeroFlag = cpu.ACC;
},
},
0x0A: "", // asl_implied()
0x0B: "", // anc("#i") // illegal
0x0C: "", // nop("a") // illegal
Expand Down Expand Up @@ -56,7 +65,16 @@ const OPCODES = {
0x26: "", // rol("d")
0x27: "", // rla("d") // illegal
0x28: "", // plp_implied()
0x29: "", // and("#i")
0x29: {
name: "AND #immediate", // AND Memory with Accumulator
t: 2,
code: 0x29,
run: (cpu) => {
cpu.ACC &= cpu.fetch();
cpu.negativeFlag = cpu.ACC;
cpu.zeroFlag = cpu.ACC;
},
},
0x2A: "", // rol_implied()
0x2B: "", // anc("#i") // illegal
0x2C: "", // bit("a")
Expand Down Expand Up @@ -88,7 +106,16 @@ const OPCODES = {
0x46: "", // lsr("d")
0x47: "", // sre("d") // illegal
0x48: "", // pha_implied()
0x49: "", // eor("#i")
0x49: {
name: "EOR #immediate", // AND Memory with Accumulator
t: 2,
code: 0x49,
run: (cpu) => {
cpu.ACC ^= cpu.fetch();
cpu.negativeFlag = cpu.ACC;
cpu.zeroFlag = cpu.ACC;
},
},
0x4A: "", // lsr_implied()
0x4B: "", // alr("#i") // illegal
0x4C: "", // jmp("a")
Expand Down Expand Up @@ -167,11 +194,44 @@ const OPCODES = {
0x81: "", // sta("(d,x)")
0x82: "", // nop("#i") // illegal
0x83: "", // sax("(d,x)") // illegal
0x84: "", // sty("d")
0x85: "", // sta("d")
0x86: "", // stx("d")
0x84: {
name: "STY zeropage", // Sore Index Y in Memory
t: 3,
code: 0x84,
run: (cpu) => {
const address = cpu.fetch();
cpu.memory.writeByte(address, cpu.Y);
}
},
0x85: {
name: "STA zeropage", // Store Accumulator in Memory
t: 3,
code: 0x85,
run: (cpu) => {
const address = cpu.fetch();
cpu.memory.writeByte(address, cpu.ACC);
}
},
0x86: {
name: "STX zeropage", // Store Index X in Memory
t: 3,
code: 0x86,
run: (cpu) => {
const address = cpu.fetch();
cpu.memory.writeByte(address, cpu.X);
}
},
0x87: "", // sax("d") // illegal
0x88: "", // dey_implied()
0x88: {
name: "DEX", // Decrement Index X by One
t: 2,
code: 0x88,
run: (cpu) => {
cpu.Y -= 1;
cpu.negativeFlag = cpu.Y;
cpu.zeroFlag = cpu.Y;
}
},
0x89: "", // nop("#i") // illegal
0x8A: {
name: "TXA", // Transfer Index X to Accumulator
Expand All @@ -188,7 +248,27 @@ const OPCODES = {
0x8D: "", // sta("a")
0x8E: "", // stx("a")
0x8F: "", // sax("a") // illegal
0x90: "", // bcc("*+d")
0x90: {
name: "BCC", // Branch if Carry Clear
t: 2,
code: 0x90,
run: (cpu) => {
const offset = cpu.fetch(); // Fetch the offset from memory
// Check if the Carry flag is clear
if (!cpu.carryFlag) {
// Calculate the new program counter (PC) considering the offset
const newPC = cpu.PC + (offset >= 0x80 ? offset - 0x100 : offset);
// Add 1 cycle if the branch is taken
cpu.cycles++;
// Add 1 more cycle if the branch crosses a page boundary
if ((cpu.PC & 0xFF00) !== (newPC & 0xFF00)) {
cpu.cycles++;
}
// Update the program counter (PC) to the new address
cpu.PC = newPC;
}
}
},
0x91: "", // sta("(d),y")
0x92: "", // stp_implied() // illegal
0x93: "", // ahx("(d),y") // illegal
Expand Down Expand Up @@ -298,9 +378,27 @@ const OPCODES = {
0xC5: "", // cmp("d")
0xC6: "", // dec("d")
0xC7: "", // dcp("d") // illegal
0xC8: "", // iny_implied()
0xC8: {
name: "INY", // Increment Index Y by One
t: 2,
code: 0xC8,
run: (cpu) => {
cpu.Y += 1;
cpu.negativeFlag = cpu.Y;
cpu.zeroFlag = cpu.Y;
}
},
0xC9: "", // cmp("#i")
0xCA: "", // dex_implied()
0xCA: {
name: "DEX", // Decrement Index X by One
t: 2,
code: 0xCA,
run: (cpu) => {
cpu.X -= 1;
cpu.negativeFlag = cpu.X;
cpu.zeroFlag = cpu.X;
}
},
0xCB: "", // axs("#i") // illegal
0xCC: "",
0xCD: "",
Expand Down Expand Up @@ -330,7 +428,16 @@ const OPCODES = {
0xE5: "",
0xE6: "",
0xE7: "", // isc("d") // illegal
0xE8: "",
0xE8: {
name: "INX", // Increment Index X by One
t: 2,
code: 0xE8,
run: (cpu) => {
cpu.X += 1;
cpu.negativeFlag = cpu.X;
cpu.zeroFlag = cpu.X;
}
},
0xE9: {
name: "SBC #immediate", // Subtract Memory from Accumulator with Borrow
t: 2,
Expand Down
88 changes: 86 additions & 2 deletions tests/cpu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ describe("memory:", () => {
test("memory size 64kb", () => {
expect(memory.memory.length).toBe(0x10000);
});

test("memory clear", () => {
const clearStatus = memory.memory.filter(Boolean).every((val) => val == 0);
expect(clearStatus).toBe(true);
});
});


describe("cpu:", () => {
let memory, cpu, params;

Expand Down Expand Up @@ -156,4 +154,90 @@ describe("cpu:", () => {
});
});
});

describe("flags check:", () => {
describe("(0x80) negativeFlag", () => {
test("set 42", () => {
cpu.negativeFlag = 42;
expect(cpu.negativeFlag).toBe(false);
})
test("set -42", () => {
cpu.negativeFlag = -42;
expect(cpu.negativeFlag).toBe(true);
})
});
describe("(0x40) overflowFlag", () => {
test("Subtract check(SBC) v1", () => {
const ACC = 0x14;
const carry = 1;
const value = 250;
const res = ACC - value - carry;
cpu.overflowFlag = ((ACC ^ res) & (~ACC ^ value)) & 0x80;
expect(cpu.overflowFlag).toBe(false);
});
test("Subtract check(SBC) v2", () => {
const ACC = 0xFF;
const carry = 0;
const value = 128;
const res = ACC - value - carry;
cpu.overflowFlag = ((ACC ^ res) & (~ACC ^ value)) & 0x80;
expect(cpu.overflowFlag).toBe(true);
});
});
describe("(0x10) breakFlag", () => {
test("true", () => {
cpu.breakFlag = true;
expect(cpu.breakFlag).toBe(true);
});
test("false", () => {
cpu.breakFlag = false;
expect(cpu.breakFlag).toBe(false);
});
});
describe("(0x08) decimalFlag", () => {
test("true", () => {
cpu.decimalFlag = true;
expect(cpu.decimalFlag).toBe(true);
});
test("false", () => {
cpu.breakFlag = false;
expect(cpu.decimalFlag).toBe(false);
});
});
describe("(0x04) interruptDisableFlag", () => {
test("true", () => {
cpu.interruptDisableFlag = true;
expect(cpu.interruptDisableFlag).toBe(true);
});
test("false", () => {
cpu.interruptDisableFlag = false;
expect(cpu.interruptDisableFlag).toBe(false);
});
});
describe("(0x02) zeroFlag", () => {
test("true", () => {
cpu.zeroFlag = 0;
expect(cpu.zeroFlag).toBe(true);
});
test("false", () => {
cpu.zeroFlag = 0x2A;
expect(cpu.zeroFlag).toBe(false);
cpu.zeroFlag = 0x01;
expect(cpu.zeroFlag).toBe(false);
cpu.zeroFlag = 0xFF;
expect(cpu.zeroFlag).toBe(false);
});
});
describe("(0x01) carryFlag", () => {
test("true", () => {
cpu.carryFlag = true;
expect(cpu.carryFlag).toBe(true);
});
test("false", () => {
cpu.zeroFlag = false;
expect(cpu.carryFlag).toBe(false);
});
});

});
});
Loading

0 comments on commit cefef97

Please sign in to comment.