-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mega65] Add DMA hardware registers and DMA examples (#347)
* Add DMA headers and example * Add simple DMA example for fill/copy * Avoid binary literals which is an extension * Add asm volatile to end of trigger function This prevents it from being optimized out, albeit the underlying reason is still unknown. * Add asm volatile hack to prevent optimization * Use simpler volatile asm * Remove comment * Add DMA audio * Use audio channel struct * Added 24-bit register access on C23 * DMA audio bitflags * Allow BitInt in C17 and C++ Remove C23 guard * Add DMA audio example * Documentation * Fix CHSGN docs * dma audio example works on real hardware * Prettify doxygen strings * Add include guard and comment to dma.hpp * Fix typo
- Loading branch information
Showing
10 changed files
with
485 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2024 LLVM-MOS Project | ||
// Licensed under the Apache License, Version 2.0 with LLVM Exceptions. | ||
// See https://github.com/llvm-mos/llvm-mos-sdk/blob/main/LICENSE for license | ||
// information. | ||
|
||
// MEGA65 DMA Audio example using C23 | ||
|
||
#include <mega65.h> | ||
|
||
typedef unsigned _BitInt(24) uint24_t; | ||
|
||
// Sample data prepared with: | ||
// ~~~ | ||
// sox -v 2.0 -V -D drums.wav drums.s8 remix - lowpass 7000 rate -v -s -I 11822 dither -S | ||
// ~~~ | ||
static const uint8_t sample[] = { | ||
#embed "drums.s8" | ||
}; | ||
|
||
int main(void) { | ||
DMA.auden = DMA_AUDEN; // enable DMA audio | ||
DMA.ch0rvol = 0; // mute right | ||
DMA.ch0.enable = 0; // mute channel 0 | ||
DMA.ch0.baddr = (uint24_t)&sample; // base address | ||
DMA.ch0.curaddr = (uint24_t)&sample; // current address | ||
DMA.ch0.taddr = (uint16_t)&sample + sizeof(sample); // top address | ||
DMA.ch0.freq = 0x001a88; // frequency | ||
DMA.ch0.volume = 0x3f; // max volume | ||
DMA.ch0.enable = DMA_CHENABLE ^ DMA_CHSBITS_8 ^ DMA_CHLOOP; // play! | ||
while (1) { | ||
// Leaves at least one cycle for DMA audio to steal | ||
VICIV.bordercol += 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Source: https://commons.wikimedia.org/wiki/File:Patró_de_bateria.wav | ||
Attribution: Escola Superior de Música de Catalunya (ESMUC) | ||
License: CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0>, via Wikimedia Commons | ||
Note: Modified from original. |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright 2024 LLVM-MOS Project | ||
// Licensed under the Apache License, Version 2.0 with LLVM Exceptions. | ||
// See https://github.com/llvm-mos/llvm-mos-sdk/blob/main/LICENSE for license | ||
// information. | ||
|
||
#include <dma.hpp> | ||
#include <mega65.h> | ||
|
||
using namespace mega65::dma; | ||
|
||
constexpr uint32_t SCREEN_ADDR = 0x0800; // Screen area | ||
constexpr uint16_t COUNT = 4; // Bytes to fill | ||
constexpr uint8_t CHAR = 41; // Char symbol to print | ||
|
||
int main(void) { | ||
{ | ||
// repeat some chars on first line | ||
const auto dma = make_dma_fill(SCREEN_ADDR, CHAR, COUNT); | ||
trigger_dma(dma); | ||
} | ||
{ | ||
// copy chars from above to second line | ||
const auto dma = make_dma_copy(SCREEN_ADDR, SCREEN_ADDR + 80, COUNT); | ||
trigger_dma(dma); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2024 LLVM-MOS Project | ||
// Licensed under the Apache License, Version 2.0 with LLVM Exceptions. | ||
// See https://github.com/llvm-mos/llvm-mos-sdk/blob/main/LICENSE for license | ||
// information. | ||
|
||
// Vertical Raster Bars using the DMAgic DMA controller. | ||
// | ||
// - Timing is crucial: specify NTSC or PAL below. | ||
// - Compile with `mos-mega65-clang++ -Os -flto` | ||
// - Originally from ACME assembler example in the MEGA65 book | ||
// - Converted to llvm-mos / C++ by Wombat, 2024. | ||
// - As of writing (Summer 2024) this runs only on real hardware | ||
|
||
#include <dma.hpp> | ||
#include <mega65.h> | ||
|
||
using namespace mega65; | ||
|
||
constexpr bool PAL = true; // Set to false on NTSC systems | ||
|
||
constexpr uint8_t RASTER_COLORS[] = { | ||
0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, | ||
5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, | ||
3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, | ||
3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, | ||
11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, | ||
5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, | ||
10, 11, 12, 13, 14, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, | ||
13, 14, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 14, | ||
13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13, 12, 11, 10, 9, | ||
8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, | ||
2, 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 10, 9, 8, 7, | ||
6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 8, 7, | ||
6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 6, 5, 4, 3, | ||
2, 1, 0, 5, 4, 3, 2, 1, 0, 4, 3, 2, 1, 0, 3, 2, 1, 0, 2, | ||
1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, | ||
4, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, | ||
5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, | ||
7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, | ||
5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | ||
12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, | ||
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 1, 2, 3, 4, 5, 6, | ||
7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, | ||
4, 3, 2, 1, 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, | ||
0, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11, 10, 9, | ||
8, 7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, | ||
1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, | ||
3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, | ||
1, 0, 6, 5, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1, 0, 4, 3, 2, 1, | ||
0, 3, 2, 1, 0, 2, 1, 0, 1, 0, 0, 0, | ||
}; | ||
|
||
int main() { | ||
asm volatile("sei"); // Disable interrupt to stabilise timing | ||
VICIV.ctrl1 = 0; // Disable screen | ||
|
||
// Set custom color palette | ||
for (size_t i = 1; i < 16; i++) { | ||
PALETTE.red[i] = 15 - i; | ||
PALETTE.green[i] = 0; | ||
PALETTE.blue[i] = i; | ||
} | ||
|
||
// Configure DMA job; see The MEGA65 Book, Appendix O. | ||
dma::DMAJob<2, DMAList_F018A> dma; | ||
dma.options[0] = DST_ADDR_BITS_OPT; | ||
dma.options[1] = 0xff; | ||
dma.dmalist.command = DMA_COPY_CMD; | ||
dma.dmalist.count = PAL ? 628 : 624; | ||
dma.dmalist.source_addr = (uint16_t)&RASTER_COLORS; | ||
dma.dmalist.source_bank = 0x00; | ||
dma.dmalist.dest_addr = 0x0020; | ||
dma.dmalist.dest_bank = 0x0d ^ DMA_HOLD; | ||
dma.dmalist.modulo = 0; | ||
|
||
// Wait for new raster line | ||
const auto line = VICIV.rasterline; | ||
while (line == VICIV.rasterline) | ||
; | ||
|
||
#pragma clang loop unroll(disable) | ||
while (true) { | ||
dma::trigger_dma(dma); | ||
if constexpr (PAL) { | ||
asm volatile("nop"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright 2023 LLVM-MOS Project | ||
// Licensed under the Apache License, Version 2.0 with LLVM Exceptions. | ||
// See https://github.com/llvm-mos/llvm-mos-sdk/blob/main/LICENSE for license | ||
// information. | ||
|
||
#ifndef _DMAGIC_H | ||
#define _DMAGIC_H | ||
|
||
#ifndef __cplusplus | ||
#include <stddef.h> | ||
#include <stdint.h> | ||
#endif | ||
|
||
/// DMA commands | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
DMA_COPY_CMD = 0x00, //!< DMA copy command | ||
DMA_MIX_CMD = 0x01, //!< DMA mix command (unimplemented) | ||
DMA_SWAP_CMD = 0x02, //!< DMA swap command (unimplemented) | ||
DMA_FILL_CMD = 0x03, //!< DMA fill command | ||
}; | ||
|
||
/// Addressing modes | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
DMA_LINEAR_ADDR = 0x00, //!< DMA linear (normal) addressing mode | ||
DMA_MODULO_ADDR = 0x01, //!< DMA modulo (rectangular) addressing mode | ||
DMA_HOLD_ADDR = 0x02, //!< DMA hold (constant address) addressing mode | ||
DMA_XYMOD_ADDR = | ||
0x03 //!< DMA XY MOD (bitmap rectangular) addressing mode (unimplemented) | ||
}; | ||
|
||
/// BANK and FLAGS field has the following structure | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
DMA_HOLD = 16, //!< Do not change the address (bit 4) | ||
DMA_MODULO = 32, //!< Apply the MODULO field to wrap around (bit 5) | ||
DMA_DIRECTION = 64, //!< Apply the MODULO field to wrap around (bit 6) | ||
DMA_IO = 128, //!< I/O registers visible during the DMA controller at $D000–$DFFF (bit 7). | ||
}; | ||
|
||
/// DMA options (incomplete) | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
/// Use 11 byte F011A DMA list format [no value] | ||
ENABLE_F018A_OPT = 0x0a, | ||
/// Use 12 byte F011B DMA list format [no value] | ||
ENABLE_F018B_OPT = 0x0b, | ||
/// Source address bits 20 – 27 [value follows] | ||
SRC_ADDR_BITS_OPT = 0x80, | ||
/// Destination address bits 20 – 27 [value follows] | ||
DST_ADDR_BITS_OPT = 0x81, | ||
/// Destination skip rate (whole bytes) [value follows] | ||
DST_SKIP_RATE_OPT = 0x85, | ||
}; | ||
|
||
/// DMA audio channel structure | ||
struct DMAAudioChannel { | ||
uint8_t enable; //!< Enable Audio DMA channel X (offset 0x00) | ||
union { | ||
struct { | ||
uint8_t baddr_lsb; //!< Base address LSB (offset 0x01) | ||
uint8_t baddr_msb; //!< Base address MSB (offset 0x02) | ||
uint8_t baddr_mb; //!< Base address middle byte (offset 0x03) | ||
}; | ||
#ifdef __clang__ | ||
unsigned _BitInt(24) baddr; //!< 24-bit base address (offset 0x01) | ||
#endif | ||
}; | ||
union { | ||
struct { | ||
uint8_t freq_lsb; //!< Frequency LSB (offset 0x04) | ||
uint8_t freq_mb; //!< Frequency middle byte (offset 0x05) | ||
uint8_t freq_msb; //!< Frequency MSB (offset 0x06) | ||
}; | ||
#ifdef __clang__ | ||
unsigned _BitInt(24) freq; //!< 24-bit frequency (offset 0x04) | ||
#endif | ||
}; | ||
union { | ||
struct { | ||
uint8_t taddr_lsb; //!< Top address LSB (offset 0x07) | ||
uint8_t taddr_msb; //!< Top address MSB (offset 0x08) | ||
}; | ||
#ifdef __clang__ | ||
unsigned _BitInt(16) taddr; //!< 16-bit top address (offset 0x07) | ||
#endif | ||
}; | ||
uint8_t volume; //!< playback volume (offset 0x09) | ||
union { | ||
struct { | ||
uint8_t curaddr_lsb; //!< Current address LSB (offset 0x0a) | ||
uint8_t curaddr_mb; //!< Current address middle byte (offset 0x0b) | ||
uint8_t curaddr_msb; //!< Current address MSB (offset 0x0c) | ||
}; | ||
#ifdef __clang__ | ||
unsigned _BitInt(24) curaddr; //!< 24-bit current address (offset 0x0a) | ||
#endif | ||
}; | ||
union { | ||
struct { | ||
uint8_t tmraddr_lsb; //!< Timing counter LSB (offset 0x0d) | ||
uint8_t tmraddr_mb; //!< Timing counter middle byte (offset 0x0e) | ||
uint8_t tmraddr_msb; //!< Timing counter MSB (offset 0x0f) | ||
}; | ||
#ifdef __clang__ | ||
unsigned _BitInt(24) tmraddr; //!< 24-bit timing counter (offset 0x0d) | ||
#endif | ||
}; | ||
}; | ||
|
||
#ifdef __cplusplus | ||
static_assert(sizeof(DMAAudioChannel) == 0x10); | ||
#endif | ||
|
||
/// Bitflags for controlling the DMA audio enable register ($d711) | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
DMA_AUDEN = 0b10000000, //!< Enable Audio DMA | ||
DMA_BLKD = 0b01000000, //!< Block DMA | ||
DMA_AUD_WRBLK = 0b00100000, //!< Audio write block | ||
DMA_NOMIX = 0b00010000, //!< No mix; disables SID chips etc. | ||
DMA_AUDBLKTO = 0b00000111, //!< Audio block timeout (DEBUG) Bits 0-2 | ||
}; | ||
|
||
/// Bitflags for controlling individual DMA audio channel enable registers | ||
/// ($d720, etc) | ||
enum | ||
#ifdef __clang__ | ||
: uint8_t | ||
#endif | ||
{ | ||
DMA_CHENABLE = 0b10000000, //!< Enable channel | ||
DMA_CHLOOP = 0b01000000, //!< Channel looping | ||
DMA_CHSGN = 0b00100000, //!< Sample sign bit: 0=signed, 1=unsigned. | ||
DMA_CHSINE = 0b00010000, //!< Play 32-sample sine wave instead of DMA data | ||
DMA_CHSTP = 0b00001000, //!< Stop flag | ||
DMA_CHSBITS_16 = 0b00000011, //!< 16-bit samples | ||
DMA_CHSBITS_8 = 0b00000010, //!< 8-bit samples | ||
}; | ||
|
||
/// The F018 "DMAgic" DMA controller at 0xd700 | ||
struct DMAgicController { | ||
uint8_t addr_lsb_trigger; //!< DMAgic DMA list address LSB, and trigger DMA (when written) (offset 0x00) | ||
uint8_t addr_msb; //!< DMA list address high byte (address bits 8 – 15) (offset 0x01) | ||
uint8_t addr_bank; //!< DMA list address bank (address bits 16 – 22). Writing clears $D704 (offset 0x02) | ||
uint8_t enable_f018b; //!< Offset 0x03, extensed fields | ||
uint8_t addr_mb; //!< DMA list address mega-byte (offset 0x04) | ||
uint8_t trigger_enhanced; //!< Set low-order byte of DMA list address, and trigger Enhanced DMA job (offset 0x05) | ||
uint8_t etrigmapd; //!< Set low-order byte of DMAlistaddress and trigger EnhancedDMA job, with list in current CPU memory map (offset 0x06) | ||
uint8_t unused1[7]; //!< Offset 0x07-0x0d | ||
uint8_t addr_lsb; //!< DMA list address low byte (address bits 0 – 7) WITHOUT STARTING A DMA JOB (offset 0x0e) | ||
uint8_t unused2[2]; //!< Offset 0x0f-0x10 | ||
uint8_t auden; //!< Enable Audio DMA (offset 0x11) | ||
uint8_t unused3[10]; //!< Offset 0x12-0x1b | ||
uint8_t ch0rvol; //!< Channel 0 right channel volume (offset 0x1c) | ||
uint8_t ch1rvol; //!< Channel 1 right channel volume (offset 0x1d) | ||
uint8_t ch2lvol; //!< Channel 2 left channel volume (offset 0x1e) | ||
uint8_t ch3lvol; //!< Channel 3 left channel volume (offset 0x1f) | ||
union { | ||
struct { | ||
struct DMAAudioChannel ch0; //!< Audio DMA channel 0 (offset 0x20) | ||
struct DMAAudioChannel ch1; //!< Audio DMA channel 1 (offset 0x30) | ||
struct DMAAudioChannel ch2; //!< Audio DMA channel 2 (offset 0x40) | ||
struct DMAAudioChannel ch3; //!< Audio DMA channel 3 (offset 0x50) | ||
}; | ||
struct DMAAudioChannel channel[4]; //!< Audio channels as an array (offset 0x20) | ||
}; | ||
}; | ||
|
||
#ifdef __cplusplus | ||
static_assert(sizeof(DMAgicController) == 0x60); | ||
#endif | ||
|
||
/// Older 11 byte DMA list structure; also known as just "F018" | ||
struct DMAList_F018A { | ||
uint8_t command; //!< Offset 0x00 | ||
uint16_t count; //!< Offset 0x01 | ||
uint16_t source_addr; //!< Offset 0x03 | ||
uint8_t source_bank; //!< Offset 0x05 | ||
uint16_t dest_addr; //!< Offset 0x06 | ||
uint8_t dest_bank; //!< Offset 0x08 | ||
uint16_t modulo; //!< Offset 0x09 | ||
}; | ||
|
||
/// Newer 12-byte "F018B" DMA list structure | ||
struct DMAList_F018B { | ||
uint8_t command; //!< Offset 0x00 | ||
uint16_t count; //!< Offset 0x01 | ||
uint16_t source_addr; //!< Offset 0x03 | ||
uint8_t source_bank; //!< Offset 0x05 | ||
uint16_t dest_addr; //!< Offset 0x06 | ||
uint8_t dest_bank; //!< Offset 0x08 | ||
uint8_t command_msb; //!< Offset 0x09 | ||
uint16_t modulo; //!< Offset 0x0a | ||
}; | ||
|
||
#endif |
Oops, something went wrong.