学习Rust编程——snake game

学习Rust编程——snake game

首页游戏大全RustRed更新时间:2024-04-21

[dependencies] lazy_static = "1.4.0" bitflags = "1.3.2" sdl2 = "0.35" rand = "=0.8"

src\cpu.rs

use crate::opcodes; use std::collections::HashMap; bitflags! { /// # Status Register (P) http://wiki.nesdev.com/w/index.php/Status_flags /// /// 7 6 5 4 3 2 1 0 /// N V _ B D I Z C /// | | | | | | --- Carry Flag /// | | | | | ----- zero Flag /// | | | | ------- Interrupt Disable /// | | | --------- decimal Mode (not used on NES) /// | | ----------- BREAK Command /// | --------------- Overflow Flag /// ----------------- Negative Flag /// pub struct CpuFlags: u8 { const CARRY = 0b00000001; const ZERO = 0b00000010; const INTERRUPT_DISABLE = 0b00000100; const DECIMAL_MODE = 0b00001000; const BREAK = 0b00010000; const BREAK2 = 0b00100000; const OVERFLOW = 0b01000000; const NEGATIV = 0b10000000; } } const STACK: u16 = 0x0100; const STACK_RESET: u8 = 0xfd; pub struct CPU { pub register_a: u8, pub register_x: u8, pub register_y: u8, pub status: CPUFlags, pub program_counter: u16, pub stack_pointer: u8, memory: [u8; 0xFFFF], } #[derive(Debug)] #[allow(non_camel_case_types)] pub enum AddressingMode { Immediate, ZeroPage, ZeroPage_X, ZeroPage_Y, Absolute, Absolute_X, Absolute_Y, Indirect_X, Indirect_Y, NoneAddressing, } pub trait Mem { fn mem_read(&Self, addr: u16) -> u8; fn mem_write(&mut self, addr: u16, data: u8); fn mem_read_u16(&self, pos: u16) -> u16 { let lo = self.mem_read(pos) as u16; let hi = self.mem_read(pos 1) as u16; (hi << 8) | (lo as u16) } fn mem_write_u16(&mut self, pos: u16, data: u16) { let hi = (data >> 8) as u8; let lo = (data & 0xff) as u8; self.mem_write(pos, lo); self.mem_write(pos 1, hi); } } impl Mem for CPU { fn mem_read(&self, addr: u16) -> u8 { self.memory[addr as usize] } fn mem_write(&mut self, addr: u16, data: u8) { self.memory[addr as usize] = data; } } impl CPU { pub fn new() -> Self { CPU { register_a: 0, register_x: 0, register_y: 0, stack_pointer: STACK_RESET, program_counter: 0, status: CpuFlags::from_bits_truncate(0b100100), memory: [0; 0xFFFF], } } fn get_operand_address(&self, mode: &AddressingMode) -> u16 { match mode { AddressingMode::Immediate => self.program_counter, AddressingMode::ZeroPage => self.mem_read(self.program_counter) as u16, AddressingMode::Absolute => self.mem_read_u16(self.program_counter), AddressingMode::ZeroPage_X => { let pos = self.mem_read(self.program_counter); let addr = pos.wrapping_add(self.register_x) as u16; addr } AddressingMode::ZeroPage_Y => { let pos = self.mem_read(self.program_counter); let addr = pos.wrapping_add(self.register_y) as u16; addr } AddressingMode::Absolute_X => { let base = self.mem_read_u16(self.program_counter); let addr = base.wrapping_add(self.register_x as u16); addr } AddressingMode::Absolute_Y => { let base = self.mem_read_u16(self.program_counter); let addr = base.wrapping_add(self.register_y as u16); addr } AddressingMode::Indirect_X => { let base = self.mem_read(self.program_counter); let ptr: u8 = (base as u8).wrapping_add(self.register_x); let lo = self.mem_read(ptr as u16); let hi = self.mem_read(ptr.wrapping_add(1) as u16); (hi as u16) << 8 | (lo as u16) } AddressingMode::Indirect_Y => { let base = self.mem_read(self.program_counter); let lo = self.mem_read(base as u16); let hi = self.mem_read((base as u8).wrapping_add(1) as u16); let deref_base = (hi as u16) << 8 | (lo as u16); let deref = deref_base.wrapping_add(self.register_y as u16); deref } AddressingMode::NoneAddressing => { panic!("mode {:?} is not supported", mode); } } } fn ldy(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); self.register_y = data; self.update_zero_and_negative_flags(self.register_y); } fn ldx(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); self.register_x = data; self.update_zero_and_negative_flags(self.register_x); } fn lda(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(&mode); let value = self.mem_read(addr); self.set_register_a(value); } fn sta(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); self.mem_write(addr, self.register_a); } fn set_register_a(&mut self, value: u8) { self.register_a = value; self.update_zero_and_negative_flags(self.register_a); } fn and(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); self.set_register_a(data & self.register_a); } fn eor(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); self.set_register_a(data ^ self.register_a); } fn ora(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); self.set_register_a(data | self.register_a); } fn tax(&mut self) { self.register_x = self.register_a; self.update_zero_and_negative_flags(self.register_x); } fn update_zero_and_negative_flags(&mut self, result: u8) { if result == 0 { self.status.insert(CpuFlags::ZERO); } else { self.status.remove(CpuFlags::ZERO); } if result >> 7 == 1 { self.status.insert(CpuFlags::NEGATIV); } else { self.status.remove(CpuFlags::NEGATIV); } } fn update_negative_flags(&mut self, result: u8) { if result >> 7 == 1 { self.status.insert(CpuFlags::NEGATIV) } else { self.status.remove(CpuFlags::NEGATIV) } } fn inx(&mut self) { self.register_x = self.register_x.wrapping_add(1); self.update_zero_and_negative_flags(self.register_x); } fn iny(&mut self) { self.register_y = self.register_y.wrapping_add(1); self.update_zero_and_negative_flags(self.register_y); } pub fn load_and_run(&mut self, program: Vec<u8>) { self.load(program); self.reset(); self.run() } pub fn load(&mut self, program: Vec<u8>) { self.memory[0x0600..(0x0600 program.len())].copy_from_slice(&program[..]); self.mem_write_u16(0xFFFC, 0x0600); } pub fn reset(&mut self) { self.register_a = 0; self.register_x = 0; self.register_y = 0; self.stack_pointer = STACK_RESET; self.status = CpuFlags::from_bits_truncate(0b100100); // self.memory = [0; 0xFFFF]; self.program_counter = self.mem_read_u16(0xFFFC); } fn set_carry_flag(&mut self) { self.status.insert(CpuFlags::CARRY) } fn clear_carry_flag(&mut self) { self.status.remove(CpuFlags::CARRY) } /// note: ignoring decimal mode /// http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html fn add_to_register_a(&mut self, data: u8) { let sum = self.register_a as u16 data as u16 (if self.status.contains(CpuFlags::CARRY) { 1 } else { 0 }) as u16; let carry = sum > 0xff; if carry { self.status.insert(CpuFlags::CARRY); } else { self.status.remove(CpuFlags::CARRY); } let result = sum as u8; if (data ^ result) & (result ^ self.register_a) & 0x80 != 0 { self.status.insert(CpuFlags::OVERFLOW); } else { self.status.remove(CpuFlags::OVERFLOW) } self.set_register_a(result); } fn sbc(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(&mode); let data = self.mem_read(addr); self.add_to_register_a(((data as i8).wrapping_neg().wrapping_sub(1)) as u8); } fn adc(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let value = self.mem_read(addr); self.add_to_register_a(value); } fn stack_pop(&mut self) -> u8 { self.stack_pointer = self.stack_pointer.wrapping_add(1); self.mem_read((STACK as u16) self.stack_pointer as u16) } fn stack_push(&mut self, data: u8) { self.mem_write((STACK as u16) self.stack_pointer as u16, data); self.stack_pointer = self.stack_pointer.wrapping_sub(1) } fn stack_push_u16(&mut self, data: u16) { let hi = (data >> 8) as u8; let lo = (data & 0xff) as u8; self.stack_push(hi); self.stack_push(lo); } fn stack_pop_u16(&mut self) -> u16 { let lo = self.stack_pop() as u16; let hi = self.stack_pop() as u16; hi << 8 | lo } fn asl_accumulator(&mut self) { let mut data = self.register_a; if data >> 7 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data << 1; self.set_register_a(data) } fn asl(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); if data >> 7 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data << 1; self.mem_write(addr, data); self.update_zero_and_negative_flags(data); data } fn lsr_accumulator(&mut self) { let mut data = self.register_a; if data & 1 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data >> 1; self.set_register_a(data) } fn lsr(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); if data & 1 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data >> 1; self.mem_write(addr, data); self.update_zero_and_negative_flags(data); data } fn rol(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); let old_carry = self.status.contains(CpuFlags::CARRY); if data >> 7 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data << 1; if old_carry { data = data | 1; } self.mem_write(addr, data); self.update_negative_flags(data); data } fn rol_accumulator(&mut self) { let mut data = self.register_a; let old_carry = self.status.contains(CpuFlags::CARRY); if data >> 7 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data << 1; if old_carry { data = data | 1; } self.set_register_a(data); } fn ror(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); let old_carry = self.status.contains(CpuFlags::CARRY); if data & 1 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data >> 1; if old_carry { data = data | 0b10000000; } self.mem_write(addr, data); self.update_negative_flags(data); data } fn ror_accumulator(&mut self) { let mut data = self.register_a; let old_carry = self.status.contains(CpuFlags::CARRY); if data & 1 == 1 { self.set_carry_flag(); } else { self.clear_carry_flag(); } data = data >> 1; if old_carry { data = data | 0b10000000; } self.set_register_a(data); } fn inc(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); data = data.wrapping_add(1); self.mem_write(addr, data); self.update_zero_and_negative_flags(data); data } fn dey(&mut self) { self.register_y = self.register_y.wrapping_sub(1); self.update_zero_and_negative_flags(self.register_y); } fn dex(&mut self) { self.register_x = self.register_x.wrapping_sub(1); self.update_zero_and_negative_flags(self.register_x); } fn dec(&mut self, mode: &AddressingMode) -> u8 { let addr = self.get_operand_address(mode); let mut data = self.mem_read(addr); data = data.wrapping_sub(1); self.mem_write(addr, data); self.update_zero_and_negative_flags(data); data } fn pla(&mut self) { let data = self.stack_pop(); self.set_register_a(data); } fn plp(&mut self) { self.status.bits = self.stack_pop(); self.status.remove(CpuFlags::BREAK); self.status.insert(CpuFlags::BREAK2); } fn php(&mut self) { //http://wiki.nesdev.com/w/index.php/CPU_status_flag_behavior let mut flags = self.status.clone(); flags.insert(CpuFlags::BREAK); flags.insert(CpuFlags::BREAK2); self.stack_push(flags.bits()); } fn bit(&mut self, mode: &AddressingMode) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); let and = self.register_a & data; if and == 0 { self.status.insert(CpuFlags::ZERO); } else { self.status.remove(CpuFlags::ZERO); } self.status.set(CpuFlags::NEGATIV, data & 0b10000000 > 0); self.status.set(CpuFlags::OVERFLOW, data & 0b01000000 > 0); } fn compare(&mut self, mode: &AddressingMode, compare_with: u8) { let addr = self.get_operand_address(mode); let data = self.mem_read(addr); if data <= compare_with { self.status.insert(CpuFlags::CARRY); } else { self.status.remove(CpuFlags::CARRY); } self.update_zero_and_negative_flags(compare_with.wrapping_sub(data)); } fn branch(&mut self, condition: bool) { if condition { let jump: i8 = self.mem_read(self.program_counter) as i8; let jump_addr = self .program_counter .wrapping_add(1) .wrapping_add(jump as u16); self.program_counter = jump_addr; } } pub fn run(&mut self) { self.run_with_callback(|_| {}); } pub fn run_with_callback<F>(&mut self, mut callback: F) where F: FnMut(&mut CPU), { let ref opcodes: HashMap<u8, &'static opcodes::OpCode> = *opcodes::OPCODES_MAP; loop { let code = self.mem_read(self.program_counter); self.program_counter = 1; let program_counter_state = self.program_counter; let opcode = opcodes.get(&code).unwrap(); match code { 0xa9 | 0xa5 | 0xb5 | 0xad | 0xbd | 0xb9 | 0xa1 | 0xb1 => { self.lda(&opcode.mode); } 0xAA => self.tax(), 0xe8 => self.inx(), 0x00 => return, /* CLD */ 0xd8 => self.status.remove(CpuFlags::DECIMAL_MODE), /* CLI */ 0x58 => self.status.remove(CpuFlags::INTERRUPT_DISABLE), /* CLV */ 0xb8 => self.status.remove(CpuFlags::OVERFLOW), /* CLC */ 0x18 => self.clear_carry_flag(), /* SEC */ 0x38 => self.set_carry_flag(), /* SEI */ 0x78 => self.status.insert(CpuFlags::INTERRUPT_DISABLE), /* SED */ 0xf8 => self.status.insert(CpuFlags::DECIMAL_MODE), /* PHA */ 0x48 => self.stack_push(self.register_a), /* PLA */ 0x68 => { self.pla(); } /* PHP */ 0x08 => { self.php(); } /* PLP */ 0x28 => { self.plp(); } /* ADC */ 0x69 | 0x65 | 0x75 | 0x6d | 0x7d | 0x79 | 0x61 | 0x71 => { self.adc(&opcode.mode); } /* SBC */ 0xe9 | 0xe5 | 0xf5 | 0xed | 0xfd | 0xf9 | 0xe1 | 0xf1 => { self.sbc(&opcode.mode); } /* AND */ 0x29 | 0x25 | 0x35 | 0x2d | 0x3d | 0x39 | 0x21 | 0x31 => { self.and(&opcode.mode); } /* EOR */ 0x49 | 0x45 | 0x55 | 0x4d | 0x5d | 0x59 | 0x41 | 0x51 => { self.eor(&opcode.mode); } /* ORA */ 0x09 | 0x05 | 0x15 | 0x0d | 0x1d | 0x19 | 0x01 | 0x11 => { self.ora(&opcode.mode); } /* LSR */ 0x4a => self.lsr_accumulator(), /* LSR */ 0x46 | 0x56 | 0x4e | 0x5e => { self.lsr(&opcode.mode); } /*ASL*/ 0x0a => self.asl_accumulator(), /* ASL */ 0x06 | 0x16 | 0x0e | 0x1e => { self.asl(&opcode.mode); } /*ROL*/ 0x2a => self.rol_accumulator(), /* ROL */ 0x26 | 0x36 | 0x2e | 0x3e => { self.rol(&opcode.mode); } /* ROR */ 0x6a => self.ror_accumulator(), /* ROR */ 0x66 | 0x76 | 0x6e | 0x7e => { self.ror(&opcode.mode); } /* INC */ 0xe6 | 0xf6 | 0xee | 0xfe => { self.inc(&opcode.mode); } /* INY */ 0xc8 => self.iny(), /* DEC */ 0xc6 | 0xd6 | 0xce | 0xde => { self.dec(&opcode.mode); } /* DEX */ 0xca => { self.dex(); } /* DEY */ 0x88 => { self.dey(); } /* CMP */ 0xc9 | 0xc5 | 0xd5 | 0xcd | 0xdd | 0xd9 | 0xc1 | 0xd1 => { self.compare(&opcode.mode, self.register_a); } /* CPY */ 0xc0 | 0xc4 | 0xcc => { self.compare(&opcode.mode, self.register_y); } /* CPX */ 0xe0 | 0xe4 | 0xec => self.compare(&opcode.mode, self.register_x), /* JMP Absolute */ 0x4c => { let mem_address = self.mem_read_u16(self.program_counter); self.program_counter = mem_address; } /* JMP Indirect */ 0x6c => { let mem_address = self.mem_read_u16(self.program_counter); // let indirect_ref = self.mem_read_u16(mem_address); //6502 bug mode with with page boundary: // if address $3000 contains $40, $30FF contains $80, and $3100 contains $50, // the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended // i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000 let indirect_ref = if mem_address & 0x00FF == 0x00FF { let lo = self.mem_read(mem_address); let hi = self.mem_read(mem_address & 0xFF00); (hi as u16) << 8 | (lo as u16) } else { self.mem_read_u16(mem_address) }; self.program_counter = indirect_ref; } /* JSR */ 0x20 => { self.stack_push_u16(self.program_counter 2 - 1); let target_address = self.mem_read_u16(self.program_counter); self.program_counter = target_address } /* RTS */ 0x60 => { self.program_counter = self.stack_pop_u16() 1; } /* RTI */ 0x40 => { self.status.bits = self.stack_pop(); self.status.remove(CpuFlags::BREAK); self.status.insert(CpuFlags::BREAK2); self.program_counter = self.stack_pop_u16(); } /* BNE */ 0xd0 => { self.branch(!self.status.contains(CpuFlags::ZERO)); } /* BVS */ 0x70 => { self.branch(self.status.contains(CpuFlags::OVERFLOW)); } /* BVC */ 0x50 => { self.branch(!self.status.contains(CpuFlags::OVERFLOW)); } /* BPL */ 0x10 => { self.branch(!self.status.contains(CpuFlags::NEGATIV)); } /* BMI */ 0x30 => { self.branch(self.status.contains(CpuFlags::NEGATIV)); } /* BEQ */ 0xf0 => { self.branch(self.status.contains(CpuFlags::ZERO)); } /* BCS */ 0xb0 => { self.branch(self.status.contains(CpuFlags::CARRY)); } /* BCC */ 0x90 => { self.branch(!self.status.contains(CpuFlags::CARRY)); } /* BIT */ 0x24 | 0x2c => { self.bit(&opcode.mode); } /* STA */ 0x85 | 0x95 | 0x8d | 0x9d | 0x99 | 0x81 | 0x91 => { self.sta(&opcode.mode); } /* STX */ 0x86 | 0x96 | 0x8e => { let addr = self.get_operand_address(&opcode.mode); self.mem_write(addr, self.register_x); } /* STY */ 0x84 | 0x94 | 0x8c => { let addr = self.get_operand_address(&opcode.mode); self.mem_write(addr, self.register_y); } /* LDX */ 0xa2 | 0xa6 | 0xb6 | 0xae | 0xbe => { self.ldx(&opcode.mode); } /* LDY */ 0xa0 | 0xa4 | 0xb4 | 0xac | 0xbc => { self.ldy(&opcode.mode); } /* NOP */ 0xea => { //do nothing } /* TAY */ 0xa8 => { self.register_y = self.register_a; self.update_zero_and_negative_flags(self.register_y); } /* TSX */ 0xba => { self.register_x = self.stack_pointer; self.update_zero_and_negative_flags(self.register_x); } /* TXA */ 0x8a => { self.register_a = self.register_x; self.update_zero_and_negative_flags(self.register_a); } /* TXS */ 0x9a => { self.stack_pointer = self.register_x; } /* TYA */ 0x98 => { self.register_a = self.register_y; self.update_zero_and_negative_flags(self.register_a); } _ => todo!(), } if program_counter_state == self.program_counter { self.program_counter = (opcode.len - 1) as u16; } callback(self); } } } #[cfg(test)] mod test { use super::*; #[test] fn test_0xa9_lda_immediate_load_data() { let mut cpu = CPU::new(); cpu.load_and_run(vec![0xa9, 0x05, 0x00]); assert_eq!(cpu.register_a, 5); assert!(cpu.status.bits() & 0b0000_0010 == 0b00); assert!(cpu.status.bits() & 0b1000_0000 == 0); } #[test] fn test_0xaa_tax_move_a_to_x() { let mut cpu = CPU::new(); cpu.register_a = 10; cpu.load_and_run(vec![0xaa, 0x00]); assert_eq!(cpu.register_x, 10) } #[test] fn test_5_ops_working_together() { let mut cpu = CPU::new(); cpu.load_and_run(vec![0xa9, 0xc0, 0xaa, 0xe8, 0x00]); assert_eq!(cpu.register_x, 0xc1) } #[test] fn test_inx_overflow() { let mut cpu = CPU::new(); cpu.register_x = 0xff; cpu.load_and_run(vec![0xe8, 0xe8, 0x00]); assert_eq!(cpu.register_x, 1) } #[test] fn test_lda_from_memory() { let mut cpu = CPU::new(); cpu.mem_write(0x10, 0x55); cpu.load_and_run(vec![0xa5, 0x10, 0x00]); assert_eq!(cpu.register_a, 0x55); } }

src\opcodes.rs

use crate::cpu::AddressingMode; use std::collections::HashMap; pub struct OpCode { pub code: u8, pub mnemonic: &'static str, pub len: u8, pub cycles: u8, pub mode: AddressingMode, } impl OpCode { fn new(code: u8, mnemonic: &'static str, len: u8, cycles: u8, mode: AddressingMode) -> Self { OpCode { code: code, mnemonic: mnemonic, len: len, cycles: cycles, mode: mode, } } } lazy_static! { pub static ref CPU_OPS_CODES: Vec<OpCode> = vec![ OpCode::new(0x00, "BRK", 1, 7, AddressingMode::NoneAddressing), OpCode::new(0xea, "NOP", 1, 2, AddressingMode::NoneAddressing), /* Arithmetic */ OpCode::new(0x69, "ADC", 2, 2, AddressingMode::Immediate), OpCode::new(0x65, "ADC", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x75, "ADC", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x6d, "ADC", 3, 4, AddressingMode::Absolute), OpCode::new(0x7d, "ADC", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0x79, "ADC", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0x61, "ADC", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x71, "ADC", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0xe9, "SBC", 2, 2, AddressingMode::Immediate), OpCode::new(0xe5, "SBC", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xf5, "SBC", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0xed, "SBC", 3, 4, AddressingMode::Absolute), OpCode::new(0xfd, "SBC", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0xf9, "SBC", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0xe1, "SBC", 2, 6, AddressingMode::Indirect_X), OpCode::new(0xf1, "SBC", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0x29, "AND", 2, 2, AddressingMode::Immediate), OpCode::new(0x25, "AND", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x35, "AND", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x2d, "AND", 3, 4, AddressingMode::Absolute), OpCode::new(0x3d, "AND", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0x39, "AND", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0x21, "AND", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x31, "AND", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0x49, "EOR", 2, 2, AddressingMode::Immediate), OpCode::new(0x45, "EOR", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x55, "EOR", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x4d, "EOR", 3, 4, AddressingMode::Absolute), OpCode::new(0x5d, "EOR", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0x59, "EOR", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0x41, "EOR", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x51, "EOR", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0x09, "ORA", 2, 2, AddressingMode::Immediate), OpCode::new(0x05, "ORA", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x15, "ORA", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x0d, "ORA", 3, 4, AddressingMode::Absolute), OpCode::new(0x1d, "ORA", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0x19, "ORA", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0x01, "ORA", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x11, "ORA", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), /* Shifts */ OpCode::new(0x0a, "ASL", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x06, "ASL", 2, 5, AddressingMode::ZeroPage), OpCode::new(0x16, "ASL", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0x0e, "ASL", 3, 6, AddressingMode::Absolute), OpCode::new(0x1e, "ASL", 3, 7, AddressingMode::Absolute_X), OpCode::new(0x4a, "LSR", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x46, "LSR", 2, 5, AddressingMode::ZeroPage), OpCode::new(0x56, "LSR", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0x4e, "LSR", 3, 6, AddressingMode::Absolute), OpCode::new(0x5e, "LSR", 3, 7, AddressingMode::Absolute_X), OpCode::new(0x2a, "ROL", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x26, "ROL", 2, 5, AddressingMode::ZeroPage), OpCode::new(0x36, "ROL", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0x2e, "ROL", 3, 6, AddressingMode::Absolute), OpCode::new(0x3e, "ROL", 3, 7, AddressingMode::Absolute_X), OpCode::new(0x6a, "ROR", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x66, "ROR", 2, 5, AddressingMode::ZeroPage), OpCode::new(0x76, "ROR", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0x6e, "ROR", 3, 6, AddressingMode::Absolute), OpCode::new(0x7e, "ROR", 3, 7, AddressingMode::Absolute_X), OpCode::new(0xe6, "INC", 2, 5, AddressingMode::ZeroPage), OpCode::new(0xf6, "INC", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0xee, "INC", 3, 6, AddressingMode::Absolute), OpCode::new(0xfe, "INC", 3, 7, AddressingMode::Absolute_X), OpCode::new(0xe8, "INX", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xc8, "INY", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xc6, "DEC", 2, 5, AddressingMode::ZeroPage), OpCode::new(0xd6, "DEC", 2, 6, AddressingMode::ZeroPage_X), OpCode::new(0xce, "DEC", 3, 6, AddressingMode::Absolute), OpCode::new(0xde, "DEC", 3, 7, AddressingMode::Absolute_X), OpCode::new(0xca, "DEX", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x88, "DEY", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xc9, "CMP", 2, 2, AddressingMode::Immediate), OpCode::new(0xc5, "CMP", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xd5, "CMP", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0xcd, "CMP", 3, 4, AddressingMode::Absolute), OpCode::new(0xdd, "CMP", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0xd9, "CMP", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0xc1, "CMP", 2, 6, AddressingMode::Indirect_X), OpCode::new(0xd1, "CMP", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0xc0, "CPY", 2, 2, AddressingMode::Immediate), OpCode::new(0xc4, "CPY", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xcc, "CPY", 3, 4, AddressingMode::Absolute), OpCode::new(0xe0, "CPX", 2, 2, AddressingMode::Immediate), OpCode::new(0xe4, "CPX", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xec, "CPX", 3, 4, AddressingMode::Absolute), /* Branching */ OpCode::new(0x4c, "JMP", 3, 3, AddressingMode::NoneAddressing), //AddressingMode that acts as Immidiate OpCode::new(0x6c, "JMP", 3, 5, AddressingMode::NoneAddressing), //AddressingMode:Indirect with 6502 bug OpCode::new(0x20, "JSR", 3, 6, AddressingMode::NoneAddressing), OpCode::new(0x60, "RTS", 1, 6, AddressingMode::NoneAddressing), OpCode::new(0x40, "RTI", 1, 6, AddressingMode::NoneAddressing), OpCode::new(0xd0, "BNE", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x70, "BVS", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x50, "BVC", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x30, "BMI", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0xf0, "BEQ", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0xb0, "BCS", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x90, "BCC", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x10, "BPL", 2, 2 /*( 1 if branch succeeds 2 if to a new page)*/, AddressingMode::NoneAddressing), OpCode::new(0x24, "BIT", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x2c, "BIT", 3, 4, AddressingMode::Absolute), /* Stores, Loads */ OpCode::new(0xa9, "LDA", 2, 2, AddressingMode::Immediate), OpCode::new(0xa5, "LDA", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xb5, "LDA", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0xad, "LDA", 3, 4, AddressingMode::Absolute), OpCode::new(0xbd, "LDA", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0xb9, "LDA", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0xa1, "LDA", 2, 6, AddressingMode::Indirect_X), OpCode::new(0xb1, "LDA", 2, 5/* 1 if page crossed*/, AddressingMode::Indirect_Y), OpCode::new(0xa2, "LDX", 2, 2, AddressingMode::Immediate), OpCode::new(0xa6, "LDX", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xb6, "LDX", 2, 4, AddressingMode::ZeroPage_Y), OpCode::new(0xae, "LDX", 3, 4, AddressingMode::Absolute), OpCode::new(0xbe, "LDX", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_Y), OpCode::new(0xa0, "LDY", 2, 2, AddressingMode::Immediate), OpCode::new(0xa4, "LDY", 2, 3, AddressingMode::ZeroPage), OpCode::new(0xb4, "LDY", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0xac, "LDY", 3, 4, AddressingMode::Absolute), OpCode::new(0xbc, "LDY", 3, 4/* 1 if page crossed*/, AddressingMode::Absolute_X), OpCode::new(0x85, "STA", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x95, "STA", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x8d, "STA", 3, 4, AddressingMode::Absolute), OpCode::new(0x9d, "STA", 3, 5, AddressingMode::Absolute_X), OpCode::new(0x99, "STA", 3, 5, AddressingMode::Absolute_Y), OpCode::new(0x81, "STA", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x91, "STA", 2, 6, AddressingMode::Indirect_Y), OpCode::new(0x86, "STX", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x96, "STX", 2, 4, AddressingMode::ZeroPage_Y), OpCode::new(0x8e, "STX", 3, 4, AddressingMode::Absolute), OpCode::new(0x84, "STY", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x94, "STY", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x8c, "STY", 3, 4, AddressingMode::Absolute), /* Flags clear */ OpCode::new(0xD8, "CLD", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x58, "CLI", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xb8, "CLV", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x18, "CLC", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x38, "SEC", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x78, "SEI", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xf8, "SED", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xaa, "TAX", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xa8, "TAY", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0xba, "TSX", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x8a, "TXA", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x9a, "TXS", 1, 2, AddressingMode::NoneAddressing), OpCode::new(0x98, "TYA", 1, 2, AddressingMode::NoneAddressing), /* Stack */ OpCode::new(0x48, "PHA", 1, 3, AddressingMode::NoneAddressing), OpCode::new(0x68, "PLA", 1, 4, AddressingMode::NoneAddressing), OpCode::new(0x08, "PHP", 1, 3, AddressingMode::NoneAddressing), OpCode::new(0x28, "PLP", 1, 4, AddressingMode::NoneAddressing), ]; pub static ref OPCODES_MAP: HashMap<u8, &'static OpCode> = { let mut map = HashMap::new(); for cpuop in &*CPU_OPS_CODES { map.insert(cpuop.code, cpuop); } map }; }

src\main.rs

pub mod cpu; pub mod opcodes; use cpu::Mem; use cpu::CPU; use rand::Rng; use sdl2::event::Event; use sdl2::EventPump; use sdl2::keyboard::Keycode; use sdl2::pixels::Color; use sdl2::pixels::PixelFormatEnum; use std::time::Duration; #[macro_use] extern crate lazy_static; #[macro_use] extern crate bitflags; fn color(byte: u8) -> Color { match byte { 0 => sdl2::pixels::Color::BLACK, 1 => sdl2::pixels::Color::WHITE, 2 | 9 => sdl2::pixels::Color::GREY, 3 | 10 => sdl2::pixels::Color::RED, 4 | 11 => sdl2::pixels::Color::GREEN, 5 | 12 => sdl2::pixels::Color::BLUE, 6 | 13 => sdl2::pixels::Color::MAGENTA, 7 | 14 => sdl2::pixels::Color::YELLOW, _ => sdl2::pixels::Color::CYAN, } } fn read_screen_state(cpu: &CPU, frame: &mut [u8; 32 * 3 * 32]) -> bool { let mut frame_idx = 0; let mut update = false; for i in 0x0200..0x600 { let color_idx = cpu.mem_read(i as u16); let (b1, b2, b3) = color(color_idx).rgb(); if frame[frame_idx] != b1 || frame[frame_idx 1] != b2 || frame[frame_idx 2] != b3 { frame[frame_idx] = b1; frame[frame_idx 1] = b2; frame[frame_idx 2] = b3; update = true; } frame_idx = 3; } update } fn handle_user_input(cpu: &mut CPU, event_pump: &mut EventPump) { for event in event_pump.poll_iter() { match event { Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { std::process::exit(0) }, Event::KeyDown { keycode: Some(Keycode::W), .. } => { cpu.mem_write(0xff, 0x77); }, Event::KeyDown { keycode: Some(Keycode::S), .. } => { cpu.mem_write(0xff, 0x73); }, Event::KeyDown { keycode: Some(Keycode::A), .. } => { cpu.mem_write(0xff, 0x61); }, Event::KeyDown { keycode: Some(Keycode::D), .. } => { cpu.mem_write(0xff, 0x64); } _ => {/* do nothing */} } } } fn main() { // init sdl2 let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem .window("Snake game", (32.0 * 10.0) as u32, (32.0 * 10.0) as u32) .position_centered() .build().unwrap(); let mut canvas = window.into_canvas().present_vsync().build().unwrap(); let mut event_pump = sdl_context.event_pump().unwrap(); canvas.set_scale(10.0, 10.0).unwrap(); let creator = canvas.texture_creator(); let mut texture = creator .create_texture_target(PixelFormatEnum::RGB24, 32, 32).unwrap(); let game_code = vec![ 0x20, 0x06, 0x06, 0x20, 0x38, 0x06, 0x20, 0x0d, 0x06, 0x20, 0x2a, 0x06, 0x60, 0xa9, 0x02, 0x85, 0x02, 0xa9, 0x04, 0x85, 0x03, 0xa9, 0x11, 0x85, 0x10, 0xa9, 0x10, 0x85, 0x12, 0xa9, 0x0f, 0x85, 0x14, 0xa9, 0x04, 0x85, 0x11, 0x85, 0x13, 0x85, 0x15, 0x60, 0xa5, 0xfe, 0x85, 0x00, 0xa5, 0xfe, 0x29, 0x03, 0x18, 0x69, 0x02, 0x85, 0x01, 0x60, 0x20, 0x4d, 0x06, 0x20, 0x8d, 0x06, 0x20, 0xc3, 0x06, 0x20, 0x19, 0x07, 0x20, 0x20, 0x07, 0x20, 0x2d, 0x07, 0x4c, 0x38, 0x06, 0xa5, 0xff, 0xc9, 0x77, 0xf0, 0x0d, 0xc9, 0x64, 0xf0, 0x14, 0xc9, 0x73, 0xf0, 0x1b, 0xc9, 0x61, 0xf0, 0x22, 0x60, 0xa9, 0x04, 0x24, 0x02, 0xd0, 0x26, 0xa9, 0x01, 0x85, 0x02, 0x60, 0xa9, 0x08, 0x24, 0x02, 0xd0, 0x1b, 0xa9, 0x02, 0x85, 0x02, 0x60, 0xa9, 0x01, 0x24, 0x02, 0xd0, 0x10, 0xa9, 0x04, 0x85, 0x02, 0x60, 0xa9, 0x02, 0x24, 0x02, 0xd0, 0x05, 0xa9, 0x08, 0x85, 0x02, 0x60, 0x60, 0x20, 0x94, 0x06, 0x20, 0xa8, 0x06, 0x60, 0xa5, 0x00, 0xc5, 0x10, 0xd0, 0x0d, 0xa5, 0x01, 0xc5, 0x11, 0xd0, 0x07, 0xe6, 0x03, 0xe6, 0x03, 0x20, 0x2a, 0x06, 0x60, 0xa2, 0x02, 0xb5, 0x10, 0xc5, 0x10, 0xd0, 0x06, 0xb5, 0x11, 0xc5, 0x11, 0xf0, 0x09, 0xe8, 0xe8, 0xe4, 0x03, 0xf0, 0x06, 0x4c, 0xaa, 0x06, 0x4c, 0x35, 0x07, 0x60, 0xa6, 0x03, 0xca, 0x8a, 0xb5, 0x10, 0x95, 0x12, 0xca, 0x10, 0xf9, 0xa5, 0x02, 0x4a, 0xb0, 0x09, 0x4a, 0xb0, 0x19, 0x4a, 0xb0, 0x1f, 0x4a, 0xb0, 0x2f, 0xa5, 0x10, 0x38, 0xe9, 0x20, 0x85, 0x10, 0x90, 0x01, 0x60, 0xc6, 0x11, 0xa9, 0x01, 0xc5, 0x11, 0xf0, 0x28, 0x60, 0xe6, 0x10, 0xa9, 0x1f, 0x24, 0x10, 0xf0, 0x1f, 0x60, 0xa5, 0x10, 0x18, 0x69, 0x20, 0x85, 0x10, 0xb0, 0x01, 0x60, 0xe6, 0x11, 0xa9, 0x06, 0xc5, 0x11, 0xf0, 0x0c, 0x60, 0xc6, 0x10, 0xa5, 0x10, 0x29, 0x1f, 0xc9, 0x1f, 0xf0, 0x01, 0x60, 0x4c, 0x35, 0x07, 0xa0, 0x00, 0xa5, 0xfe, 0x91, 0x00, 0x60, 0xa6, 0x03, 0xa9, 0x00, 0x81, 0x10, 0xa2, 0x00, 0xa9, 0x01, 0x81, 0x10, 0x60, 0xa6, 0xff, 0xea, 0xea, 0xca, 0xd0, 0xfb, 0x60, ]; //load the game let mut cpu = CPU::new(); cpu.load(game_code); cpu.reset(); let mut screen_state = [0 as u8; 32 * 3 * 32]; let mut rng = rand::thread_rng(); // run the game cycle cpu.run_with_callback(move |cpu| { handle_user_input(cpu, &mut event_pump); cpu.mem_write(0xfe, rng.gen_range(1..16)); if read_screen_state(cpu, &mut screen_state) { texture.update(None, &screen_state, 32 * 3).unwrap(); canvas.copy(&texture, None, None).unwrap(); canvas.present(); } ::std::thread::sleep(Duration::new(0, 70_000)); }); }

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved