class CPU
- CPU
- Reference
- Object
Overview
The 6502 CPU
Assembly:
The main powerhouse of the emulator is the CPU#load_asm()
method.
The method allows you to type in 6502 asm code and run it through Crystal.
The assembler has the ability to use labels as well as a few custom instructions
It also uses a semicolon ';' for inline comments
Example:
; Look at this cool comment!
cpu = CPU.new(1.0, 0x0600_u16, CPU::RES_LOCATION - 2)
cpu.load_asm("
lda #$14
")
cpu.execute
puts cpu.accumulator
To see all of the custom instructions, please see custom_instructions.cr
Example:
cpu.load_asm("
prt 22
")
cpu.execute # => puts "Type: UInt8 | Hex: 0x16 | Decimal: 22 | Binary: 0b00010110"
The assembler also has some predefined labels:
resvec:
will set the value at RES_LOCATION
to the label's memory location.
brkvec:
will set the value at BRK_LOCATION
to the label's memory location.
Example:
cpu = CPU.new(1.0, 0x0200_u16)
cpu.load_asm("
resvec:
nop
brkvec:
")
puts cpu.peek(CPU::BRK_LOCATION, true).to_s(16) # => 200
puts cpu.peek(CPU::RES_LOCATION, true).to_s(16) # => 201
Being written in Crystal, you can use string interpolation when writing assembly code, giving access for any UInt8 and UInt16 to be injected into the code.
Example:
x = 0xa4
cpu.load_asm("
lda ##{x}
")
cpu.execute
puts cpu.accumulator.to_s(16) # => a4
Note that the values are assigned at the assembler's compile time, therefore
x = 0xa4_u8
cpu.load_asm("
lda ##{x}
prt #{cpu.accumulator}
")
cpu.execute # => puts "Type: UInt8 | Hex: 0x00 | Decimal: 0 | Binary: 0b00000000"
puts cpu.accumulator.to_s(16) # => a4
In the above example, at compile time, cpu.accumulator
is set to 0. It only gets changed at the runtime of the code.
You can however achieve the hoped for effect by using multiple CPU#load_asm
methods:
x = 0xa4_u8
cpu.load_asm("
lda ##{x}
")
cpu.execute
cpu.load_asm("
prt #{cpu.accumulator}
")
cpu.execute # => puts "Type: UInt8 | Hex: 0xa4 | Decimal: 164 | Binary: 0b10100100"
Be careful when doing this though as you must keep in mind that the memory has not reset, but using CPU#load_asm
will reset the CPU#program_counter
to its original value, or to the value of a resvec:
This means that all the instructions set by any previous CPU#load_asm
's will still be there in memory.
To counteract this issue, ensure that a brk is set at the end of any code
You can however "append" code by setting the start_location
of CPU#load_asm
manually
Example
cpu = CPU.new(1.0, 0x0600_u16, CPU::RES_LOCATION - 2)
cpu.load_asm("
lda #01
")
# Code = a9 01 #
cpu.load_asm(0x0603"
lda #01
")
# Code = a9 01 a9 01 #
You can also use resvec:
to edit the default CPU#program_counter
location when editing code
Example
cpu.load_asm("
lda #01
resvec:
")
# Code = a9 01 #
cpu.load_asm("
lda #01
")
# Code = a9 01 a9 01 #
Defined in:
cr6502/6502.crcr6502/addressing.cr
cr6502/instructions.cr
cr6502/instructions/bitwise_instructions.cr
cr6502/instructions/branch_instructions.cr
cr6502/instructions/compare_instructions.cr
cr6502/instructions/custom_instructions.cr
cr6502/instructions/flag_instructions.cr
cr6502/instructions/jump_instructions.cr
cr6502/instructions/math_instructions.cr
cr6502/instructions/memory_instructions.cr
cr6502/instructions/other_instructions.cr
cr6502/instructions/register_instructions.cr
cr6502/instructions/stack_instructions.cr
cr6502/parser.cr
cr6502/parser/error.cr
cr6502/parser/scanner.cr
cr6502/parser/token.cr
Constant Summary
-
BRK_LOCATION =
65534_u16
-
Vector address for BRK
-
INSTRUCTIONS =
[{"BRK", 0_u8, 7}, {"ORAindx", 1_u8, 6}, {"ORAzpg", 5_u8, 3}, {"ASLzpg", 6_u8, 5}, {"PHP", 8_u8, 3}, {"ORAi", 9_u8, 2, 2}, {"ASLa", 10_u8, 2}, {"ORAabs", 13_u8, 4}, {"ASLabs", 14_u8, 6}, {"BPL", 16_u8, 2}, {"ORAindy", 17_u8, 5}, {"ORAzpgx", 21_u8, 3}, {"ASLzpgx", 22_u8, 6}, {"CLC", 24_u8, 2}, {"ORAabsy", 25_u8, 4}, {"ORAabsx", 29_u8, 4}, {"ASLabsx", 30_u8, 7}, {"JSR", 32_u8, 6}, {"ANDindx", 33_u8, 6}, {"BITzpg", 36_u8, 3}, {"ANDzpg", 37_u8, 2}, {"ROLzpg", 38_u8, 5}, {"PLP", 40_u8, 4}, {"ANDi", 41_u8, 2}, {"ROLa", 42_u8, 2}, {"BITabs", 44_u8, 4}, {"ANDabs", 45_u8, 4}, {"ROLabs", 46_u8, 6}, {"BMI", 48_u8, 2}, {"ANDindy", 49_u8, 5}, {"ANDzpgx", 53_u8, 3}, {"ROLzpgx", 54_u8, 6}, {"SEC", 56_u8, 2}, {"ANDabsy", 57_u8, 4}, {"ANDabsx", 61_u8, 4}, {"ROLabsx", 62_u8, 7}, {"RTI", 64_u8, 6}, {"EORindx", 65_u8, 6}, {"EORzpg", 69_u8, 3}, {"LSRzpg", 70_u8, 5}, {"PHA", 72_u8, 3}, {"EORi", 73_u8, 2}, {"LSRa", 74_u8, 2}, {"JMPabs", 76_u8, 3}, {"EORabs", 77_u8, 4}, {"LSRabs", 78_u8, 6}, {"BVC", 80_u8, 2}, {"EORindy", 81_u8, 5}, {"EORzpgx", 85_u8, 4}, {"LSRzpgx", 86_u8, 6}, {"CLI", 88_u8, 2}, {"EORabsy", 89_u8, 4}, {"EORabsx", 93_u8, 4}, {"LSRabs", 94_u8, 7}, {"RTS", 96_u8, 6}, {"ADCindx", 97_u8, 6}, {"ADCzpg", 101_u8, 3}, {"RORzpg", 102_u8, 5}, {"PLA", 104_u8, 4}, {"ADCi", 105_u8, 2}, {"RORa", 106_u8, 2}, {"JMPind", 108_u8, 5}, {"ADCabs", 109_u8, 4}, {"RORabs", 110_u8, 6}, {"BVS", 112_u8, 2}, {"ADCindy", 113_u8, 5}, {"ADCzpgx", 117_u8, 4}, {"RORzpgx", 118_u8, 6}, {"SEI", 120_u8, 2}, {"ADCabsy", 121_u8, 4}, {"ADCabsx", 125_u8, 4}, {"RORabsx", 126_u8, 7}, {"STAindx", 129_u8, 6}, {"STYzpg", 132_u8, 3}, {"STAzpg", 133_u8, 3}, {"STXzpg", 134_u8, 3}, {"DEY", 136_u8, 2}, {"TXA", 138_u8, 2}, {"STYabs", 140_u8, 4}, {"STAabs", 141_u8, 4}, {"STXabs", 142_u8, 4}, {"BCC", 144_u8, 2}, {"STAindy", 145_u8, 6}, {"STYzpgx", 148_u8, 4}, {"STAzpgx", 149_u8, 4}, {"STXzpgy", 150_u8, 4}, {"TYA", 152_u8, 2}, {"STAabsy", 153_u8, 5}, {"TXS", 154_u8, 2}, {"STAabsx", 157_u8, 5}, {"LDYi", 160_u8, 2}, {"LDAindx", 161_u8, 6}, {"LDXi", 162_u8, 2}, {"LDYzpg", 164_u8, 3}, {"LDAzpg", 165_u8, 3}, {"LDXzpg", 166_u8, 3}, {"TAY", 168_u8, 2}, {"LDAi", 169_u8, 2}, {"TAX", 170_u8, 2}, {"LDYabs", 172_u8, 4}, {"LDAabs", 173_u8, 4}, {"LDXabs", 174_u8, 4}, {"BCS", 176_u8, 2}, {"LDAindy", 177_u8, 5}, {"LDYzpgx", 180_u8, 4}, {"LDAzpgx", 181_u8, 4}, {"LDXzpgy", 182_u8, 4}, {"CLV", 184_u8, 2}, {"LDAabsy", 185_u8, 4}, {"TSX", 186_u8, 2}, {"LDYabsx", 188_u8, 4}, {"LDAabsx", 189_u8, 4}, {"LDXabsy", 190_u8, 4}, {"CPYi", 192_u8, 2}, {"CMPindx", 193_u8, 6}, {"CPYzpg", 196_u8, 3}, {"CMPzpg", 197_u8, 3}, {"DECzpg", 198_u8, 5}, {"INY", 200_u8, 2}, {"CMPi", 201_u8, 2}, {"DEX", 202_u8, 2}, {"CPYabs", 204_u8, 4}, {"CMPabs", 205_u8, 4}, {"DECabs", 206_u8, 6}, {"BNE", 208_u8, 2}, {"CMPindy", 209_u8, 5}, {"CMPzpgx", 213_u8, 4}, {"DECzpgx", 214_u8, 6}, {"CLD", 216_u8, 2}, {"CMPabsy", 217_u8, 4}, {"CMPabsx", 221_u8, 4}, {"DECabsx", 222_u8, 7}, {"CPXi", 224_u8, 2}, {"SBCindx", 225_u8, 6}, {"CPXzpg", 228_u8, 3}, {"SBCzpg", 229_u8, 3}, {"INCzpg", 230_u8, 5}, {"INX", 232_u8, 2}, {"SBCi", 233_u8, 2}, {"NOP", 234_u8, 2}, {"CPXabs", 236_u8, 4}, {"SBCabs", 237_u8, 4}, {"INCabs", 238_u8, 6}, {"BEQ", 240_u8, 2}, {"SBCindy", 241_u8, 5}, {"SBCzpgx", 245_u8, 4}, {"INCzpgx", 246_u8, 6}, {"SED", 248_u8, 2}, {"SBCabsy", 249_u8, 4}, {"SBCabsx", 253_u8, 4}, {"INCabsx", 254_u8, 7}, {"PRTzpg", 2_u8, 2}, {"PRTabs", 3_u8, 3}, {"LOG", 4_u8, 10}, {"STP", 7_u8, 2}, {"BCClabel", 11_u8, 2}, {"BCSlabel", 27_u8, 2}, {"BEQlabel", 43_u8, 2}, {"BMIlabel", 59_u8, 2}, {"BNElabel", 75_u8, 2}, {"BPLlabel", 91_u8, 2}, {"BVClabel", 107_u8, 2}, {"BVSlabel", 123_u8, 2}]
-
List of instructions sorted by its opcode
Format is {"InstructionName", opcode, cycle length, byte length}
-
KEYWORDS =
{"ADC" => TokenType::ADC, "AND" => TokenType::AND, "ASL" => TokenType::ASL, "BCC" => TokenType::BCC, "BCS" => TokenType::BCS, "BEQ" => TokenType::BEQ, "BIT" => TokenType::BIT, "BMI" => TokenType::BMI, "BNE" => TokenType::BNE, "BPL" => TokenType::BPL, "BRK" => TokenType::BRK, "BVC" => TokenType::BVC, "BVS" => TokenType::BVS, "CLC" => TokenType::CLC, "CLD" => TokenType::CLD, "CLI" => TokenType::CLI, "CLV" => TokenType::CLV, "CMP" => TokenType::CMP, "CPX" => TokenType::CPX, "CPY" => TokenType::CPY, "DEC" => TokenType::DEC, "DEX" => TokenType::DEX, "DEY" => TokenType::DEY, "EOR" => TokenType::EOR, "INC" => TokenType::INC, "INX" => TokenType::INX, "INY" => TokenType::INY, "JMP" => TokenType::JMP, "JSR" => TokenType::JSR, "LDA" => TokenType::LDA, "LDX" => TokenType::LDX, "LDY" => TokenType::LDY, "LSR" => TokenType::LSR, "NOP" => TokenType::NOP, "ORA" => TokenType::ORA, "PHA" => TokenType::PHA, "PHP" => TokenType::PHP, "PLA" => TokenType::PLA, "PLP" => TokenType::PLP, "ROL" => TokenType::ROL, "ROR" => TokenType::ROR, "RTI" => TokenType::RTI, "RTS" => TokenType::RTS, "SBC" => TokenType::SBC, "SEC" => TokenType::SEC, "SED" => TokenType::SED, "SEI" => TokenType::SEI, "STA" => TokenType::STA, "STX" => TokenType::STX, "STY" => TokenType::STY, "TAX" => TokenType::TAX, "TAY" => TokenType::TAY, "TSX" => TokenType::TSX, "TXA" => TokenType::TXA, "TXS" => TokenType::TXS, "TYA" => TokenType::TYA, "PRT" => TokenType::PRT, "LOG" => TokenType::LOG, "STP" => TokenType::STP}
-
The keywords for the tokens, used when parsing
-
RES_LOCATION =
65532_u16
-
Vector address for RESET
Constructors
-
.new(clock_cycle_mhz : Float = 1.79, reset : UInt16 = 0, brk : UInt16 = 0)
Creates a 6502 CPU
Macro Summary
-
parse_address
Parses the address of the line of code
-
parse_address_mode
Parses the address mode of the current line of code
Instance Method Summary
-
#accumulator : UInt8
The 8-bit accumulator.
-
#adc(m_value : UInt8)
Add with Carry
-
#add_instruction(hex : UInt8 | UInt16)
Adds an instruction, given it's opcode, into the current location in memory of the
CPU#program_counter
and increments theCPU#program_counter
by the byte length of the given hex -
#and(m_value : UInt8)
Bitwise AND with Accumulator
-
#asl(m_value : UInt8, m : UInt16 | UInt8, accumulator : Bool = false)
Arithmetic Shift Left
-
#bcc(m_value : UInt8 | UInt16)
Branch on Carry Clear
-
#bcd(byte : UInt8 | Int8)
Calculates a byte into a Binary Coded Decimal (BCD)
-
#bcs(m_value : UInt8 | UInt16)
Branch on Carry Set
-
#beq(m_value : UInt8 | UInt16)
Branch on Equal
-
#bit(m_value : UInt8)
Test Bits
-
#bmi(m_value : UInt8 | UInt16)
Branch on Minus
-
#bne(m_value : UInt8 | UInt16)
Branch on Not Equal
-
#bpl(m_value : UInt8 | UInt16)
Branch on Plus
-
#brk
Break
-
#bvc(m_value : UInt8 | UInt16)
Branch on Overflow Clear
-
#bvs(m_value : UInt8 | UInt16)
Branch on Overflow Set
-
#clc
Clear Carry
-
#cld
Clear Decimal
-
#cli
Clear Interrupt
-
#clock_cycle_mhz : Float64
The clock cycle in megahertz to run at.
-
#clv
Clear Overflow
-
#cmp(m_value : UInt8)
Compare Accumulator
-
#cpx(m_value : UInt8)
Compare X Register
-
#cpy(m_value : UInt8)
Compare Y Register
-
#dec(m_value : UInt8, m : UInt16 | UInt8)
Decrement Memory
-
#dex
Decrement X
-
#dey
Decrement Y
-
#eor(m_value : UInt8)
Bitwise Exclusive-OR with Accumulator
-
#execute(end_on_tight_loop : Bool = true, reset : Bool = true)
Runs all instructions
-
#flags : UInt8
The CPU's 7 flag bits
-
#get_flag(flag : Flags) : Bool
Gets a the value of a bit in
#flags
-
#get_ind(address : Int)
Gets the Indirect Address of a given address
-
#get_indx(address : Int)
Gets the Indirect X Address of a given address
-
#get_indy(address : Int)
Gets the Indirect Y Address of a given address
-
#inc(m_value : UInt8, m : UInt16 | UInt8)
Increment Memory
-
#inx
Increment X
-
#iny
Increment Y
-
#jmp(m_value : UInt16)
Jump
-
#jsr(address : UInt16)
Jump Saving Return
-
#lda(m_value : UInt8)
Load Accumulator
-
#ldx(m_value : UInt8)
Load X Register
-
#ldy(m_value : UInt8)
Load Y Register
-
#load_asm(code : String, start_location : UInt16 = (peek(RES_LOCATION, true)).to_u16)
Loads 6502 assembly instructions
-
#log
Prints out information about the CPU in its current state
-
#lsr(m_value : UInt8, m : UInt16 | UInt8, accumulator : Bool = false)
Logical Shift Right
-
#memory : IO::Memory
The 64kb (65536 bytes) of accessible memory
-
#nop
No Operation
-
#ora(m_value : UInt8)
Bitwise OR with Accumulator
-
#peek(mem_location : Int, two_byte : Bool = false)
Reads a value from memory.
-
#pha
Push Accumulator
-
#php
Push Processor Status (
CPU#flags
) -
#pla
Pull Accumulator
-
#plp
Pull Processor Status
-
#poke(mem_location : Int, data : UInt8 | UInt16)
Pokes a value into a location in memory
-
#print_memory(start_pos : Int = 0, length : Int = 65536)
Prints out the current memory.
-
#program_counter : UInt16
The 16-bit program counter which points to the next instruction in memory to execute.
-
#prt(value_to_print : UInt8 | UInt16)
Print a 8-bit or 16-bit value.
-
#rol(m_value : UInt8, m : UInt16 | UInt8, accumulator : Bool = false)
Rotate Left
-
#ror(m_value : UInt8, m : UInt16 | UInt8, accumulator : Bool = false)
Rotate Right
-
#rti
Return from Interrupt
-
#rts
Return to Saved
-
#run_instruction
Runs the current value of
CPU#program_counter
's location in memory as an instruction -
#sbc(m_value : UInt8)
Subtract with Carry
-
#sec
Set Carry
-
#sed
Set Decimal
-
#sei
Set Interrupt
-
#set_flag(flag : Flags, set : Bool)
Sets the value of a bit in
#flags
-
#sta(m : UInt16 | UInt8)
Store Accumulator
-
#stack_pointer : UInt8
The 8-bit stack pointer which points to the current position in the Stack.
-
#step(end_on_tight_loop : Bool = true)
Runs the next instruction
-
#stp
Stops any active running
CPU#execute
-
#stx(m : UInt16 | UInt8)
Store X Register
-
#sty(m : UInt16 | UInt8)
Store Y Register
-
#tax
Transfer A to X
-
#tay
Transfer A to Y
-
#tsx
Transfer Stack Pointer to X
-
#txa
Tranfer X to A
-
#txs
Transfer X to Stack Pointer
-
#tya
Transfer Y to A
-
#x_index : UInt8
The 8-bit x index register
-
#y_index : UInt8
The 8-bit y index register
Constructor Detail
Creates a 6502 CPU
The clock cycle is set in megahertz
reset
is the value set at RES_LOCATION
and is used to find where the #program_counter
should start
#brk
is the value set at BRK_LOCATION
and is used to find where CPU#brk
should goto
Macro Detail
Instance Method Detail
Add with Carry
ADC behavior depends on the state of the CPU::Flags::DecimalMode
flag. In decimal mode, the values upon which the addition is performed are interpreted as packed BCD (Binary Coded Decimal).
Adds an instruction, given it's opcode, into the current location in memory of the CPU#program_counter
and increments the CPU#program_counter
by the byte length of the given hex
Arithmetic Shift Left
ASL shifts all bits left one position. 0 is shifted into bit 0 and the original bit 7 is shifted into the Carry.
Calculates a byte into a Binary Coded Decimal (BCD)
BCD is whereby the upper and lower nibbles (4-bits) of a byte (8-bits) are treated as two digits in a decimal number;
The upper nibble contains the number from the 'tens column'; and the lower nibble, the number from the 'units column'
Test Bits
BIT sets the Z flag as though the value in the address tested were ANDed with the accumulator.
The N and V flags are set equal to bits 7 and 6 respectively of the value in the tested address.
Break
BRK sets the B flag, and then generates a forced interrupt. The Interrupt flag is ignored and the CPU goes through the normal interrupt process. In the interrupt service routine, the state of the B flag can be used to distinguish a BRK from a standard interrupt.
BRK causes a non-maskable interrupt and increments the program counter by one. Therefore an CPU#rti
will go to the address of the BRK +2 so that BRK may be used to replace a two-byte instruction for debugging and the subsequent RTI will be correct.
The clock cycle in megahertz to run at.
Defaults to the NES's 6502 speed, 1.79mhz.
Set in CPU#initialize
Compare Accumulator
Compare sets processor flags as if a subtraction had been carried out.
If the accumulator and the compared value are equal, the result of the subtraction is zero and the Zero (Z) flag is set. If the accumulator is equal or greater than the compared value, the Carry (C) flag is set.
Compare X Register
Operation and flag results are identical to equivalent mode accumulator CPU#cmp
operations.
Compare Y Register
Operation and flag results are identical to equivalent mode accumulator CPU#cmp
operations.
Runs all instructions
If end_on_tight_loop
is true
, it will not step if the current instruction sets the #program_counter
to itself, creating a tight loop
NOTE A real 6502 does not end on tight loops, this is only used to ensure that a program doesn't run forever
If reset
is true, it will set the CPU#program_counter
to its original value. If reset
is false, it simply continues the code
from the last instruction. This is only really matters when end_on_tight_loop
is true or when using CPU#stp
Jump
JMP loads the program counter with the absolute address, or the address stored at the memory location of the indirect address. Program execution proceeds from the new program counter value.
Jump Saving Return
JSR pushes the address-1 of the next operation to the stack before transferring the value of the argument to the program counter. JSR behaves just like a JMP, but saves the return address to the stack first, thus creating a subroutine.
Subroutines are normally terminated by an CPU#rts
instruction.
Loads 6502 assembly instructions
Uses ; for comments
Works with labels label:
the resvec:
label will set the value at RES_LOCATION
to the label's memory location
the brkvec:
label will set the value at BRK_LOCATION
to the label's memory location
You can also manually set the starting location to write the instructions at. Useful for appending or editing code
Logical Shift Right
LSR shifts all bits right one position. 0 is shifted into bit 7 and the original bit 0 is shifted into the Carry.
No Operation
A NOP takes 2 machine cycles to execute, but it has no effect on any register, memory location, or processor flag. Thus, it takes up time and space but performs no operation.
NOP can be used to reserve space for future modifications or to remove existing code without changing the memory locations of code that follows it.
NOP can also be used in tightly timed code, to idly take up 2 cycles without having any other side effects.
Reads a value from memory. Will read as a UInt16
if two_byte
is true
Prints out the current memory.
Prints out 16 bytes per line. Therefore #print_memory(0x0F4, 1)
will print 0x0F40
through 0x0F4F
The 16-bit program counter which points to the next instruction in memory to execute.
Gets set after a command is read, but before it is executed.
Meaning it points to the next instruction to execute, not the one that is currently executing
Print a 8-bit or 16-bit value. Mainly used with string interpolation
Example:
cpu = CPU.new
cpu.load_asm("
prt #{cpu.stack_pointer}
")
cpu.execute # => puts "Type: UInt8 | Hex: ff | Decimal 255 | Binary: 11111111"
Rotate Left
ROL shifts all bits left one position. The Carry is shifted into bit 0 and the original bit 7 is shifted into the Carry.
Rotate Right
ROR shifts all bits right one position. The Carry is shifted into bit 7 and the original bit 0 is shifted into the Carry.
Return from Interrupt
RTI retrieves the Processor Status byte and Program Counter from the stack in that order. Interrupts push the program counter first and then the processor status.
Unlike RTS, the return address on the stack is the actual address rather than the address-1.
Return to Saved
RTS pulls the top two bytes off the stack (low byte first) and transfers them to the program counter. The program counter is incremented by one and then execution proceeds from there.
RTS is typically used in combination with a CPU#jsr
which saves the return address-1 to the stack.
Runs the current value of CPU#program_counter
's location in memory as an instruction
Subtract with Carry
SBC behavior depends on the state of the CPU::Flags::DecimalMode
flag. In decimal mode, the values upon which the subtraction is performed are interpreted as packed BCD (Binary Coded Decimal).
The 8-bit stack pointer which points to the current position in the Stack.
The stack ranges from 0x100 to 0x1FF, starting at 0x1FF
Runs the next instruction
if end_on_tight_loop
is true
, it will not step if the current instruction sets the #program_counter
to itself, creating a tight loop
NOTE A real 6502 does not end on tight loops, this is only used to ensure that a program doesn't run forever
Stops any active running CPU#execute
When used with CPU#execute(reset: false)
, it can act as a way to pause