#[non_exhaustive]
pub struct Quirks {
Show 14 fields pub shift: Option<bool>, pub load_store: Option<bool>, pub jump0: Option<bool>, pub logic: Option<bool>, pub clip: Option<bool>, pub vblank: Option<bool>, pub vf_order: Option<bool>, pub lores_dxy0: Option<LoResDxy0Behavior>, pub res_clear: Option<bool>, pub delay_wrap: Option<bool>, pub hires_collision: Option<bool>, pub clip_collision: Option<bool>, pub scroll: Option<bool>, pub overflow_i: Option<bool>,
}
Expand description

Represents the different “quirks”, ie. divergent behaviors, of the CHIP-8 runtime. These are the most important ones to support, as many games depend on specific settings here to run properly.

In the following, “original behavior” refers to how the original CHIP-8 interpreter on the COSMAC VIP operated.

All these quirks are Options, because they can be considered to be ternary values. A Some(true) value means that the interpreter should use the “quirky” behavior in a particular scenario. A Some(false) value means that it should use the “default” behavior. However, a None value means that this quirk setting was absent from the metadata, so we don’t know what the game requires. This also implies that the interpreter should use some default behavior. This could be either because the game’s creator wasn’t aware of that particular quirk, or that the program that exported the metadata (usually Octo) wasn’t aware of it (probably because it uses the default behavior for that quirk, without any option of configuring it). This is fine, because some of the quirks are obscure, but we still use Option in these cases so we don’t serialize these quirk settings as false when we don’t know that the game requires the quirk to be disabled.

Note that whether a specific behavior is considered “quirky”/“default” or not doesn’t necessarily mean that’s the original behavior; in many cases, the original behavior is considered “quirky” and requires a true value to enable. This is for historical reasons.

Note also that Octo doesn’t support all of these quirks. This struct should support all possible divergent behaviors between widely used CHIP-8 interpreters. A CHIP-8 interpreter should ignore any quirks they don’t recognize, or don’t have any intention of supporting.

Fields (Non-exhaustive)

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
shift: Option<bool>

Decides the behavior of the CHIP-8 shift instructions 8XY6 (right shift) and 8XYE (left shift):

  • False: The value in the VY register is shifted, and the result is placed in the VX register. (Original behavior)
  • True: The VX register is shifted in-place, and the VY register is ignored. (CHIP48 and SUPER-CHIP behavior)
load_store: Option<bool>

Decides the behavior of the CHIP-8 serialization FX55 (dump registers V0–VX to memory location I) and FX65 (load registers V0–VX from memory location I):

  • False: The value in the I register is incremented for each register loaded/stored. (Original behavior)
  • True: The I register is left unchanged after the operation. (SUPER-CHIP behavior)
jump0: Option<bool>

Decides the behavior of the CHIP-8 relative jump instruction BXNN (jump to address XNN, plus the value in a register):

  • False: The value in the V0 register is used for the offset (original behavior)
  • True: The value in the VX register is used, where X is the first digit in the target address XNN (CHIP48 and SUPER-CHIP behavior)
logic: Option<bool>

Decides the value of the VF flag register after logical instructions 8XY1 (logical OR), 8XY2 (logical AND) and 8XY3 (logical XOR):

  • False: The VF flag register is unchanged by logical instructions (Octo, CHIP48 and SUPER-CHIP behavior)
  • True: The state of the VF flag register is undefined after logical instructions (original behavior)
clip: Option<bool>

Decides the behavior of sprites drawn out of bounds:

  • False: Sprites wrap on screen edges (Octo behavior)
  • True: Sprites are clipped on screen edges (original, CHIP-48 and SUPER-CHIP behavior)
vblank: Option<bool>

Decides whether the CHIP-8 interpreter should wait for the rest of the current frame after each drawing operation:

  • False: No special behavior (CHIP-48, SUPER-CHIP and Octo behavior)
  • True: After a draw instruction, the CPU does no more work for the rest of the frame, ie. it waits for a “VBlank interrupt” (original behavior)
vf_order: Option<bool>

Decides whether arithmetic or logical instructions that have the VF register as one of the operands should set the resulting flag in the VF flag register before or after the value:

  • False: The resulting flags are discarded, and the result is placed in the VF register
  • True: The resulting value is discarded, and the flag is placed in the VF register (original behavior)
lores_dxy0: Option<LoResDxy0Behavior>

Decides what the behavior of the draw instruction should be if the given sprite height is 0 (DXY0) and the interpreter is in lores (low-resolution 64x32 CHIP-8) mode:

  • NoOp: No operation (original behavior)
  • TallSprite: Draw a 16-byte sprite (DREAM 6800 behavior)
  • BigSprite: Draw a 16x16 pixel sprite, ie. the same behavior as in hires (high-resolution 128x64 SUPER-CHIP/XO-CHIP) mode (Octo behavior)
res_clear: Option<bool>

Decides whether the screen should be cleared when there is a resolution change (00FE and 00FF). Note that if this is true, then the screen should retain the current image when going from lores (low resolution) to hires (high resolution), which implies that the existing image on the screen should be scaled up 2x.

  • True: The screen is cleared if the resolution is changed (Octo behavior)
  • False: The screen retains its image if the resolution is changed (original SUPER-CHIP behavior)
delay_wrap: Option<bool>

Decides whether the delay timer should wrap around when it has counted down to 0 or not:

  • True: The delay timer never stops, but overflows from 0 to 255 and keeps counting (DREAM 6800 behavior)
  • False: The delay timer counts down to 0, and then stops (original behavior)
hires_collision: Option<bool>

Decides the result in the VF flag register when there’s a collision of sprites in hires (high resolution) mode:

  • True: VF is set to the number of sprite pixel ros that detected a collision (SUPER-CHIP 1.1 behavior, hires mode only)
  • False: VF is always set to 1 if there is a collision (original behavior)
clip_collision: Option<bool>

Decides whether sprites clipping at the bottom of the screen should cound as a collision. Note that this was probably a bug in the SUPER-CHIP 1.1 interpreter, and might not be required by any games. Also, this doesn’t make much sense if clip_quirks is false.

  • True: VF is set if a sprite runs off the bottom of the screen (SUPER-CHIP 1.1 behavior)
  • False: VF is unchanged if a sprite runs off the bottom of the screen (original behavior)
scroll: Option<bool>

Decides whether scrolling in lores (low-resolution) mode scrolls by half the number of pixels as in the high resolution mode. This occured in SUPER-CHIP because the low resolution display was scaled up 2x; see also the res_clear quirk.

  • True: In low resolution mode, scrolling left and right will scroll by 2 pixels rather than 4 (as in high resolution), and scrolling down (and up, with the XO-CHIP instruction) will scroll by half a pixel, since pixels are scaled upx) (SUPER-CHIP behavior)
  • False: Scrolling acts the same in high and low resolution mode (Octo behavior)
overflow_i: Option<bool>

Decides whether the I address register should set the VF flag register if it “overflows” from 0x0FFF to above 0x1000. Only one known game, Spacefight! 2091, relies on this quirk, which was only present in the obscure CHIP-8 interpreter for the Amiga, while at least one game (Animal Race) relies on the standard behavior.

  • True: VF is set to 1 if the I register takes a value larger than 0x0FFF (Amiga behavior)
  • False: VF is not affected by the I register (original behavior)

Trait Implementations

Formats the value using the given formatter. Read more

Returns a default where no quirks are enabled, except the ones Octo observe.

Returns the “default value” for a type. Read more

Deserialize this value from the given Serde deserializer. Read more

This method tests for self and other values to be equal, and is used by ==. Read more

This method tests for !=.

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.