From 12f9b21bd0a1aa8cd4bcd0febae05644160c016f Mon Sep 17 00:00:00 2001 From: Johannes Muenzel Date: Tue, 30 Sep 2014 00:28:46 -0400 Subject: [PATCH] Move files around to match the usual structure more --- 6502emu/Cargo.toml | 5 - 6502emu/src/instruction.rs | 105 --------------------- 6502emu/src/machine.rs | 149 ----------------------------- 6502emu/src/registers.rs | 93 ------------------ Cargo.toml | 40 ++++++++ helloworld.txt | Bin 26 -> 0 bytes {6502emu/src => src}/address.rs | 94 +++++++++++-------- {6502emu/src => src/bin}/main.rs | 21 ++--- src/instruction.rs | 111 ++++++++++++++++++++++ 6502emu/src/util.rs => src/lib.rs | 26 +---- src/machine.rs | 151 ++++++++++++++++++++++++++++++ {6502emu/src => src}/memory.rs | 73 ++++++++------- src/registers.rs | 147 +++++++++++++++++++++++++++++ 13 files changed, 557 insertions(+), 458 deletions(-) delete mode 100644 6502emu/Cargo.toml delete mode 100644 6502emu/src/instruction.rs delete mode 100644 6502emu/src/machine.rs delete mode 100644 6502emu/src/registers.rs create mode 100644 Cargo.toml delete mode 100644 helloworld.txt rename {6502emu/src => src}/address.rs (55%) rename {6502emu/src => src/bin}/main.rs (85%) create mode 100644 src/instruction.rs rename 6502emu/src/util.rs => src/lib.rs (86%) create mode 100644 src/machine.rs rename {6502emu/src => src}/memory.rs (57%) create mode 100644 src/registers.rs diff --git a/6502emu/Cargo.toml b/6502emu/Cargo.toml deleted file mode 100644 index 03524db..0000000 --- a/6502emu/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] - -name = "6502emu" -version = "0.0.1" -authors = ["Andrew Keeton "] diff --git a/6502emu/src/instruction.rs b/6502emu/src/instruction.rs deleted file mode 100644 index 3c168b6..0000000 --- a/6502emu/src/instruction.rs +++ /dev/null @@ -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..... | .. ..... -} \ No newline at end of file diff --git a/6502emu/src/machine.rs b/6502emu/src/machine.rs deleted file mode 100644 index cd7c600..0000000 --- a/6502emu/src/machine.rs +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/6502emu/src/registers.rs b/6502emu/src/registers.rs deleted file mode 100644 index 9a7072c..0000000 --- a/6502emu/src/registers.rs +++ /dev/null @@ -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() - } - } -} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a2953db --- /dev/null +++ b/Cargo.toml @@ -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" + diff --git a/helloworld.txt b/helloworld.txt deleted file mode 100644 index b3b8a4bd83e652f2c9932b036684d19dfa628253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26 dcmezWFM}bKAqNQa8OnjU2*^rd;AP-q0044c22lV2 diff --git a/6502emu/src/address.rs b/src/address.rs similarity index 55% rename from 6502emu/src/address.rs rename to src/address.rs index 6bec5b5..3ca7918 100644 --- a/6502emu/src/address.rs +++ b/src/address.rs @@ -25,35 +25,49 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // 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 { - Immediate, - Absolute, - ZeroPage, - Implied, - IndirectAbsolute, - AbsoluteIndexedX, - AbsoluteIndexedY, - ZeroPageIndexedX, - ZeroageIndexedY, - IndexedIndirect, - IndirectIndexed, - Relative, - Accumulator + Accumulator, // LSR A work directly on accumulator + Immediate, // LDA #10 8-bit constant in instruction + ZeroPage, // LDA $00 zero-page address + ZeroPageX, // LDA $80,X address is X register + 8-bit constant + ZeroPageY, // LDX $10,Y address is Y register + 8-bit constant + Relative, // BNE LABEL branch target as signed relative offset + Absolute, // JMP $1000 full 16-bit address + AbsoluteX, // STA $1000,X full 16-bit address plus X register + AbsoluteY, // STA $1000,Y full 16-bit address plus Y register + Indirect, // JMP ($1000) jump to address stored at address + IndexedIndirectX, // LDA ($10,X) load from address stored at (constant + // zero page address plus X register) + 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 // does make sense to add an address and an "address-difference". (If this // 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 for Address { fn add(&self, &AddressDiff(rhs): &AddressDiff) -> Address { 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 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 // code. @@ -64,26 +78,28 @@ impl Add 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 - } -} diff --git a/6502emu/src/main.rs b/src/bin/main.rs similarity index 85% rename from 6502emu/src/main.rs rename to src/bin/main.rs index be0181e..6154dee 100644 --- a/6502emu/src/main.rs +++ b/src/bin/main.rs @@ -25,17 +25,16 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -mod address; -mod machine; -mod memory; -mod registers; -mod util; +extern crate emu6502; + +use emu6502::machine; fn main() { - let mut machine = machine::Machine::new(); - - println!("A: {}", machine.registers.accumulator); - println!("add_with_carry(1)"); - machine.add_with_carry(1); - println!("A: {}", machine.registers.accumulator); + let mut machine = machine::Machine::new(); + + println!("A: {}", machine.registers.accumulator); + println!("add_with_carry(1)"); + machine.add_with_carry(1); + println!("A: {}", machine.registers.accumulator); } + diff --git a/src/instruction.rs b/src/instruction.rs new file mode 100644 index 0000000..44025df --- /dev/null +++ b/src/instruction.rs @@ -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 +} + diff --git a/6502emu/src/util.rs b/src/lib.rs similarity index 86% rename from 6502emu/src/util.rs rename to src/lib.rs index 710876c..bda0793 100644 --- a/6502emu/src/util.rs +++ b/src/lib.rs @@ -25,25 +25,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -#[deriving(PartialEq, Eq, Show)] -pub enum BitFlag { - Off, - On -} +pub mod address; +pub mod instruction; +pub mod machine; +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 - } - } -} \ No newline at end of file diff --git a/src/machine.rs b/src/machine.rs new file mode 100644 index 0000000..5ae4086 --- /dev/null +++ b/src/machine.rs @@ -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); +} diff --git a/6502emu/src/memory.rs b/src/memory.rs similarity index 57% rename from 6502emu/src/memory.rs rename to src/memory.rs index 9d2f5e7..ddc9031 100644 --- a/6502emu/src/memory.rs +++ b/src/memory.rs @@ -26,48 +26,51 @@ // POSSIBILITY OF SUCH DAMAGE. use address::Address; -use address::AddressDiff; -use registers::StackPointer; -pub static MEMORY_ADDRESS_BEGIN: Address = Address(0x0000); -pub static MEMORY_ADDRESS_END: Address = Address(0xFFFF); -pub static STACK_ADDRESS_BEGIN: Address = Address(0x0100); -pub static STACK_ADDRESS_END: Address = Address(0x01FF); +// JAM: We can probably come up with a better way to represent address ranges. +// Address range type? +// +// // 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_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 { - // Rust doesn't seem to like this: - // bytes: [u8, ..MEMORY_SIZE] - bytes: [u8, ..2^16] + bytes: [u8, ..MEMORY_SIZE] } impl Memory { - pub fn new() -> Memory { - Memory { bytes: [0, ..2^16] } - } - - pub fn get_byte(&self, address: &Address) -> u8 { - self.bytes[address.to_uint()] - } - - // Sets the byte at the given address to the given value and returns the - // previous value at the address. - pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 { - let old_value = self.get_byte(address); - self.bytes[address.to_uint()] = value; - - return old_value; - } - - pub fn is_stack_address(address: &Address) -> bool { - STACK_ADDRESS_BEGIN <= *address && *address <= STACK_ADDRESS_END - } - - pub fn stack_pointer_to_address(&StackPointer(sp): &StackPointer) -> Address - { - STACK_ADDRESS_BEGIN + AddressDiff(sp as u16) - } + pub fn new() -> Memory { + Memory { bytes: [0, ..MEMORY_SIZE] } + } + + pub fn get_byte(&self, address: &Address) -> u8 { + self.bytes[address.to_uint()] + } + + // Sets the byte at the given address to the given value and returns the + // previous value at the address. + pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 { + let old_value = self.get_byte(address); + self.bytes[address.to_uint()] = value; + + return old_value; + } + + pub fn is_stack_address(address: &Address) -> bool { + STACK_ADDRESS_LO <= *address && *address <= STACK_ADDRESS_HI + } } diff --git a/src/registers.rs b/src/registers.rs new file mode 100644 index 0000000..d31f821 --- /dev/null +++ b/src/registers.rs @@ -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() + } + } +} +