-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mega65] Add DMA hardware registers and DMA examples #347
Merged
Merged
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
b1bb1e7
Add DMA headers and example
mlund 3bf19a9
Add simple DMA example for fill/copy
mlund 61d17e7
Avoid binary literals which is an extension
mlund 803ec73
Add asm volatile to end of trigger function
mlund 2a8d6f9
Add asm volatile hack to prevent optimization
mlund c89ff54
Use simpler volatile asm
mlund 6105424
Remove comment
mlund d36867f
Add DMA audio
mlund 56d004b
Use audio channel struct
mlund 9d00480
Added 24-bit register access on C23
mlund 754b804
DMA audio bitflags
mlund bd7226d
Allow BitInt in C17 and C++
mlund 5d76702
Add DMA audio example
mlund 944f4f8
Documentation
mlund 5396861
Fix CHSGN docs
mlund 775e593
dma audio example works on real hardware
mlund b4878a7
Prettify doxygen strings
mlund 8a863bb
Add include guard and comment to dma.hpp
mlund 597bd4b
Fix typo
mlund File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,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,107 @@ | ||
// 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 | ||
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, | ||
}; | ||
|
||
/// The F018 "DMAgic" DMA controller at 0xd700 | ||
struct DMAgicController { | ||
uint8_t addr_lsb_trigger; //!< offset 0x00 | ||
uint8_t addr_msb; //!< offset 0x01 | ||
uint8_t addr_bank; //!< offset 0x02, writing clears $d704 | ||
uint8_t enable_f018b; //!< offset 0x03, extensed fields | ||
uint8_t addr_mb; //!< offset 0x04 | ||
uint8_t trigger_enhanced; //!< offset 0x05 | ||
uint8_t etrigmapd; //!< offset 0x06 | ||
uint8_t unused1[7]; //!< offset 0x07-0x0d | ||
uint8_t addr_lsb; //!< offset 0x0e | ||
uint8_t unused2[2]; //!< offset 0x0f-0x10 | ||
}; | ||
|
||
/// 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 |
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,106 @@ | ||
// 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. | ||
// | ||
|
||
#pragma once | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer include guards for nit-picky compiler-person reasons. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <mega65.h> | ||
#include <type_traits> | ||
|
||
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 <size_t N, typename T = DMAList_F018B> struct DMAJob { | ||
static_assert(N > 0); | ||
static_assert(std::is_same<T, DMAList_F018A>::value || | ||
std::is_same<T, DMAList_F018B>::value); | ||
std::array<uint8_t, N> 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 <size_t N, typename T> | ||
inline void trigger_dma(const DMAJob<N, T> &dma_job) { | ||
DMA.enable_f018b = std::is_same<T, DMAList_F018B>::value; | ||
DMA.addr_bank = 0; | ||
DMA.addr_msb = ((uint16_t)&dma_job) >> 8; | ||
DMA.trigger_enhanced = ((uint16_t)&dma_job) & 0xff; | ||
// The following is to prevent `dma_job` from being optimized out | ||
mysterymath marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// by making sure it's used in fictive asm code. A simple | ||
// `asm volatile("")` also seems to work for unknown reasons(?). | ||
asm volatile("" | ||
: /* no output */ | ||
: "R"(dma_job.options[0]) | ||
: /* no clobbers */); | ||
} | ||
|
||
} // namespace mega65::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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here an initializer list would have been nice, but it doesn't seem to be supported by the compiler. That is
dma.options = {DST_ADDR_BITS_OPT, 0xff};