Skip to content

Audio Pipeline

sy2002 edited this page Jul 22, 2026 · 2 revisions

Every core you port was, at heart, a small musical instrument. A Commodore 64 sings through the SID — a three-voice analog synthesizer with a resonant filter that a generation of demo coders bent into basslines and lead solos. An Amiga plays four streams of digitized samples at once, the sound of a thousand tracker modules. A Game Boy chirps square waves out of a chip barely more complicated than a doorbell. Three machines, three utterly different ways of making noise — and your job, and the framework's, is to catch whatever each one produces and carry it, faithfully and at the same time, to two very different destinations: a 3.5 mm analog jack on the back of a MEGA65, and the HDMI cable, which — surprisingly — carries sound as well as picture.

There is a twist that makes this more than plumbing. The original machines sounded the way they did partly because their hardware was imperfect: a cheap converter and a simple resistor-and-capacitor filter softened every note before it reached the speaker, and that softness is baked into how we remember these machines sounding. A perfect digital re-creation, played through a clean modern converter, can actually sound too sharp — the warmth is missing. So the framework does not only carry the sound; it can optionally re-impose that lost analog character, the audio twin of the "CRT emulation" look on the video side.

This is the cover-to-cover guide to how all of that happens. It starts from the very idea of digital sound — no prior knowledge of sampling, DACs, or filters assumed — walks through what a MiSTer core actually hands you, and then follows one stereo stream all the way to both outputs, stage by stage, naming every module it passes through. It uses the three reference cores as running examples: the Commodore 64 (C64MEGA65), the Amiga 500 (AExp), and the Game Boy Color (gbc4mega65). Between them they exercise almost every feature the pipeline has. By the end you should be able to read — and reproduce — every audio choice those three cores make.

It is a concepts article. It teaches you why the pipeline is shaped the way it is and what each piece does, so that the exact porting steps make sense. Those steps — the lines you edit in globals.vhd, mega65.vhd, config.vhd, and main.vhd — live in The Ultimate MiSTer2MEGA65 Porting Guide, and this page points you there. Audio and video share the HDMI wire and the clock-crossing machinery, so where they meet this page leans on its sister articles, Video Pipeline and Clocking and Clock Domain Crossing, rather than re-teaching them.

The MiSTer2MEGA65 audio pipeline takes one stream of signed 16-bit stereo samples from your core, optionally re-shapes it with a filter that recreates the gentle analog character of the original machine, and delivers it — at the same time — to a 3.5 mm analog jack (driven by a real audio DAC on newer boards, or synthesized one bit at a time on the R3) and to the HDMI connector (resampled to a standards-clean 48 kHz and woven into the video signal), and almost everything you do as an audio porter is choosing that filter and handing your samples over in the right format.


Part 1 — How a machine makes sound, and how a wire carries it

Before we can talk about a pipeline we have to agree on what "digital audio" even is. If you have never had to think about it, this part builds the vocabulary from zero, in the order the rest of the article needs it. If sampling and DACs are already familiar, skim to §1.5 — the retro-specific quirks there are where the framework earns its keep.

1.1 Sound is a wiggling voltage

A loudspeaker makes sound by pushing air: a coil moves a paper cone back and forth, and the moving cone sends pressure waves down the room to your ear. What tells the cone how to move is a single electrical quantity — a voltage that rises and falls over time. Push the voltage up, the cone moves out; drop it, the cone pulls back. So at the very bottom, "an audio signal" is just a voltage that varies over time — a continuous, smoothly wiggling waveform. Everything a retro sound chip does, however cleverly, ends in producing such a wiggle.

The catch is that an FPGA cannot store or transmit a smooth wiggle. It deals in numbers. So the first thing that has to happen is to turn the wiggle into numbers — and, at the very end, turn numbers back into a wiggle.

1.2 Turning the wiggle into numbers — sampling and PCM

The trick is to stop trying to capture the whole continuous curve and instead measure the voltage at regular instants — thousands of times a second — and write down each measurement as a number. Each measurement is a sample; the steady stream of samples is called PCM, for pulse-code modulation, which is a fancy name for "the waveform written down as a list of numbers." Nearly all digital audio, from a CD to the HDMI cable on your desk, is PCM under the hood.

Two numbers describe any PCM stream. The first is the sample rate: how many measurements you take per second, quoted in hertz (Hz) or kilohertz (kHz). A music CD uses 44,100 samples per second (44.1 kHz); the MiSTer world, the MEGA65, and HDMI all settle on 48,000 (48 kHz), and that number will follow us through the whole article.

Why 48,000 and not, say, 5,000? Because of a hard rule called the Nyquist limit: to faithfully capture a sound whose highest frequency is F, you must sample at a rate greater than 2 × F. Human hearing tops out around 20 kHz, so a sample rate a bit above 40 kHz captures everything we can hear — which is exactly why the common rates cluster just above 40 kHz. (If sampling is new to you, this gentle primer on PCM and sampling is a ten-minute foundation.)

1.3 How big is each number — bit depth, and why samples are signed

A sample is an integer, and like any integer in hardware it has a fixed width in bits — its bit depth. M2M uses 16 bits per sample, the same as a CD. Sixteen bits give 2^16 = 65,536 distinct levels to place each measurement on; more bits would resolve quieter details, fewer bits would add audible "gravel" (quantization noise). Sixteen is the sweet spot the whole MiSTer ecosystem uses.

Now the subtle part, and the single most important fact about the format M2M expects. A sound wave swings symmetrically around a resting point — the cone sits still at rest, moves out, comes back through the middle, moves in, and so on. The natural way to write that down is with signed numbers: zero means silence, positive numbers mean the wave is above rest, negative numbers mean below. A 16-bit signed sample runs from -32768 (as far "down" as it goes) through 0 (silence) to +32767 (as far "up"). The negative values are stored in two's complement, the standard hardware convention for signed integers — you rarely have to think about the encoding, only that 0 is the middle.

Contrast this with an unsigned representation, where the numbers run 0 to 65535 and silence sits at the midpoint, 32768 (hex 0x8000). Both encodings carry the same wave; they just disagree about where "silence" lives. Converting a chip that speaks unsigned into the signed form M2M wants is a real porting task, and getting it wrong is audible — hold that thought for the Game Boy in Part 2.

   16-bit SIGNED  (what M2M wants)          16-bit UNSIGNED  (e.g. the Game Boy APU)

   +32767  ..... loudest "up"                65535  ..... loudest "up"
        0  ..... SILENCE                     32768  ..... silence  (the midpoint)
   -32768  ..... loudest "down"                  0  ..... loudest "down"

Finally, sound is stereo: two independent PCM streams, one for the left speaker and one for the right. So, reduced to its essence, "the audio signal" inside M2M is exactly this: two signed 16-bit numbers — left and right — produced fresh every sample period.

1.4 Turning the numbers back into sound — the DAC and its filter

At the far end, to actually hear anything, the numbers must become a voltage again. The device that does this is a DAC — a digital-to-analog converter. Give it a stream of samples and it produces a stream of voltages.

But a DAC cannot magically draw a smooth curve through the samples. The simplest and most common thing it does is hold each sample's voltage steady until the next sample arrives — producing not a smooth wave but a staircase, a series of flat steps. That staircase is almost the original wave, but the sharp corners of the steps contain high-frequency energy that was never in the sound — spurious tones (called images or aliases) sitting above the audible band.

The cure is a reconstruction filter: a low-pass filter — a circuit (or computation) that lets low frequencies through and blocks high ones — placed after the DAC to round the staircase back into a smooth curve. Every DAC needs one. In cheap 1980s hardware that filter was often just a single resistor and capacitor (an "RC filter") soldered across the audio output — the crudest possible low-pass.

1.5 The retro twist — the output filter was part of the instrument

Here is where retro audio stops being generic. That crude RC filter on the original machine did not only remove the DAC staircase; it also gently rolled off the top of the audible band, softening the sound. And because every unit shipped with the same cheap filter, that softening became part of the machine's voice — the rounded warmth of a real SID, the particular grain of an Amiga sample. It is not a defect people want removed; it is how the instrument is remembered.

A modern FPGA core computes its samples mathematically, without that analog roll-off, and feeds them to a clean modern DAC. The result can sound noticeably brighter and harsher than the real machine — technically more accurate, emotionally wrong. So M2M offers an optional digital filter that re-imposes the original's gentle roll-off before the sound goes out, trading mathematical purity for the character people actually remember. This is the exact audio counterpart of the video pipeline's "CRT emulation": deliberately re-adding a beloved imperfection. We build that filter in Part 4.

1.6 Two ways off the machine — a line, and a passenger on HDMI

Historically, sound left a home computer as an analog line: a voltage on a wire to an amplifier or a pair of headphones, through an RCA or headphone jack. The MEGA65 keeps that tradition with a 3.5 mm analog audio jack.

But the MEGA65 also has HDMI, and here is a fact that surprises many people: an HDMI cable carries audio as well as video. It does so by tucking little packets of audio samples into the brief gaps between video lines — the same blanking intervals the Video Pipeline article describes. So the identical sound must be delivered two ways at once: as a real analog voltage on the jack, and as digital sample-packets riding inside the HDMI signal. That fork — one stream, two outputs — is the spine of the whole pipeline, and we meet it properly in Part 3.

1.7 What our three example cores actually produce

To make all of this concrete, here is the native sound each reference core generates, before the framework touches it — the three specimens dissected throughout the article.

  • Commodore 64 (C64MEGA65). The MOS SID chip (6581 or 8580) is a genuine three-voice analog-style synthesizer with a resonant filter — the reason C64 music is so distinctive. In the core it is emulated to an 18-bit signed value per channel, and because the MEGA65 can host two SIDs, the output is genuinely stereo.

  • Amiga 500 (AExp). The Paula chip plays four independent channels of 8-bit PCM samples — each streamed straight from memory — and mixes them in hardware into a 15-bit signed left/right pair (channels 0 and 3 feed the left, 1 and 2 the right). This is the chip that made the Amiga the home of tracker music.

  • Game Boy Color (gbc4mega65). The APU (audio processing unit) has four simple voices — two pulse-wave channels, one programmable-wave channel, one noise channel — mixed to a 16-bit unsigned value that, awkwardly, is not centered on 0x8000: its resting level drifts depending on how many voices are active. That quirk turns its format conversion into the trickiest of the three.

Three chips; three sample formats — 18-bit signed, 15-bit signed, and 16-bit unsigned-and-uncentered. Part 2 is the story of how each of those becomes M2M's one canonical format: signed 16-bit, centered on zero.


Part 2 — What a MiSTer core hands you, and how to shape it

M2M exists to host a MiSTer core on MEGA65 hardware, so the practical starting point is not "digital audio" in the abstract but "the specific signals a MiSTer core produces," and what you must do to them in CORE/vhdl/main.vhd before the framework will accept them. If you have never seen a MiSTer core, the MiSTer Quick Reference Guide covers the MiSTer side in full; this part covers only the audio handoff.

2.1 The signals a MiSTer core produces

A MiSTer core emits its sound on a tiny, conventional set of wires:

  • AUDIO_L, AUDIO_R — the two PCM channels, each a signed 16-bit value.
  • AUDIO_S — a one-bit flag: 1 if the samples are signed, 0 if unsigned.
  • AUDIO_MIX — a two-bit hint asking the MiSTer framework to blend some left into right and vice-versa (mono-ing the sound to taste).

On real MiSTer hardware, the ARM-plus-Linux "framework" reads these, runs them through a filter and a mixer, and drives the board's audio DAC. M2M has no ARM and no Linux — it reimplements that whole chain in pure FPGA — so the first thing it needs from your core is those samples, in its own conventions.

2.2 The M2M boundary — the contract

The contract between your core and the framework is a pair of VHDL ports, both outputs of your core, declared in CORE/vhdl/mega65.vhd:125-127:

   -- Audio output (Signed PCM)
   main_audio_left_o       : out signed(15 downto 0);
   main_audio_right_o      : out signed(15 downto 0);

Two facts are load-bearing. First, these live in your core's main clock domain — the main_clk your core generates (about 54 MHz in the template). The framework will move them into its own audio clock domain later (Part 3), so you do not have to; you just present the samples on your own clock. Second, and this is the rule the whole rest of the pipeline assumes: the samples must be signed (the framework hardwires MiSTer's AUDIO_S flag to "signed" — we see exactly where in Part 4), and they must be 16-bit. Your sound chip almost certainly is neither exactly 16 bits nor already centered on zero, so your one real audio job in main.vhd is to convert your chip's native samples into signed 16-bit, centered on zero.

That is the entire porter-side audio-in task. It is small — usually a line or two — but it is exactly where the three reference cores differ, and getting it wrong is audible. So the conversion is the worked example.

2.3 The one job you do — three cores, three conversions

Commodore 64 — 18 bits down to 16, with an overflow guard. The dual-SID core produces 18-bit signed samples; it keeps the top 16 of those 18 bits (dropping the two least significant, an effective divide-by-four into 16-bit headroom) and then saturates if the result would overflow. From C64MEGA65/CORE/vhdl/main.vhd:1672-1705:

  -- MiSTer audio signal processing: Convert the core's 18-bit signal to a signed 16-bit signal
  audio_processing_proc : process (all)
    variable alm, arm : std_logic_vector(16 downto 0);
  begin
    alm(16)          := c64_sid_l(17);
    alm(15 downto 0) := c64_sid_l(17 downto 2);
    ...
    if alm(16) /= alm(15) then          -- sign disagrees with top magnitude bit = overflow
      alo(15)          <= alm(16);       -- clamp to the rail
      alo(14 downto 0) <= (others => alm(15));
    else
      alo <= alm(15 downto 0);
    end if;
    ...
  audio_left_o    <= signed(alo);

The saturating clamp is not strictly needed today — a single SID never overflows the downshift — but the C64 authors left it as a deliberate scaffold: the comment notes that MiSTer also lets you add OPL, a DAC channel, and even tape-drive noise into this same sum, and those could overflow, so the guard is ready in advance.

Amiga — 15 bits up to 16, by a single shift. Paula has already mixed its four channels into a 15-bit signed pair, so AExp only has to widen it to 16 bits. From AExp/CORE/vhdl/main.vhd:861-866:

   -- Audio: Paula 15-bit signed -> 16-bit signed PCM (as MiSTer: {data, 1'b0})
   audio_left_o  <= signed(aud_ldata & '0');
   audio_right_o <= signed(aud_rdata & '0');

Appending a '0' as a new least-significant bit is a one-bit left shift — a multiply by two — which fills the extra bit of range while preserving the sign. It matches MiSTer's own {data, 1'b0} idiom exactly. Clean, because Paula did the hard part (the four-channel mix) in silicon.

Game Boy — the cautionary tale. The APU speaks unsigned audio that is not centered on 0x8000, and this is where a careless conversion bites. From gbc4mega65/CORE/vhdl/main.vhd:406-413:

   -- Convert the Game Boy's unsigned audio to M2M's signed PCM format.
   -- gbc_snd's output is unsigned and NOT centered around 0x8000 (the center drifts with
   -- the number of active voices), so we use a logical shift right by one - silence stays
   -- at level 0 and the maximum stays in the positive range. Do not use an arithmetic
   -- shift or a plain "minus 0x8000" conversion here: both lead to loud crackling
   -- (verified in the original gbc4mega65 with Dig Dug and Super Mario Land 1).
   audio_left_o  <= signed("0" & main_audio_l(15 downto 1));
   audio_right_o <= signed("0" & main_audio_r(15 downto 1));

The fix is a logical shift right by one with a forced 0 sign bit ("0" & x(15 downto 1)), which folds the APU's uncentered unsigned range into the non-negative half of signed 16-bit — silence stays at zero, the peak stays positive. The comment is emphatic about what not to do: an arithmetic shift, or the textbook "subtract 0x8000" trick, both produce loud crackling, verified in real games (Dig Dug, Super Mario Land 1). It is the perfect object lesson: "hand M2M signed 16-bit" is a contract, and honoring it — matching your chip's sign convention and center point — is the whole of the porter's audio-in work.

2.4 What the template does instead

The demo core has no sound chip to convert, so it fabricates a tone: a small DSP multiplier (M2M/vhdl/democore/democore_audio.vhd) generates an oscillator whose pitch tracks the Breakout ball's height and whose left/right balance tracks its horizontal position, then truncates the product into signed(15 downto 0). It exists purely so the framework makes sound when it runs stand-alone — a hello-world beep you delete the moment a real core arrives. Everything downstream of these two ports, however, is identical whether the samples came from a demo oscillator or a real SID — which is exactly what the rest of this article is about.


Part 3 — The pipeline: architecture, the audio clock, and where you wire

Your core produces one stereo stream. The MEGA65 has two audio outputs — the jack and HDMI. The framework's job is to carry that one stream to both, and it does so inside the same VHDL entity that handles video, av_pipeline.vhd. This part is the map: what happens where, on which clock, and — just as important — which files you edit to steer it.

3.1 One stream in, two outputs out — but the fork is late

Here is the whole audio pipeline on one page. Read it top to bottom.

        CORE — your MiSTer core, in CORE/vhdl/main.vhd
        main_audio_left_o / main_audio_right_o : signed(15 downto 0), on YOUR main_clk
                │
                ▼
   cdc_stable ── cross into the framework's 12.288 MHz audio clock domain
                │
                ▼
   audio_out ─── the MiSTer "audio-improvement" IIR filter              (Part 4)
                │
                ▼
   select/mute ─ pick raw / filtered / silence, from the on-screen menu (Part 4)
                │
   ┌────────────┴─────────────────────┐
   ▼                                  ▼
   ANALOG JACK (3.5 mm)               HDMI (audio rides inside the video)
   │                                  │
   your BOARD picks the DAC:          resample to 48 kHz, add ACR (N/CTS),
   • R4/R5/R6: AK4432VT I2S DAC       pack into HDMI data islands, and feed
   • R3: 1-bit PDM into an RC         the SAME TMDS serializers as the picture
     filter on the jack               (see the Video Pipeline article, §5.4)
   ▼                                  ▼
   headphones / amplifier            the TV or monitor speakers

One structural fact is worth pausing on, because it is the biggest difference from the video pipeline. The video pipeline forks first and then reprocesses each branch heavily — the analog path scandoubles, the digital path rescales, and they share almost nothing. The audio pipeline does the opposite: it processes once, then forks. The single filter-and-mute stage runs upstream of the split, so the jack and HDMI receive the identical processed samples; the only thing that differs afterward is the physical packaging (an analog DAC on one side, HDMI data-islands on the other). That is why this article is shorter than its video sibling — there is one processing stage to understand, not two divergent ones.

3.2 The audio clock — 12.288 MHz, and why exactly that

Everything from the core boundary onward runs in a single audio clock domain clocked at 12.288 MHz. That oddly specific number is not arbitrary; it is 256 × 48,000. Here is why the 256 is there.

An audio DAC chip needs more than the samples — it needs a fast, steady master clock (traditionally written MCLK) from which it derives its own internal timing, and the long-standing convention is that MCLK is a power-of-two multiple of the sample rate, most commonly 256 × Fs (256 times the sample frequency). With Fs = 48 kHz, that is 256 × 48000 = 12.288 MHz. Rather than run the audio logic at some convenient rate and then generate a separate MCLK for the DAC, the framework simply runs its entire audio domain at the DAC's master-clock rate. So the whole subsystem's heartbeat is 12.288 MHz, and every downstream rate — the 48 kHz sample tick, the I2S bit clock, the HDMI resampler — is a clean division of it.

The clock comes from a dedicated mixed-mode clock manager in M2M/vhdl/clk_m2m.vhd:104-125:

   i_clk_audio : MMCME2_BASE
      generic map (
         CLKFBOUT_MULT_F      => 48.000,     -- 960 MHz
         CLKIN1_PERIOD        => 10.0,       -- INPUT @ 100 MHz
         CLKOUT0_DIVIDE_F     => 78.125,     -- AUDIO @ 12.288 MHz
         DIVCLK_DIVIDE        => 5,
         ...

The arithmetic: the 100 MHz board clock is multiplied to a 100 × 48 / 5 = 960 MHz internal frequency, then divided by 78.125 to land on exactly 960 / 78.125 = 12.288 MHz.

A gotcha for anyone reading the source. Two comments in the framework still call this clock "30 MHz" (clk_m2m.vhd:8) and "60 MHz" (framework.vhd:265). Both are stale and wrong; the real rate is 12.288 MHz, confirmed by the MMCM generics above, by the port comment at clk_m2m.vhd:36, and by the generic G_AUDIO_CLOCK_RATE => 12_288_000 passed into the pipeline at framework.vhd:868. Trust the generics, not the two stray comments.

The clock-generation details themselves — how an MMCME2 works, what "locking" means — belong to Clocking and Clock Domain Crossing; here it is enough to know the audio domain ticks at 256 × 48 kHz for the DAC's sake.

3.3 Crossing into the audio domain — cdc_stable

Your core emits its samples on its own main clock; the audio domain runs on the framework's 12.288 MHz clock. These are two unrelated clocks, so the left and right samples must cross a clock-domain boundary — and crossing a multi-bit value between unrelated clocks naïvely will, sooner or later, latch a value with some bits from the old sample and some from the new (a "torn" read). The framework prevents that with a small crossing block, cdc_stable, in framework.vhd:761-774:

   -- Clock domain crossing: CORE to AUDIO
   i_main2audio: entity work.cdc_stable
      generic map (
         G_REGISTER_SRC => true,
         G_DATA_SIZE    => 32
      )
      port map (
         src_clk_i                => main_clk_i,
         src_data_i(15 downto  0) => std_logic_vector(main_audio_l_i),
         src_data_i(31 downto 16) => std_logic_vector(main_audio_r_i),
         dst_clk_i                => audio_clk,
         dst_data_o(15 downto  0) => audio_left,
         dst_data_o(31 downto 16) => audio_right
      ); -- i_main2audio

It packs both 16-bit channels into one 32-bit word and, as its name says, only forwards a new value once it has sampled the same value on two consecutive audio clocks — i.e. once the crossing has settled — so a half-updated word is never passed on. Why is such a simple, cheap crossing safe here, when the video and HyperRAM paths need full asynchronous FIFOs? Because audio changes slowly. A fresh sample appears only 48,000 times a second, but the audio clock ticks 12.288 million times a second — over 250 clocks per sample — so the value sits still far longer than it takes the crossing to settle. A stability-gated crossing is exactly the right tool for slowly-varying data, and it costs a few flip-flops rather than a FIFO. (The full menu of crossing techniques, and when each applies, is the subject of Clocking and Clock Domain Crossing.)

3.4 Reset, pause, and when the sound falls silent

Three questions a porter eventually asks — does audio survive a core reset? does it keep playing while the menu is open? why is there a soft pause at power-on? — all turn on one signal, the audio reset. It is generated right beside the audio clock, in clk_m2m.vhd, and it asserts in exactly two situations: at power-on until the audio clock manager locks, and on a full M2M reset (the long reset-button press). It deliberately does not assert on an ordinary core reset. The happy consequence: your sound keeps playing when the user resets the core — a game reset mid-tune does not click or drop out.

That reset has two visible effects, each landing on a stage we meet later:

  • No power-on pop. On the R4-R6 boards the audio DAC is physically held in power-down while the reset is asserted (Part 5), so the speakers stay silent until everything is stable rather than thumping as the logic settles.
  • A brief start-up mute on the filtered path. The improvement filter (Part 4) mutes itself for roughly the first 8000 samples — about 170 ms — after a reset, while its internal DC-blocker settles, then fades in. When the filter is on you may notice the very first note arrive a heartbeat late; that is this settling window, not a fault.

The framework carries this reset (and the audio clock itself) out to the board alongside the samples: av_pipeline exports audio_clk_o and audio_reset_o next to audio_left_o/audio_right_o (av_pipeline.vhd:694-697), and the board top feeds them to the DAC as its master clock and its power-down control.

One last "is there sound?" question is settled by the menu, not the reset. A config switch, OPTM_PAUSE (config.vhd), decides whether opening the on-screen menu pauses the core. It defaults to false, so by default the game keeps running — and keeps playing — underneath the menu. Set it true (some cores prefer a clean freeze) and opening the menu halts the core, so the audio stops too until you close the menu. It is a one-line choice with an audible consequence, which is why it earns a mention here.

3.5 Where you actually wire the audio — the four-file map

Almost none of the pipeline is something you build; it is something you configure, and — exactly as on the video side — four files carry all of your audio decisions. Knowing which file owns which kind of decision saves real time.

File What it owns for audio
CORE/vhdl/globals.vhd The fixed facts about your machine's sound: the improvement-filter coefficients (audio_flt_rate, audio_cx, audio_cx0..2, audio_cy0..2, audio_att, audio_mix).
CORE/vhdl/mega65.vhd The live policy: the qnice_audio_filter_o and qnice_audio_mute_o outputs, driven from menu bits.
CORE/vhdl/config.vhd The menu the user sees: the "Audio improvements" item (and, if you add one, a "Mute" item).
CORE/vhdl/main.vhd The samples: the signed-16 conversion from Part 2.

globals and config and mega65 decide how the pipeline treats the sound; main.vhd decides what the sound is. The rest of the article walks these in order: the filter and its controls (Part 4), then the two physical outputs (Parts 5 and 6).


Part 4 — The audio-improvement filter

This is the one genuine processing stage in the whole audio pipeline, and it is the piece a porter most needs to understand, because its behavior is data you supply. Its purpose, from §1.5: the original machine's crude analog output filter softened its sound, a mathematically exact core omits that softening, and this stage optionally puts it back.

4.1 What this filter is — an IIR filter, from first principles

A digital filter computes each output sample as a weighted combination of recent input and output samples. The kind MiSTer uses is an IIR filterinfinite impulse response, so named because it feeds its own past outputs back into the calculation, so a single input sample can keep influencing the output long after it has passed. That feedback is the family's defining trick: it is what lets a small, cheap filter have a long, smooth effect. MiSTer's filter remembers the last three inputs and the last three outputs — it is a third-order IIR — and combines them like this (using the coefficient names the code uses):

   out[n] = cx · ( in[n] + cx0·in[n-1] + cx1·in[n-2] + cx2·in[n-3] )   ← 4 feedforward (INPUT) taps
          −       ( cy0·out[n-1] + cy1·out[n-2] + cy2·out[n-3] )       ← 3 feedback (OUTPUT) taps

Do not worry about the exact algebra — the shape is the point. The feedforward side weights the current input and its three predecessors; the feedback side weights the three previous outputs; and cx is one overall input gain — seven tunable numbers in all. Together they define the filter's frequency response — here a gentle low-pass that rolls off the harsh top end, mimicking the original RC filter. The crucial idea for a porter is this: the coefficients are the filter's personality. Change the numbers and you change the sound; and since every machine's analog output filter was different, every machine gets its own set.

4.2 The MiSTer filter, as M2M uses it

M2M does not write its own filter — it imports MiSTer's, verbatim, so that the sound matches the MiSTer core you are porting. The Verilog module is audio_out.v (M2M/vhdl/av_pipeline/audio_out.v); it wraps the actual IIR filter (IIR_filter, in M2M/vhdl/controllers/MiSTer/iir_filter.v), a DC-blocker, and a stereo mixer. The framework instantiates it in av_pipeline.vhd:318-349:

   i_audio_out : audio_out
      generic map ( CLK_RATE => G_AUDIO_CLOCK_RATE )        -- 12.288 MHz
      port map (
         reset       => audio_rst_i,    -- audio-domain reset (see section 3.4)
         clk         => audio_clk_i,
         sample_rate => '0',            -- 0 = 48 kHz  (M2M never uses 96 kHz)
         flt_rate    => audio_flt_rate, -- filter processing-rate coefficient (from MiSTer)
         cx          => audio_cx,       -- overall input gain
         cx0         => audio_cx0,      -- ┐
         cx1         => audio_cx1,      -- │ feedforward taps (weight recent INPUTS)
         cx2         => audio_cx2,      -- ┘
         cy0         => audio_cy0,      -- ┐
         cy1         => audio_cy1,      -- │ feedback taps (weight recent OUTPUTS)
         cy2         => audio_cy2,      -- ┘
         att         => audio_att,      -- attenuation / volume (0 = untouched)
         mix         => audio_mix,      -- stereo/mono crossfeed (0 = full stereo)
         is_signed   => '1',            -- M2M ALWAYS feeds signed samples
         core_l      => audio_left_i,
         core_r      => audio_right_i,
         alsa_l      => (others => '0'),-- no second audio source (see below)
         alsa_r      => (others => '0'),
         al          => audio_filt_left,
         ar          => audio_filt_right
      );

Three of these inputs repay a closer look, because each teaches something about M2M:

  • is_signed => '1', always. Inside audio_out.v there is a one-line trick that flips a sample's sign bit when is_signed is 0, converting unsigned to signed on the way in. M2M never uses it — it hardwires '1' and relies on your core to have already produced signed samples (Part 2). This is precisely why the contract in §2.2 demands signed, and precisely why the Game Boy had to convert its unsigned APU output by hand.

  • alsa_l/alsa_r => 0. On real MiSTer these carry a second audio source — the ARM/Linux side's "ALSA" sound (menu blips, CD audio, and so on) — to be mixed with the core's. M2M has no ARM and no Linux, so there is no second source; the ports are tied to zero and the built-in mixer simply passes the core's audio through. It is a small, telling reminder that M2M is MiSTer with the Linux computer removed.

  • flt_rate (here 7,056,000). This is not a clock — it is another number you copy verbatim from MiSTer, setting the filter's internal processing cadence; you never compute it. Like the seven weights above, it lives in your globals.vhd.

Two more stages sit inside audio_out.v, after the filter itself. A DC-blocker strips any constant offset — any slow drift away from the zero that means silence — from the stream; it is a second guard on the very centering Part 2 laboured over, but note it runs only in the filtered path, so it cannot rescue a mis-centered core when the filter is switched off. After it, a small mixer applies the att and mix controls described next.

4.3 The coefficients are core-owned — copy them from your MiSTer core

The filter's personality lives in CORE/vhdl/globals.vhd, as a block of constants. The template ships the C64's values as a placeholder (globals.vhd:187-203):

-- Audio filters
-- If you use audio filters, then you need to copy the correct values from the MiSTer core
-- that you are porting: sys/sys_top.v
constant audio_flt_rate : std_logic_vector(31 downto 0) := std_logic_vector(to_signed(7056000, 32));
constant audio_cx       : std_logic_vector(39 downto 0) := std_logic_vector(to_signed(4258969, 40));
constant audio_cx0      : std_logic_vector( 7 downto 0) := std_logic_vector(to_signed(3, 8));
constant audio_cx1      : std_logic_vector( 7 downto 0) := std_logic_vector(to_signed(2, 8));
constant audio_cx2      : std_logic_vector( 7 downto 0) := std_logic_vector(to_signed(1, 8));
constant audio_cy0      : std_logic_vector(23 downto 0) := std_logic_vector(to_signed(-6216759, 24));
constant audio_cy1      : std_logic_vector(23 downto 0) := std_logic_vector(to_signed( 6143386, 24));
constant audio_cy2      : std_logic_vector(23 downto 0) := std_logic_vector(to_signed(-2023767, 24));
constant audio_att      : std_logic_vector( 4 downto 0) := "00000";  -- no attenuation
constant audio_mix      : std_logic_vector( 1 downto 0) := "00";     -- full stereo

Every MiSTer core defines a tuned filter in its sys/sys_top.v; the porter's job is simply to copy those numbers into this block. Two of the constants deserve a note:

  • audio_att is a 5-bit attenuator — its top bit forces silence and its low four bits shift the volume down — and audio_mix blends the channels toward mono (00 = full stereo; 01, 10, 11 = 25%, 50%, 100% blended). All three reference cores leave both at their neutral values; they exist for cores that need a quieter or narrower mix out of the box.

  • audio_cx1 is a well-known trap. Recall the four feedforward weights from §4.1: 1, cx0, cx1, cx2. MiSTer intends these to be the binomial 1, 3, 3, 1 — that is, cx0 = 3, cx1 = 3, cx2 = 1. But the M2M template — and, copying from it, the C64 and Game Boy cores — carry cx1 = 2, a long-standing typo; only the Amiga core uses the correct 3. The lesson is the one the file's own comment states: copy the numbers from your core's sys/sys_top.v, not from the template, and you get the right value regardless.

4.4 Turning it on, off, and to silence

The filter always runs, but whether the listener hears its output — or the raw samples, or silence — is decided by two one-bit controls that come from the on-screen menu. Those controls originate as core outputs in mega65.vhd:39-40:

   qnice_audio_mute_o      : out std_logic;
   qnice_audio_filter_o    : out std_logic;

They are set from menu bits — in the template, mega65.vhd:413-414:

   qnice_audio_mute_o    <= '0';                                         -- never muted
   qnice_audio_filter_o  <= qnice_osm_control_i(C_MENU_IMPROVE_AUDIO);   -- 0 = raw, 1 = filtered

where C_MENU_IMPROVE_AUDIO is the zero-based line number of the " Audio improvements\n" item in the menu (config.vhd:378). These bits are produced in the QNICE clock domain (the menu runs on the QNICE co-processor), so they too are crossed into the audio domain — by a small xpm_cdc_array_single in av_pipeline.vhd:303-315 — before they take effect. The crossed bits then drive a plain selector, av_pipeline.vhd:351-365:

   select_or_mute_audio : process(all)
   begin
      if audio_mute = '1' then
         audio_left  <= (others => '0');           -- silence
         audio_right <= (others => '0');
      else
         if audio_filter = '0' then
            audio_left  <= audio_left_i;           -- raw core audio (filter bypassed)
            audio_right <= audio_right_i;
         else
            audio_left  <= audio_filt_left;        -- the filtered output
            audio_right <= audio_filt_right;
         end if;
      end if;
   end process select_or_mute_audio;

So the improvement filter switches in and out glitch-free and with no core reset — flipping the menu item just re-points this multiplexer. Note also that M2M does its own muting here (forcing zeros), independent of the filter's internal att mute; the mute path is fully supported by the framework even though the template never exercises it (it wires qnice_audio_mute_o to '0').

The three reference cores make three different default choices, which is the whole point of having the switch:

  • The C64 enables the filter by default — the SID's remembered warmth comes largely from that analog roll-off, so most users want it on.
  • The Game Boy ships it off by default, leaving the crisp chiptune untouched unless the user asks for softening.
  • The Amiga removes the menu item and runs the raw Paula output — Paula's sampled sound already carries its own character, so AExp bypasses the filter entirely (while keeping the corrected audio_cx1 = 3 in its globals.vhd, ready for the day it is switched on).

None of the three expose a "Mute" item today, though adding one is a trivial win: declare a menu group and point qnice_audio_mute_o at its bit. The whole zero-to-silence path already exists.


Part 5 — The analog output: the 3.5 mm jack

After the select/mute stage, the finished samples leave av_pipeline on a pair of signed outputs and travel out to the board's top level (av_pipeline.vhd:694-697):

   audio_left_o  <= signed(audio_left);
   audio_right_o <= signed(audio_right);

From here to the physical jack, what happens depends on which MEGA65 you have — and that board-by-board split is the analog audio path's defining feature, the sound-side echo of the 15 kHz-versus-31 kHz divide on the video side.

A quick gotcha to clear up first. The analog video pipeline (analog_pipeline.vhd) is also handed these audio samples, but it never uses them — the ports are vestigial. The jack's audio is routed around the video pipeline, straight from the select/mute stage to the board top. So do not go looking for audio processing inside the VGA path; there is none.

5.1 R4, R5, R6 — a real audio DAC over I2S

The R4 and later boards carry a dedicated stereo DAC chip, the AK4432VT, and the framework talks to it in I2S — the standard three-wire serial format for shipping PCM to a DAC. I2S carries a stereo sample stream on three signals, all derived from that 12.288 MHz master clock:

   MCLK   12.288 MHz  — the master clock  (= 256 × 48 kHz), the DAC's heartbeat
   BICK    3.072 MHz  — the bit clock: one tick shifts one bit of sample data
   LRCLK  48.000 kHz  — the word-select clock: one full cycle = one stereo sample
   SDTI    (serial)   — the sample bits themselves, most-significant bit first

The driver is M2M/vhdl/controllers/M65/audio.vhd. It counts the 12.288 MHz clock in a 0..255 cycle (256 clocks = one 48 kHz sample period), loads the left sample into the top of a 64-bit shift register and the right sample into the middle, and clocks the bits out on SDTI while generating BICK and the 48 kHz LRCLK from the same counter:

   audio_mclk_o   <= audio_clk_i;                        -- MCLK is simply the 12.288 MHz clock
   ...
   i2s_data(63 downto 48) <= std_logic_vector(audio_left_i);
   i2s_data(31 downto 16) <= std_logic_vector(audio_right_i);

The DAC needs almost no setup. A hardwired pin (audio_i2cfil_o <= '0') selects its I2C interface mode, and it otherwise comes up in a sane default — I2S input, 48 kHz — straight from power-on, so the stock framework sends it no configuration at all. It does wire the DAC's control lines into its shared I2C bus (audio_scl/audio_sda) — a separate, general-purpose control bus, not to be confused with the I2S audio link above: I2S carries the sound; I2C would only poke the chip's registers. Those lines exist so a core could retune the DAC (volume, mode), but none of the reference cores do, and the I2C bus itself belongs to Devices. The board top wires the driver up in one instance (top_mega65-r4.vhd:429-440, identical on R5 and R6). If I2S is new to you, the I²S serial format is a short read; the key idea is just "MCLK, a bit clock, a left/right clock, and serial data."

5.2 R3 — synthesizing the analog signal one bit at a time (PDM)

The very first MEGA65 board revision, the R3, does not use a multi-bit DAC for the jack. Instead the FPGA drives the jack pin with a single-bit stream and lets a simple resistor-capacitor filter on the board turn it into an analog voltage. The technique is pulse-density modulation (PDM), also called first-order sigma-delta.

The idea is elegant. Instead of outputting a number, output a fast stream of 1s and 0s whose density of 1s encodes the amplitude: a stream that is 1 three-quarters of the time represents three-quarters full-scale, a stream that is 1 half the time represents half, and so on. An RC low-pass filter averages the bitstream — literally computing its running average as a voltage — and out comes the analog wave. All the FPGA has to do is decide, every clock, whether to emit a 1 or a 0 so that the long-run density tracks the sample. It does that with an accumulator (a "digital integrator"), in M2M/vhdl/controllers/M65/pcm_to_pdm.vhdl:

      if pdm_left_accumulator < 65536 then
        pdm_left_accumulator <= pdm_left_accumulator + pcm_value_left;
        ampPWM_pdm_l <= '0';
      else
        pdm_left_accumulator <= pdm_left_accumulator + pcm_value_left - 65536;
        ampPWM_pdm_l <= '1';
      end if;

Every clock it adds the sample value to a running accumulator; whenever the accumulator reaches full-scale it emits a 1 and subtracts full-scale back off, otherwise it emits a 0. Watch it settle on a half-scale sample (full-scale here is 65536, half is 32768):

clock tick accumulator (before) reached full-scale? output bit
1 0 no 0
2 32768 no 0
3 65536 yes 1
4 32768 no 0
5 65536 yes 1

From tick 3 on it locks into 1, 0, 1, 0, … — high exactly half the time, encoding the half-scale level. The long-run density of 1s always equals the sample's level, and the RC filter turns that density back into the voltage. (The same module can also produce ordinary PWM — a pulse whose width rather than density carries the level — selectable by a mode input; M2M chooses PDM, which sounds cleaner on the MEGA65.) The R3 top wires it up at top_mega65-r3.vhd:403-413, feeding the jack's pwm_l_o/pwm_r_o pins.

On the R3, the I2S DAC chip that is on the board (an SSM2518) is deliberately switched off — held in power-down and its clock lines forced low (top_mega65-r3.vhd:453-457), because the PDM path drives the jack instead. If PDM or sigma-delta is unfamiliar, pulse-density modulation is a gentle primer.

5.3 The takeaway

Whichever board you are on, the jack carries the same post-filter samples; only the last step — a real DAC or a one-bit bitstream — differs, and it is entirely the framework's problem. A porter never touches any of this. You hand over signed 16-bit samples once, and they come out of the headphone jack of every board revision, correctly, without another line of your code.


Part 6 — The digital output: audio inside the HDMI signal

The other branch of the fork carries the same samples to HDMI. This is where audio borrows the video pipeline's machinery, so this part leans on Video Pipeline for the parts the two share and focuses on what is specific to sound.

6.1 HDMI carries audio in the gaps between video

An HDMI signal is, at bottom, the video pipeline's TMDS stream — HDMI's serial line code (see Video Pipeline §5.4). But HDMI adds something plain-video DVI never had: data islands, small packets transmitted during the blanking gaps between video lines. Audio rides in those islands. So the sound does not get its own cable or its own wires; it is a passenger tucked into the idle moments of the picture, encoded and serialized by the very same hardware. Two consequences follow immediately, and both matter later: the audio has to be squeezed into the blanking budget alongside the picture, and turning the islands off (DVI mode, §6.5) silences HDMI entirely.

6.2 First job — arrive at exactly 48 kHz

HDMI audio in M2M is fixed at 48 kHz — chosen in digital_pipeline.vhd:164-167 as the most universally compatible rate. The samples arrive in the 12.288 MHz audio domain, and since 12.288 MHz = 256 × 48 kHz, a 48 kHz sample tick is simply "one clock in every 256." The framework generates that tick generically, with a small accumulator (clk_synthetic_enable) fed the two frequencies, in digital_pipeline.vhd:297-304:

   i_clk_synthetic_enable : entity work.clk_synthetic_enable
      port map (
         clk_i       => audio_clk_i,
         src_speed_i => G_AUDIO_CLOCK_RATE,   -- 12_288_000
         dst_speed_i => HDMI_PCM_SAMPLING,    -- 48_000
         enable_o    => audio_pcm_clken       -- one pulse every 256 clocks
      );

For this particular ratio the accumulator lands on a perfect 256:1 division, so the 48 kHz strobe is exactly periodic and jitter-free.

6.3 The clever part — audio clock regeneration (ACR)

Now a genuinely elegant problem. The HDMI sink — your TV — has to reconstruct the 48 kHz audio clock in order to play the samples. But the cable carries no separate audio clock; the only clock crossing it is the pixel clock that paces the video. So how does the TV recover 48 kHz from a 74.25 MHz pixel clock?

The answer is audio clock regeneration (ACR): the source periodically tells the sink two integers, N and CTS, from which the sink derives the audio rate off the pixel clock, by the HDMI-defined relation

   f_audio  =  f_pixel × N / (128 × CTS)

M2M's values are set in digital_pipeline.vhd:306-309: N = 6144 (the HDMI-standard value for 48 kHz, since 48000 × 128 / 1000 = 6144) and CTS = the pixel clock in kHz (hdmi_video_mode.CLK_KHZ — e.g. 74250 at 720p). Because CTS is wired to the exact pixel clock of the current video mode, the ratio N / CTS self-adjusts per mode and the sink always recovers exactly 48 kHz, whatever HDMI mode is selected — no measurement, no drift. The source resends this N/CTS pair in an ACR packet 1000 times a second (a 1 kHz strobe, 128 × Fs / N = 1 kHz, generated in digital_pipeline.vhd:311-326) so the sink stays locked.

6.4 Framing and packing — vga_to_hdmi

The encoder that assembles the islands is vga_to_hdmi.vhd (from the Tyto2 project; the same module the video pipeline uses to turn VGA-style timing into HDMI). Its banner cheerfully notes that it "also injects PCM stereo audio." For sound it does three things:

   filtered samples (48 kHz, in the 12.288 MHz audio domain)
        │  widen each 16-bit sample to a 24-bit IEC 60958 subframe (the sample, low byte zero)
        │  cross from the audio clock to the HDMI pixel clock
        ▼
   HDMI data islands ── packed into the blanking gaps, alongside the ACR (N/CTS) packet
        │                and two InfoFrames (audio + video) that describe the streams
        ▼
   the SAME TMDS serializers that carry the picture  ──▶  HDMI connector
        (the video pipeline's Part 5.4 does this half)

IEC 60958 is the consumer-digital-audio frame format (the thing inside every optical S/PDIF cable); each 16-bit sample is left-justified into a 24-bit subframe. The samples then cross from the audio clock to the pixel clock inside the encoder, and are packed into audio-sample data islands. Two InfoFrames — small descriptor packets — tell the sink what it is receiving: the audio InfoFrame and channel-status bits declare 2-channel PCM, 16-bit, 48 kHz, front-left/front-right, no copyright, no pre-emphasis. The bit-level TMDS encoding and serialization are the video pipeline's job (Video Pipeline §5.4); here it is enough to know the sound is framed, described, and handed to the shared serializers.

6.5 DVI mode strips it all

One menu bit, qnice_dvi_o, puts the encoder into plain DVI mode. DVI is HDMI without data islands — video only — so in DVI mode there are no audio packets, no ACR, and no InfoFrames: the sound over HDMI simply disappears. This is deliberate, and occasionally a lifesaver: some DVI-only monitors and cheap HDMI-to-DVI adapters choke on the audio islands and show no picture at all until you switch to DVI. But it also means that "I get a picture but no HDMI sound" often has a one-word cause — DVI — worth checking before anything harder. (The same bit is discussed from the video side in Video Pipeline §5.4.)

And that is the whole digital path: resample to 48 kHz, tell the sink how to regenerate that rate from the pixel clock, frame the samples as IEC 60958, and let the picture's own serializers carry the sound out the connector. HDMI audio in M2M is stereo, 16-bit, 48 kHz, full stop — there is no surround, no higher bit depth, and (though the encoder technically supports it) no 44.1 kHz path; it is wired permanently to 48 kHz for maximum compatibility.


Part 7 — Three cores, three sets of choices

Everything in this article converges on the handful of decisions a porter actually makes. The three reference cores make them three ways; laid side by side they turn the abstract pipeline into a menu of concrete choices you can copy.

Commodore 64 (C64MEGA65) Amiga 500 (AExp) Game Boy Color (gbc4mega65)
Sound chip SID (6581/8580): 3 analog voices + filter Paula: 4 DMA sample channels APU: 2 pulse + wave + noise
Native sample format 18-bit signed (dual-SID stereo) 15-bit signed (HW-mixed) 16-bit unsigned, uncentered
Conversion in main.vhd keep top 16 bits (÷4) + saturate append a 0 LSB (×2) logical shift right 1, forced 0 sign
Improvement filter on by default bypassed (raw Paula) off by default
audio_cx1 coefficient 2 (template typo, inherited) 3 (corrected binomial) 2 (template typo, inherited)
Mute menu item none (mute wired to '0') none none
Analog jack DAC framework — I2S on R4-R6, PDM on R3 same same
HDMI audio framework — stereo 16-bit 48 kHz same same

Read down a column and you have that core's entire audio design; read across a row and you have the menu of how one decision can go. The bottom two rows are grey for a reason: the jack DAC and the HDMI packaging are identical for every core, because they are the framework's job, not the porter's. Three short syntheses:

The Commodore 64 is the textbook case. Its chip is a beloved analog synthesizer whose warmth people expect, so the improvement filter is on by default and the only real work is the 18-to-16-bit downshift — with a saturating guard left in place as a scaffold for the extra audio sources (OPL, DAC, tape) a fuller port might add.

The Amiga shows that the filter is optional. Paula already sounds like Paula — sampled, characterful — so AExp bypasses the filter and passes the raw stream, its conversion a single one-bit shift. Tellingly, it still carries the corrected audio_cx1 = 3 in globals.vhd, ready the moment anyone turns the filter on: the right numbers, waiting.

The Game Boy is the cautionary tale. Its APU speaks unsigned, uncentered audio, and the wrong conversion — an arithmetic shift, or a naïve subtract of 0x8000 — produces loud crackling, verified in real games. It is the concrete proof that "hand M2M signed 16-bit centered on zero" is a contract you must actually honor, not a formality.

The porter's audio checklist

Distilling the whole article into the order you actually do things:

  1. Convert your samples in main.vhd. Map your sound chip's native output onto signed 16-bit centered on zero (§2.3). Test with a game that has near-silent passages: crackle or hum at silence means a sign or centering bug, not a volume one.
  2. Copy the filter coefficients into globals.vhd. Take them from your MiSTer core's sys/sys_top.v, not from the template (mind audio_cx1 — §4.3). Decide whether the filter should default on (like the C64) or off (like the Game Boy).
  3. Add the menu item(s) in config.vhd. An "Audio improvements" toggle at minimum, and — a cheap win the reference cores skip — an optional "Mute."
  4. Wire the policy in mega65.vhd. Drive qnice_audio_filter_o (and, if you added one, qnice_audio_mute_o) from those menu bits, and keep the C_MENU_* indices in sync with the menu order.
  5. Stop there. The 12.288 MHz clock, the clock-domain crossing, the per-board jack DAC, and the HDMI 48 kHz packaging are all handled for you. Hand over good samples and the framework does the rest.

Steps 1–2 get sound out of both outputs; steps 3–4 give the user control over the filter; step 5 is the reminder that most of the pipeline is not yours to build.

Where to go next

  • The signal that carries HDMI audioVideo Pipeline is this article's sister; its Part 5.4 covers the TMDS serializers and data islands that ferry the sound, and its Part 5 covers the DVI bit from the video side.
  • The audio clock and the clock-domain crossingClocking and Clock Domain Crossing explains the 12.288 MHz MMCME2, what cdc_stable guarantees, and when to reach for a FIFO instead.
  • The coefficient constants in contextglobals.vhd is the hardware-facts file where the filter coefficients (and much else) live.
  • The menu items and general settingsconfig.vhd Switches and Settings is the exhaustive reference for the OSM lines you add.
  • The exact edits, file by fileThe Ultimate MiSTer2MEGA65 Porting Guide turns the checklist above into concrete steps with the MiSTer-to-M2M pattern catalog.
  • The MiSTer side you start fromMiSTer Quick Reference Guide describes the AUDIO_L/AUDIO_R/AUDIO_S/AUDIO_MIX signals a MiSTer core hands you.
  • Background primers — the PCM, I²S, and pulse-density-modulation links in Parts 1 and 5 are a gentle foundation if any of the digital-audio vocabulary still feels new.

Clone this wiki locally