wedNESday #8 - A long delay

Posted on Wed 24 April 2024 in nes

a long time ago

A Long Time Ago

It's been a while since my last update, and if you've been visiting this page eagerly awaiting news, thank you for your patience. Let's dive right in. Recently, I've been using the "wedNESday" project to thoroughly test NES emulators and improve them.

In a Galaxy Far Far Away

Emulators are being tested using a CPU specification derived from Nintego. Here are the emulators I've been putting through their paces:

  • Arbne: Discovered in a serendipitous encounter within a Reddit thread, a call for feedback that demands an answer.
  • ApplePy: An Apple 2 emulator by James Tauber, although not a NES emulator, it relies on the 6502 architecture.
  • JSnes: A NES emulator written in Javascript, pivotal for my experiments in nodeNES.
  • Py3NES: Constructed amidst live streaming sessions, Py3NES offers a fascinating insight into NES emulation, with its entire development journey documented on YouTube.
  • Py65: A Python-based 6502 emulator, serving as a critical component in the testing arsenal.
  • Pyntendo: Implemented also CPython, Pyntendo promises an intriguing exploration into NES emulation.
  • 6502js: Gregory Estrade's 6502 emulator, a very interesting for quest for accuracy and reliability.

The Goal is to improve the overwall accuracy

Summary of Results

The following table succinctly summarizes the outcomes gleaned from the 6502 instruction tests:

Instruction ApplePy Arbne JSnes Py3NES Py65 Pyntendo 6502js
adc immediate with bcd NOT NOT NOT NOT OK OK OK
bcc OK NOT OK OK OK OK OK
beq OK OK NOT OK OK OK OK
bne OK OK NOT OK OK OK OK
brk NOT NOT NOT NOT OK OK OK
irq interrupt NOT NOT NOT NOT NOT NOT NOT
jsr NOT NOT NOT OK NOT OK NOT
jsr stack pointer NOT OK NOT OK NOT OK NOT
jsr with illegal opcode NOT OK OK NOT OK NOT NOT
nmi interrupt NOT NOT NOT NOT NOT NOT NOT
php OK OK NOT OK OK OK OK
pla OK OK OK OK OK OK NOT
pla n flag set OK OK OK OK OK OK NOT
pla z flag set OK OK OK OK OK OK NOT
plp NOT NOT NOT NOT NOT NOT NOT
rst interrupt NOT NOT NOT NOT NOT NOT NOT
rti OK OK NOT OK OK OK NOT
rts OK OK NOT OK OK OK NOT
sbc immediate with bcd NOT OK OK OK OK OK OK
tsx OK OK NOT OK OK OK OK
txs OK OK NOT OK OK OK OK

You can find an updated version of this table at ẁedNESday's documentation.

Result Details

NES emulators typically do not implement the decimal mode, rendering ADC and SEC operations with BCD optional, whereas these functionalities are essential for 6502 emulators. Currently, my focus lies on refining the interrupt specification. That's explain why irq, nmi, and reset interruption tests fail across all emulators.

  • PLP Instruction Issue: Across all emulators, the PLP instruction presents a notable issue, which will be addressed in further detail in a dedicated topic.
  • ApplePy Limitations: ApplePy lacks implementations for math operations with BCD and interrupts.
  • Arbne Quirk: Arbne exhibits a minor issue with the BRK instruction, specifically concerning the return of the Program Counter, resulting in a peculiar plus one offset.
  • JSnes Challenges: JSnes faces challenges with all branch instructions, JSR, and RTS due to issues with instructions that alter the Program Counter position. This may stem from underlying logic in the bridge.
  • Py3NES BRK Issue: Py3NES encounters an issue with the BRK instruction.
  • Py65 Differences: Py65 demonstrates discrepancies in the behavior of jump instructions.
  • Pyntendo, Pytendo has only a minor issue with invalid opcodes.
  • 6502js Procedure Call: The 6502.js emulator encounters issues with the JSR (Jump to Subroutine) and RTS (Return from Subroutine) instructions. These issues impact the proper execution of subroutine calls and returns.
  • Number of Cycles: Across all emulators, I found it necessary to disable cycle checking in certain instructions to ensure proper functionality. I will be publishing a new table documenting these instances where cycle checking had to be skipped.

In response to these findings, I've initiated forks of each emulator to address the some of the identified issues. The goal is to synchronize the behavior of all emulators, ensuring consistency and compatibility. To achieve this, I'm focusing on improving the test cases for instructions such as BRK, JSR, PLP, and RST, which are crucial for accurate emulation.

PLP Instruction

The PLP instruction, short for "Pull Processor Status from Stack," retrieves the last value from the stack and updates the Status register accordingly. It affects all flags except the Break (B) and Unused (U) flags.

Essentially, this means that whatever value the B flag had before the PLP execution will remain unchanged afterward, as will the U flag. According to the Nintego PLP Test, if the value 0xFF is pushed onto the stack, PLP will pull 0xCF into the Status register.

However, giving the Nintengo example, it's worth noting that although fetching the U flag will always return "1", although the corresponding bit in the status register is now set to 0 (0xCF). Further testing on real hardware may be necessary to validate this behavior.

STX $FF
TXS
PLP
JSR print_p_reg
JSR print_flags

Testing Specification

I often emphasize that a good test is built upon a solid specification. Following the principles of Lean Architecture and Model Driven Architecture (MDA), I consider a specification to be effective when it exhibits the following characteristics:

  • Self-contained: A good specification should be self-contained, meaning it doesn't rely on any external references. It defines the scope of the problem within itself, providing clear boundaries for testing.
  • Platform Independent: A specification should be platform independent, meaning it can be applied uniformly across different environments and architectures. This ensures consistency and compatibility, regardless of the underlying technology stack or infrastructure.
  • Abstracted: A specification should be abstracted, avoiding implementation details or reliance on concrete methods. This ensures flexibility and adaptability, allowing for changes or enhancements without impacting the overall testing framework.
  • Extendable: An ideal specification is designed to be extendable, capable of accommodating future requirements or modifications. This flexibility enables the testing framework to evolve alongside the project it supports.
  • Readable: Perhaps most importantly, a specification must be readable by humans. Clear and concise documentation ensures that the intent and requirements of the system under test are easily understood, facilitating collaboration and maintenance efforts.

While the CPU specs still lack complete abstraction due to explicit calls to assert methods from the unittest.TestCase mixin, efforts are underway to further abstract these specifications in alignment with these principles.

Aside from the abstracted, which the CPU specs still lacks since it does explicit calls assert methods from the unittest.TestCase as a mixin.