Skip to content

Instantly share code, notes, and snippets.

@scratchminer
Last active January 27, 2024 19:35
Show Gist options
  • Save scratchminer/64a9774f3959a5f9fdf70ffe57448486 to your computer and use it in GitHub Desktop.
Save scratchminer/64a9774f3959a5f9fdf70ffe57448486 to your computer and use it in GitHub Desktop.
PowerNoise sound chip reference

PowerNoise sound chip reference

The PowerNoise is a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!. The Hexheld fantasy console incorporates a PowerNoise core into its HiveCraft SoC.

It is equipped with three configurable "noise" channels, as well as a unique "slope" channel for more traditional waveform synthesis. It also has two 8-bit bidirectional GPIO ports (similar to the AY-3-8910), but in the Hexheld these are reserved.

Channel implementations

Each channel has a 4-bit "octave" value, which corresponds to the divider that the master clock signal is fed through before it increments the frequency counter. For example, a value of 1 means that the frequency counter is incremented every other cycle.

Unlike some other sound chips, a higher value of the counter actually corresponds to a higher clock frequency: When the frequency counter reaches the maximum value of 4096, the channel it corresponds to is clocked once. Then, the frequency counter is reloaded with the value in the channel's 12-bit frequency register.

Noise

Each noise channel is implemented as a 16-bit LFSR with a configurable feedback bit.

There are two taps to the LFSR, labeled A and B. If tap B is disabled, then the feedback will just be the result of tap A.

When the channel is clocked:

  1. The feedback bit is calculated as the XOR of the two taps.
  2. The register is shifted left one place, with the most significant bit sent to the channel output.
  3. The least significant bit of the LFSR is set to the feedback bit.

The noise channel also contains an amplitude modulation feature. When it is enabled, the amplitude of the slope channel will be ANDed with the channel output before being sent to the mixer. (Multiple channels can have AM enabled, in which case the corresponding channel outputs are ANDed together.)

Slope

The slope channel uses an interesting method to generate lots of different waveforms. It contains a 7-bit portion counter which can be in one of two states (A or B). The highest 4 bits of this counter are sent to the mixer.

When the channel is clocked:

  1. The counter gets the current portion's 4-bit offset added to it (or subtracted from it).
  2. If the current portion has been active for the number of cycles specified in its length register, plus 1, the counter transitions to the opposite state (from B to A, or from A to B).
  3. If the portion has its "reset" flag set to 1, the counter is reset to 0x00 (or 0x7f, if the count direction is down) before beginning the next portion.

Chip registers

Channel offsets and registers

Offset Description
0x00 Audio control register
0x01 - 0x07 Noise channel 1 registers
0x08 GPIO Port A
0x09 - 0x0F Noise channel 2 registers
0x10 GPIO Port B
0x11 - 0x17 Noise channel 3 registers
0x18 - 0x1F Slope channel registers

Audio control register

EBA- -VVV
|||   |||
|||   +++-- Master volume (0..7)
||+-------- GPIO Port A direction (0: Output, 1: Input)
|+--------- GPIO Port B direction (0: Output, 1: Input)
+---------- Audio output enable

Noise

Offset 0x00: Control

E--- --AB
|      ||
|      |+-- Enable Tap B
|      +--- Enable amplitude modulation with the slope channel
+---------- Channel output enable

Offsets 0x01-0x02: Frequency and octave (little endian)

OOOO FFFF FFFF FFFF
|||| |||| |||| ||||
|||| ++++-++++-++++-- Frequency counter reload value
++++----------------- Octave (clock divider)

Offsets 0x03-0x04: LFSR value (little endian)

LLLL LLLL LLLL LLLL
|||| |||| |||| ||||
++++-++++-++++-++++-- LFSR value (this is only stable when the channel output is disabled!)

Offset 0x05: LFSR tap locations

AAAA BBBB
|||| ||||
|||| ++++-- Tap B location (0: LSB, 15: MSB)
++++------- Tap A location (0: LSB, 15: MSB)

Offset 0x06: Stereo volume

LLLL RRRR
|||| ||||
|||| ++++-- Right volume (0: Mute, 15: Loudest)
++++------- Left volume (0: Mute, 15: Loudest)

Slope

Offset 0x00: Output counter value

-AAA AAAA
 ||| ||||
 +++-++++-- Output counter value (this is only stable when the channel output is disabled!)

Offset 0x01: Control

ERAB ABAB
|||| ||||
|||| |||+-- Count direction for portion B (0: Up, 1: Down)
|||| ||+--- Count direction for portion A (0: Up, 1: Down)
|||| |+---- Reset the counter at the start of portion B
|||| +----- Reset the counter at the start of portion A
|||+------- Clip the counter during portion B (0: Enable rollover, 1: Prevent rollover)
||+-------- Clip the counter during portion A (0: Enable rollover, 1: Prevent rollover)
|+--------- Waveform reset (this resets both counters, so the wave will restart at the beginning of Portion A)
+---------- Channel output enable

Offsets 0x02-0x03: Frequency and octave (little endian)

OOOO FFFF FFFF FFFF
|||| |||| |||| ||||
|||| ++++-++++-++++-- Frequency counter reload value
++++----------------- Octave (clock divider)

Offset 0x04: Portion A length

AAAA AAAA
|||| ||||
++++-++++-- Number of counts for Portion A, minus 1

Offset 0x05: Portion B length

BBBB BBBB
|||| ||||
++++-++++-- Number of counts for Portion B, minus 1

Offset 0x06: Portion offsets

AAAA BBBB
|||| ||||
|||| ++++-- Value to change counter by during Portion B
++++------- Value to change counter by during Portion A

Offset 0x07: Stereo volume

LLLL RRRR
|||| ||||
|||| ++++-- Right volume (0: Mute, 15: Loudest)
++++------- Left volume (0: Mute, 15: Loudest)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment