diff --git a/binaries/compactflash.img b/binaries/disk-with-partition-table.img similarity index 95% rename from binaries/compactflash.img rename to binaries/disk-with-partition-table.img index 01fcb19..0c041f0 100644 Binary files a/binaries/compactflash.img and b/binaries/disk-with-partition-table.img differ diff --git a/src/cpus/m68k/debugger.rs b/src/cpus/m68k/debugger.rs index 215c545..c83c2ed 100644 --- a/src/cpus/m68k/debugger.rs +++ b/src/cpus/m68k/debugger.rs @@ -30,7 +30,7 @@ pub struct M68kDebugger { pub breakpoints: Vec, pub use_tracing: bool, pub use_debugger: bool, - pub step_until_return: bool, + pub step_until_return: Option, pub stack_tracer: StackTracer, } @@ -40,7 +40,7 @@ impl M68kDebugger { breakpoints: vec!(), use_tracing: false, use_debugger: false, - step_until_return: false, + step_until_return: None, stack_tracer: StackTracer::new(), } } @@ -69,15 +69,10 @@ impl MC68010 { pub fn run_debugger(&mut self, space: &mut AddressSpace) { self.dump_state(space); - if self.debugger.step_until_return { - match self.decoder.instruction { - Instruction::RTS | Instruction::RTE | Instruction::RTR => { - self.debugger.step_until_return = false; - } - _ => { - return; - }, - } + match self.debugger.step_until_return { + Some(level) if level == self.debugger.stack_tracer.calls.len() => { self.debugger.step_until_return = None; }, + Some(_) => { return; }, + None => { }, } loop { @@ -125,7 +120,7 @@ impl MC68010 { } }, "so" | "stepout" => { - self.debugger.step_until_return = true; + self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1); return Ok(true); }, "c" | "continue" => { diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index bc53897..c617799 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -1,4 +1,6 @@ +use std::fmt; + use crate::error::Error; use crate::memory::{Address, AddressSpace}; @@ -639,7 +641,7 @@ impl M68kDecoder { (rtype, xreg, data, size) } - fn get_mode_as_target(&mut self, space: &mut AddressSpace, mode: u8, reg: u8, size: Option) -> Result { + pub fn get_mode_as_target(&mut self, space: &mut AddressSpace, mode: u8, reg: u8, size: Option) -> Result { let value = match mode { 0b000 => Target::DirectDReg(reg), 0b001 => Target::DirectAReg(reg), @@ -773,3 +775,21 @@ pub fn sign_extend_to_long(value: u32, from: Size) -> i32 { } } +impl fmt::Display for Target { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Target::Immediate(value) => write!(f, "#{:08x}", value), + Target::DirectDReg(reg) => write!(f, "%d{}", reg), + Target::DirectAReg(reg) => write!(f, "%a{}", reg), + Target::IndirectAReg(reg) => write!(f, "(%a{})", reg), + Target::IndirectARegInc(reg) => write!(f, "(%a{})+", reg), + Target::IndirectARegDec(reg) => write!(f, "-(%a{})", reg), + Target::IndirectARegOffset(reg, offset) => write!(f, "(%a{} + #{})", reg, offset), + Target::IndirectARegXRegOffset(reg, rtype, xreg, offset, _) => write!(f, "(%a{} + %{}{} + #{})", reg, if *rtype == RegisterType::Data { 'd' } else { 'a' }, xreg, offset), + Target::IndirectMemory(value) => write!(f, "(#{:08x})", value), + Target::IndirectPCOffset(offset) => write!(f, "(%pc + #{})", offset), + Target::IndirectPCXRegOffset(rtype, xreg, offset, _) => write!(f, "(%pc + %{}{} + #{})", if *rtype == RegisterType::Data { 'd' } else { 'a' }, xreg, offset), + } + } +} + diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 04827b0..9e88996 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -142,9 +142,9 @@ impl MC68010 { self.execute_current(space)?; self.timer.cycle.end(timer); - if (self.timer.cycle.events % 500) == 0 { - println!("{}", self.timer); - } + //if (self.timer.cycle.events % 500) == 0 { + // println!("{}", self.timer); + //} Ok(()) }, @@ -191,8 +191,9 @@ impl MC68010 { Instruction::AND(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - self.set_target_value(space, dest, existing & value, size)?; - self.set_logic_flags(value, size); + let result = get_value_sized(existing & value, size); + self.set_target_value(space, dest, result, size)?; + self.set_logic_flags(result, size); }, Instruction::ANDtoCCR(value) => { self.state.sr = self.state.sr | value as u16; @@ -300,8 +301,9 @@ impl MC68010 { Instruction::EOR(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - self.set_target_value(space, dest, existing ^ value, size)?; - self.set_logic_flags(value, size); + let result = get_value_sized(existing ^ value, size); + self.set_target_value(space, dest, result, size)?; + self.set_logic_flags(result, size); }, Instruction::EORtoCCR(value) => { self.state.sr = self.state.sr ^ value as u16; @@ -402,18 +404,21 @@ impl MC68010 { // TODO moving words requires a sign extension to 32 bits if size != Size::Long { return Err(Error::new("Unsupported size in MOVEM instruction")); } + let mut addr = self.get_target_address(target)?; if dir == Direction::ToTarget { let mut mask = mask; for i in (0..8).rev() { if (mask & 0x01) != 0 { let value = *self.get_a_reg_mut(i); - self.set_target_value(space, target, value, size)?; + addr -= size.in_bytes(); + set_address_sized(space, addr as Address, value, size); } mask >>= 1; } for i in (0..8).rev() { if (mask & 0x01) != 0 { - self.set_target_value(space, target, self.state.d_reg[i], size)?; + addr -= size.in_bytes(); + set_address_sized(space, addr as Address, self.state.d_reg[i], size); } mask >>= 1; } @@ -421,19 +426,28 @@ impl MC68010 { let mut mask = mask; for i in 0..8 { if (mask & 0x01) != 0 { - self.state.d_reg[i] = self.get_target_value(space, target, size)?; + self.state.d_reg[i] = get_address_sized(space, addr as Address, size)?; + addr += size.in_bytes(); } mask >>= 1; } for i in 0..8 { if (mask & 0x01) != 0 { - let value = self.get_target_value(space, target, size)?; - let addr = self.get_a_reg_mut(i); - *addr = value; + *self.get_a_reg_mut(i) = get_address_sized(space, addr as Address, size)?; + addr += size.in_bytes(); } mask >>= 1; } } + + // If it was Post-Inc/Pre-Dec target, then update the value + match target { + Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => { + let a_reg_mut = self.get_a_reg_mut(reg); + *a_reg_mut = addr; + } + _ => { }, + } }, Instruction::MOVEQ(data, reg) => { let value = sign_extend_to_long(data as u32, Size::Byte) as u32; @@ -473,8 +487,9 @@ impl MC68010 { Instruction::OR(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - self.set_target_value(space, dest, existing | value, size)?; - self.set_logic_flags(value, size); + let result = get_value_sized(existing | value, size); + self.set_target_value(space, dest, result, size)?; + self.set_logic_flags(result, size); }, Instruction::ORtoCCR(value) => { self.state.sr = self.state.sr | value as u16; @@ -488,8 +503,18 @@ impl MC68010 { }, //Instruction::RESET => { //}, - //Instruction::ROd(Target, Target, Size, ShiftDirection) => { - //}, + Instruction::ROd(count, target, size, shift_dir) => { + let count = self.get_target_value(space, count, size)? % 64; + let mut pair = (self.get_target_value(space, target, size)?, false); + for _ in 0..count { + pair = rotate_operation(pair.0, size, shift_dir); + } + self.set_logic_flags(pair.0, size); + if pair.1 { + self.state.sr |= FLAGS_CARRY; + } + self.set_target_value(space, target, pair.0, size)?; + }, //Instruction::ROXd(Target, Target, Size, ShiftDirection) => { //}, //Instruction::RTE => { @@ -563,7 +588,7 @@ impl MC68010 { Ok(value) } - fn get_target_value(&mut self, space: &mut AddressSpace, target: Target, size: Size) -> Result { + pub fn get_target_value(&mut self, space: &mut AddressSpace, target: Target, size: Size) -> Result { match target { Target::Immediate(value) => Ok(value), Target::DirectDReg(reg) => Ok(get_value_sized(self.state.d_reg[reg as usize], size)), @@ -602,7 +627,7 @@ impl MC68010 { } } - fn set_target_value(&mut self, space: &mut AddressSpace, target: Target, value: u32, size: Size) -> Result<(), Error> { + pub fn set_target_value(&mut self, space: &mut AddressSpace, target: Target, value: u32, size: Size) -> Result<(), Error> { match target { Target::DirectDReg(reg) => { set_value_sized(&mut self.state.d_reg[reg as usize], value, size); @@ -640,9 +665,9 @@ impl MC68010 { Ok(()) } - fn get_target_address(&mut self, target: Target) -> Result { + pub fn get_target_address(&mut self, target: Target) -> Result { let addr = match target { - Target::IndirectAReg(reg) => *self.get_a_reg_mut(reg), + Target::IndirectAReg(reg) | Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => *self.get_a_reg_mut(reg), Target::IndirectARegOffset(reg, offset) => { let addr = self.get_a_reg_mut(reg); (*addr).wrapping_add(offset as u32) @@ -706,6 +731,10 @@ impl MC68010 { } } + fn set_flag(&mut self, flag: u16, value: bool) { + self.state.sr = (self.state.sr & !flag) | (if value { flag } else { 0 }); + } + fn set_compare_flags(&mut self, value: u32, size: Size, carry: bool, overflow: bool) { let value = sign_extend_to_long(value, size); @@ -803,10 +832,11 @@ fn overflowing_sub_sized(operand1: u32, operand2: u32, size: Size) -> (u32, bool fn shift_operation(value: u32, size: Size, dir: ShiftDirection, arithmetic: bool) -> (u32, bool) { match dir { ShiftDirection::Left => { + let bit = get_msb(value, size); match size { - Size::Byte => (((value as u8) << 1) as u32, get_msb(value, size)), - Size::Word => (((value as u16) << 1) as u32, get_msb(value, size)), - Size::Long => ((value << 1) as u32, get_msb(value, size)), + Size::Byte => (((value as u8) << 1) as u32, bit), + Size::Word => (((value as u16) << 1) as u32, bit), + Size::Long => ((value << 1) as u32, bit), } }, ShiftDirection::Right => { @@ -816,6 +846,25 @@ fn shift_operation(value: u32, size: Size, dir: ShiftDirection, arithmetic: bool } } +fn rotate_operation(value: u32, size: Size, dir: ShiftDirection) -> (u32, bool) { + match dir { + ShiftDirection::Left => { + let bit = get_msb(value, size); + let mask = if bit { 0x01 } else { 0x00 }; + match size { + Size::Byte => (mask | ((value as u8) << 1) as u32, bit), + Size::Word => (mask | ((value as u16) << 1) as u32, bit), + Size::Long => (mask | (value << 1) as u32, bit), + } + }, + ShiftDirection::Right => { + let bit = if (value & 0x01) != 0 { true } else { false }; + let mask = if bit { get_msb_mask(0xffffffff, size) } else { 0x0 }; + ((value >> 1) | mask, bit) + }, + } +} + fn get_value_sized(value: u32, size: Size) -> u32 { match size { diff --git a/src/cpus/m68k/tests.rs b/src/cpus/m68k/tests.rs index 7475547..9ba5ba9 100644 --- a/src/cpus/m68k/tests.rs +++ b/src/cpus/m68k/tests.rs @@ -2,7 +2,7 @@ use crate::memory::{Address, AddressSpace, MemoryBlock}; use super::execute::MC68010; -use super::decode::{Instruction, Target, Size, Sign}; +use super::decode::{Instruction, Target, Size, Sign, ShiftDirection}; const INIT_STACK: Address = 0x00002000; const INIT_ADDR: Address = 0x00000010; @@ -27,8 +27,9 @@ fn init_test() -> (MC68010, AddressSpace) { #[cfg(test)] mod tests { + use crate::memory::Address; use super::{init_test, INIT_ADDR}; - use super::{Instruction, Target, Size, Sign}; + use super::{Instruction, Target, Size, Sign, ShiftDirection}; #[test] fn instruction_nop() { @@ -79,7 +80,7 @@ mod tests { cpu.decode_next(&mut space).unwrap(); assert_eq!(cpu.decoder.instruction, Instruction::CMP(Target::Immediate(0x30), Target::DirectDReg(0), Size::Byte)); cpu.execute_current(&mut space).unwrap(); - assert_eq!(cpu.state.sr & 0x0F, 0x00B); + assert_eq!(cpu.state.sr & 0x0F, 0x009); } #[test] @@ -119,5 +120,164 @@ mod tests { //cpu.execute_current(&mut space).unwrap(); //assert_eq!(cpu.state.sr & 0x0F, 0x00); } + + #[test] + fn instruction_asli() { + let (mut cpu, mut space) = init_test(); + + space.write_beu16(INIT_ADDR, 0xE300).unwrap(); + cpu.decode_next(&mut space).unwrap(); + assert_eq!(cpu.decoder.instruction, Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)); + + cpu.state.d_reg[0] = 0x01; + cpu.execute_current(&mut space).unwrap(); + assert_eq!(cpu.state.d_reg[0], 0x00000002); + assert_eq!(cpu.state.sr & 0x1F, 0x00); + } + + #[test] + fn instruction_asri() { + let (mut cpu, mut space) = init_test(); + + space.write_beu16(INIT_ADDR, 0xE200).unwrap(); + cpu.decode_next(&mut space).unwrap(); + assert_eq!(cpu.decoder.instruction, Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)); + + cpu.state.d_reg[0] = 0x81; + cpu.execute_current(&mut space).unwrap(); + assert_eq!(cpu.state.d_reg[0], 0x000000C0); + assert_eq!(cpu.state.sr & 0x1F, 0x19); + } + + #[test] + fn instruction_roli() { + let (mut cpu, mut space) = init_test(); + + space.write_beu16(INIT_ADDR, 0xE318).unwrap(); + cpu.decode_next(&mut space).unwrap(); + assert_eq!(cpu.decoder.instruction, Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)); + + cpu.state.d_reg[0] = 0x80; + cpu.execute_current(&mut space).unwrap(); + assert_eq!(cpu.state.d_reg[0], 0x00000001); + assert_eq!(cpu.state.sr & 0x1F, 0x01); + } + + #[test] + fn instruction_rori() { + let (mut cpu, mut space) = init_test(); + + space.write_beu16(INIT_ADDR, 0xE218).unwrap(); + cpu.decode_next(&mut space).unwrap(); + assert_eq!(cpu.decoder.instruction, Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)); + + cpu.state.d_reg[0] = 0x01; + cpu.execute_current(&mut space).unwrap(); + assert_eq!(cpu.state.d_reg[0], 0x00000080); + assert_eq!(cpu.state.sr & 0x1F, 0x09); + } + + + + + + #[test] + fn target_value_direct_d() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Word; + let expected = 0x1234; + + let target = cpu.decoder.get_mode_as_target(&mut space, 0b000, 0b001, Some(size)).unwrap(); + assert_eq!(target, Target::DirectDReg(1)); + + cpu.state.d_reg[1] = expected; + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn target_value_direct_a() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Word; + let expected = 0x1234; + + let target = cpu.decoder.get_mode_as_target(&mut space, 0b001, 0b010, Some(size)).unwrap(); + assert_eq!(target, Target::DirectAReg(2)); + + cpu.state.a_reg[2] = expected; + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn target_value_indirect_a() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Long; + let expected_addr = INIT_ADDR; + let expected = 0x12345678; + + space.write_beu32(INIT_ADDR, expected).unwrap(); + let target = cpu.decoder.get_mode_as_target(&mut space, 0b010, 0b010, Some(size)).unwrap(); + assert_eq!(target, Target::IndirectAReg(2)); + + cpu.state.a_reg[2] = INIT_ADDR as u32; + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn target_value_indirect_a_inc() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Long; + let expected_addr = INIT_ADDR; + let expected = 0x12345678; + + space.write_beu32(INIT_ADDR, expected).unwrap(); + let target = cpu.decoder.get_mode_as_target(&mut space, 0b011, 0b010, Some(size)).unwrap(); + assert_eq!(target, Target::IndirectARegInc(2)); + + cpu.state.a_reg[2] = INIT_ADDR as u32; + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + assert_eq!(cpu.state.a_reg[2], (INIT_ADDR as u32) + 4); + } + + #[test] + fn target_value_indirect_a_dec() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Long; + let expected_addr = INIT_ADDR + 4; + let expected = 0x12345678; + + space.write_beu32(INIT_ADDR, expected).unwrap(); + let target = cpu.decoder.get_mode_as_target(&mut space, 0b100, 0b010, Some(size)).unwrap(); + assert_eq!(target, Target::IndirectARegDec(2)); + + cpu.state.a_reg[2] = (INIT_ADDR as u32) + 4; + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + assert_eq!(cpu.state.a_reg[2], INIT_ADDR as u32); + } + + + #[test] + fn target_value_immediate() { + let (mut cpu, mut space) = init_test(); + + let size = Size::Word; + let expected = 0x1234; + + space.write_beu16(cpu.decoder.end as Address, expected as u16).unwrap(); + let target = cpu.decoder.get_mode_as_target(&mut space, 0b111, 0b100, Some(size)).unwrap(); + assert_eq!(target, Target::Immediate(expected)); + + let result = cpu.get_target_value(&mut space, target, size).unwrap(); + assert_eq!(result, expected); + } } diff --git a/src/devices/ata.rs b/src/devices/ata.rs index bcdcc43..d35735d 100644 --- a/src/devices/ata.rs +++ b/src/devices/ata.rs @@ -7,7 +7,7 @@ use crate::memory::{Address, Addressable}; const ATA_REG_DEV_CONTROL: Address = 0x1D; const ATA_REG_DEV_ADDRESS: Address = 0x1F; -const ATA_REG_DATA: Address = 0x20; +const ATA_REG_DATA_WORD: Address = 0x20; const ATA_REG_DATA_BYTE: Address = 0x21; const ATA_REG_FEATURE: Address = 0x23; const ATA_REG_ERROR: Address = 0x23; @@ -24,12 +24,17 @@ const ATA_CMD_WRITE_SECTORS: u8 = 0x30; const ATA_CMD_IDENTIFY: u8 = 0xEC; const ATA_CMD_SET_FEATURE: u8 = 0xEF; +const ATA_ST_BUSY: u8 = 0x80; +const ATA_ST_DATA_READY: u8 = 0x08; +const ATA_ST_ERROR: u8 = 0x01; + +const ATA_SECTOR_SIZE: u32 = 512; const DEV_NAME: &'static str = "ata"; pub struct AtaDevice { - pub read_addr: u32, - pub read_count: u32, + pub selected_sector: u32, + pub selected_count: u32, pub contents: Vec, } @@ -37,8 +42,8 @@ pub struct AtaDevice { impl AtaDevice { pub fn new() -> Self { AtaDevice { - read_addr: 0, - read_count: 0, + selected_sector: 0, + selected_count: 0, contents: vec![], } } @@ -63,8 +68,21 @@ impl Addressable for AtaDevice { let mut data = vec![0; count]; match addr { - ATA_REG_COMMAND => { - //data[0] = self.input; + ATA_REG_DATA_WORD => { + self.selected_count -= 2; + let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE -1 - self.selected_count)) as usize; + data[0] = self.contents[offset]; + data[1] = self.contents[offset + 1]; +println!(">> {:x}{:x}", data[0], data[1]); + }, + ATA_REG_DATA_BYTE => { + self.selected_count -= 1; + let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize; + data[0] = self.contents[offset]; +println!(">> {:x}", data[0]); + }, + ATA_REG_STATUS => { + data[0] = ATA_ST_DATA_READY; }, _ => { println!("{}: reading from {:0x}", DEV_NAME, addr); }, } @@ -73,7 +91,28 @@ impl Addressable for AtaDevice { } fn write(&mut self, mut addr: Address, data: &[u8]) { + println!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); match addr { + ATA_REG_DRIVE_HEAD => { self.selected_sector |= ((data[0] & 0x1F) as u32) << 24; }, + ATA_REG_CYL_HIGH => { self.selected_sector |= (data[0] as u32) << 16; }, + ATA_REG_CYL_LOW => { self.selected_sector |= (data[0] as u32) << 8; }, + ATA_REG_SECTOR_NUM => { self.selected_sector |= data[0] as u32; }, + ATA_REG_SECTOR_COUNT => { self.selected_count = (data[0] as u32) * ATA_SECTOR_SIZE; }, + ATA_REG_COMMAND => { + match data[0] { + ATA_CMD_READ_SECTORS => { println!("{}: reading sector {:x}", DEV_NAME, self.selected_sector); }, + ATA_CMD_WRITE_SECTORS => { println!("{}: writing sector {:x}", DEV_NAME, self.selected_sector); }, + ATA_CMD_IDENTIFY => { }, + ATA_CMD_SET_FEATURE => { }, + _ => { println!("{}: unrecognized command {:x}", DEV_NAME, data[0]); }, + } + }, + ATA_REG_FEATURE => { + // TODO implement features + }, + ATA_REG_DATA => { + // TODO implement writing + }, _ => { println!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); }, } } diff --git a/src/main.rs b/src/main.rs index 44682d6..60a1e63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ fn main() { space.insert(0x00100000, Box::new(ram)); let mut ata = AtaDevice::new(); - ata.load("binaries/compactflash.img").unwrap(); + ata.load("binaries/disk-with-partition-table.img").unwrap(); space.insert(0x00600000, Box::new(ata)); let mut serial = MC68681::new(); @@ -32,11 +32,12 @@ fn main() { space.insert(0x00700000, Box::new(serial)); let mut cpu = MC68010::new(); - //cpu.enable_tracing(); + cpu.enable_tracing(); //cpu.add_breakpoint(0x0c94); //cpu.add_breakpoint(0x103234); - //cpu.add_breakpoint(0x106e6a); + cpu.add_breakpoint(0x224); + cpu.add_breakpoint(0x10407e); while cpu.is_running() { match cpu.step(&mut space) {