rilo

Copy sample from C64 RAM to REU, then play via Ultimate Audio

Apr 26th, 2026 (edited)
223
0
Never
5
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ------------------------------------------------------------
  2. // Ultimate 64 / Ultimate-II+
  3. // Copy a signed 8-bit PCM sample from C64 RAM to REU,
  4. // then play it using Ultimate Audio channel 0.
  5. //
  6. // Very simple example player by Scan/House Designs/DESiRE
  7. // Richard Loerakker
  8. //
  9. // This is not a complete music player.
  10. // It only demonstrates the minimal steps needed to:
  11. //
  12. //   1. copy sample data from C64 memory to REU memory
  13. //   2. point Ultimate Audio channel 0 to that REU sample
  14. //   3. set length, rate, volume and pan
  15. //   4. start playback
  16. //
  17. // Other programmers can build on this for sample triggers,
  18. // drum playback, speech, tracker-style routines, or SID +
  19. // Ultimate Audio hybrid demos.
  20. //
  21. // Compile using Kick Assembler:
  22. //     http://www.theweb.dk/KickAssembler/
  23. //
  24. // Sample format:
  25. //
  26. //     RAW header-less
  27. //     Mono
  28. //     Signed 8-bit PCM
  29. //     8000 Hz in this example
  30. //
  31. // Using Audacity:
  32. //
  33. //     Open an audio file
  34. //     Set project/sample rate to 8000 Hz
  35. //     Convert audio to mono
  36. //     Optionally apply normalize/compressor/etc.
  37. //     Select the audio you want to export
  38. //     File -> Export Audio
  39. //         Format: Other uncompressed files
  40. //         Header: RAW (header-less)
  41. //         Encoding: Signed 8-bit PCM
  42. //
  43. // Notes:
  44. //
  45. //     Ultimate Audio 8-bit PCM expects signed sample data.
  46. //     Do not use unsigned 8-bit PCM unless you convert it first.
  47. //
  48. .var sampleBin = LoadBinary("sample.raw")
  49. .const SAMPLE_LEN = sampleBin.getSize()
  50.  
  51. .if (SAMPLE_LEN > $c000) {
  52.     .error "This sample is too big, use a smaller one"
  53. }
  54.  
  55. // ------------------------------------------------------------
  56. // IMPORTANT:
  57. //
  58. // Ultimate Audio sample rate is derived from a divider.
  59. // Bigger divider = slower playback.
  60. // Smaller divider = faster playback.
  61. //
  62. // Approximate values from 6.25 MHz:
  63. //
  64. //   4000 Hz  -> 1563
  65. //   8000 Hz  -> 781
  66. //   11025 Hz -> 567
  67. //   16000 Hz -> 391
  68. //   22050 Hz -> 283
  69. //   44100 Hz -> 142
  70. //   48000 Hz -> 130
  71. //
  72. // If your sample plays 2x too fast, double this value.
  73. // If it plays 4x too fast, multiply by 4.
  74. //
  75. // ------------------------------------------------------------
  76.  
  77. .const SAMPLE_RATE_DIVIDER = 781   // 8000 Hz-ish
  78.  
  79. // ------------------------------------------------------------
  80. // REU registers
  81. // ------------------------------------------------------------
  82.  
  83. .const REU_STATUS   = $DF00
  84. .const REU_COMMAND  = $DF01
  85. .const REU_C64ADDR  = $DF02     // low, high
  86. .const REU_REUADDR  = $DF04     // low, high, bank
  87. .const REU_LENGTH   = $DF07     // low, high
  88. .const REU_IRQMASK  = $DF09
  89. .const REU_ADDRCTRL = $DF0A
  90.  
  91. .const REU_CMD_C64_TO_REU_IMMEDIATE = %10010000
  92. // bit 7 = execute
  93. // bit 4 = immediate
  94. // bits 1-0 = 00, C64 -> REU
  95.  
  96. // ------------------------------------------------------------
  97. // Ultimate Audio channel 0
  98. // ------------------------------------------------------------
  99.  
  100. .const UA0_BASE     = $DF20
  101.  
  102. .const UA0_CONTROL  = UA0_BASE + $00
  103. .const UA0_VOLUME   = UA0_BASE + $01
  104. .const UA0_PAN      = UA0_BASE + $02
  105.  
  106. .const UA0_START    = UA0_BASE + $04     // 4 bytes, big endian
  107. .const UA0_LENGTH   = UA0_BASE + $09     // 3 bytes, big endian
  108. .const UA0_RATE     = UA0_BASE + $0E     // 2 bytes, big endian
  109.  
  110. .const UA0_IRQCLEAR = UA0_BASE + $1F
  111.  
  112. .const UA_GATE      = %00000001
  113. .const UA_REPEAT    = %00000010
  114. .const UA_IRQ       = %00000100
  115.  
  116. .const UA_MODE_8BIT  = %00000000
  117. .const UA_MODE_16BIT = %00010000
  118.  
  119. // ------------------------------------------------------------
  120. // BASIC start
  121. // ------------------------------------------------------------
  122.  
  123. * = $0801 "BASIC"
  124. :BasicUpstart2(start)
  125.  
  126. * = $0810 "Code"
  127.  
  128. start:
  129.         sei
  130.  
  131.         // Make sure I/O is visible at $D000-$DFFF, swap out BASIC ROM
  132.         lda #$36
  133.         sta $01
  134.  
  135.         // Put Ultimate Audio into a known quiet state
  136.         jsr ua_init_silent
  137.  
  138.         // Copy sample from C64 RAM to REU
  139.         jsr copy_sample_to_reu
  140.  
  141.         // Play it
  142.         jsr play_sample_from_reu
  143.  
  144.         cli
  145.         rts
  146.  
  147. // ------------------------------------------------------------
  148. // Clear Ultimate Audio register area $DF20-$DFFF
  149. //
  150. // This deliberately does NOT touch $DF00-$DF1F,
  151. // because that area contains the REU registers.
  152. // ------------------------------------------------------------
  153.  
  154. ua_init_silent:
  155.         lda #$00
  156.         ldx #$20
  157.  
  158. clear_loop:
  159.         sta $DF00,x
  160.         inx
  161.         bne clear_loop
  162.  
  163.         // Give the audio core a tiny moment to see gate=0 everywhere
  164.         jsr ua_short_delay
  165.  
  166.         rts
  167.  
  168. // ------------------------------------------------------------
  169. // Stop channel 0 cleanly
  170. // ------------------------------------------------------------
  171.  
  172. ua_stop_ch0:
  173.         lda #$00
  174.         sta UA0_CONTROL
  175.  
  176.         lda #$ff
  177.         sta UA0_IRQCLEAR
  178.  
  179.         jsr ua_short_delay
  180.  
  181.         rts
  182.  
  183. // ------------------------------------------------------------
  184. // Small delay after gate-off
  185. // Not beautiful. Does the job. Humanity survives another day.
  186. // ------------------------------------------------------------
  187.  
  188. ua_short_delay:
  189.         ldx #$08
  190. outer:
  191.         ldy #$00
  192. inner:
  193.         dey
  194.         bne inner
  195.         dex
  196.         bne outer
  197.         rts
  198.  
  199. // ------------------------------------------------------------
  200. // Copy embedded sample from C64 RAM to REU address $000000
  201. // ------------------------------------------------------------
  202.  
  203. copy_sample_to_reu:
  204.         // Clear old REU status, just in case
  205.         lda REU_STATUS
  206.  
  207.         // Let both C64 and REU addresses increment during DMA
  208.         lda #$00
  209.         sta REU_ADDRCTRL
  210.  
  211.         // No REU IRQs
  212.         lda #$00
  213.         sta REU_IRQMASK
  214.  
  215.         // C64 source address = sample_data
  216.         lda #<sample_data
  217.         sta REU_C64ADDR + 0
  218.         lda #>sample_data
  219.         sta REU_C64ADDR + 1
  220.  
  221.         // REU destination address = $000000
  222.         lda #$00
  223.         sta REU_REUADDR + 0       // REU addr low
  224.         sta REU_REUADDR + 1       // REU addr high
  225.         sta REU_REUADDR + 2       // REU bank
  226.  
  227.         // Transfer length, little endian for REU
  228.         lda #<SAMPLE_LEN
  229.         sta REU_LENGTH + 0
  230.         lda #>SAMPLE_LEN
  231.         sta REU_LENGTH + 1
  232.  
  233.         // Execute C64 -> REU DMA immediately
  234.         lda #REU_CMD_C64_TO_REU_IMMEDIATE
  235.         sta REU_COMMAND
  236.  
  237.         // Turn back on BASIC ROM
  238.         lda #$37
  239.         sta $01
  240.  
  241.         // Clear/read status after DMA
  242.         lda REU_STATUS
  243.  
  244.         rts
  245.  
  246. // ------------------------------------------------------------
  247. // Play sample from REU address $000000 using Ultimate Audio ch0
  248. // ------------------------------------------------------------
  249.  
  250. play_sample_from_reu:
  251.         // Force clean gate-off before programming/retriggering
  252.         jsr ua_stop_ch0
  253.  
  254.         // Volume: 0..63
  255.         lda #$3f
  256.         sta UA0_VOLUME
  257.  
  258.         // Pan: try center-ish
  259.         lda #$07
  260.         sta UA0_PAN
  261.  
  262.         // Sample start address is 4 bytes, big endian.
  263.         //
  264.         // REU address $000000 is addressed by Ultimate Audio as:
  265.         // $01 00 00 00
  266.         lda #$01
  267.         sta UA0_START + 0
  268.         lda #$00
  269.         sta UA0_START + 1
  270.         sta UA0_START + 2
  271.         sta UA0_START + 3
  272.  
  273.         // Sample length is 3 bytes, big endian.
  274.         // For this test SAMPLE_LEN is < 64K, so top byte is 0.
  275.         lda #$00
  276.         sta UA0_LENGTH + 0
  277.         lda #>SAMPLE_LEN
  278.         sta UA0_LENGTH + 1
  279.         lda #<SAMPLE_LEN
  280.         sta UA0_LENGTH + 2
  281.  
  282.         // Sample rate divider is 2 bytes, big endian.
  283.         lda #>SAMPLE_RATE_DIVIDER
  284.         sta UA0_RATE + 0
  285.         lda #<SAMPLE_RATE_DIVIDER
  286.         sta UA0_RATE + 1
  287.  
  288.         // Clear pending channel IRQ/status
  289.         lda #$ff
  290.         sta UA0_IRQCLEAR
  291.  
  292.         // Start one-shot 8-bit sample:
  293.         // gate=1, repeat=0, irq=0, mode=8-bit
  294.         lda #(UA_GATE | UA_MODE_8BIT)
  295.         sta UA0_CONTROL
  296.  
  297.         rts
  298.  
  299. // ------------------------------------------------------------
  300. // Embedded sample data
  301. // ------------------------------------------------------------
  302.  
  303. * = $1000 "Sample data"
  304.  
  305. sample_data:
  306.         .fill sampleBin.getSize(), sampleBin.get(i)
  307.  
  308. sample_end:
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment