mirror of
https://github.com/mre/mos6502.git
synced 2024-11-28 22:51:26 +00:00
Move files around to match the usual structure more
This commit is contained in:
parent
2675725a3d
commit
12f9b21bd0
@ -1,5 +0,0 @@
|
|||||||
[package]
|
|
||||||
|
|
||||||
name = "6502emu"
|
|
||||||
version = "0.0.1"
|
|
||||||
authors = ["Andrew Keeton <andrewrkeeton@gmail.com>"]
|
|
@ -1,105 +0,0 @@
|
|||||||
// Copyright (C) 2014 The 6502-rs Developers
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions
|
|
||||||
// are met:
|
|
||||||
// 1. Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer in the
|
|
||||||
// documentation and/or other materials provided with the distribution.
|
|
||||||
// 3. Neither the names of the copyright holders nor the names of any
|
|
||||||
// contributors may be used to endorse or promote products derived from this
|
|
||||||
// software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Show, PartialEq, Eq)]
|
|
||||||
pub enum Instruction
|
|
||||||
// Abbreviations
|
|
||||||
//
|
|
||||||
// General
|
|
||||||
//
|
|
||||||
// M | `Memory location`
|
|
||||||
//
|
|
||||||
// Registers
|
|
||||||
//
|
|
||||||
// A | accumulator
|
|
||||||
// X | general purpose register
|
|
||||||
// Y | general purpose register
|
|
||||||
// NV-BDIZC | processor status flags -- see ProcessorStatus bitflags
|
|
||||||
// SP | stack pointer
|
|
||||||
// PC | program counter
|
|
||||||
// i/o vars should be listed as follows:
|
|
||||||
// NV BDIZC A X Y SP PC M
|
|
||||||
//
|
|
||||||
// | outputs | inputs
|
|
||||||
{ ADC // ADd with Carry................ | NV ...ZC A = A + M + C
|
|
||||||
, AND // logical AND (bitwise)......... | N. ...Z. A = A && M
|
|
||||||
, ASL // Arithmetic Shift Left......... | N. ...ZC A = M << 1
|
|
||||||
, BCC // Branch if Carry Clear......... | .. ..... PC = !C
|
|
||||||
, BCS // Branch if Carry Set........... | .. ..... PC = C
|
|
||||||
, BEQ // Branch if Equal (to zero?).... | .. ..... PC = Z
|
|
||||||
, BIT // BIT test...................... | NV ...Z. = A & M
|
|
||||||
, BMI // Branch if Minus............... | .. ..... PC = N
|
|
||||||
, BNE // Branch if Not Equal........... | .. ..... PC = !Z
|
|
||||||
, BPL // Branch if Positive............ | .. ..... PC = Z
|
|
||||||
, BRK // BReaK......................... | .. B.... SP PC =
|
|
||||||
, BVC // Branch if oVerflow Clear...... | .. ..... PC = !V
|
|
||||||
, BVS // Branch if oVerflow Set........ | .. ..... PC = V
|
|
||||||
, CLC // CLear Carry flag.............. | .. ....C = 0
|
|
||||||
, CLD // Clear Decimal Mode............ | .. .D... = 0
|
|
||||||
, CLI // Clear Interrupt Disable....... | .. ..I.. = 0
|
|
||||||
, CLV // Clear oVerflow flag........... | .V ..... = 0
|
|
||||||
, CMP // Compare....................... | N. ...ZC = A - M
|
|
||||||
, CPX // Compare X register............ | N. ...ZC = X - M
|
|
||||||
, CPY // Compare Y register............ | N. ...ZC = Y - M
|
|
||||||
, DEC // DECrement memory.............. | N. ...Z. M = M - 1
|
|
||||||
, DEX // DEcrement X register.......... | N. ...Z. X = X - 1
|
|
||||||
, DEY // DEcrement Y register.......... | N. ...Z. Y = Y - 1
|
|
||||||
, EOR // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M
|
|
||||||
, INC // INCrement memory.............. | N. ...Z. M = M + 1
|
|
||||||
, INX // INcrement X register.......... | N. ...Z. X = X + 1
|
|
||||||
, INY // INcrement Y register.......... | N. ...Z. Y = Y + 1
|
|
||||||
, JMP // JuMP.......................... | .. ..... PC =
|
|
||||||
, JSR // Jump to SubRoutine............ | .. .....
|
|
||||||
, LDA // LoaD Accumulator.............. | .. .....
|
|
||||||
, LDX // LoaD X register............... | .. .....
|
|
||||||
, LDY // LoaD Y register............... | .. .....
|
|
||||||
, LSR // Logical Shift Right........... | .. .....
|
|
||||||
, NOP // No OPeration.................. | .. .....
|
|
||||||
, ORA // inclusive OR.................. | .. .....
|
|
||||||
, PHA // PusH Accumulator.............. | .. .....
|
|
||||||
, PHP // PusH Processor status......... | .. .....
|
|
||||||
, PLA // PuLl Accumulator.............. | .. .....
|
|
||||||
, PLP // PuLl Processor status......... | .. .....
|
|
||||||
, ROL // ROtate Left................... | .. .....
|
|
||||||
, ROR // ROtate Right.................. | .. .....
|
|
||||||
, RTI // ReTurn from Interrupt......... | .. .....
|
|
||||||
, RTS // ReTurn from Subroutine........ | .. .....
|
|
||||||
, SBC // SuBtract with Carry........... | .. .....
|
|
||||||
, SEC // SEt Carry flag................ | .. .....
|
|
||||||
, SED // SEt Decimal flag.............. | .. .....
|
|
||||||
, SEI // SEt Interrupt disable......... | .. .....
|
|
||||||
, STA // STore Accumulator............. | .. .....
|
|
||||||
, STX // STore X register.............. | .. .....
|
|
||||||
, STY // STore Y register.............. | .. .....
|
|
||||||
, TAX // Transfer Accumulator to X..... | .. .....
|
|
||||||
, TAY // Transfer Accumulator to Y..... | .. .....
|
|
||||||
, TSX // Transfer Stack pointer to X... | .. .....
|
|
||||||
, TXA // Transfer X to Accumulator..... | .. .....
|
|
||||||
, TXS // Transfer X to Stack pointer... | .. .....
|
|
||||||
, TYA // Transfer Y to Accumulator..... | .. .....
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
// Copyright (C) 2014 The 6502-rs Developers
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions
|
|
||||||
// are met:
|
|
||||||
// 1. Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer in the
|
|
||||||
// documentation and/or other materials provided with the distribution.
|
|
||||||
// 3. Neither the names of the copyright holders nor the names of any
|
|
||||||
// contributors may be used to endorse or promote products derived from this
|
|
||||||
// software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
use util::{ BitFlag, Off, On };
|
|
||||||
use memory::Memory;
|
|
||||||
use registers::Registers;
|
|
||||||
|
|
||||||
pub struct Machine {
|
|
||||||
pub registers: Registers,
|
|
||||||
pub memory: Memory
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Machine {
|
|
||||||
pub fn new() -> Machine {
|
|
||||||
Machine{
|
|
||||||
registers: Registers::new(),
|
|
||||||
memory: Memory::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
*self = Machine::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO akeeton: Implement binary-coded decimal.
|
|
||||||
pub fn add_with_carry(&mut self, value: i8) {
|
|
||||||
let a_before: i8 = self.registers.accumulator;
|
|
||||||
let c_before: u8 = self.registers.status.carry.to_bit();
|
|
||||||
let a_after: i8 = a_before + c_before as i8 + value;
|
|
||||||
|
|
||||||
assert_eq!(a_after as u8, a_before as u8 + c_before + value as u8);
|
|
||||||
|
|
||||||
let did_carry = (a_after as u8) < (a_before as u8);
|
|
||||||
let is_zero = a_after == 0;
|
|
||||||
let is_negative = a_after < 0;
|
|
||||||
let did_overflow =
|
|
||||||
(a_before < 0 && value < 0 && a_after >= 0)
|
|
||||||
|| (a_before > 0 && value > 0 && a_after <= 0);
|
|
||||||
|
|
||||||
self.registers.accumulator = a_after;
|
|
||||||
self.registers.status.carry = BitFlag::new(did_carry);
|
|
||||||
self.registers.status.zero = BitFlag::new(is_zero);
|
|
||||||
self.registers.status.negative = BitFlag::new(is_negative);
|
|
||||||
self.registers.status.overflow = BitFlag::new(did_overflow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_with_carry_test() {
|
|
||||||
{
|
|
||||||
let mut machine = Machine::new();
|
|
||||||
|
|
||||||
machine.add_with_carry(1);
|
|
||||||
assert_eq!(machine.registers.accumulator, 1);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.add_with_carry(-1);
|
|
||||||
assert_eq!(machine.registers.accumulator, 0);
|
|
||||||
assert_eq!(machine.registers.status.carry, On);
|
|
||||||
assert_eq!(machine.registers.status.zero, On);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.add_with_carry(1);
|
|
||||||
assert_eq!(machine.registers.accumulator, 2);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut machine = Machine::new();
|
|
||||||
|
|
||||||
machine.add_with_carry(127);
|
|
||||||
assert_eq!(machine.registers.accumulator, 127);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.add_with_carry(-127);
|
|
||||||
assert_eq!(machine.registers.accumulator, 0);
|
|
||||||
assert_eq!(machine.registers.status.carry, On);
|
|
||||||
assert_eq!(machine.registers.status.zero, On);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.registers.status.carry = Off;
|
|
||||||
machine.add_with_carry(-128);
|
|
||||||
assert_eq!(machine.registers.accumulator, -128);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, On);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.add_with_carry(127);
|
|
||||||
assert_eq!(machine.registers.accumulator, -1);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, On);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut machine = Machine::new();
|
|
||||||
|
|
||||||
machine.add_with_carry(127);
|
|
||||||
assert_eq!(machine.registers.accumulator, 127);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, Off);
|
|
||||||
assert_eq!(machine.registers.status.overflow, Off);
|
|
||||||
|
|
||||||
machine.add_with_carry(1);
|
|
||||||
assert_eq!(machine.registers.accumulator, -128);
|
|
||||||
assert_eq!(machine.registers.status.carry, Off);
|
|
||||||
assert_eq!(machine.registers.status.zero, Off);
|
|
||||||
assert_eq!(machine.registers.status.negative, On);
|
|
||||||
assert_eq!(machine.registers.status.overflow, On);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
// Copyright (C) 2014 The 6502-rs Developers
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions
|
|
||||||
// are met:
|
|
||||||
// 1. Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer in the
|
|
||||||
// documentation and/or other materials provided with the distribution.
|
|
||||||
// 3. Neither the names of the copyright holders nor the names of any
|
|
||||||
// contributors may be used to endorse or promote products derived from this
|
|
||||||
// software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
use memory::STACK_ADDRESS_END;
|
|
||||||
use util::{ BitFlag, Off, On };
|
|
||||||
|
|
||||||
pub struct Status {
|
|
||||||
pub carry: BitFlag,
|
|
||||||
pub zero: BitFlag,
|
|
||||||
pub disable_interrupts: BitFlag,
|
|
||||||
pub decimal_mode: BitFlag,
|
|
||||||
pub brk: BitFlag,
|
|
||||||
pub unused: BitFlag,
|
|
||||||
pub overflow: BitFlag,
|
|
||||||
pub negative: BitFlag
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Status {
|
|
||||||
pub fn to_byte(&self) -> u8 {
|
|
||||||
self.carry.to_bit() << 0
|
|
||||||
| self.zero.to_bit() << 1
|
|
||||||
| self.disable_interrupts.to_bit() << 2
|
|
||||||
| self.decimal_mode.to_bit() << 3
|
|
||||||
| self.brk.to_bit() << 4
|
|
||||||
| self.unused.to_bit() << 5
|
|
||||||
| self.overflow.to_bit() << 6
|
|
||||||
| self.negative.to_bit() << 7
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> Status {
|
|
||||||
// TODO akeeton: Revisit these defaults.
|
|
||||||
Status {
|
|
||||||
carry: Off,
|
|
||||||
zero: Off,
|
|
||||||
disable_interrupts: On,
|
|
||||||
decimal_mode: Off,
|
|
||||||
brk: Off,
|
|
||||||
unused: On,
|
|
||||||
overflow: Off,
|
|
||||||
negative: Off
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct StackPointer(pub u8);
|
|
||||||
|
|
||||||
pub struct Registers {
|
|
||||||
pub accumulator: i8,
|
|
||||||
pub index_x: i8,
|
|
||||||
pub index_y: i8,
|
|
||||||
pub stack_pointer: StackPointer,
|
|
||||||
pub program_counter: u16,
|
|
||||||
pub status: Status
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Registers {
|
|
||||||
pub fn new() -> Registers {
|
|
||||||
// TODO akeeton: Revisit these defaults.
|
|
||||||
Registers {
|
|
||||||
accumulator: 0,
|
|
||||||
index_x: 0,
|
|
||||||
index_y: 0,
|
|
||||||
stack_pointer: StackPointer(STACK_ADDRESS_END.get_offset()),
|
|
||||||
program_counter: 0,
|
|
||||||
status: Status::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
40
Cargo.toml
Normal file
40
Cargo.toml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright (C) 2014 The 6502-rs Developers
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. Neither the names of the copyright holders nor the names of any
|
||||||
|
# contributors may be used to endorse or promote products derived from this
|
||||||
|
# software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "emu6502"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["The 6502-rs Developers"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
# This will look in src/lib.rs
|
||||||
|
name = "emu6502"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
# This will look in src/bin/emu6502.rs
|
||||||
|
name = "emu6502"
|
||||||
|
|
BIN
helloworld.txt
BIN
helloworld.txt
Binary file not shown.
@ -25,35 +25,49 @@
|
|||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct Address(pub u16);
|
|
||||||
|
|
||||||
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct AddressDiff(pub u16);
|
|
||||||
|
|
||||||
pub enum AddressingMode {
|
pub enum AddressingMode {
|
||||||
Immediate,
|
Accumulator, // LSR A work directly on accumulator
|
||||||
Absolute,
|
Immediate, // LDA #10 8-bit constant in instruction
|
||||||
ZeroPage,
|
ZeroPage, // LDA $00 zero-page address
|
||||||
Implied,
|
ZeroPageX, // LDA $80,X address is X register + 8-bit constant
|
||||||
IndirectAbsolute,
|
ZeroPageY, // LDX $10,Y address is Y register + 8-bit constant
|
||||||
AbsoluteIndexedX,
|
Relative, // BNE LABEL branch target as signed relative offset
|
||||||
AbsoluteIndexedY,
|
Absolute, // JMP $1000 full 16-bit address
|
||||||
ZeroPageIndexedX,
|
AbsoluteX, // STA $1000,X full 16-bit address plus X register
|
||||||
ZeroageIndexedY,
|
AbsoluteY, // STA $1000,Y full 16-bit address plus Y register
|
||||||
IndexedIndirect,
|
Indirect, // JMP ($1000) jump to address stored at address
|
||||||
IndirectIndexed,
|
IndexedIndirectX, // LDA ($10,X) load from address stored at (constant
|
||||||
Relative,
|
// zero page address plus X register)
|
||||||
Accumulator
|
IndirectIndexedY, // LDA ($10),Y load from (address stored at constant
|
||||||
|
// zero page address) plus Y register
|
||||||
}
|
}
|
||||||
|
|
||||||
// The idea here is that it doesn't make sense to add two addresses, but it
|
// The idea here is that it doesn't make sense to add two addresses, but it
|
||||||
// does make sense to add an address and an "address-difference". (If this
|
// does make sense to add an address and an "address-difference". (If this
|
||||||
// is too annoying to work with we should let it go.)
|
// is too annoying to work with we should let it go.)
|
||||||
|
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct AddressDiff(pub u16);
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Address(pub u16);
|
||||||
|
|
||||||
impl Add<AddressDiff, Address> for Address {
|
impl Add<AddressDiff, Address> for Address {
|
||||||
fn add(&self, &AddressDiff(rhs): &AddressDiff) -> Address {
|
fn add(&self, &AddressDiff(rhs): &AddressDiff) -> Address {
|
||||||
let &Address(lhs) = self;
|
let &Address(lhs) = self;
|
||||||
|
Address(lhs + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rustc doesn't seem to like having multiple implementations of Add for
|
||||||
|
// Address. I believe this is a Rust bug (possibly resolved by "associated
|
||||||
|
// types" RFC?). Or I wrote it wrong. Anyway, here's some living dead code:
|
||||||
|
/*
|
||||||
|
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct CheckedAddressDiff(u16);
|
||||||
|
|
||||||
|
impl Add<CheckedAddressDiff, Address> for Address {
|
||||||
|
fn add(&self, &CheckedAddressDiff(rhs): &CheckedAddressDiff) -> Address {
|
||||||
|
let &Address(lhs) = self;
|
||||||
|
|
||||||
// We probably don't want to overflow when doing arithmetic in our own
|
// We probably don't want to overflow when doing arithmetic in our own
|
||||||
// code.
|
// code.
|
||||||
@ -64,26 +78,28 @@ impl Add<AddressDiff, Address> for Address {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Address(lhs + rhs);
|
Address(lhs + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
match *self {
|
||||||
|
Address(address_) => address_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_uint(&self) -> uint {
|
||||||
|
self.to_u16() as uint
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_page_number(&self) -> u8 {
|
||||||
|
(self.to_u16() & 0xff00 >> 8) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_offset(&self) -> u8 {
|
||||||
|
(self.to_u16() & 0x00ff) as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
|
||||||
pub fn to_u16(&self) -> u16 {
|
|
||||||
match *self {
|
|
||||||
Address(address_) => address_
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_uint(&self) -> uint {
|
|
||||||
self.to_u16() as uint
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_page_number(&self) -> u8 {
|
|
||||||
(self.to_u16() & 0xff00 >> 8) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_offset(&self) -> u8 {
|
|
||||||
(self.to_u16() & 0x00ff) as u8
|
|
||||||
}
|
|
||||||
}
|
|
@ -25,17 +25,16 @@
|
|||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
mod address;
|
extern crate emu6502;
|
||||||
mod machine;
|
|
||||||
mod memory;
|
use emu6502::machine;
|
||||||
mod registers;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut machine = machine::Machine::new();
|
let mut machine = machine::Machine::new();
|
||||||
|
|
||||||
println!("A: {}", machine.registers.accumulator);
|
println!("A: {}", machine.registers.accumulator);
|
||||||
println!("add_with_carry(1)");
|
println!("add_with_carry(1)");
|
||||||
machine.add_with_carry(1);
|
machine.add_with_carry(1);
|
||||||
println!("A: {}", machine.registers.accumulator);
|
println!("A: {}", machine.registers.accumulator);
|
||||||
}
|
}
|
||||||
|
|
111
src/instruction.rs
Normal file
111
src/instruction.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright (C) 2014 The 6502-rs Developers
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// 3. Neither the names of the copyright holders nor the names of any
|
||||||
|
// contributors may be used to endorse or promote products derived from this
|
||||||
|
// software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Abbreviations
|
||||||
|
//
|
||||||
|
// General
|
||||||
|
//
|
||||||
|
// M | `Memory location`
|
||||||
|
//
|
||||||
|
// Registers
|
||||||
|
//
|
||||||
|
// A | accumulator
|
||||||
|
// X | general purpose register
|
||||||
|
// Y | general purpose register
|
||||||
|
// F | processor status flags, collectively
|
||||||
|
// NV-BDIZC | processor status flags, individually
|
||||||
|
// S | stack pointer
|
||||||
|
// PC | program counter
|
||||||
|
//
|
||||||
|
|
||||||
|
#[deriving(Show, PartialEq, Eq)]
|
||||||
|
pub enum Instruction
|
||||||
|
// i/o vars should be listed as follows:
|
||||||
|
// NV BDIZC A X Y S PC M
|
||||||
|
//
|
||||||
|
// | outputs | inputs
|
||||||
|
{ ADC // ADd with Carry................ | NV ...ZC A = A + M + C
|
||||||
|
, AND // logical AND (bitwise)......... | N. ...Z. A = A && M
|
||||||
|
, ASL // Arithmetic Shift Left......... | N. ...ZC A = M << 1
|
||||||
|
, BCC // Branch if Carry Clear......... | .. ..... PC = !C
|
||||||
|
, BCS // Branch if Carry Set........... | .. ..... PC = C
|
||||||
|
, BEQ // Branch if Equal (to zero?).... | .. ..... PC = Z
|
||||||
|
, BIT // BIT test...................... | NV ...Z. = A & M
|
||||||
|
, BMI // Branch if Minus............... | .. ..... PC = N
|
||||||
|
, BNE // Branch if Not Equal........... | .. ..... PC = !Z
|
||||||
|
, BPL // Branch if Positive............ | .. ..... PC = Z
|
||||||
|
, BRK // BReaK......................... | .. B.... S PC =
|
||||||
|
, BVC // Branch if oVerflow Clear...... | .. ..... PC = !V
|
||||||
|
, BVS // Branch if oVerflow Set........ | .. ..... PC = V
|
||||||
|
, CLC // CLear Carry flag.............. | .. ....C = 0
|
||||||
|
, CLD // Clear Decimal Mode............ | .. .D... = 0
|
||||||
|
, CLI // Clear Interrupt Disable....... | .. ..I.. = 0
|
||||||
|
, CLV // Clear oVerflow flag........... | .V ..... = 0
|
||||||
|
, CMP // Compare....................... | N. ...ZC = A - M
|
||||||
|
, CPX // Compare X register............ | N. ...ZC = X - M
|
||||||
|
, CPY // Compare Y register............ | N. ...ZC = Y - M
|
||||||
|
, DEC // DECrement memory.............. | N. ...Z. M = M - 1
|
||||||
|
, DEX // DEcrement X register.......... | N. ...Z. X = X - 1
|
||||||
|
, DEY // DEcrement Y register.......... | N. ...Z. Y = Y - 1
|
||||||
|
, EOR // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M
|
||||||
|
, INC // INCrement memory.............. | N. ...Z. M = M + 1
|
||||||
|
, INX // INcrement X register.......... | N. ...Z. X = X + 1
|
||||||
|
, INY // INcrement Y register.......... | N. ...Z. Y = Y + 1
|
||||||
|
, JMP // JuMP.......................... | .. ..... S PC =
|
||||||
|
, JSR // Jump to SubRoutine............ | .. ..... S PC =
|
||||||
|
, LDA // LoaD Accumulator.............. | N. ...Z. A = M
|
||||||
|
, LDX // LoaD X register............... | N. ...Z. X = M
|
||||||
|
, LDY // LoaD Y register............... | N. ...Z. Y = M
|
||||||
|
, LSR // Logical Shift Right........... | N. ...ZC A = A/2
|
||||||
|
// or N. ...ZC M = M/2
|
||||||
|
, NOP // No OPeration.................. | .. ..... =
|
||||||
|
, ORA // inclusive OR (bitwise)........ | N. ...Z. A = A | M
|
||||||
|
, PHA // PusH Accumulator.............. | .. ..... S M = A
|
||||||
|
, PHP // PusH Processor status......... | .. ..... S M = F
|
||||||
|
, PLA // PuLl Accumulator.............. | N. ...Z. A S = M (stack)
|
||||||
|
, PLP // PuLl Processor status......... | NV BDIZC S = M (stack)
|
||||||
|
, ROL // ROtate Left................... | N. ...ZC A = C A rotated
|
||||||
|
// or N. ...ZC M = C M rotated
|
||||||
|
, ROR // ROtate Right.................. | N. ...ZC A = C A rotated
|
||||||
|
// or N. ...ZC M = C M rotated
|
||||||
|
, RTI // ReTurn from Interrupt......... | NV BDIZC PC = M (stack)
|
||||||
|
, RTS // ReTurn from Subroutine........ | .. ..... PC = M (stack)
|
||||||
|
, SBC // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
|
||||||
|
, SEC // SEt Carry flag................ | .. ....C = 1
|
||||||
|
, SED // SEt Decimal flag.............. | .. .D... = 1
|
||||||
|
, SEI // SEt Interrupt disable......... | .. ..I.. = 1
|
||||||
|
, STA // STore Accumulator............. | .. ..... M = A
|
||||||
|
, STX // STore X register.............. | .. ..... M = X
|
||||||
|
, STY // STore Y register.............. | .. ..... M = Y
|
||||||
|
, TAX // Transfer Accumulator to X..... | N. ...Z. X = A
|
||||||
|
, TAY // Transfer Accumulator to Y..... | N. ...Z. Y = A
|
||||||
|
, TSX // Transfer Stack pointer to X... | N. ...Z. X = S
|
||||||
|
, TXA // Transfer X to Accumulator..... | N. ...Z. A = X
|
||||||
|
, TXS // Transfer X to Stack pointer... | .. ..... S = X
|
||||||
|
, TYA // Transfer Y to Accumulator..... | N. ...Z. A = Y
|
||||||
|
}
|
||||||
|
|
@ -25,25 +25,9 @@
|
|||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#[deriving(PartialEq, Eq, Show)]
|
pub mod address;
|
||||||
pub enum BitFlag {
|
pub mod instruction;
|
||||||
Off,
|
pub mod machine;
|
||||||
On
|
pub mod memory;
|
||||||
}
|
pub mod registers;
|
||||||
|
|
||||||
impl BitFlag {
|
|
||||||
pub fn new(is_set: bool) -> BitFlag {
|
|
||||||
if is_set {
|
|
||||||
On
|
|
||||||
} else {
|
|
||||||
Off
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bit(&self) -> u8 {
|
|
||||||
match *self {
|
|
||||||
Off => 0,
|
|
||||||
On => 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
151
src/machine.rs
Normal file
151
src/machine.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright (C) 2014 The 6502-rs Developers
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// 3. Neither the names of the copyright holders nor the names of any
|
||||||
|
// contributors may be used to endorse or promote products derived from this
|
||||||
|
// software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
use memory::Memory;
|
||||||
|
use registers::{ Registers, Status, StatusArgs };
|
||||||
|
use registers::{ ps_negative, ps_overflow, ps_zero, ps_carry };
|
||||||
|
|
||||||
|
pub struct Machine {
|
||||||
|
pub registers: Registers,
|
||||||
|
pub memory: Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Machine {
|
||||||
|
pub fn new() -> Machine {
|
||||||
|
Machine{
|
||||||
|
registers: Registers::new(),
|
||||||
|
memory: Memory::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
*self = Machine::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO akeeton: Implement binary-coded decimal.
|
||||||
|
pub fn add_with_carry(&mut self, value: i8) {
|
||||||
|
let a_before: i8 = self.registers.accumulator;
|
||||||
|
let c_before: i8 = self.registers.status.get_carry();
|
||||||
|
let a_after: i8 = a_before + c_before + value;
|
||||||
|
|
||||||
|
debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
|
||||||
|
+ value as u8);
|
||||||
|
|
||||||
|
let did_carry = (a_after as u8) < (a_before as u8);
|
||||||
|
|
||||||
|
let is_zero = a_after == 0;
|
||||||
|
let is_negative = a_after < 0;
|
||||||
|
let did_overflow =
|
||||||
|
(a_before < 0 && value < 0 && a_after >= 0)
|
||||||
|
|| (a_before > 0 && value > 0 && a_after <= 0);
|
||||||
|
|
||||||
|
let mask = ps_carry | ps_zero | ps_negative | ps_overflow;
|
||||||
|
|
||||||
|
self.registers.status.set_with_mask(mask,
|
||||||
|
Status::new(StatusArgs { carry: did_carry,
|
||||||
|
zero: is_zero,
|
||||||
|
negative: is_negative,
|
||||||
|
overflow: did_overflow,
|
||||||
|
..StatusArgs::none() } ));
|
||||||
|
|
||||||
|
self.registers.accumulator = a_after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_with_carry_test() {
|
||||||
|
|
||||||
|
let mut machine = Machine::new();
|
||||||
|
|
||||||
|
machine.add_with_carry(1);
|
||||||
|
assert_eq!(machine.registers.accumulator, 1);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.add_with_carry(-1);
|
||||||
|
assert_eq!(machine.registers.accumulator, 0);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.add_with_carry(1);
|
||||||
|
assert_eq!(machine.registers.accumulator, 2);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
let mut machine = Machine::new();
|
||||||
|
|
||||||
|
machine.add_with_carry(127);
|
||||||
|
assert_eq!(machine.registers.accumulator, 127);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.add_with_carry(-127);
|
||||||
|
assert_eq!(machine.registers.accumulator, 0);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.registers.status.remove(ps_carry);
|
||||||
|
machine.add_with_carry(-128);
|
||||||
|
assert_eq!(machine.registers.accumulator, -128);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.add_with_carry(127);
|
||||||
|
assert_eq!(machine.registers.accumulator, -1);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
let mut machine = Machine::new();
|
||||||
|
|
||||||
|
machine.add_with_carry(127);
|
||||||
|
assert_eq!(machine.registers.accumulator, 127);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), false);
|
||||||
|
|
||||||
|
machine.add_with_carry(1);
|
||||||
|
assert_eq!(machine.registers.accumulator, -128);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_carry), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_zero), false);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_negative), true);
|
||||||
|
assert_eq!(machine.registers.status.contains(ps_overflow), true);
|
||||||
|
}
|
@ -26,48 +26,51 @@
|
|||||||
// POSSIBILITY OF SUCH DAMAGE.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
use address::Address;
|
use address::Address;
|
||||||
use address::AddressDiff;
|
|
||||||
use registers::StackPointer;
|
|
||||||
|
|
||||||
pub static MEMORY_ADDRESS_BEGIN: Address = Address(0x0000);
|
// JAM: We can probably come up with a better way to represent address ranges.
|
||||||
pub static MEMORY_ADDRESS_END: Address = Address(0xFFFF);
|
// Address range type?
|
||||||
pub static STACK_ADDRESS_BEGIN: Address = Address(0x0100);
|
//
|
||||||
pub static STACK_ADDRESS_END: Address = Address(0x01FF);
|
// // Address range -- inclusive on both sides
|
||||||
|
// pub struct AddressRangeIncl {
|
||||||
|
// begin: Address,
|
||||||
|
// end: Address,
|
||||||
|
// }
|
||||||
|
|
||||||
|
static ADDR_LO_BARE: u16 = 0x0000;
|
||||||
|
static ADDR_HI_BARE: u16 = 0xFFFF;
|
||||||
|
|
||||||
|
pub static MEMORY_ADDRESS_LO: Address = Address(ADDR_LO_BARE);
|
||||||
|
pub static MEMORY_ADDRESS_HI: Address = Address(ADDR_HI_BARE);
|
||||||
|
pub static STACK_ADDRESS_LO: Address = Address(0x0100);
|
||||||
|
pub static STACK_ADDRESS_HI: Address = Address(0x01FF);
|
||||||
pub static IRQ_INTERRUPT_VECTOR_LO: Address = Address(0xFFFE);
|
pub static IRQ_INTERRUPT_VECTOR_LO: Address = Address(0xFFFE);
|
||||||
pub static IRQ_INTERRUPT_VECTOR_HI: Address = Address(0xFFFF);
|
pub static IRQ_INTERRUPT_VECTOR_HI: Address = Address(0xFFFF);
|
||||||
|
|
||||||
|
static MEMORY_SIZE: uint = (ADDR_HI_BARE - ADDR_LO_BARE) as uint + 1u;
|
||||||
|
|
||||||
// static MEMORY_SIZE: uint = MEMORY_ADDRESS_END - MEMORY_ADDRESS_BEGIN + 1;
|
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
// Rust doesn't seem to like this:
|
bytes: [u8, ..MEMORY_SIZE]
|
||||||
// bytes: [u8, ..MEMORY_SIZE]
|
|
||||||
bytes: [u8, ..2^16]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
pub fn new() -> Memory {
|
pub fn new() -> Memory {
|
||||||
Memory { bytes: [0, ..2^16] }
|
Memory { bytes: [0, ..MEMORY_SIZE] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_byte(&self, address: &Address) -> u8 {
|
pub fn get_byte(&self, address: &Address) -> u8 {
|
||||||
self.bytes[address.to_uint()]
|
self.bytes[address.to_uint()]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the byte at the given address to the given value and returns the
|
// Sets the byte at the given address to the given value and returns the
|
||||||
// previous value at the address.
|
// previous value at the address.
|
||||||
pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 {
|
pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 {
|
||||||
let old_value = self.get_byte(address);
|
let old_value = self.get_byte(address);
|
||||||
self.bytes[address.to_uint()] = value;
|
self.bytes[address.to_uint()] = value;
|
||||||
|
|
||||||
return old_value;
|
return old_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_stack_address(address: &Address) -> bool {
|
pub fn is_stack_address(address: &Address) -> bool {
|
||||||
STACK_ADDRESS_BEGIN <= *address && *address <= STACK_ADDRESS_END
|
STACK_ADDRESS_LO <= *address && *address <= STACK_ADDRESS_HI
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stack_pointer_to_address(&StackPointer(sp): &StackPointer) -> Address
|
|
||||||
{
|
|
||||||
STACK_ADDRESS_BEGIN + AddressDiff(sp as u16)
|
|
||||||
}
|
|
||||||
}
|
}
|
147
src/registers.rs
Normal file
147
src/registers.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// Copyright (C) 2014 The 6502-rs Developers
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// 3. Neither the names of the copyright holders nor the names of any
|
||||||
|
// contributors may be used to endorse or promote products derived from this
|
||||||
|
// software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
use address::{Address, AddressDiff};
|
||||||
|
use memory::{STACK_ADDRESS_LO, STACK_ADDRESS_HI};
|
||||||
|
|
||||||
|
// Useful for constructing Status instances
|
||||||
|
pub struct StatusArgs {
|
||||||
|
pub negative: bool,
|
||||||
|
pub overflow: bool,
|
||||||
|
pub unused: bool,
|
||||||
|
pub brk: bool,
|
||||||
|
pub decimal_mode: bool,
|
||||||
|
pub disable_interrupts: bool,
|
||||||
|
pub zero: bool,
|
||||||
|
pub carry: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusArgs {
|
||||||
|
pub fn none() -> StatusArgs {
|
||||||
|
StatusArgs { negative: false,
|
||||||
|
overflow: false,
|
||||||
|
unused: false,
|
||||||
|
brk: false,
|
||||||
|
decimal_mode: false,
|
||||||
|
disable_interrupts: false,
|
||||||
|
zero: false,
|
||||||
|
carry: false, }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub bitflags! {
|
||||||
|
flags Status: u8 {
|
||||||
|
static ps_negative = 0b10000000,
|
||||||
|
static ps_overflow = 0b01000000,
|
||||||
|
static ps_unused = 0b00100000, // JAM: Should this exist?
|
||||||
|
static ps_brk = 0b00010000,
|
||||||
|
static ps_decimal_mode = 0b00001000,
|
||||||
|
static ps_disable_interrupts = 0b00000100,
|
||||||
|
static ps_zero = 0b00000010,
|
||||||
|
static ps_carry = 0b00000001,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Status {
|
||||||
|
pub fn default() -> Status {
|
||||||
|
// TODO akeeton: Revisit these defaults.
|
||||||
|
|
||||||
|
Status::new(StatusArgs { negative: false,
|
||||||
|
overflow: false,
|
||||||
|
unused: true,
|
||||||
|
brk: false,
|
||||||
|
decimal_mode: false,
|
||||||
|
disable_interrupts: true,
|
||||||
|
zero: false,
|
||||||
|
carry: false, } )
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(StatusArgs { negative,
|
||||||
|
overflow,
|
||||||
|
unused,
|
||||||
|
brk,
|
||||||
|
decimal_mode,
|
||||||
|
disable_interrupts,
|
||||||
|
zero,
|
||||||
|
carry }: StatusArgs) -> Status
|
||||||
|
{
|
||||||
|
let mut out = Status::empty();
|
||||||
|
|
||||||
|
if negative { out = out | ps_negative }
|
||||||
|
if overflow { out = out | ps_overflow }
|
||||||
|
if unused { out = out | ps_unused }
|
||||||
|
if brk { out = out | ps_brk }
|
||||||
|
if decimal_mode { out = out | ps_decimal_mode }
|
||||||
|
if disable_interrupts { out = out | ps_disable_interrupts }
|
||||||
|
if zero { out = out | ps_zero }
|
||||||
|
if carry { out = out | ps_carry }
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_with_mask(&mut self, mask: Status, rhs: Status) {
|
||||||
|
*self = (*self & !mask) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_carry(self) -> i8 {
|
||||||
|
if self.contains(ps_carry) { 1 } else { 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct StackPointer(pub u8);
|
||||||
|
|
||||||
|
impl StackPointer {
|
||||||
|
pub fn to_address(&StackPointer(sp): &StackPointer) -> Address
|
||||||
|
{
|
||||||
|
STACK_ADDRESS_LO + AddressDiff(sp as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Registers {
|
||||||
|
pub accumulator: i8,
|
||||||
|
pub index_x: i8,
|
||||||
|
pub index_y: i8,
|
||||||
|
pub stack_pointer: StackPointer,
|
||||||
|
pub program_counter: Address,
|
||||||
|
pub status: Status
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registers {
|
||||||
|
pub fn new() -> Registers {
|
||||||
|
// TODO akeeton: Revisit these defaults.
|
||||||
|
Registers {
|
||||||
|
accumulator: 0,
|
||||||
|
index_x: 0,
|
||||||
|
index_y: 0,
|
||||||
|
stack_pointer: StackPointer(STACK_ADDRESS_HI.get_offset()),
|
||||||
|
program_counter: Address(0),
|
||||||
|
status: Status::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user