summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuncan Wilkie <antigravityd@gmail.com>2023-08-07 17:29:29 -0500
committerDuncan Wilkie <antigravityd@gmail.com>2023-08-07 17:29:29 -0500
commit2a3940d8a36433485a6ef489d5123cd491618b50 (patch)
tree75354e5dc5f81535339895d6d08c4969be78f157
parent6380915e6e8d98e2c933ea6eb8df02fb1b123b97 (diff)
slave progress and WCET
-rw-r--r--controller/Makefile14
-rw-r--r--controller/inc/base_midi.h15
-rw-r--r--controller/ld/TM4C123GH6PM.ld20
-rw-r--r--controller/libs/base_midi.c47
-rw-r--r--controller/libs/midi_ci.c536
-rw-r--r--controller/src/main.c104
-rw-r--r--specs/AVR-Instruction-Set-Manual-DS40002198A.pdfbin0 -> 1196344 bytes
-rw-r--r--specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdfbin0 -> 2644021 bytes
8 files changed, 688 insertions, 48 deletions
diff --git a/controller/Makefile b/controller/Makefile
index 9be9eb3..aedbfe4 100644
--- a/controller/Makefile
+++ b/controller/Makefile
@@ -17,23 +17,23 @@ SRCS = $(wildcard src/*.c) \
OBJ = obj/
OBJS = $(addprefix $(OBJ),$(notdir $(SRCS:.c=.o)))
LD_SCRIPT = ld/$(MCU).ld
-IPATH = /home/dnw/Code/TivaC/libs/driverlib
+IPATH = /home/dnw/Code/TivaC/libs
# Flags.
CFLAGS = -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections \
- -fdata-sections -MD -std=c2x -Wall -Wextra -Werror -DPART_${MCU} -c -Os -Dgcc -ggdb
+ -fdata-sections -MD -std=c2x -Wall -Wextra -Werror -DPART_${MCU} -c -O0 -Dgcc -ggdb
CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}} -iquote inc
# Compiler/standard resource locations.
LIBGCC := ${shell ${CC} ${CFLAGS} -print-libgcc-file-name}
-LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc.a}
+LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc_nano.a}
LIBM := ${shell ${CC} ${CFLAGS} -print-file-name=libm.a}
+LIBG := ${shell ${CC} ${CFLAGS} -print-file-name=libg_nano.a}
+LIBSTUB := ${shell ${CC} ${CFLAGS} -print-file-name=libnosys.a}
+LIBDRIVER := /home/dnw/Code/TivaC/libs/driverlib/gcc/libdriver.a
# More flags.
-LDFLAGS = -T $(LD_SCRIPT) -e Reset_Handler --gc-sections '${LIBGCC}' '${LIBC}' '${LIBM}'
-
-
-
+LDFLAGS = -T $(LD_SCRIPT) --gc-sections '${LIBM}' '${LIBGCC}' '${LIBDRIVER}' '${LIBC}' '${LIBSTUB}'
# Targets.
diff --git a/controller/inc/base_midi.h b/controller/inc/base_midi.h
index 8320803..b6e5b19 100644
--- a/controller/inc/base_midi.h
+++ b/controller/inc/base_midi.h
@@ -8,7 +8,8 @@
// Configuration.
#define DEBUG 0 // If defined, enable input safety checking (e.g. too large data bytes, bad ID numbers, etc).
// These checks have some performance downside.
-#define RUNNING_STATUS 0 // If defined, the MIDI instrument stores a transmit status and uses running statuses whenever possible.
+/* #define RUNNING_STATUS 0 // If defined, the MIDI instrument stores a transmit status and uses running statuses whenever possible .*/
+
#define KEY_ON_VELOCITY 0 // If defined, the MIDI instrument sends velocities with Note On messages.
#undef RELEASE_VELOCITY // If defined, the MIDI instrument sends Note Off messages with velocities.
// Otherwise, sends Note On velocity 0 or Note Off velocity 0 according to ACTUAL_OFF_MESSAGE.
@@ -69,7 +70,17 @@ typedef struct {
bool (*unimplemented_universal_sysex_collector)(uint8_t); // Same as above. Parsing ends with end_of_sysex_handler.
} ConsumerBehavior;
-extern ConsumerBehavior pfns; // Initialize this globally in your main.
+typedef struct {
+ uint8_t status;
+ bool first_byte;
+ uint8_t byte0;
+ bool send_eox;
+ uint8_t *stack;
+ size_t top;
+ size_t size;
+} ParserMemory;
+
+void midi_init(ConsumerBehavior new_pfns);
// System exclusive sends.
void sysex(uint16_t manufacturer_id, uint8_t* contents, size_t contents_len);
diff --git a/controller/ld/TM4C123GH6PM.ld b/controller/ld/TM4C123GH6PM.ld
index 29ff8d8..611ac9e 100644
--- a/controller/ld/TM4C123GH6PM.ld
+++ b/controller/ld/TM4C123GH6PM.ld
@@ -21,13 +21,19 @@
* Description: linker file for the TM4C Launchpad
*/
+ENTRY(Reset_Handler)
+
MEMORY
{
+
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K /* FLASH size 256KB */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K/* RAM size 32KB */
}
+_Min_Heap_Size = 0x3000;
+_Min_Stack_Size = 0x3000;
+
SECTIONS
{
@@ -38,7 +44,17 @@ SECTIONS
* those lesser than 0x20007FFF to 0x2000000,which is the origina address of RAM,
* until it comes in contact with .bss or .data in which case a buffer overflow occurs
*/
- PROVIDE( _stack_ptr = ORIGIN(RAM) + LENGTH(RAM));
+ PROVIDE( _stack_ptr = ORIGIN(RAM) + _Min_Stack_Size);
+
+ ._user_heap_stack :
+ {
+ . = ALIGN(8);
+ . = . + _Min_Stack_Size;
+ . = . + _Min_Heap_Size;
+ PROVIDE ( end = . );
+ PROVIDE ( _end = . );
+ . = ALIGN(8);
+ } > RAM
/* constants and other code stored in FLASH */
.text :
@@ -67,6 +83,4 @@ SECTIONS
_ebss = .; /* end of .bss segment */
} > RAM
-
-
}
diff --git a/controller/libs/base_midi.c b/controller/libs/base_midi.c
index 6efd2f3..0394406 100644
--- a/controller/libs/base_midi.c
+++ b/controller/libs/base_midi.c
@@ -1,14 +1,18 @@
- // This file is written to be read side-along with the MIDI 1.0 spec.
+// This file is written to be read side-along with the MIDI 1.0 spec.
// Note the tables at its very end, with /absolutely no direct allusion in the text/.
#include "base_midi.h"
#include <stdlib.h>
#include <string.h>
-// TODO: startup function to initialize ParseMemory buffer.
+
#ifdef RUNNING_STATUS
uint8_t last_status = 0x00;
#endif
+ConsumerBehavior pfns;
+ParserMemory memory;
+
+
// Helper functions
#define MAX_DATA_BYTE 0b01111111
static inline void MIDI_bare_send(uint8_t status) {
@@ -539,24 +543,21 @@ void single_note_tuning_change(uint8_t device_id, uint8_t program, uint8_t *note
// Parsing.
// Stream parsing.
-typedef struct {
- uint8_t status;
- bool first_byte;
- uint8_t byte0;
- bool send_eox;
- uint8_t *stack;
- size_t top;
- size_t size;
-} ParserMemory;
#define WAITING_FOR_STATUS 0x00
-ParserMemory memory = {.status = WAITING_FOR_STATUS,
- .first_byte = false,
- .byte0 = 0x00,
- .send_eox = false,
- .stack = NULL, // TODO: initialize from main.
- .top = 0,
- .size = 128};
+
+
+void midi_init(ConsumerBehavior new_pfns) {
+ memory.status = WAITING_FOR_STATUS;
+ memory.first_byte = false;
+ memory.send_eox = false;
+ memory.stack = malloc(128);
+ memory.top = 0;
+ memory.size = 128;
+
+ pfns = new_pfns;
+}
+
static inline void push_fn(uint8_t val) {
if (memory.top == memory.size - 1) {
@@ -571,14 +572,6 @@ static inline void push_fn(uint8_t val) {
push_fn(byte); \
} while(0)
-static inline uint8_t pop() {
- if (memory.size - memory.top > 128) {
- memory.stack = realloc(memory.stack, memory.size - 128);
- }
-
- --memory.top;
- return memory.stack[memory.top + 1];
-}
static inline void reset() {
memory.stack = realloc(memory.stack, 128);
@@ -594,7 +587,7 @@ static inline void reset() {
#define channel (memory.status & 0x0f)
-
+#define MIDI_CI_SUBID 0x0d
// Memory should be large enough to handle the largest message that's expected to be processed automatically here.
void parse_midi_stream(uint8_t byte) {
diff --git a/controller/libs/midi_ci.c b/controller/libs/midi_ci.c
index 7013d7a..cecef52 100644
--- a/controller/libs/midi_ci.c
+++ b/controller/libs/midi_ci.c
@@ -4,9 +4,13 @@
// - MIDI CI Specification, minimum requirements + Property Exchange chapter,
// - Common Rules for MIDI CI Property Exchange.
+#define DISABLE true
+#ifndef DISABLE
#include "base_midi.h"
+#include "parson.h"
uint32_t our_muid = rand(); // TODO: think.
+uint8_t request_id = 0;
#define MIDI_CI_SUBID 0x0d
#define PROFILE_CONFIGURATION_MASK 0x20
@@ -16,7 +20,7 @@ uint32_t our_muid = rand(); // TODO: think.
#define MIDI_CI_VERSION 0x02
#define TO_FUNCTION_BLOCK_ID 0x7f
static void ci(uint8_t source_target, uint8_t message_type, uint32_t destination_miud,
- uint8_t *contents, uint8_t contents_len) {
+ uint8_t *contents, size_t contents_len) {
uint8_t *new = malloc(1 + 4 + 4 + contents_len);
new[0] = MIDI_CI_VERSION;
new[1] = our_muid & 0x00'00'00'ff;
@@ -106,16 +110,14 @@ void discovery_reply(uint8_t initiator_muid, uint8_t initiator_output_path_id) {
}
-// TODO: Decide if Inquiry: Endpoint and Reply to Endpoint are necessary for standard conformace if we're over MIDI 1.0.
-
// TODO: should we set a new MUID inside this function?
#define INVALIDATE_MUID_SUBID 0x7e
-void invalidate_muid() {
+void invalidate_muid(uint8_t muid) {
uint8_t *new = malloc(4);
- new[0] = our_muid & 0x00'00'00'ff;
- new[1] = (our_muid >> 8) & 0x00'00'00'ff;
- new[2] = (our_muid >> 16) & 0x00'00'00'ff;
- new[3] = (our_muid >> 24) & 0x00'00'00'ff;
+ new[0] = muid & 0x00'00'00'ff;
+ new[1] = (muid >> 8) & 0x00'00'00'ff;
+ new[2] = (muid >> 16) & 0x00'00'00'ff;
+ new[3] = (muid >> 24) & 0x00'00'00'ff;
ci(TO_FUNCTION_BLOCK_ID, BROADCAST_MUID, new, 4);
@@ -123,6 +125,10 @@ void invalidate_muid() {
}
+static inline void invalidate_our_muid() {
+ invalidate_muid(our_muid);
+}
+
// TODO: consider structifying?
#define ACK_SUBID 0x7d
void ci_ack(uint8_t acking_device_id, uint8_t acking_muid, uint8_t original_classification, uint8_t status_code,
@@ -172,3 +178,517 @@ void ci_nack(uint8_t naking_device_id, uint8_t naking_muid, uint8_t original_cla
free(new);
}
+
+#define WHOLE_FUNCTION_BLOCK_ID 0x0f
+#define PROPERTY_EXCHANGE_MAJOR_VERSION 0x00
+#define PROPERTY_EXCHANGE_MINOR_VERSION 0x00
+#define SIMULTANEOUS_REQUESTS 0x01
+#define INQUIRE_EXCHANGE_CAPABILITIES_SUBID 0x30
+void inquire_property_exchange_capabilities(uint8_t destination_muid) {
+ uint8_t *new = malloc(3);
+ new[0] = SIMULTANEOUS_REQUESTS;
+ new[1] = PROPERTY_EXCHANGE_MAJOR_VERSION;
+ new[2] = PROPERTY_EXCHANGE_MINOR_VERSION;
+
+ ci(WHOLE_FUNCTION_BLOCK_ID, INQUIRE_EXCHANGE_CAPABILITIES_SUBID, destination_muid, new, 3);
+
+ free(new);
+
+}
+
+#define REPLY_EXCHANGE_CAPABILITIES_SUBID 0x31
+void reply_proprty_exchange_capabilities (uint8_t initiator_muid) {
+ uint8_t *new = malloc(3);
+ new[0] = SIMULTANEOUS_REQUESTS;
+ new[1] = PROPERTY_EXCHANGE_MAJOR_VERSION;
+ new[2] = PROPERTY_EXCHANGE_MINOR_VERSION;
+
+ ci(WHOLE_FUNCTION_BLOCK_ID, REPLY_EXCHANGE_CAPABILITIES_SUBID, initiator_muid, new, 3);
+
+ free(new);
+
+}
+
+// TODO: complain; standard doesn't specify what goes into the "Max SysEx size." E.g. do I include the EOX?
+#define CI_MESSAGE_NONVARIABLE_SIZE (1 + 1 + 1 + 1 + 1 + 1 + 4 + 4 + 1 + 2 + 2 + 2 + 2 + 1)
+#define min(X, Y) ((X) < (Y) ? (X) : (Y))
+static void chunked_property_exchange_send(uint32_t receiver_max_sysex, uint8_t message_subid, uint32_t destination_muid,
+ uint8_t request_id, uint8_t *header_data, uint16_t header_len,
+ uint8_t *property_data, uint16_t property_len) {
+ uint32_t total_len = CI_MESSAGE_NONVARIABLE_SIZE + header_len + property_len;
+ uint32_t leftover_bytes = total_len % receiver_max_sysex;
+ uint32_t chunk_count = total_len / receiver_max_sysex + (leftover_bytes == 0 ? 0 : 1);
+ uint32_t leftover_property_bytes = leftover_bytes - CI_MESSAGE_NONVARIABLE_SIZE;
+ uint32_t max_per_message_property_bytes = receiver_max_sysex - CI_MESSAGE_NONVARIABLE_SIZE;
+
+ uint8_t *new = malloc(3 + header_len + 6 + max_per_message_property_bytes);
+ new[0] = request_id;
+ new[1] = header_len & 0x00ff;
+ new[2] = header_len >> 8;
+ memcpy(new + 3, header_data, header_len);
+
+ for (uint32_t chunk = 1; chunk < chunk_count, i++) {
+ new[3 + header_len] = chunk_count & 0x00ff;
+ new[3 + header_len + 1] = chunk_count >> 8;
+ new[3 + header_len + 2] = chunk & 0x00ff;
+ new[3 + header_len + 3] = chunk >> 8;
+ new[3 + header_len + 4] = max_per_message_property_bytes & 0x00ff;
+ new[3 + header_len + 5] = max_per_message_property_bytes >> 8;
+
+ memcpy(3 + header_len + 6, property_data + (chunk - 1) * max_per_message_property_bytes, max_per_message_property_bytes);
+
+ ci(WHOLE_FUNCTION_BLOCK_ID, message_subid, destination_muid, new, 3 + header_len + 6 + max_per_message_property_bytes);
+
+ }
+
+ if (leftover_property_bytes != 0) {
+ new[3 + header_len] = chunk_count & 0x00ff;
+ new[3 + header_len + 1] = chunk_count >> 8;
+ new[3 + header_len + 2] = chunk & 0x00ff;
+ new[3 + header_len + 3] = chunk >> 8;
+ new[3 + header_len + 4] = leftover_property_bytes & 0x00ff;
+ new[3 + header_len + 5] = leftover_property_bytes >> 8;
+
+ memcpy(3 + header_len + 6, property_data + (chunk_count - 1) * max_per_message_property_bytes, leftover_property_bytes);
+
+ ci(WHOLE_FUNCTION_BLOCK_ID, message_subid, destination_muid, new, 3 + header_len + 6 + leftover_property_bytes);
+
+ }
+
+
+ free(new);
+
+}
+
+#define INQUIRE_GET_SUBID 0x34
+void inquiry_get_property(uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, uint16_t header_len) {
+ uint8_t *new = malloc(3 + header_len + 3);
+ new[0] = request_id;
+ new[1] = header_len & 0x00ff;
+ new[2] = header_len >> 8;
+
+ memcpy(new + 3, header_data, header_len);
+
+ // Chunk count.
+ new[3 + header_len] = 0x01;
+ new[3 + header_len + 1] = 0x00;
+ // Current chunk number.
+ new[3 + header_len + 2] = 0x01;
+ new[3 + header_len + 3] = 0x00;
+ // Property length.
+ new[3 + header_len + 4] = 0x00;
+ new[3 + header_len + 5] = 0x00;
+
+ ci(WHOLE_FUNCTION_BLOCK_ID, INQUIRE_GET_SUBID, destination_muid, new, 3 + header_len + 6);
+
+ free(new);
+}
+
+#define REPLY_GET_SUBID 0x35
+void reply_get_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+
+ chunked_property_exchange_send(receiver_max_sysex, REPLY_GET_SUBID, destination_muid, request_id, header_data,
+ header_len, property_data, property_len);
+
+}
+
+#define INQURIE_SET_SUBID 0x36
+void inquiry_set_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+ chunked_property_exchange_send(receiver_max_sysex, INQUIRE_GET_SUBID, destination_muid, request_id,
+ header_data, header_len, property_data, property_len)
+
+}
+
+#define REPLY_SET_SUBID 0x37
+void reply_set_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+
+ chunked_property_exchange_send(receiver_max_sysex, REPLY_SET_SUBID, destination_muid, request_id,
+ header_data, header_len, property_data, property_len);
+
+}
+
+#define SUBSCRIPTION_SUBID 0x38
+void subscription(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+ chunked_property_exchange_send(receiver_max_sysex, SUBSCRIPTION_SUBID, destination_muid, request_id,
+ header_data, header_len, property_data, property_len);
+
+}
+
+#define REPLY_SUBSCRIPTION_SUBID 0x39
+void reply_subscription(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+ chunked_property_exchange_send(receiver_max_sysex, REPLY_SUBSCRIPTION_SUBID, destination_muid, request_id,
+ header_data, header_len, property_data, property_len);
+
+}
+
+#define NOTIFY_SUBID 0x3f
+void notify(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data,
+ uint16_t header_len, uint8_t *property_data, uint16_t property_len) {
+
+ chunked_property_exchange_send(receiver_max_sysex, NOTIFY_SUBID, destination_muid, request_id,
+ header_data, header_len, property_data, property_len);
+
+}
+
+
+// Parsing.
+
+typedef enum {
+ DEVICE_ID,
+ SUBID,
+ VERSION,
+ SOURCE,
+ DESTINATION,
+ // Discovery.
+ MANUFACTURER,
+ FAMILY,
+ MODEL,
+ REVISION,
+ CI_CATEGORY,
+ MAX_SYSEX,
+ INITIATOR_OUTPUT_PATH,
+ // Invalidate.
+ TARGET_MUID,
+ // Ack.
+ ACK_ORIG_SUBID,
+ ACK_CODE,
+ ACK_DATA,
+ ACK_DETAILS,
+ ACK_TEXT_LENGTH,
+ ACK_TEXT,
+ // Nak.
+ NAK_ORIG_SUBID,
+ NAK_CODE,
+ NAK_DATA,
+ NAK_DETAILS,
+ NAK_LENGTH,
+ NAK_TEXT,
+ // Property Exchange Capabilites.
+ MAX_SIMUL_EXCHANGE,
+ EXCHANGE_MAJOR_VER,
+ EXCHANGE_MINOR_VER,
+ // Property Exchange messages.
+ REQUEST_ID,
+ HEADER_LEN,
+ HEADER_DATA,
+ CHUNK_COUNT,
+ THIS_CHUNK,
+ PROPERTY_LEN,
+ PROPERTY_DATA
+
+} ParseState;
+
+
+const JSON_String supported_resources[] = {
+ {"ResourceList"},
+ {"DeviceInfo"},
+ {"ChannelList"},
+ {"X-KeyLayout"},
+ /* {"X-"} */
+};
+
+typedef struct {
+ uint8_t *stack;
+ size_t top;
+ size_t size;
+ size_t field_position;
+ ParseState state;
+} CIParser;
+
+extern CIParser ci_memory;
+
+static inline void next_in_field() {
+ ++ci_memory.field_position;
+}
+
+static inline void new_field(ParseState to) {
+ ci_memory.field_position = 0;
+ ci_memory.state = to;
+}
+
+static inline void push_fn(uint8_t val) {
+ if (ci_memory.top == ci_memory.size - 1) {
+ ci_memory.stack = realloc(ci_memory.stack, ci_memory.size + 128);
+ }
+
+ ci_memory.stack[ci_memory.top + 1] = val;
+ ++ci_memory.top;
+}
+
+#define push() do { \
+ push_fn(byte); \
+ } while(0)
+
+
+static inline void reset() {
+ ci_memory.stack = realloc(ci_memory.stack, 128);
+ ci_memory.size = 128;
+ ci_memory.top = 0;
+ ci_memory.status = WAITING_FOR_STATUS;
+}
+
+static inline uint8_t device_id() {
+ return ci_memory.stack[0];
+}
+
+static inline uint8_t command_id() {
+ return ci_memory.stack[1];
+}
+
+static inline uint32_t lsb_32bit_from(size_t start_index) {
+ return (ci_memory.stack[start_index + 3] << 24) | (ci_memory.stack[start_index + 2] << 16) \
+ | (ci_memory.stack[start_index + 1] << 8) | (ci_memory.stack[start_index]);
+}
+static inline uint32_t source_muid() {
+ return lsb_32bit_from(2);
+}
+
+static inline uint32_t dest_muid() {
+ return lsb_32bit_from(6);
+}
+
+#define STACK_FIRST_NONCOMMON_IDX 10
+
+#define push_until(val, then_block) do { \
+ switch(ci_memory.field_position) { \
+ case 0 ... (val - 1): \
+ push(); \
+ next_in_field(); \
+ return true; \
+ case val: \
+ then_block \
+ } \
+ } while(0)
+
+#define push_whole_field(val, new) do { \
+ switch(ci_memory.field_position) { \
+ case 0 ... (val - 1): \
+ push(); \
+ next_in_field(); \
+ return true; \
+ case val: \
+ push(); \
+ new_field(new); \
+ return true; \
+ } \
+ } while(0)
+
+
+typedef struct {
+ void (*ack_handler)(uint8_t, uint8_t, uint8_t, uint32_t, uint8_t, uint16_t, char*);
+ void (*nak_handler)(uint8_t, uint8_t, uint8_t, uint32_t, uint8_t, uint16_t, char*);
+} CIConsumerBehavior;
+
+extern CIConsumerBehavior ci_pfns;
+
+// The user's `universal_system_exclusive_handler` should feed everything into this, device-ID down,
+// once it recognizes a MIDI-CI message.
+bool midi_ci_parse(uint8_t byte) {
+ switch (ci_memory.state) {
+ case DEVICE_ID:
+ push();
+ new_field(SUBID);
+ return true;
+ case SUBID:
+ switch (ci_memory.field_position) {
+ case 0:
+#ifdef DEBUG
+ if (byte != 0x0d) {
+ while(true); // Non-MIDI-CI Universal SysEx Sub ID 1.
+ }
+#endif
+ next_in_field();
+ return true;
+ case 1:
+ push()
+ new_field(VERSION);
+ return true;
+ }
+ case VERSION:
+#ifdef DEBUGp
+ if (byte > 0x01) {
+ while(true); // Unsupported future MIDI-CI Message Version/Format.
+ }
+#endif
+ new_field(SOURCE);
+ return true;
+ case SOURCE:
+ push_whole_field(4, DESTINATION);
+ case DESTINATION:
+ push_until(4, {
+ push();
+ switch (command_id()) {
+ case DISCOVERY_SUBID:
+ new_field(MANUFACTURER);
+ return true;
+ case REPLY_TO_DISCOVERY_SUBID:
+ new_field(MANUFACTURER);
+ return true;
+ case INVALIDATE_MUID_SUBID:
+ new_field(TARGET_MUID);
+ return true;
+ case ACK_SUBID:
+ new_field(ACK_ORIG_SUBID);
+ return true;
+ case NAK_SUBID:
+ new_field(NAK_ORIG_SUBID);
+ return true;
+ case INQUIRE_EXCHANGE_CAPABILITIES_SUBID:
+ new_field(MAX_SIMUL_EXCHANGE);
+ return true;
+ case REPLY_EXCHANGE_CAPABILITIES_SUBID:
+ new_field(MAX_SIMUL_EXCHANGE);
+ return true;
+ case INQUIRE_GET_SUBID:
+ case REPLY_GET_SUBID:
+ case INQUIRE_SET_SUBID:
+ case REPLY_SET_SUBID:
+ case SUBSCRIPTION_SUBID:
+ case REPLY_SUBSCRIPTION_SUBID:
+ case NOTIFY_SUBID:
+ new_field(REQUEST_ID);
+ return true;
+ default:
+#ifdef DEBUG
+ while(true); // Unsupported MIDI-CI command.
+#endif
+ return false;
+ }});
+
+ // Discovery messages.
+ case MANUFACTURER:
+ push_whole_field(3, FAMILY);
+ case FAMILY:
+ push_whole_field(2, MODEL);
+ case REVISION:
+ push_whole_field(4, CI_SUPPORTED);
+ case CI_SUPPORTED:
+ push_whole_field(1, MAX_SYSEX);
+ case MAX_SYSEX:
+ push_whole_field(4, INITIATOR_OUTPUT_PATH);
+ case INITIATOR_OUTPUT_PATH:
+ switch (command_id()) {
+ case DISCOVERY_SUBID:
+ if (source_muid() == our_muid) {
+ invalidate_our_muid();
+ our_muid = rand(); // TODO: user function for new MUID?
+ discovery();
+ reset();
+ return false;
+ }
+ discovery_reply(source_muid(), byte);
+ reset();
+ return false;
+ case DISCOVERY_REPLY_SUBID:
+ if (already_seen(source_muid())) { // TODO: already_seen and associated logging.
+ invalidate_muid(source_muid());
+ discovery();
+ reset();
+ return false;
+ }
+ default:
+#ifdef DEBUG
+ while(true); // Unexpected non-Discovery command id.
+#endif
+ reset();
+ return false;
+ }
+ // Invalidate.
+ case TARGET_MUID:
+ push_until(4, {
+ uint32_t to_invalidate = lsb_32bit_from(STACK_FIRST_NONCOMMON_IDX);
+ if (to_invalidate == our_muid) {
+ our_muid = rand();
+ discovery();
+ } else if (already_seen(to_invalidate)) {
+ invalidate_stored_data(to_invalidate); // TODO: to_invalidate; see above.
+
+ }
+ reset();
+ return false;
+ });
+ // ACK.
+ case ACK_ORIG_SUBID:
+ push_whole_field(1, ACK_CODE);
+ case ACK_CODE:
+ push_whole_field(1, ACK_DATA);
+ case ACK_DATA:
+ push_whole_field(1, ACK_DETAILS);
+ case ACK_DETAILS:
+ push_whole_field(5, ACK_TEXT_LENGTH);
+ case ACK_TEXT_LENGTH:
+ push_whole_field(2, ACK_TEXT);
+ case ACK_TEXT:
+ push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], {
+ // TODO:
+ });
+ reset();
+ return false;
+ // NAK.
+ case NAK_ORIG_SUBID:
+ push_whole_field(1, ACK_CODE);
+ case NAK_CODE:
+ push_whole_field(1, ACK_DATA);
+ case NAK_DATA:
+ push_whole_field(1, ACK_DETAILS);
+ case NAK_DETAILS:
+ push_whole_field(5, ACK_TEXT_LENGTH);
+ case NAK_LENGTH:
+ push_whole_field(2, ACK_TEXT);
+ case NAK_TEXT:
+ push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], {
+ // TODO:
+ });
+ reset();
+ return false;
+
+ // TODO: Property Exchange Capabilites
+ // Property Exchange.
+ case REQUEST_ID:
+ push_whole_field(1, HEADER_LEN);
+ case HEADER_LEN:
+ push_whole_field(2, HEADER_DATA);
+ case HEADER_DATA:
+ push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], {
+ // TODO:
+ });
+ case CHUNK_COUNT:
+ push_whole_field(2, THIS_CHUNK);
+ case THIS_CHUNK:
+ push_whole_field(2, PROPERTY_LEN);
+ case PROPERTY_LEN:
+ push_whole_field(2, PROPERTY_DATA);
+ case PROPERTY_DATA:
+ push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], {
+ JSON_Value *header = json_parse_string(ci_memory.stack + \
+ (ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top]);
+ switch (command_id()) {
+ case INQUIRE_GET_SUBID:
+#ifdef DEBUG
+ if (header->type != JSONObject) {
+ while(true); // Non-object JSON Value.
+ }
+#endif
+
+ // TODO: the spec kinda sucks, ngl. I'll get MIDI 1.0, and then return here.
+ case REPLY_GET_SUBID:
+ case INQUIRE_SET_SUBID:
+ case REPLY_SET_SUBID:
+ case SUBSCRIPTION_SUBID:
+ case REPLY_SUBSCRIPTION_SUBID:
+ case NOTIFY_SUBID:
+ }
+ });
+ }
+}
+#endif
diff --git a/controller/src/main.c b/controller/src/main.c
index 1bfe8b0..a5f47c4 100644
--- a/controller/src/main.c
+++ b/controller/src/main.c
@@ -1,6 +1,108 @@
#include "base_midi.h"
-#include "uart.h"
+
+#include "driverlib/uart.h"
+#include "driverlib/sysctl.h"
+#include "driverlib/gpio.h"
+#include "driverlib/pin_map.h"
+#include "inc/hw_uart.h"
+#include "inc/hw_ints.h"
+#include "inc/hw_memmap.h"
+
+
+void UART1IntHandler(void) {
+ UARTIntClear(UART1_BASE, UART_INT_RX);
+ while (UARTCharsAvail(UART1_BASE)) {
+ parse_midi_stream(UARTCharGet(UART1_BASE));
+ }
+}
+
+void UARTWrite(uint8_t byte) {
+ UARTCharPut(UART1_BASE, byte);
+}
+
+static void nothing0(void) {}
+static void nothing1(uint8_t) {}
+static void nothing2(uint8_t, uint8_t) {}
+static void nothing3(uint8_t, uint8_t, uint8_t) {}
+static bool quit_parsing(uint8_t) { // TODO: fix library parsers so this works.
+ return false;
+}
+
+
+bool note_on_flag = false;
+
+void button_handler(void) {
+ GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
+ if (!note_on_flag) {
+ note_on(0x02, 0x40, 0x40);
+ note_on_flag = true;
+ } else {
+ note_off(0x02, 0x40);
+ note_on_flag = false;
+ }
+}
+
+
int main() {
+ SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
+
+ // Initialize UART1.
+ SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
+
+
+ while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART1));
+
+ // Initialize the GPIO pins for UART1.
+ GPIOPinConfigure(GPIO_PC4_U1RX);
+ GPIOPinConfigure(GPIO_PC5_U1TX);
+ GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5);
+
+ // Configure the UART using the specifications found in the minipix_uart_interface.
+ // Cource code: word length - 8, 1 stop bit, no parity.
+ UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 31250, UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
+ UARTEnable(UART1_BASE);
+ UARTIntEnable(UART1_BASE, UART_INT_RX);
+ UARTIntRegister(UART1_BASE, UART1IntHandler);
+
+ // Set up MIDI 1.0 library to ignore basically everything.
+ ConsumerBehavior new_pfns = {.uart_write = UARTWrite,
+ .note_on_handler = nothing3,
+ .note_off_handler = nothing3,
+ .poly_key_handler = nothing3,
+ .control_change_handler = nothing3,
+ .program_change_handler = nothing2,
+ .all_sound_off_handler = nothing1,
+ .local_control_handler = nothing2,
+ .all_notes_off_handler = nothing1,
+ .poly_on_handler = nothing1,
+ .mtc_quarter_frame_handler = nothing2,
+ .song_position_pointer_handler = nothing2,
+ .song_select_handler = nothing1,
+ .tune_request_handler = nothing0,
+ .timing_clock_handler = nothing0,
+ .start_handler = nothing0,
+ .continue_handler = nothing0,
+ .stop_handler = nothing0,
+ .active_sensing_handler = nothing0,
+ .system_reset_handler = nothing0, // TODO: change.
+ .sysex_collector = quit_parsing,
+ .end_of_sysex_handler = nothing0,
+ /* .bulk_tuning_dump_request_handler = nothing, // TODO: change. */
+ /* .bulk_tuning_dump_handler = nothing, */
+ /* .single_note_tuning_change_handler = nothing, */
+ .unimplemented_universal_sysex_collector = quit_parsing};
+
+ midi_init(new_pfns);
+
+ // Interrupt whenever GPIO pin PF4 changes, due to SW1 button press.
+ SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
+ GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
+ GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU);
+ GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_RISING_EDGE);
+ GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
+ GPIOIntRegister(GPIO_PORTF_BASE, button_handler);
+ while(true); // Main application loop.
+
}
diff --git a/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf b/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf
new file mode 100644
index 0000000..0f2903c
--- /dev/null
+++ b/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf
Binary files differ
diff --git a/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf b/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf
new file mode 100644
index 0000000..e211238
--- /dev/null
+++ b/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf
Binary files differ