From 7699987e8215c2417aecd2b79ac9e4a23e2b49fe Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 13:27:06 +0200 Subject: [PATCH 01/22] Add tests to cc65 build as well as fileio test --- .gitignore | 4 ++ Makefile_cc65 | 41 +++++++++++++++++---- tests/CMakeLists.txt | 1 + tests/test-fileio.c | 77 +++++++++++++++++++++++++++++++++++++++ tests/test-integer-size.c | 3 ++ tests/test-memory.c | 1 + 6 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 tests/test-fileio.c diff --git a/.gitignore b/.gitignore index fd880de..70406b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# PRG files +*.prg +tests/*.prg + # Prerequisites *.d diff --git a/Makefile_cc65 b/Makefile_cc65 index aa760ef..ae5f9b2 100644 --- a/Makefile_cc65 +++ b/Makefile_cc65 @@ -1,5 +1,9 @@ SHELL:=/bin/bash +XEMU=xmega65 +TESTS_DIR := tests +TESTS_SRCS := $(wildcard $(TESTS_DIR)/*.c) +TESTS_PRGS := $(TESTS_SRCS:.c=.prg) ifdef USE_LOCAL_CC65 # use locally installed binary (requires 'cc65,ld65,etc' to be in the $PATH) @@ -10,10 +14,11 @@ else endif CC65=$(CC65_PREFIX)cc65 +CL65=$(CC65_PREFIX)cl65 CA65=$(CC65_PREFIX)ca65 --cpu 4510 AR65=$(CC65_PREFIX)ar65 -CFLAGS=-O -t c64 -W +unused-param,+error,+unused-label,+no-effect,+unused-var,+no-effect -I include/ +CFLAGS=-O -t c64 -I include/ LIB_OBJECTS = \ src/cc65/dirent.o \ @@ -32,21 +37,20 @@ LIB_OBJECTS = \ src/tests.o \ src/time.o -.PHONY: all clean cleanall +.PHONY: all clean cleanall test -all: libmega65.a +libmega65.a: $(LIB_OBJECTS) + $(AR65) a $@ $^ + +all: libmega65.a $(TESTS_PRGS) clean: - rm -f src/*.o - rm -f src/cc65/*.o + rm -f $(LIB_OBJECTS) $(TESTS_PRGS) tests/*.o rm -rf work cleanall: clean rm -f libmega65.a -libmega65.a: $(LIB_OBJECTS) - $(AR65) a $@ $^ - src/cc65/%.o: src/cc65/%.s $(CA65) -o $@ $< @@ -56,3 +60,24 @@ src/%.o: src/%.c fi $(CC65) $(CFLAGS) -o work/$*.s $< $(CA65) -o $@ work/$*.s + +# Unit tests using Xemu in "testing" mode +# +# - Xemu `xmega65` must be in your path +# - Xemu's return code is controlled by register 0xD6CF +# - All tests are in the `tests/` directory +tests/%.prg: tests/%.c libmega65.a + $(CL65) $(CFLAGS) $< -o $@ libmega65.a +test: | $(TESTS_PRGS) + @if ! command -v $(XEMU) &> /dev/null; then \ + echo "Cannot run tests as xmega65 (Xemu) is not in your path."; \ + else \ + for test in $(TESTS_PRGS); do \ + if $(XEMU) -testing -sleepless -headless -8 MEGADEMO.d81 -prg $$test &> /dev/null; then \ + echo "Test passed: $$test"; \ + else \ + echo "Test FAILED: $$test"; \ + fi; \ + done; \ + fi + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d0db48a..f4298df 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,5 +11,6 @@ function(TEST NAME) endif() endfunction() +TEST(test-fileio) TEST(test-memory) TEST(test-integer-size) diff --git a/tests/test-fileio.c b/tests/test-fileio.c new file mode 100644 index 0000000..3ffb1d2 --- /dev/null +++ b/tests/test-fileio.c @@ -0,0 +1,77 @@ +/** + * Tests for fileio.h for loading CHARROM.M65 from the SD card + * + * This can be run in Xemu in testing mode with e.g. + * + * xmega65 -testing -headless -sleepless -prg test-fileio.prg + * + * If a test fails, Xemu exits with a non-zero return code. + */ +#include +#include +#include + +#define XEMU_CONTROL 0xD6CF +#define XEMU_QUIT 0x42 + +void xemu_exit(int exit_code) +{ + POKE(XEMU_CONTROL, (uint8_t)exit_code); + POKE(XEMU_CONTROL, XEMU_QUIT); + exit(exit_code); +} + +#define assert_eq(A, B) \ + if (A != B) \ + xemu_exit(EXIT_FAILURE) + +// Input file on SD card: CHARROM.M65 +char filename[11 + 1] = { 0x63, 0x68, 0x61, 0x72, 0x72, 0x6f, 0x6d, 0x2e, 0x6d, + 0x36, 0x35, 0x00 }; +uint8_t file; +uint8_t buffer[512]; +size_t num_bytes_read; + +int main(void) +{ + // Tests run in C64 mode so we need to enable mega65 + mega65_io_enable(); + + // Good practice + closeall(); + chdirroot(); + + // Check open status + file = open(filename); + if (file == 0xff) { + xemu_exit(EXIT_FAILURE); + } + + // Read single 512 byte chunk + num_bytes_read = read512(buffer); + assert_eq(num_bytes_read, 512); + + // Check first two bytes of chunk + assert_eq(buffer[0], 0x3c); + assert_eq(buffer[1], 0x66); + + // Check last two bytes of chunk + assert_eq(buffer[510], 0x18); + assert_eq(buffer[511], 0x00); + + // The size of CHARROM is 8 x 512 = 4096 bytes; let's read until EOF + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 512); + assert_eq(read512(buffer), 0); + + // This has no effect on the test, but let's call anyway + close(file); + closeall(); + + xemu_exit(EXIT_SUCCESS); +} diff --git a/tests/test-integer-size.c b/tests/test-integer-size.c index 5e3a5a8..4c8eae4 100644 --- a/tests/test-integer-size.c +++ b/tests/test-integer-size.c @@ -27,6 +27,8 @@ void xemu_exit(int exit_code) int main(void) { + mega65_io_enable(); + // Integer sizes assert_eq(sizeof(uint8_t), 1); assert_eq(sizeof(unsigned char), 1); @@ -44,5 +46,6 @@ int main(void) assert_eq(INT16_MAX, 0x7FFF); assert_eq(INT32_MAX, 0x7FFFFFFF); + xemu_exit(EXIT_SUCCESS); } diff --git a/tests/test-memory.c b/tests/test-memory.c index e944db5..f72f243 100644 --- a/tests/test-memory.c +++ b/tests/test-memory.c @@ -26,6 +26,7 @@ void xemu_exit(int exit_code) int main(void) { + mega65_io_enable(); // DMAGIC DMA list size assert_eq(sizeof(struct dmagic_dmalist), 20); From fbe9ab9884b0d5f362dcb6c1af1c82063418d04a Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 13:32:13 +0200 Subject: [PATCH 02/22] Trigger test build (but not run) in github actions --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 893af21..2d90188 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -27,7 +27,7 @@ jobs: - name: Build using cc65 working-directory: ${{github.workspace}} - run: USE_LOCAL_CC65=1 make -f Makefile_cc65 + run: USE_LOCAL_CC65=1 make -f Makefile_cc65 all - name: Install llvm-mos-sdk working-directory: ${{github.workspace}} From 79ee6b3d3b6a0a812e5119f74ddff7a577c1991b Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 13:54:31 +0200 Subject: [PATCH 03/22] Make xemu_exit() part of tests.h --- include/mega65/tests.h | 9 +++++++++ src/tests.c | 8 ++++++++ tests/test-fileio.c | 11 +---------- tests/test-integer-size.c | 11 +---------- tests/test-memory.c | 11 +---------- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/include/mega65/tests.h b/include/mega65/tests.h index fca7288..6d6c174 100644 --- a/include/mega65/tests.h +++ b/include/mega65/tests.h @@ -11,6 +11,15 @@ #define TEST_LOG 0xfd #define TEST_SETNAME 0xfe #define TEST_DONEALL 0xff +#define XEMU_CONTROL 0xD6CF +#define XEMU_QUIT 0x42 + +/** + * @brief Quits Xemu with given exit code + * @param exit_code Exit code passed to Xemu, e.g. EXIT_SUCCESS or EXIT_FAILURE + * @note Xemu must be run in `-testing` mode for this to have any effect + */ +void xemu_exit(int exit_code); // convenience methods diff --git a/src/tests.c b/src/tests.c index 7730966..15daa84 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3,11 +3,19 @@ #include #include #include +#include unsigned char __tests_out; unsigned short __ut_issueNum; unsigned char __ut_subissue; +void xemu_exit(int exit_code) +{ + POKE(XEMU_CONTROL, (uint8_t)exit_code); + POKE(XEMU_CONTROL, XEMU_QUIT); + exit(exit_code); +} + void unit_test_report( unsigned short issue, unsigned char sub, unsigned char status) { diff --git a/tests/test-fileio.c b/tests/test-fileio.c index 3ffb1d2..4ec06f9 100644 --- a/tests/test-fileio.c +++ b/tests/test-fileio.c @@ -9,18 +9,9 @@ */ #include #include +#include #include -#define XEMU_CONTROL 0xD6CF -#define XEMU_QUIT 0x42 - -void xemu_exit(int exit_code) -{ - POKE(XEMU_CONTROL, (uint8_t)exit_code); - POKE(XEMU_CONTROL, XEMU_QUIT); - exit(exit_code); -} - #define assert_eq(A, B) \ if (A != B) \ xemu_exit(EXIT_FAILURE) diff --git a/tests/test-integer-size.c b/tests/test-integer-size.c index 4c8eae4..1693358 100644 --- a/tests/test-integer-size.c +++ b/tests/test-integer-size.c @@ -8,19 +8,10 @@ * If a test fails, xemu will exit with a non-zero return code. */ #include +#include #include #include -#define XEMU_CONTROL 0xD6CF -#define XEMU_QUIT 0x42 - -void xemu_exit(int exit_code) -{ - POKE(XEMU_CONTROL, (uint8_t)exit_code); - POKE(XEMU_CONTROL, XEMU_QUIT); - exit(exit_code); -} - #define assert_eq(A, B) \ if (A != B) \ xemu_exit(EXIT_FAILURE) diff --git a/tests/test-memory.c b/tests/test-memory.c index f72f243..1ff7574 100644 --- a/tests/test-memory.c +++ b/tests/test-memory.c @@ -8,18 +8,9 @@ * If a test fails, xemu will exit with a non-zero return code. */ #include +#include #include -#define XEMU_CONTROL 0xD6CF -#define XEMU_QUIT 0x42 - -void xemu_exit(int exit_code) -{ - POKE(XEMU_CONTROL, (uint8_t)exit_code); - POKE(XEMU_CONTROL, XEMU_QUIT); - exit(exit_code); -} - #define assert_eq(A, B) \ if (A != B) \ xemu_exit(EXIT_FAILURE) From 98aa2a062875a1289c70d676263ab322503083f7 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:02:13 +0200 Subject: [PATCH 04/22] Add getrtc test and fix xemu target detection --- include/mega65/targets.h | 6 +++++- include/mega65/time.h | 2 ++ src/targets.c | 6 +----- src/time.c | 5 +++-- tests/CMakeLists.txt | 3 ++- tests/test-time.c | 41 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 tests/test-time.c diff --git a/include/mega65/targets.h b/include/mega65/targets.h index b2b437c..cc7f483 100644 --- a/include/mega65/targets.h +++ b/include/mega65/targets.h @@ -1,6 +1,8 @@ #ifndef __MEGA65_TARGETS_H #define __MEGA65_TARGETS_H +#include + #define TARGET_UNKNOWN 0 #define TARGET_MEGA65R1 1 #define TARGET_MEGA65R2 2 @@ -12,7 +14,9 @@ #define TARGET_NEXYS4DDRWIDGET 0x42 #define TARGET_WUKONG 0xFD #define TARGET_SIMULATION 0xFE +#define TARGET_EMULATION 0xFF //!< Emulator like Xemu -unsigned char detect_target(void); +/// Detects the target on which we're running +uint8_t detect_target(void); #endif // __MEGA65_TARGETS_H diff --git a/include/mega65/time.h b/include/mega65/time.h index ac51cb3..29cc7d1 100644 --- a/include/mega65/time.h +++ b/include/mega65/time.h @@ -25,7 +25,9 @@ struct m65_tm { unsigned char tm_isdst; /* Daylight saving time */ }; +/// Get real-time clock void getrtc(struct m65_tm* tm); +/// Set real-time clock void setrtc(struct m65_tm* tm); #endif // __MEGA65_TIME_H diff --git a/src/targets.c b/src/targets.c index 2fa28b2..97d89dc 100644 --- a/src/targets.c +++ b/src/targets.c @@ -1,13 +1,9 @@ -#include #include #include -unsigned char detect_target(void) +uint8_t detect_target(void) { // We use the different I2C device blocks to identify the various hardware // targets - return lpeek(0xffd3629); - - return TARGET_UNKNOWN; } diff --git a/src/time.c b/src/time.c index a990fec..fed4e6c 100644 --- a/src/time.c +++ b/src/time.c @@ -1,3 +1,4 @@ + /* The MEGA65 really only has a Real-Time Clock (RTC), so we just have handy functions for that. @@ -10,7 +11,6 @@ #include #include #include -#include #define I2CDELAY 5000L @@ -59,6 +59,7 @@ void getrtc(struct m65_tm* tm) tm->tm_isdst = 0; switch (detect_target()) { + case TARGET_EMULATION: case TARGET_MEGA65R2: case TARGET_MEGA65R3: tm->tm_sec = unbcd(lpeek_debounced(0xffd7110)); @@ -129,7 +130,7 @@ void setrtc(struct m65_tm* tm) lpoke(0xffd7114, tobcd(tm->tm_mon)); if (tm->tm_year >= 100 && tm->tm_year <= 355) { usleep(I2CDELAY); - lpoke(0xffd7115, tobcd(tm->tm_year - 100)); + lpoke(0xffd7115, tobcd((uint8_t)(tm->tm_year - 100))); } usleep(I2CDELAY); lpoke(0xffd7116, tobcd(tm->tm_wday)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f4298df..fb81d8c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,5 +12,6 @@ function(TEST NAME) endfunction() TEST(test-fileio) -TEST(test-memory) TEST(test-integer-size) +TEST(test-memory) +TEST(test-time) diff --git a/tests/test-time.c b/tests/test-time.c new file mode 100644 index 0000000..ef51f66 --- /dev/null +++ b/tests/test-time.c @@ -0,0 +1,41 @@ +/** + * Tests for time.h + * + * This can be run in Xemu in testing mode with e.g. + * + * xmega65 -testing -headless -sleepless -prg test-time.prg + * + * If a test fails, Xemu exits with a non-zero return code. + */ +#include +#include +#include +#include +#include + +#define assert_eq(A, B) \ + if (A != B) \ + xemu_exit(EXIT_FAILURE) + +struct m65_tm tm; + +int main(void) +{ + // Tests run in C64 mode so we need to enable mega65 + mega65_io_enable(); + + getrtc(&tm); + + assert_eq(detect_target(), TARGET_EMULATION); + assert_eq(tm.tm_year + 1900 >= 2000, 1); + assert_eq(tm.tm_mon < 12, 1); + assert_eq(tm.tm_mday > 0, 1); + assert_eq(tm.tm_mday < 32, 1); + assert_eq(tm.tm_hour < 24, 1); + assert_eq(tm.tm_min < 60, 1); + assert_eq(tm.tm_sec < 61, 1); + assert_eq(tm.tm_wday < 7, 1); + assert_eq(tm.tm_yday < 366, 1); + + xemu_exit(EXIT_SUCCESS); +} From aa3d01ba8b19ee4f495bed1303173f8941c9c61f Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:05:43 +0200 Subject: [PATCH 05/22] Implicit to explicit casts in memory --- src/memory.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/memory.c b/src/memory.c index 058bf33..208504f 100644 --- a/src/memory.c +++ b/src/memory.c @@ -35,7 +35,7 @@ uint8_t dma_peek(uint32_t address) dmalist.option_0b = 0x0b; dmalist.option_80 = 0x80; - dmalist.source_mb = (address >> 20); + dmalist.source_mb = (uint8_t)(address >> 20); dmalist.option_81 = 0x81; dmalist.dest_mb = 0x00; // dma_byte lives in 1st MB dmalist.option_85 = 0x85; @@ -87,7 +87,7 @@ void dma_poke(uint32_t address, uint8_t value) dmalist.option_80 = 0x80; dmalist.source_mb = 0x00; // dma_byte lives in 1st MB dmalist.option_81 = 0x81; - dmalist.dest_mb = (address >> 20); + dmalist.dest_mb = (uint8_t)(address >> 20); dmalist.option_85 = 0x85; dmalist.dest_skip = 1; dmalist.end_of_options = 0x00; @@ -110,9 +110,9 @@ void lcopy( { dmalist.option_0b = 0x0b; dmalist.option_80 = 0x80; - dmalist.source_mb = source_address >> 20; + dmalist.source_mb = (uint8_t)(source_address >> 20); dmalist.option_81 = 0x81; - dmalist.dest_mb = (destination_address >> 20); + dmalist.dest_mb = (uint8_t)(destination_address >> 20); dmalist.option_85 = 0x85; dmalist.dest_skip = 1; dmalist.end_of_options = 0x00; @@ -144,7 +144,7 @@ void lfill(uint32_t destination_address, uint8_t value, uint16_t count) dmalist.option_80 = 0x80; dmalist.source_mb = 0x00; dmalist.option_81 = 0x81; - dmalist.dest_mb = destination_address >> 20; + dmalist.dest_mb = (uint8_t)(destination_address >> 20); dmalist.option_85 = 0x85; dmalist.dest_skip = 1; dmalist.end_of_options = 0x00; @@ -164,14 +164,14 @@ void lfill(uint32_t destination_address, uint8_t value, uint16_t count) return; } -void lfill_skip(uint32_t destination_address, uint8_t value, uint16_t count, - uint8_t skip) +void lfill_skip( + uint32_t destination_address, uint8_t value, uint16_t count, uint8_t skip) { dmalist.option_0b = 0x0b; dmalist.option_80 = 0x80; dmalist.source_mb = 0x00; dmalist.option_81 = 0x81; - dmalist.dest_mb = destination_address >> 20; + dmalist.dest_mb = (uint8_t)(destination_address >> 20); dmalist.option_85 = 0x85; dmalist.dest_skip = skip; dmalist.end_of_options = 0x00; From 3077598644da1300d539b5c363f937949045c28c Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:48:56 +0200 Subject: [PATCH 06/22] Use define for DMA commands --- src/memory.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/memory.c b/src/memory.c index 208504f..350aff6 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,5 +1,4 @@ #include -#include #ifdef __clang__ volatile struct dmagic_dmalist dmalist; @@ -43,7 +42,7 @@ uint8_t dma_peek(uint32_t address) dmalist.end_of_options = 0x00; dmalist.sub_cmd = 0x00; - dmalist.command = 0x00; // copy + dmalist.command = DMA_COPY_CMD; dmalist.count = 1; dmalist.source_addr = address & 0xffff; dmalist.source_bank = (address >> 16) & 0x0f; @@ -94,7 +93,7 @@ void dma_poke(uint32_t address, uint8_t value) dmalist.sub_cmd = 0x00; dma_byte = value; - dmalist.command = 0x00; // copy + dmalist.command = DMA_COPY_CMD; dmalist.count = 1; dmalist.source_addr = (uint16_t)&dma_byte; dmalist.source_bank = 0; @@ -118,7 +117,7 @@ void lcopy( dmalist.end_of_options = 0x00; dmalist.sub_cmd = 0x00; - dmalist.command = 0x00; // copy + dmalist.command = DMA_COPY_CMD; dmalist.count = count; dmalist.sub_cmd = 0; dmalist.source_addr = source_address & 0xffff; @@ -149,7 +148,7 @@ void lfill(uint32_t destination_address, uint8_t value, uint16_t count) dmalist.dest_skip = 1; dmalist.end_of_options = 0x00; - dmalist.command = 0x03; // fill + dmalist.command = DMA_FILL_CMD; dmalist.sub_cmd = 0; dmalist.count = count; dmalist.source_addr = value; @@ -176,7 +175,7 @@ void lfill_skip( dmalist.dest_skip = skip; dmalist.end_of_options = 0x00; - dmalist.command = 0x03; // fill + dmalist.command = DMA_FILL_CMD; dmalist.sub_cmd = 0; dmalist.count = count; dmalist.source_addr = value; From 8137e30e2ce637444f0087ef7b1162a8a7f2845c Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:49:30 +0200 Subject: [PATCH 07/22] Doxygen comments for fileio --- include/mega65/fileio.h | 48 ++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/include/mega65/fileio.h b/include/mega65/fileio.h index 2ab8d1a..4ec4b29 100644 --- a/include/mega65/fileio.h +++ b/include/mega65/fileio.h @@ -1,22 +1,46 @@ #ifndef __MEGA65_FILEIO_H #define __MEGA65_FILEIO_H -void toggle_rom_write_protect(); +#include +#include + +void toggle_rom_write_protect(void); + +/** Closes all open files */ void closeall(void); -void close(unsigned char fd); -// Returns file descriptor -unsigned char open(char* filename); +/** + * @brief Close a single file + * @param fd File descriptor pointing to file to close + */ +void close(uint8_t fd); + +/** + * @brief Open file + * @param filename to open + * @return File descriptor or `0xff` if error + */ +uint8_t open(char* filename); -// Read upto one sector of data into the supplied buffer. -// Returns the number of bytes actually read. -unsigned short read512(unsigned char* buffer); +/** + * @brief Read up to 512 bytes from file + * @param buffer Input buffer + * @return Number of bytes read + */ +size_t read512(uint8_t* buffer); -// Change working directory -// (only accepts one directory segment at a time -unsigned char chdir(char* filename); +/** + * @brief Change working directory + * @param filename Directory name + * @return Error code (currently unused) + * @note Only accepts one directory segment at a time + */ +uint8_t chdir(char* filename); -// Change working directory to the root directory -unsigned char chdirroot(void); +/** + * @brief Change working directory to the root directory + * @return Error code (currently unused) + */ +uint8_t chdirroot(void); #endif // __MEGA65_FILEIO_H From 3f3c94de9fda1b7312a7c022b41a77f20e42a0e5 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:50:04 +0200 Subject: [PATCH 08/22] Memory size_t and doxygen comments --- include/mega65/memory.h | 63 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/include/mega65/memory.h b/include/mega65/memory.h index a81884b..7795cf5 100644 --- a/include/mega65/memory.h +++ b/include/mega65/memory.h @@ -2,7 +2,14 @@ #define __MEGA65_MEMORY_H #include +#include +#define DMA_COPY_CMD 0x00; +#define DMA_FILL_CMD 0x03; + +/** + * @brief DMA list structure + */ #ifdef __clang__ struct __attribute__((__packed__)) dmagic_dmalist { #else @@ -37,17 +44,65 @@ extern struct dmagic_dmalist dmalist; extern uint8_t dma_byte; #endif +/** + * @brief Enable Mega65 mode + */ void mega65_io_enable(void); + +/** + * @brief Peek a byte from the given address + * @param address 28-bit address + * @return uint8_t with the value at the given address + */ uint8_t lpeek(uint32_t address); uint8_t lpeek_debounced(uint32_t address); + +/** + * @brief Peek a byte from the given address using DMA copy + * @param address 28-bit address + * @return Single byte from the given address + */ uint8_t dma_peek(uint32_t address); + +/** + * @brief Poke a byte to the given address + * @param address 28-bit address + * @param value Single byte to write to the given address + */ void lpoke(uint32_t address, uint8_t value); + +/** + * @brief Poke a byte to the given address using DMA copy + * @param address 28-bit address + * @param value Single byte to write to the given address + */ void dma_poke(uint32_t address, uint8_t value); -void lcopy( - uint32_t source_address, uint32_t destination_address, uint16_t count); -void lfill(uint32_t destination_address, uint8_t value, uint16_t count); + +/** + * @brief Copy a block of memory using DMA + * @param source_address 28-bit address to copy from + * @param destination_address 28-bit address to copy to + * @param count Number of bytes to copy + */ +void lcopy(uint32_t source_address, uint32_t destination_address, size_t count); + +/** + * @brief Fill a block of memory with a single byte using DMA + * @param destination_address Start address (28-bit) + * @param value Fill value + * @param count Number of bytes to fill + */ +void lfill(uint32_t destination_address, uint8_t value, size_t count); + +/** + * @brief Fill a block of memory with a single byte using DMA with a step + * @param destination_address Start address (28-bit) + * @param value Fill value + * @param count Number of bytes to fill + * @param skip Skip every n bytes + */ void lfill_skip( - uint32_t destination_address, uint8_t value, uint16_t count, uint8_t skip); + uint32_t destination_address, uint8_t value, size_t count, uint8_t skip); #ifdef __clang__ #define POKE(X, Y) (*(volatile uint8_t*)(X)) = Y From 9577c4f4168abcc6b2c1d1676e35b325cbf05d08 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:54:34 +0200 Subject: [PATCH 09/22] Hal doxygen comment --- include/mega65/hal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/mega65/hal.h b/include/mega65/hal.h index 4aa1143..46995c4 100644 --- a/include/mega65/hal.h +++ b/include/mega65/hal.h @@ -3,6 +3,10 @@ #include +/** + * @brief Sleep for the given number of microseconds + * @param micros Microseconds to sleep + */ void usleep(uint32_t micros); #endif // __MEGA65_HAL_H From 663ab2f6ffb05118f73fd853339fa6e765be5f2a Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 16:56:17 +0200 Subject: [PATCH 10/22] debug doxygen comment --- include/mega65/debug.h | 6 +++++- src/debug.c | 7 +++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/mega65/debug.h b/include/mega65/debug.h index 1b7c736..17c1d40 100644 --- a/include/mega65/debug.h +++ b/include/mega65/debug.h @@ -1,6 +1,10 @@ #ifndef __MEGA65_DEBUG_H #define __MEGA65_DEBUG_H -void debug_msg(char* m); +/** + * @brief Write debug message to serial monitor + * @param msg Text message to write + */ +void debug_msg(char* msg); #endif // __MEGA65_DEBUG_H diff --git a/src/debug.c b/src/debug.c index 948b5e1..8597784 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,11 +1,10 @@ #include unsigned char the_char; -void debug_msg(char* m) +void debug_msg(char* msg) { - // Write debug message to serial monitor - while (*m) { - the_char = *m; + while (*msg) { + the_char = *msg; #ifdef __CC65__ __asm__("LDA %v", the_char); __asm__("STA $D643"); From 778d05af08d4dcf09e7dd31353616daaf1955a87 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 17:23:30 +0200 Subject: [PATCH 11/22] Dirent doxygen comments --- include/mega65/dirent.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/include/mega65/dirent.h b/include/mega65/dirent.h index dd0c02f..ed6ac79 100644 --- a/include/mega65/dirent.h +++ b/include/mega65/dirent.h @@ -3,20 +3,26 @@ #include +/// Open a directory unsigned char opendir(void); + +/// Read directory entry struct m65_dirent* readdir(unsigned char); + +/// Close directory entry void closedir(unsigned char); +/// Structure describing an open directory #ifdef __clang__ struct __attribute__((__packed__)) m65_dirent { #else struct m65_dirent { #endif - uint32_t d_ino; - uint16_t d_off; - uint32_t d_reclen; - uint16_t d_type; - char d_name[256]; + uint32_t d_ino; //!< File number of entry + uint16_t d_off; //!< Offset + uint32_t d_reclen; //!< Length of this record + uint16_t d_type; //!< File type + char d_name[256]; //!< Filename string of entry }; #endif // __MEGA65_DIRENT_H From 142bc1188c2f26109abb513c8b3314b946d0fbe2 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 17:34:19 +0200 Subject: [PATCH 12/22] Fix bug in debug --- src/debug.c | 2 +- src/time.c | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/debug.c b/src/debug.c index 8597784..13d7af8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -9,13 +9,13 @@ void debug_msg(char* msg) __asm__("LDA %v", the_char); __asm__("STA $D643"); __asm__("NOP"); - m++; #else asm volatile("lda the_char\n" "sta $d643\n" "nop" :: : "a"); #endif + msg++; } #ifdef __CC65__ __asm__("LDA #$0d"); diff --git a/src/time.c b/src/time.c index fed4e6c..09942e6 100644 --- a/src/time.c +++ b/src/time.c @@ -14,14 +14,17 @@ #define I2CDELAY 5000L -unsigned char bcd_work; - -/* - While the double dabble algorithm is more time efficient, we mostly care about - saving space, so use a simple loop. Getting/setting time should not be called - particularly often. +uint8_t bcd_work; + +/** + * @brief Convert a binary value to binary coded decimal (BCD) + * @return uint8_t BCD value + * + * While the double dabble algorithm is more time efficient, we mostly care + * about saving space, so use a simple loop. Getting/setting time should not be + * called particularly often. */ -unsigned char tobcd(unsigned char in) +uint8_t tobcd(uint8_t in) { bcd_work = 0; while (in > 9) { @@ -32,7 +35,12 @@ unsigned char tobcd(unsigned char in) return bcd_work; } -unsigned char unbcd(unsigned char in) +/** + * @brief Convert a binary coded decimal (BCD) value to binary + * @param in Input BCD value + * @return unsigned char Decimal value + */ +uint8_t unbcd(uint8_t in) { bcd_work = 0; while (in & 0xf0) { From 5a54d1acda2156c8b5c7c80f065d7bf47fb65ce8 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 18:26:17 +0200 Subject: [PATCH 13/22] Remove disk insertion in xemu --- Makefile_cc65 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile_cc65 b/Makefile_cc65 index ae5f9b2..18df96c 100644 --- a/Makefile_cc65 +++ b/Makefile_cc65 @@ -73,7 +73,7 @@ test: | $(TESTS_PRGS) echo "Cannot run tests as xmega65 (Xemu) is not in your path."; \ else \ for test in $(TESTS_PRGS); do \ - if $(XEMU) -testing -sleepless -headless -8 MEGADEMO.d81 -prg $$test &> /dev/null; then \ + if $(XEMU) -testing -sleepless -headless -prg $$test &> /dev/null; then \ echo "Test passed: $$test"; \ else \ echo "Test FAILED: $$test"; \ From 5ea6bac853c061bd1af38152be0554131e7f713a Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 20:12:23 +0200 Subject: [PATCH 14/22] Update documentation --- include/mega65/tests.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/mega65/tests.h b/include/mega65/tests.h index 6d6c174..91c2e1a 100644 --- a/include/mega65/tests.h +++ b/include/mega65/tests.h @@ -17,7 +17,9 @@ /** * @brief Quits Xemu with given exit code * @param exit_code Exit code passed to Xemu, e.g. EXIT_SUCCESS or EXIT_FAILURE - * @note Xemu must be run in `-testing` mode for this to have any effect + * + * Xemu must be run in `-testing` mode for this to have any effect and + * also make sure to call `mega65_io_enable()` before calling this function. */ void xemu_exit(int exit_code); From d8c89a237c5c4d200ef284ead23a713493431589 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 20:18:47 +0200 Subject: [PATCH 15/22] Doxygen comment --- include/mega65/fcio.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/mega65/fcio.h b/include/mega65/fcio.h index c2145a8..a082a5c 100644 --- a/include/mega65/fcio.h +++ b/include/mega65/fcio.h @@ -1,9 +1,7 @@ - -/*! @file fcio.h - @brief Full colour mode i/o library for the MEGA65 - - Details. -*/ +/** + * @file fcio.h + * @brief Full colour mode i/o library for the MEGA65 + */ #ifndef __MEGA65_FCIO_H #define __MEGA65_FCIO_H From a8561546f1e03919a7789f046ea4ff00d677c724 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 20:52:40 +0200 Subject: [PATCH 16/22] Remove unused code in cc65/fileio.s --- src/cc65/fileio.s | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/cc65/fileio.s b/src/cc65/fileio.s index ab24957..c60a6a2 100644 --- a/src/cc65/fileio.s +++ b/src/cc65/fileio.s @@ -15,29 +15,6 @@ mega65_io_enable: sta $d02f rts -cc65_args_read_ptr1_16: - ;; sp here is the ca65 sp ZP variable, not the stack pointer of a 4510 - .p02 - - lda (sp),y - sta ptr1 - iny - lda (sp),y - .p4510 - sta ptr1+1 - iny - rts - -cc65_args_read_tmp1_8: - ;; sp here is the ca65 sp ZP variable, not the stack pointer of a 4510 - .p02 - lda (sp),y - sta tmp1 - iny - .p4510 - rts - - cc65_copy_ptr1_string_to_0100: ;; Copy file name phy From a00c79b31173ca25c0fd825a8c4ebd5409eb1022 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 21:21:01 +0200 Subject: [PATCH 17/22] Fix broken llvm fileio test --- src/llvm/fileio.s | 54 ++++++++++++++++++--------------------------- tests/test-fileio.c | 3 ++- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/llvm/fileio.s b/src/llvm/fileio.s index 9477b4c..2e3dae2 100644 --- a/src/llvm/fileio.s +++ b/src/llvm/fileio.s @@ -1,19 +1,15 @@ .global closeall, open, close, read512, toggle_rom_write_protect, chdir, chdirroot ;; allocate space in zeropage - .section .zeropage,"",@nobits + .section .zeropage,"aw",@nobits ptr1: .ds.b 1 ;; 1 byte ptr2: .ds.b 1 -tmp1: - .ds.b 1 tmp2: .ds.b 1 tmp3: .ds.b 1 -sp: - .ds.b 1 .section code,"a" @@ -23,23 +19,6 @@ mega65_io_enable: lda #$53 sta $d02f rts -cc65_args_read_ptr1_16: - ;; sp here is the ca65 sp ZP variable, not the stack pointer of a 4510 - - lda (sp),y - sta ptr1 - iny - lda (sp),y - sta ptr1+1 - iny - rts - -cc65_args_read_tmp1_8: - ;; sp here is the ca65 sp ZP variable, not the stack pointer of a 4510 - lda (sp),y - sta tmp1 - iny - rts cc65_copy_ptr1_string_to_0100: ;; Copy file name @@ -75,7 +54,8 @@ closeall: LDA #$22 STA $D640 NOP - LDX #$00 + ;Not needed on llvm-mos + ;LDX #$00 RTS toggle_rom_write_protect: @@ -83,11 +63,13 @@ toggle_rom_write_protect: lda #$70 STA $d640 nop - ldx #0 + ;ldx #0 rts read512: ;; Get pointer to buffer + lda __rc2 + ldx __rc3 sta ptr1+0 stx ptr1+1 @@ -99,7 +81,7 @@ read512: LDA #$1A STA $D640 NOP - LDX #$00 + ;LDX #$00 ;; Number of bytes read returned in X and Y ;; Store these for returning @@ -153,9 +135,13 @@ copysectorbuffer_destaddr: .short $8000 ;; destination address is $8000 .byte $00 ;; of bank $0 .short $0000 ;; modulo (unused) - + + .section code,"a" + open: - ;; Get pointer to file name + ; argument pointer stored in rc2 and rc3 + lda __rc2 + ldx __rc3 sta ptr1+0 stx ptr1+1 @@ -184,8 +170,9 @@ open_file_exists: LDA #$18 STA $D640 NOP - - LDX #$00 + + ;clearing X not needed on llvm-mos + ;LDX #$00 RTS close: @@ -194,7 +181,7 @@ close: LDA #$20 STA $D640 NOP - LDX #$00 + ;LDX #$00 RTS chdirroot: @@ -204,11 +191,13 @@ chdirroot: sta $d640 nop - ldx #$00 + ;ldx #$00 rts chdir: ;; Get pointer to file name + lda __rc2 + ldx __rc3 sta ptr1+0 stx ptr1+1 @@ -238,5 +227,6 @@ chdir_file_exists: STA $D640 NOP - LDX #$00 + ;; Not needed on llvm-mos + ;;LDX #$00 RTS diff --git a/tests/test-fileio.c b/tests/test-fileio.c index 4ec06f9..8cde95a 100644 --- a/tests/test-fileio.c +++ b/tests/test-fileio.c @@ -20,6 +20,7 @@ char filename[11 + 1] = { 0x63, 0x68, 0x61, 0x72, 0x72, 0x6f, 0x6d, 0x2e, 0x6d, 0x36, 0x35, 0x00 }; uint8_t file; +uint8_t error; uint8_t buffer[512]; size_t num_bytes_read; @@ -30,7 +31,7 @@ int main(void) // Good practice closeall(); - chdirroot(); + error = chdirroot(); // Check open status file = open(filename); From a49f6c7a8ca40f1a9936b6f2f18bd40239a8df9e Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Tue, 25 Jul 2023 21:23:18 +0200 Subject: [PATCH 18/22] Reduce zeropage usage --- src/llvm/fileio.s | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/llvm/fileio.s b/src/llvm/fileio.s index 2e3dae2..d09f25b 100644 --- a/src/llvm/fileio.s +++ b/src/llvm/fileio.s @@ -3,13 +3,7 @@ ;; allocate space in zeropage .section .zeropage,"aw",@nobits ptr1: - .ds.b 1 ;; 1 byte -ptr2: - .ds.b 1 -tmp2: - .ds.b 1 -tmp3: - .ds.b 1 + .ds.b 2 .section code,"a" @@ -85,8 +79,8 @@ read512: ;; Number of bytes read returned in X and Y ;; Store these for returning - stx tmp2 - sty tmp3 + stx __rc2 + sty __rc3 ;; Make sure SD buffer is selected, not FDC buffer lda #$80 @@ -112,8 +106,8 @@ read512: sta $d705 ;; Retrieve the return value - lda tmp2 - ldx tmp3 + lda __rc2 + ldx __rc3 RTS .data From e03d0e43dca3adf930cbf2f3034a8ab0fb4120c3 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Wed, 26 Jul 2023 10:34:52 +0200 Subject: [PATCH 19/22] Xemu use target model 3 instead of 255 This requires Xemu 20230726102337/next or later (June 26, 2023) --- Makefile_cc65 | 2 +- tests/CMakeLists.txt | 2 +- tests/test-time.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile_cc65 b/Makefile_cc65 index 18df96c..f025ac5 100644 --- a/Makefile_cc65 +++ b/Makefile_cc65 @@ -73,7 +73,7 @@ test: | $(TESTS_PRGS) echo "Cannot run tests as xmega65 (Xemu) is not in your path."; \ else \ for test in $(TESTS_PRGS); do \ - if $(XEMU) -testing -sleepless -headless -prg $$test &> /dev/null; then \ + if $(XEMU) -testing -sleepless -headless -model 3 -prg $$test &> /dev/null; then \ echo "Test passed: $$test"; \ else \ echo "Test FAILED: $$test"; \ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb81d8c..24ceb4c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ find_program(XEMU NAMES xmega65 xmega65.native) -set(XEMU_ARGS "-besure -headless -sleepless -testing") +set(XEMU_ARGS "-headless -sleepless -testing -model 3") function(TEST NAME) add_executable(${NAME} ${NAME}.c) diff --git a/tests/test-time.c b/tests/test-time.c index ef51f66..6cfb71b 100644 --- a/tests/test-time.c +++ b/tests/test-time.c @@ -26,7 +26,6 @@ int main(void) getrtc(&tm); - assert_eq(detect_target(), TARGET_EMULATION); assert_eq(tm.tm_year + 1900 >= 2000, 1); assert_eq(tm.tm_mon < 12, 1); assert_eq(tm.tm_mday > 0, 1); From 3c0be5613f84d74e4e527a118fb6f991a043b684 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Wed, 26 Jul 2023 10:54:23 +0200 Subject: [PATCH 20/22] Update README --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c3b7fc8..862d672 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,23 @@ Simple C library for the MEGA65 ## Clang 1. Install [llvm-mos-sdk](https://github.com/llvm-mos/llvm-mos-sdk#getting-started). +This e.g. downloads for linux and unpacks into `$HOME/llvm-mos`: +~~~ sh +wget https://github.com/llvm-mos/llvm-mos-sdk/releases/latest/download/llvm-mos-linux.tar.xz +tar xf llvm-mos-linux.tar.xz -C $HOME +~~~ 2. Configure and make with: ~~~ sh -cmake -DCMAKE_PREFIX_PATH= -B build/ mega65-libc/ -cd build/ +cmake -DCMAKE_PREFIX_PATH=$HOME/llvm-mos -B build/ mega65-libc/ +cd build make -make test # if `xmega65` (Xemu) is in your path +make test # if `xmega65` (Xemu) was in your patch when running cmake ~~~ -### Dependent CMake projects +### Dependent projects -`CMakeLists.txt` of a dependent project could look like this: +It's trivial to write a classic `Makefile` for clang. +For dependent CMake based projects, `CMakeLists.txt` could look like this: ~~~ cmake cmake_minimum_required(VERSION 3.5) set(LLVM_MOS_PLATFORM mega65) From a20098aba4ae1ff77cef65e9cf13fc43aba25d54 Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Sun, 30 Jul 2023 19:07:54 +0200 Subject: [PATCH 21/22] Make all the first target in Makefile --- Makefile_cc65 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile_cc65 b/Makefile_cc65 index f025ac5..2d5d75b 100644 --- a/Makefile_cc65 +++ b/Makefile_cc65 @@ -39,11 +39,11 @@ LIB_OBJECTS = \ .PHONY: all clean cleanall test +all: libmega65.a + libmega65.a: $(LIB_OBJECTS) $(AR65) a $@ $^ -all: libmega65.a $(TESTS_PRGS) - clean: rm -f $(LIB_OBJECTS) $(TESTS_PRGS) tests/*.o rm -rf work From 288401d449317adf30711b1a0ace75cc8c6b708d Mon Sep 17 00:00:00 2001 From: Mikael Lund Date: Mon, 31 Jul 2023 08:34:01 +0200 Subject: [PATCH 22/22] Build tests with make all --- Makefile_cc65 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile_cc65 b/Makefile_cc65 index 2d5d75b..eacc29a 100644 --- a/Makefile_cc65 +++ b/Makefile_cc65 @@ -39,7 +39,7 @@ LIB_OBJECTS = \ .PHONY: all clean cleanall test -all: libmega65.a +all: libmega65.a ${TESTS_PRGS} libmega65.a: $(LIB_OBJECTS) $(AR65) a $@ $^