From ae3a96f2b22c0fc0ed0493391d8aad3ee0229ae4 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 30 Jul 2024 08:18:49 +0200 Subject: [PATCH] [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 --- examples/mega65/CMakeLists.txt | 10 ++ examples/mega65/dma_audio.c | 34 +++++ examples/mega65/drums.license | 4 + examples/mega65/drums.s8 | Bin 0 -> 44031 bytes examples/mega65/simple_dma.cc | 26 ++++ examples/mega65/vertical_raster.cc | 88 ++++++++++++ mos-platform/mega65/CMakeLists.txt | 2 + mos-platform/mega65/_dmagic.h | 213 +++++++++++++++++++++++++++++ mos-platform/mega65/dma.hpp | 105 ++++++++++++++ mos-platform/mega65/mega65.h | 3 + 10 files changed, 485 insertions(+) create mode 100644 examples/mega65/dma_audio.c create mode 100644 examples/mega65/drums.license create mode 100644 examples/mega65/drums.s8 create mode 100644 examples/mega65/simple_dma.cc create mode 100644 examples/mega65/vertical_raster.cc create mode 100644 mos-platform/mega65/_dmagic.h create mode 100644 mos-platform/mega65/dma.hpp diff --git a/examples/mega65/CMakeLists.txt b/examples/mega65/CMakeLists.txt index 60c6a2c0a..a58e6fe03 100644 --- a/examples/mega65/CMakeLists.txt +++ b/examples/mega65/CMakeLists.txt @@ -4,3 +4,13 @@ install_example(viciv_test.prg) add_executable(plasma.prg plasma.cc) install_example(plasma.prg) +add_executable(vertical_raster.prg vertical_raster.cc) +install_example(vertical_raster.prg) + +add_executable(simple_dma.prg simple_dma.cc) +install_example(simple_dma.prg) + +add_executable(dma_audio.prg dma_audio.c) +install_example(dma_audio.prg) +install(FILES drums.s8 TYPE BIN) + diff --git a/examples/mega65/dma_audio.c b/examples/mega65/dma_audio.c new file mode 100644 index 000000000..02dd726bb --- /dev/null +++ b/examples/mega65/dma_audio.c @@ -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 + +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; + } +} diff --git a/examples/mega65/drums.license b/examples/mega65/drums.license new file mode 100644 index 000000000..169135ce4 --- /dev/null +++ b/examples/mega65/drums.license @@ -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 , via Wikimedia Commons +Note: Modified from original. diff --git a/examples/mega65/drums.s8 b/examples/mega65/drums.s8 new file mode 100644 index 0000000000000000000000000000000000000000..8a526eed4a34c99c9918d5f9cd23804bd72a17c8 GIT binary patch literal 44031 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 bRa$aUUA1jM+sOh+3ec`Mpp;v!`}+PrirYey literal 0 HcmV?d00001 diff --git a/examples/mega65/simple_dma.cc b/examples/mega65/simple_dma.cc new file mode 100644 index 000000000..dc93298f0 --- /dev/null +++ b/examples/mega65/simple_dma.cc @@ -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 +#include + +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); + } +} diff --git a/examples/mega65/vertical_raster.cc b/examples/mega65/vertical_raster.cc new file mode 100644 index 000000000..652dff9ab --- /dev/null +++ b/examples/mega65/vertical_raster.cc @@ -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 +#include + +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"); + } + } +} diff --git a/mos-platform/mega65/CMakeLists.txt b/mos-platform/mega65/CMakeLists.txt index 37dfeaf76..63bbd0a87 100644 --- a/mos-platform/mega65/CMakeLists.txt +++ b/mos-platform/mega65/CMakeLists.txt @@ -5,8 +5,10 @@ if(NOT CMAKE_CROSSCOMPILING) endif() install(FILES _45E100.h + _dmagic.h _vic3.h _vic4.h + dma.hpp mega65.h TYPE INCLUDE) diff --git a/mos-platform/mega65/_dmagic.h b/mos-platform/mega65/_dmagic.h new file mode 100644 index 000000000..06fbafb70 --- /dev/null +++ b/mos-platform/mega65/_dmagic.h @@ -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 +#include +#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 diff --git a/mos-platform/mega65/dma.hpp b/mos-platform/mega65/dma.hpp new file mode 100644 index 000000000..f976aecca --- /dev/null +++ b/mos-platform/mega65/dma.hpp @@ -0,0 +1,105 @@ +// 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. +// +// C++ support for MEGA65 DMA operations. +// +// +#ifndef _MEGA65_DMA_HPP +#define _MEGA65_DMA_HPP + +#include +#include +#include +#include + +namespace mega65::dma { + +/** + * Data structure for running enhanced DMA jobs + * + * @tparam T Either a F018B (default) or F018A DMA list object. + * @tparam N Byte count of the options and their potential + * arguments excluding the end option (0) which is automatically + * added. + */ +template struct DMAJob { + static_assert(N > 0); + static_assert(std::is_same::value || + std::is_same::value); + std::array options; + const uint8_t end_option = 0; + T dmalist; +}; + +/// Common structure for DMA fill and copy jobs +typedef DMAJob<7, DMAList_F018B> CommonDMAJob; + +/** + * Create DMA structure for filling memory with a value and optional skip + * + * To perform the actual fill, call `trigger_dma()` on the returned structure. + * + * @param dst 28-bit destination address + * @param value Fill value + * @param count Number of values to fill + * @param skip Optional skip (default: 1) + */ +CommonDMAJob make_dma_fill(const uint32_t dst, const uint8_t value, + const uint16_t count, const uint8_t skip = 1) { + CommonDMAJob dma; + dma.options[0] = ENABLE_F018B_OPT; + dma.options[1] = SRC_ADDR_BITS_OPT; + dma.options[2] = 0; + dma.options[3] = DST_ADDR_BITS_OPT; + dma.options[4] = (uint8_t)(dst >> 20); + dma.options[5] = DST_SKIP_RATE_OPT; + dma.options[6] = skip; + dma.dmalist.command = DMA_FILL_CMD; + dma.dmalist.count = count; + dma.dmalist.source_addr = value; + dma.dmalist.source_bank = 0; + dma.dmalist.dest_addr = dst & 0xffff; + dma.dmalist.dest_bank = (dst >> 16) & 0x0f; + dma.dmalist.command_msb = 0; + dma.dmalist.modulo = 0; + return dma; +} + +/** + * Create DMA structure for copying memory + * + * To perform the actual copy, call `trigger_dma()` on the returned structure. + * + * @param src 28-bit source address + * @param dst 28-bit destination address + * @param count Number of values to copy + */ +CommonDMAJob make_dma_copy(const uint32_t src, const uint32_t dst, + const uint16_t count) { + auto dma = make_dma_fill(dst, 0, count); + dma.options[2] = (uint8_t)(src >> 20); + dma.dmalist.command = DMA_COPY_CMD; + dma.dmalist.source_addr = src & 0xffff; + dma.dmalist.source_bank = (src >> 16) & 0x0f; + return dma; +} + +/** + * Perform enhanced DMA action defined in DMAJob structure. + */ +template +inline void trigger_dma(const DMAJob &dma_job) { + DMA.enable_f018b = std::is_same::value; + DMA.addr_bank = 0; + DMA.addr_msb = ((uint16_t)&dma_job) >> 8; + DMA.trigger_enhanced = ((uint16_t)&dma_job) & 0xff; + // Avoid the above from being optimized out. Ideally + // we would want to somehow access `dma_job`, but an + // empty statement seems to be sufficient. + asm volatile(""); +} + +} // namespace mega65::dma +#endif // _MEGA65_DMA_HPP diff --git a/mos-platform/mega65/mega65.h b/mos-platform/mega65/mega65.h index aaf48f202..025d68475 100644 --- a/mos-platform/mega65/mega65.h +++ b/mos-platform/mega65/mega65.h @@ -22,6 +22,7 @@ extern "C" { #include <_45E100.h> #include <_6526.h> +#include <_dmagic.h> #include <_sid.h> #include <_vic2.h> #include <_vic3.h> @@ -162,6 +163,8 @@ struct __color_palette { #define HYPERVISOR (*(volatile struct __hypervisor *)0xd640) /// Ethernet controller #define ETHERNET (*(volatile struct __45E100 *)0xd6e0) +/// DMAgic DMA controller +#define DMA (*(volatile struct DMAgicController *)0xd700) /// Math busy flag #define MATHBUSY (*(volatile uint8_t *)0xd70f) /// Math accelerator