diff --git a/ascii85.c b/ascii85.c new file mode 100644 index 0000000..aa4ff43 --- /dev/null +++ b/ascii85.c @@ -0,0 +1,108 @@ +// intellectual property is bullshit bgdc + +#include "ascii85.h" + +#include +#include +#include + +#define ASCII85_BASE 33 + +static inline uint8_t divmod85(uint32_t * x) +{ + uint8_t mod = *x % 85; + *x /= 85; + return mod; +} + +size_t ascii85_encode(uint8_t * dest, uint8_t const * src, size_t len) +{ + size_t total = 0; + + while (len >= 4) + { + uint32_t n = ( + ((uint32_t) src[0] << 24uL) | + ((uint32_t) src[1] << 16uL) | + ((uint32_t) src[2] << 8uL) | + ((uint32_t) src[3] << 0uL)); + + dest[4] = divmod85(&n) + ASCII85_BASE; + dest[3] = divmod85(&n) + ASCII85_BASE; + dest[2] = divmod85(&n) + ASCII85_BASE; + dest[1] = divmod85(&n) + ASCII85_BASE; + dest[0] = n + ASCII85_BASE; // Guaranteed < 85 + + src += 4; + dest += 5; + len -= 4; + total += 5; + } + + if (len) + { + uint32_t n = 0; + n = (uint32_t) src[0] << 24uL; + if (len > 1) n |= ((uint32_t) src[1] << 16uL); + if (len > 2) n |= ((uint32_t) src[2] << 8uL); + if (len > 3) n |= ((uint32_t) src[3] << 0uL); + + char a85[5]; + a85[4] = divmod85(&n) + ASCII85_BASE; + a85[3] = divmod85(&n) + ASCII85_BASE; + a85[2] = divmod85(&n) + ASCII85_BASE; + a85[1] = divmod85(&n) + ASCII85_BASE; + a85[0] = n + ASCII85_BASE; // As above + + dest[0] = a85[0]; + dest[1] = a85[1]; + dest[2] = (len > 1) ? a85[2] : 0; + dest[3] = (len > 2) ? a85[3] : 0; + dest[4] = 0; // if we get here we're dropping at least one + + total += len + 1; + } + + return total; +} + +size_t ascii85_decode(uint8_t * dest, uint8_t const * src, size_t len) +{ + size_t total = 0; + + while (len >= 5) + { + uint32_t n = src[0] - ASCII85_BASE; + n = 85 * n + src[1] - ASCII85_BASE; + n = 85 * n + src[2] - ASCII85_BASE; + n = 85 * n + src[3] - ASCII85_BASE; + n = 85 * n + src[4] - ASCII85_BASE; + + dest[0] = (n >> 24uL) & 0xFF; + dest[1] = (n >> 16uL) & 0xFF; + dest[2] = (n >> 8uL) & 0xFF; + dest[3] = (n >> 0uL) & 0xFF; + + src += 5; + dest += 4; + len -= 5; + total += 4; + } + + if (len > 1) + { + uint32_t n = src[0] - ASCII85_BASE; + n = 85 * n + src[1] - ASCII85_BASE; + n = 85 * n + (len > 2 ? src[2] : 'u') - ASCII85_BASE; + n = 85 * n + (len > 3 ? src[3] : 'u') - ASCII85_BASE; + n = 85 * n; + + dest[0] = (n >> 24uL) & 0xFF; + if (len > 2) dest[1] = (n >> 16uL) & 0xFF; + if (len > 3) dest[2] = (n >> 8uL) & 0xFF; + + total += len - 1; + } + + return total; +} diff --git a/ascii85.h b/ascii85.h new file mode 100644 index 0000000..f886592 --- /dev/null +++ b/ascii85.h @@ -0,0 +1,30 @@ +// intellectual property is bullshit bgdc + +#ifndef ASCII85_H +#define ASCII85_H + +#include +#include +#include + +// Encode a block to ascii85. The destination buffer must contain at least +// (5/4) * rounded_up(len, 4) bytes (that is, enough bytes to encode src +// including padding bytes). +// +// "z" as an abbreviation for a block of four null bytes is not implemented +// to simplify buffer size requirements. +// +// Returns the total number of bytes used. +size_t ascii85_encode(uint8_t * dest, uint8_t const * src, size_t len); + +// Decode a block from ascii85. The destination buffer must contain at least +// (4/5) * rounded_up(len, 5) bytes (that is, enough bytes to encode src +// including possible padding bytes). +// +// "z" as an abbreviation for a block of four null bytes is not implemented +// to simplify buffer size requirements. +// +// Returns the total number of bytes used. +size_t ascii85_decode(uint8_t * dest, uint8_t const * src, size_t len); + +#endif // !defined(ASCII85_H) diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..a2103fe --- /dev/null +++ b/base64.c @@ -0,0 +1,152 @@ +// intellectual property is bullshit bgdc + +#include "base64.h" + +#include +#include +#include + +// TODO make these progmem +static const uint8_t enc_lut[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// ascii 0x20 through 0x7F +static const uint8_t dec_lut[] = +{ + // ! " # $ % & ' + -1, -1, -1, -1, -1, -1, -1, -1, + + // ( ) * + , - . / + -1, -1, -1, 62, -1, -1, -1, 63, + + // 0 1 2 3 4 5 6 7 + 52, 53, 54, 55, 56, 57, 58, 59, + + // 8 9 : ; < = > ? + 60, 61, -1, -1, -1, 0, -1, -1, + + // @ A B C D E F G + -1, 0, 1, 2, 3, 4, 5, 6, + + // H I J K L M N O + 7, 8, 9, 10, 11, 12, 13, 14, + + // P Q R S T U V W + 15, 16, 17, 18, 19, 20, 21, 22, + + // X Y Z [ \ ] ^ _ + 23, 24, 25, -1, -1, -1, -1, -1, + + // ` a b c d e f g + -1, 26, 27, 28, 29, 30, 31, 32, + + // h i j k l m n o + 33, 34, 35, 36, 37, 38, 39, 40, + + // p q r s t u v w + 41, 42, 43, 44, 45, 46, 47, 48, + + // x y z { | } ~ del + 49, 50, 51, -1, -1, -1, -1, -1 +}; + +#define DECODE(c) (((c) >= 0x20 && (c) <= 0x7F) ? dec_lut[(c) - 0x20] : (uint8_t)(-1)) + +static inline uint8_t divmod64(uint32_t * x) +{ + uint8_t mod = *x & 0x3F; + *x >>= 6; + return mod; +} + +size_t base64_encode(uint8_t * dest, uint8_t const * src, size_t len) +{ + size_t total = 0; + + while (len >= 3) + { + uint32_t n = ( + ((uint32_t) src[0] << 16uL) | + ((uint32_t) src[1] << 8uL) | + ((uint32_t) src[2] << 0uL)); + + dest[3] = enc_lut[divmod64(&n)]; + dest[2] = enc_lut[divmod64(&n)]; + dest[1] = enc_lut[divmod64(&n)]; + dest[0] = enc_lut[n]; // Guaranteed < 64 + + src += 3; + dest += 4; + len -= 3; + total += 4; + } + + if (len) + { + uint32_t n = 0; + n = (uint32_t) src[0] << 16uL; + if (len > 1) n |= ((uint32_t) src[1] << 8uL); + if (len > 2) n |= ((uint32_t) src[2] << 0uL); + + divmod64(&n); // dropping at least one + dest[3] = '='; + dest[2] = enc_lut[divmod64(&n)]; + dest[1] = enc_lut[divmod64(&n)]; + dest[0] = enc_lut[n]; // Guaranteed < 64 + + if (len == 1) dest[2] = '='; + total += 4; + } + + return total; +} + +size_t base64_decode(uint8_t * dest, uint8_t const * src, size_t len) +{ + size_t total = 0; + + // Splitting the loops this way ensures the final block (possibly + // containing =) is always processed by the last section but still + // tolerates extra garbage (whitespace) on the end. + + while (len >= 7) + { + uint32_t n = 0; + n |= (uint32_t) DECODE(src[0]) << 18uL; + n |= (uint32_t) DECODE(src[1]) << 12uL; + n |= (uint32_t) DECODE(src[2]) << 6uL; + n |= (uint32_t) DECODE(src[3]); + + dest[0] = (n >> 16uL) & 0xFF; + dest[1] = (n >> 8uL) & 0xFF; + dest[2] = (n >> 0uL) & 0xFF; + + src += 4; + dest += 3; + len -= 4; + total += 3; + } + + if (len >= 4) + { + + uint32_t n = 0; + n |= (uint32_t) DECODE(src[0]) << 18uL; + n |= (uint32_t) DECODE(src[1]) << 12uL; + n |= (uint32_t) DECODE(src[2]) << 6uL; + n |= (uint32_t) DECODE(src[3]); + + dest[0] = (n >> 16uL) & 0xFF; + dest[1] = (n >> 8uL) & 0xFF; + dest[2] = (n >> 0uL) & 0xFF; + + int neq = !!(src[3] == '=') + !!(src[2] == '='); + + src += 4; + dest += 3 - neq; + len -= 4; + total += 3 - neq; + } + + return total; +} diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..916fdd1 --- /dev/null +++ b/base64.h @@ -0,0 +1,24 @@ +// intellectual property is bullshit bgdc + +#ifndef BASE64_H +#define BASE64_H + +#include +#include +#include + +// Encode a block to base64. The destination buffer must contain at least +// (4/3) * rounded_up(len, 3) bytes (that is, enough bytes to encode src +// including padding bytes). +// +// Returns the total number of bytes used. +size_t base64_encode(uint8_t * dest, uint8_t const * src, size_t len); + +// Decode a block from base64. The destination buffer must contain at least +// (3/4) * rounded_up(len, 4) bytes (that is, enough bytes to encode src +// including possible padding bytes). +// +// Returns the total number of bytes used. +size_t base64_decode(uint8_t * dest, uint8_t const * src, size_t len); + +#endif // !defined(BASE64_H) diff --git a/board.cpp b/board.cpp index 635ec16..7dd3525 100644 --- a/board.cpp +++ b/board.cpp @@ -14,6 +14,8 @@ FUSES = { .BOOTSIZE = 0x00 }; +spi_avrdx spi_sdcard; + void board_pins_init() { // We have a lot of analog inputs. Just disable ALL input buffers, then @@ -68,3 +70,8 @@ void board_pins_init() DEBUGPINS_RUN(); } + +void board_peripherals_init() +{ + spi_sdcard.init(spi_sdcard.clockdiv::div2, true, 0); +} diff --git a/board.hpp b/board.hpp index 395ff68..2b7c1dd 100644 --- a/board.hpp +++ b/board.hpp @@ -1,7 +1,11 @@ #pragma once #include "boarddefs.h" +#include // Initialize pins on startup. Peripherals are not started here. void board_pins_init(); +// Initialize peripherals. +void board_peripherals_init(); +extern spi_avrdx spi_sdcard; diff --git a/boarddefs.h b/boarddefs.h index ca55c52..3f1b91d 100644 --- a/boarddefs.h +++ b/boarddefs.h @@ -50,3 +50,6 @@ #define PIN_CIPO PINDEF(PORTG, 5) #define PIN_SCK PINDEF(PORTG, 6) #define PIN_nVS PINDEF(PORTG, 7) + +#define SPI_VOICES 0 +#define SPI_SDCARD 1 diff --git a/main.cpp b/main.cpp index 1510e31..7f87b72 100644 --- a/main.cpp +++ b/main.cpp @@ -23,6 +23,7 @@ static midix midi(usbmidi_usart, midi_q); int main(void) { board_pins_init(); + board_peripherals_init(); usbmidi_usart.set_baud(31250); usbmidi_usart.start(true, true, 'N', false); diff --git a/meson.build b/meson.build index e95d441..84b250a 100644 --- a/meson.build +++ b/meson.build @@ -1,16 +1,16 @@ -project('poly1', ['cpp', 'c'], default_options: ['optimization=s', 'cpp_std=c++17']) +project('poly1', ['cpp', 'c'], default_options: ['optimization=2', 'cpp_std=c++17']) atpack_zip = 'Microchip.AVR-Dx_DFP.2.1.152.atpack' dependencies = [] -local_headers = ['.', 'etl/include', 'sd-spi'] +local_headers = ['.', 'etl/include'] sources = [ 'main.cpp', 'board.cpp', 'midix.cpp', - 'sd-spi/sd_spi.c', - 'sd-spi/sd_spi_platform_dependencies.c', + 'ascii85.c', + 'base64.c', ] _includes = local_headers @@ -23,16 +23,20 @@ if host_machine.cpu_family() != 'avr' ['test_midi.cpp', 'midix.cpp'], include_directories: _incl_dirs, ) + + test_ascii85 = executable( + 'test_ascii85', + ['test_ascii85.c', 'ascii85.c', 'base64.c'], + include_directories: _incl_dirs, + ) subdir_done() endif # debug build if get_option('buildtype') == 'debug' add_project_arguments(['-DDEBUG=1', '-ggdb3'], language: ['c', 'cpp']) - add_project_link_arguments(['-ggdb3'], language: ['c', 'cpp']) else add_project_arguments(['-ggdb3'], language: ['c', 'cpp']) - add_project_link_arguments(['-ggdb3'], language: ['c', 'cpp']) endif objcopy = find_program('objcopy') @@ -53,10 +57,7 @@ add_project_arguments(['-I' + atpack_path / 'include'], language: ['c', 'cpp']) add_project_arguments(['-DETL_NO_STL=1'], language: ['c', 'cpp']) -opt_opts = ['-mrelax', '-flto'] -add_project_arguments(opt_opts, language: ['c', 'cpp']) -add_project_link_arguments(opt_opts, language: ['c', 'cpp']) - +add_project_arguments(['-mrelax'], language: ['c', 'cpp']) atpack = custom_target( 'atpack', command: [ 'unzip', '@INPUT@', '-d', '@OUTPUT@' ], diff --git a/sd-spi/sd_spi.h b/sd-spi/sd_spi.h index 2a758e4..48bca19 100644 --- a/sd-spi/sd_spi.h +++ b/sd-spi/sd_spi.h @@ -8,6 +8,8 @@ By defining SD_BUFFER, the library also implements a 512 byte buffer for reading and writing. +modified in 2022 by alexis + @copyright Copyright 2015 Wade Penson @license Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/sd-spi/sd_spi_platform_dependencies.c b/sd-spi/sd_spi_platform_dependencies.c deleted file mode 100644 index aec758a..0000000 --- a/sd-spi/sd_spi_platform_dependencies.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "sd_spi_platform_dependencies.h" - -void -sd_spi_pin_mode( - uint8_t pin, - uint8_t mode -) -{ - -} - -void -sd_spi_digital_write( - uint8_t pin, - uint8_t state -) -{ - -} - -uint32_t -sd_spi_millis( - void -) -{ - -} - -void -sd_spi_begin( - void -) -{ - -} - -void -sd_spi_begin_transaction( - uint32_t transfer_speed_hz -) -{ - -} - -void -sd_spi_end_transaction( - void -) -{ - -} - -void -sd_spi_send_byte( - uint8_t b -) -{ - -} - -uint8_t -sd_spi_receive_byte( - void -) -{ - -} \ No newline at end of file diff --git a/sd-spi/sd_spi_platform_dependencies.cpp b/sd-spi/sd_spi_platform_dependencies.cpp new file mode 100644 index 0000000..98b4f8e --- /dev/null +++ b/sd-spi/sd_spi_platform_dependencies.cpp @@ -0,0 +1,55 @@ +#include +#include + +extern "C" { +#include "sd_spi_platform_dependencies.h" +} + +extern "C" void sd_spi_pin_mode(uint8_t pin, uint8_t mode) +{ + if (mode == OUTPUT) + PORT_FOR(pin).DIRSET = BIT_FOR(pin); + else + PORT_FOR(pin).DIRCLR = BIT_FOR(pin); +} + +extern "C" void sd_spi_digital_write(uint8_t pin, uint8_t state) +{ + if (state) + PORT_FOR(pin).OUTSET = BIT_FOR(pin); + else + PORT_FOR(pin).OUTCLR = BIT_FOR(pin); +} + +extern "C" uint32_t sd_spi_millis() +{ + // TODO - we don't have a millisecond counter until the timer module + // is implemented + static uint32_t m = 0; + return m++; +} + +extern "C" void sd_spi_begin() +{ + +} + +extern "C" void sd_spi_begin_transaction(uint32_t transfer_speed_hz) +{ + +} + +extern "C" void sd_spi_end_transaction() +{ + +} + +extern "C" void sd_spi_send_byte(uint8_t b) +{ + spi_sdcard.transfer(b); +} + +extern "C" uint8_t sd_spi_receive_byte() +{ + return spi_sdcard.transfer(0xFF); +} diff --git a/sd-spi/sd_spi_platform_dependencies.h b/sd-spi/sd_spi_platform_dependencies.h index b668cad..c20f795 100644 --- a/sd-spi/sd_spi_platform_dependencies.h +++ b/sd-spi/sd_spi_platform_dependencies.h @@ -33,7 +33,7 @@ extern "C" { #define INPUT 0 #endif -#if !defined(OUTPUT) +#if !defined(OUTPUT) #define OUTPUT 1 #endif @@ -91,4 +91,4 @@ sd_spi_receive_byte( } #endif -#endif /* SD_SPI_PLATFORM_DEPENDENCIES_H_ */ \ No newline at end of file +#endif /* SD_SPI_PLATFORM_DEPENDENCIES_H_ */ diff --git a/sysex.hpp b/sysex.hpp new file mode 100644 index 0000000..1616496 --- /dev/null +++ b/sysex.hpp @@ -0,0 +1,45 @@ +// intellectual property is bullshit bgdc + +#ifndef SYSEX_HPP +#define SYSEX_HPP + +#include +#include +#include + +// All sysex messages start with manuf id 7D (prototyping, test, private use, +// and experimentation). +// +// +// 7D 00 - front panel controls +// 7D 01 - storage commands +// 7D 7F - status codes +// +// +// -- Storage commands -- +// +// Storage commands interface with the onboard storage (SD card). Large +// arguments are generally encoded using Ascii85. +// +// 7D 01 01 ... - data response message, Ascii85 +// 7D 01 02 A A A A A - request SD block AAA... (ascii85, data sent via 7D 01 01) +// 7D 01 03 A A A A A ... - write SD block AAA... (data sent as Ascii85) +// If data is not exactly 512 bytes long, nothing +// is written and an error SYSEX_FMT is sent. +// +// -- Status codes -- +// 7D 7F 01 - general ACK +// 7D 7F 02 - message format error + +#define MIDI_MANUF_ID 0x7D + +#define MIDI_SYSEX_STORAGE_GRP 0x01 +#define MIDI_SYSEX_STORAGE_RESPONSE 0x01 +#define MIDI_SYSEX_STORAGE_READ 0x02 +#define MIDI_SYSEX_STORAGE_WRITE 0x03 + +#define MIDI_SYSEX_STATUS_GRP 0x7F +#define MIDI_SYSEX_STATUS_ACK 0x01 +#define MIDI_SYSEX_STATUS_FMT_ERR 0x02 + +#endif // !defined(SYSEX_HPP) diff --git a/test_ascii85.c b/test_ascii85.c new file mode 100644 index 0000000..c8fce28 --- /dev/null +++ b/test_ascii85.c @@ -0,0 +1,120 @@ +// intellectual property is bullshit bgdc + +#include "ascii85.h" +#include "base64.h" + +#include +#include +#include +#include +#include + +static void usage(char const * argv0) +{ + fprintf(stderr, "%s 85enc|85dec|64enc|64dec [IN [OUT]]\n", argv0); +} + +int main(int argc, char ** argv) +{ + bool decode = false; + bool base64 = false; + + if (argc < 2) + { + usage(argv[0]); + return 1; + } + + if (!strcmp(argv[1], "85enc")) + { + decode = false; + base64 = false; + } + else if (!strcmp(argv[1], "85dec")) + { + decode = true; + base64 = false; + } + else if (!strcmp(argv[1], "64enc")) + { + decode = false; + base64 = true; + } + else if (!strcmp(argv[1], "64dec")) + { + decode = true; + base64 = true; + } + else + { + usage(argv[0]); + return 1; + } + + FILE * f_in = NULL, * f_out = NULL; + + if (argc < 3) + f_in = stdin; + else if (!strcmp(argv[2], "-")) + f_in = stdin; + else + { + f_in = fopen(argv[2], "r"); + if (!f_in) + { + perror("fopen"); + return 1; + } + } + + if (argc < 4) + f_out = stdout; + else if (!strcmp(argv[3], "-")) + f_out = stdout; + else + { + f_out = fopen(argv[3], "w"); + if (!f_out) + { + perror("fopen"); + return 1; + } + } + + size_t n_raw, n_encoded; + + if (base64) + { + n_raw = 513; + n_encoded = 684; + } + else + { + n_raw = 512; + n_encoded = 640; + } + + uint8_t in_buffer[decode ? n_encoded : n_raw]; + uint8_t out_buffer[decode ? n_raw : n_encoded]; + + while (!ferror(f_in) && !feof(f_in) && !ferror(f_out) && !feof(f_out)) + { + size_t n = fread(in_buffer, 1, sizeof(in_buffer), f_in); + size_t n_converted; + + if (decode && base64) + n_converted = base64_decode(out_buffer, in_buffer, n); + else if (!decode && base64) + n_converted = base64_encode(out_buffer, in_buffer, n); + else if (decode && !base64) + n_converted = ascii85_encode(out_buffer, in_buffer, n); + else + n_converted = ascii85_decode(out_buffer, in_buffer, n); + + fwrite(out_buffer, 1, n_converted, f_out); + } + + fclose(f_in); + fclose(f_out); + return 0; +}