1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum CpuState {
13 #[default]
15 FetchOpcode,
16
17 FetchOperandLo,
19
20 FetchOperandHi,
22
23 ResolveAddress,
26
27 ReadData,
29
30 WriteData,
32
33 RmwRead,
35
36 RmwDummyWrite,
38
39 RmwWrite,
41
42 Execute,
44
45 FetchIndirectLo,
47
48 FetchIndirectHi,
50
51 AddIndex,
53
54 PushHi,
56
57 PushLo,
59
60 PushStatus,
62
63 PopLo,
65
66 PopHi,
68
69 PopStatus,
71
72 InternalCycle,
74
75 BranchTaken,
77
78 BranchPageCross,
80
81 InterruptPushPcHi,
83
84 InterruptPushPcLo,
86
87 InterruptPushStatus,
89
90 InterruptFetchVectorLo,
92
93 InterruptFetchVectorHi,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
105pub enum InstructionType {
106 #[default]
108 Read,
109
110 Write,
112
113 ReadModifyWrite,
115
116 Implied,
119
120 Accumulator,
123
124 Branch,
127
128 JumpAbsolute,
131
132 JumpIndirect,
135
136 JumpSubroutine,
139
140 ReturnSubroutine,
143
144 ReturnInterrupt,
147
148 Push,
151
152 Pull,
155
156 Break,
159
160 Jam,
162}
163
164impl CpuState {
165 #[inline]
167 pub const fn is_read(&self) -> bool {
168 matches!(
169 self,
170 Self::FetchOpcode
171 | Self::FetchOperandLo
172 | Self::FetchOperandHi
173 | Self::ReadData
174 | Self::RmwRead
175 | Self::FetchIndirectLo
176 | Self::FetchIndirectHi
177 | Self::ResolveAddress
178 | Self::PopLo
179 | Self::PopHi
180 | Self::PopStatus
181 | Self::InterruptFetchVectorLo
182 | Self::InterruptFetchVectorHi
183 )
184 }
185
186 #[inline]
188 pub const fn is_write(&self) -> bool {
189 matches!(
190 self,
191 Self::WriteData
192 | Self::RmwDummyWrite
193 | Self::RmwWrite
194 | Self::PushHi
195 | Self::PushLo
196 | Self::PushStatus
197 | Self::InterruptPushPcHi
198 | Self::InterruptPushPcLo
199 | Self::InterruptPushStatus
200 )
201 }
202
203 #[inline]
205 pub const fn is_internal(&self) -> bool {
206 matches!(
207 self,
208 Self::Execute
209 | Self::AddIndex
210 | Self::InternalCycle
211 | Self::BranchTaken
212 | Self::BranchPageCross
213 )
214 }
215}
216
217impl InstructionType {
218 #[inline]
221 pub const fn base_cycles(&self) -> u8 {
222 match self {
223 Self::Implied => 2,
224 Self::Accumulator => 2,
225 Self::Read => 2, Self::Write => 2, Self::ReadModifyWrite => 2, Self::Branch => 2, Self::JumpAbsolute => 3,
230 Self::JumpIndirect => 5,
231 Self::JumpSubroutine => 6,
232 Self::ReturnSubroutine => 6,
233 Self::ReturnInterrupt => 6,
234 Self::Push => 3,
235 Self::Pull => 4,
236 Self::Break => 7,
237 Self::Jam => 2,
238 }
239 }
240
241 #[inline]
243 pub const fn has_page_cross_penalty(&self) -> bool {
244 matches!(self, Self::Read | Self::Branch)
245 }
246
247 #[inline]
249 pub const fn is_rmw(&self) -> bool {
250 matches!(self, Self::ReadModifyWrite)
251 }
252
253 #[inline]
259 pub const fn from_opcode(opcode: u8) -> Self {
260 match opcode {
261 0x10 | 0x30 | 0x50 | 0x70 | 0x90 | 0xB0 | 0xD0 | 0xF0 => Self::Branch,
263
264 0x4C => Self::JumpAbsolute, 0x6C => Self::JumpIndirect, 0x20 => Self::JumpSubroutine, 0x60 => Self::ReturnSubroutine, 0x40 => Self::ReturnInterrupt, 0x00 => Self::Break, 0x48 | 0x08 => Self::Push, 0x68 | 0x28 => Self::Pull, 0x0A | 0x2A | 0x4A | 0x6A => Self::Accumulator, 0xAA | 0xA8 | 0x8A | 0x98 | 0xBA | 0x9A => Self::Implied,
284 0xE8 | 0xC8 | 0xCA | 0x88 => Self::Implied,
286 0x18 | 0x38 | 0x58 | 0x78 | 0xB8 | 0xD8 | 0xF8 => Self::Implied,
288 0xEA => Self::Implied,
290 0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xFA => Self::Implied,
292
293 0x85 | 0x95 | 0x8D | 0x9D | 0x99 | 0x81 | 0x91 => Self::Write,
296 0x86 | 0x96 | 0x8E => Self::Write,
298 0x84 | 0x94 | 0x8C => Self::Write,
300 0x87 | 0x97 | 0x8F | 0x83 => Self::Write,
302 0x93 | 0x9F | 0x9C | 0x9E | 0x9B => Self::Write,
304
305 0x06 | 0x16 | 0x0E | 0x1E => Self::ReadModifyWrite,
308 0x46 | 0x56 | 0x4E | 0x5E => Self::ReadModifyWrite,
310 0x26 | 0x36 | 0x2E | 0x3E => Self::ReadModifyWrite,
312 0x66 | 0x76 | 0x6E | 0x7E => Self::ReadModifyWrite,
314 0xE6 | 0xF6 | 0xEE | 0xFE => Self::ReadModifyWrite,
316 0xC6 | 0xD6 | 0xCE | 0xDE => Self::ReadModifyWrite,
318 0x07 | 0x17 | 0x0F | 0x1F | 0x1B | 0x03 | 0x13 => Self::ReadModifyWrite,
320 0x27 | 0x37 | 0x2F | 0x3F | 0x3B | 0x23 | 0x33 => Self::ReadModifyWrite,
322 0x47 | 0x57 | 0x4F | 0x5F | 0x5B | 0x43 | 0x53 => Self::ReadModifyWrite,
324 0x67 | 0x77 | 0x6F | 0x7F | 0x7B | 0x63 | 0x73 => Self::ReadModifyWrite,
326 0xC7 | 0xD7 | 0xCF | 0xDF | 0xDB | 0xC3 | 0xD3 => Self::ReadModifyWrite,
328 0xE7 | 0xF7 | 0xEF | 0xFF | 0xFB | 0xE3 | 0xF3 => Self::ReadModifyWrite,
330
331 0x02 | 0x12 | 0x22 | 0x32 | 0x42 | 0x52 | 0x62 | 0x72 | 0x92 | 0xB2 | 0xD2 | 0xF2 => {
333 Self::Jam
334 }
335
336 0xA9 | 0xA5 | 0xB5 | 0xAD | 0xBD | 0xB9 | 0xA1 | 0xB1 => Self::Read,
339 0xA2 | 0xA6 | 0xB6 | 0xAE | 0xBE => Self::Read,
341 0xA0 | 0xA4 | 0xB4 | 0xAC | 0xBC => Self::Read,
343 0x69 | 0x65 | 0x75 | 0x6D | 0x7D | 0x79 | 0x61 | 0x71 => Self::Read,
345 0xE9 | 0xE5 | 0xF5 | 0xED | 0xFD | 0xF9 | 0xE1 | 0xF1 | 0xEB => Self::Read,
347 0x29 | 0x25 | 0x35 | 0x2D | 0x3D | 0x39 | 0x21 | 0x31 => Self::Read,
349 0x09 | 0x05 | 0x15 | 0x0D | 0x1D | 0x19 | 0x01 | 0x11 => Self::Read,
351 0x49 | 0x45 | 0x55 | 0x4D | 0x5D | 0x59 | 0x41 | 0x51 => Self::Read,
353 0xC9 | 0xC5 | 0xD5 | 0xCD | 0xDD | 0xD9 | 0xC1 | 0xD1 => Self::Read,
355 0xE0 | 0xE4 | 0xEC => Self::Read,
357 0xC0 | 0xC4 | 0xCC => Self::Read,
359 0x24 | 0x2C => Self::Read,
361 0xA7 | 0xB7 | 0xAF | 0xBF | 0xA3 | 0xB3 => Self::Read,
363 0xBB => Self::Read,
365 0x0B | 0x2B => Self::Read, 0x4B => Self::Read, 0x6B => Self::Read, 0x8B => Self::Read, 0xAB => Self::Read, 0xCB => Self::Read, 0x80 | 0x82 | 0x89 | 0xC2 | 0xE2 => Self::Read,
374 0x04 | 0x44 | 0x64 | 0x14 | 0x34 | 0x54 | 0x74 | 0xD4 | 0xF4 => Self::Read,
375 0x0C | 0x1C | 0x3C | 0x5C | 0x7C | 0xDC | 0xFC => Self::Read,
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn test_cpu_state_default() {
386 assert_eq!(CpuState::default(), CpuState::FetchOpcode);
387 }
388
389 #[test]
390 fn test_instruction_type_default() {
391 assert_eq!(InstructionType::default(), InstructionType::Read);
392 }
393
394 #[test]
395 fn test_state_is_read() {
396 assert!(CpuState::FetchOpcode.is_read());
397 assert!(CpuState::ReadData.is_read());
398 assert!(!CpuState::WriteData.is_read());
399 assert!(!CpuState::Execute.is_read());
400 }
401
402 #[test]
403 fn test_state_is_write() {
404 assert!(CpuState::WriteData.is_write());
405 assert!(CpuState::RmwWrite.is_write());
406 assert!(!CpuState::ReadData.is_write());
407 assert!(!CpuState::Execute.is_write());
408 }
409
410 #[test]
411 fn test_state_is_internal() {
412 assert!(CpuState::Execute.is_internal());
413 assert!(CpuState::AddIndex.is_internal());
414 assert!(!CpuState::FetchOpcode.is_internal());
415 assert!(!CpuState::WriteData.is_internal());
416 }
417
418 #[test]
419 fn test_instruction_type_base_cycles() {
420 assert_eq!(InstructionType::Implied.base_cycles(), 2);
421 assert_eq!(InstructionType::Push.base_cycles(), 3);
422 assert_eq!(InstructionType::Pull.base_cycles(), 4);
423 assert_eq!(InstructionType::JumpIndirect.base_cycles(), 5);
424 assert_eq!(InstructionType::JumpSubroutine.base_cycles(), 6);
425 assert_eq!(InstructionType::Break.base_cycles(), 7);
426 }
427
428 #[test]
429 fn test_page_cross_penalty() {
430 assert!(InstructionType::Read.has_page_cross_penalty());
431 assert!(InstructionType::Branch.has_page_cross_penalty());
432 assert!(!InstructionType::Write.has_page_cross_penalty());
433 assert!(!InstructionType::ReadModifyWrite.has_page_cross_penalty());
434 }
435
436 #[test]
437 fn test_from_opcode_branches() {
438 assert_eq!(InstructionType::from_opcode(0x10), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0x30), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0x50), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0x70), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0x90), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0xB0), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0xD0), InstructionType::Branch); assert_eq!(InstructionType::from_opcode(0xF0), InstructionType::Branch);
447 }
449
450 #[test]
451 fn test_from_opcode_jumps() {
452 assert_eq!(
453 InstructionType::from_opcode(0x4C),
454 InstructionType::JumpAbsolute
455 );
456 assert_eq!(
457 InstructionType::from_opcode(0x6C),
458 InstructionType::JumpIndirect
459 );
460 assert_eq!(
461 InstructionType::from_opcode(0x20),
462 InstructionType::JumpSubroutine
463 );
464 assert_eq!(
465 InstructionType::from_opcode(0x60),
466 InstructionType::ReturnSubroutine
467 );
468 assert_eq!(
469 InstructionType::from_opcode(0x40),
470 InstructionType::ReturnInterrupt
471 );
472 assert_eq!(InstructionType::from_opcode(0x00), InstructionType::Break);
473 }
474
475 #[test]
476 fn test_from_opcode_stack() {
477 assert_eq!(InstructionType::from_opcode(0x48), InstructionType::Push); assert_eq!(InstructionType::from_opcode(0x08), InstructionType::Push); assert_eq!(InstructionType::from_opcode(0x68), InstructionType::Pull); assert_eq!(InstructionType::from_opcode(0x28), InstructionType::Pull); }
482
483 #[test]
484 fn test_from_opcode_accumulator() {
485 assert_eq!(
486 InstructionType::from_opcode(0x0A),
487 InstructionType::Accumulator
488 ); assert_eq!(
490 InstructionType::from_opcode(0x2A),
491 InstructionType::Accumulator
492 ); assert_eq!(
494 InstructionType::from_opcode(0x4A),
495 InstructionType::Accumulator
496 ); assert_eq!(
498 InstructionType::from_opcode(0x6A),
499 InstructionType::Accumulator
500 ); }
502
503 #[test]
504 fn test_from_opcode_implied() {
505 assert_eq!(InstructionType::from_opcode(0xAA), InstructionType::Implied); assert_eq!(InstructionType::from_opcode(0xA8), InstructionType::Implied); assert_eq!(InstructionType::from_opcode(0xE8), InstructionType::Implied); assert_eq!(InstructionType::from_opcode(0xC8), InstructionType::Implied); assert_eq!(InstructionType::from_opcode(0x18), InstructionType::Implied); assert_eq!(InstructionType::from_opcode(0xEA), InstructionType::Implied);
515 }
516
517 #[test]
518 fn test_from_opcode_write() {
519 assert_eq!(InstructionType::from_opcode(0x85), InstructionType::Write); assert_eq!(InstructionType::from_opcode(0x8D), InstructionType::Write); assert_eq!(InstructionType::from_opcode(0x86), InstructionType::Write);
524 assert_eq!(InstructionType::from_opcode(0x84), InstructionType::Write);
526 }
527
528 #[test]
529 fn test_from_opcode_rmw() {
530 assert_eq!(
532 InstructionType::from_opcode(0x06),
533 InstructionType::ReadModifyWrite
534 );
535 assert_eq!(
536 InstructionType::from_opcode(0x0E),
537 InstructionType::ReadModifyWrite
538 );
539 assert_eq!(
541 InstructionType::from_opcode(0xE6),
542 InstructionType::ReadModifyWrite
543 );
544 assert_eq!(
546 InstructionType::from_opcode(0xC6),
547 InstructionType::ReadModifyWrite
548 );
549 assert_eq!(
551 InstructionType::from_opcode(0x07),
552 InstructionType::ReadModifyWrite
553 ); }
555
556 #[test]
557 fn test_from_opcode_read() {
558 assert_eq!(InstructionType::from_opcode(0xA9), InstructionType::Read); assert_eq!(InstructionType::from_opcode(0xA5), InstructionType::Read); assert_eq!(InstructionType::from_opcode(0xA2), InstructionType::Read);
563 assert_eq!(InstructionType::from_opcode(0xA0), InstructionType::Read);
565 assert_eq!(InstructionType::from_opcode(0x69), InstructionType::Read);
567 assert_eq!(InstructionType::from_opcode(0xC9), InstructionType::Read);
569 }
570
571 #[test]
572 fn test_from_opcode_jam() {
573 assert_eq!(InstructionType::from_opcode(0x02), InstructionType::Jam);
574 assert_eq!(InstructionType::from_opcode(0x12), InstructionType::Jam);
575 assert_eq!(InstructionType::from_opcode(0x22), InstructionType::Jam);
576 }
577}