Skip to content

Commit

Permalink
Merge pull request #11 from anarkiwi/reset
Browse files Browse the repository at this point in the history
Implement reset/version, switch to fixed size commands, enable config of through mode, document
  • Loading branch information
anarkiwi committed Sep 22, 2020
2 parents 9aa8a4d + dc15e03 commit 3ec4d87
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 22 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ implementation via the C64's user port, without requiring the
C64 to handle an interupt (though the C64 can choose to receive
one - see below).

The current Vessel implementation is effectively a virtual UART with
255 byte transmit and receive buffers. Future versions of the driver
will allow the C64 to offload some MIDI parsing/processing to the driver
(for example, filter out unwanted MIDI messages).

The C64 provides a shift-register based TTL compatible serial port,
implemented on a CIA 6526/8520, via the user port. This is
inconvenient and slow as software must work with one bit at a time.
Expand Down Expand Up @@ -62,7 +57,22 @@ from the MIDI port).
The C64 has no way to know that Vessel has successfully read or
written a byte, we simply have to keep up with its schedule. An
Arduino Uno isn't fast enough, but a Due is (with time for other tasks
while PA2 changes state input/output.
while PA2 changes state input/output.


C64 to Vessel commands
----------------------

The C64 can send Vessel a command, by sending byte 0xFD (not used by MIDI),
and then a command, and then a fixed number of data bytes (depending on the
command - unless otherwise specified, a command is followed by 0 data bytes).

By default, NMI on external input is off, all MIDI channels are masked (only
channel-less messages will be sent to the C64), and MIDI through is disabled.

* 0x00 HH LL CN: Config: channel mask high byte (HH), low byte (LL), and config byte (CN). Config byte bit 0 enables NMI, bit 1 enables MIDI through.
* 0x01: Reset. Vessel will reset to default config,
* 0x02: Version. Vessel will return a version string (currently ASCII "vessel0").


Upgrading firmware
Expand Down
81 changes: 65 additions & 16 deletions vessel.ino
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
#include <Arduino.h>
#include <MIDI.h>

const char versionStr[] = {
0x16, // V
0x05, // E
0x13, // S
0x13, // S
0x05, // E
0x0C, // L
0x30, // 0
0x30, // 0
};

// TODO: custom optimized PIOC handler for PC2 int only.
// add void PIOC_Handler (void) __attribute__ ((weak)); to WInterrupts.c
void PIOC_Handler(void) {
Expand All @@ -21,7 +32,9 @@ void PIOC_Handler(void) {
}
}

const byte vesselCmd = 0xf9;
// The C64 can send us a command by sending a reserved cmd byte, then another byte specifying the command number, then any other bytes the command requires.
// Each command has a fixed number of bytes expected after the command byte.
const byte vesselCmd = 0xfd;

#define PINDESC(pin) g_APinDescription[pin].ulPin
#define PINPPORT(pin, PPIO) g_APinDescription[pin].pPort->PPIO
Expand Down Expand Up @@ -96,9 +109,13 @@ volatile bool nmiEnabled = false;

void (*cmds[])(void) = {
configCmd,
resetCmd,
versionCmd,
};
const byte cmdLens[] = {
3,
0,
0,
};
byte const *cmdLen = cmdLens;
volatile byte *cmdByte = inCmdBuf + 1;
Expand Down Expand Up @@ -146,8 +163,10 @@ struct VesselSettings : public midi::DefaultSettings {

MIDI_CREATE_CUSTOM_INSTANCE(FakeSerial, fs, MIDI, VesselSettings);

#define NMI_SEND(x) { if (nmiEnabled) { flagPin.write(HIGH); } MIDI.x; }
#define NMI_CHANNEL_SEND(x) if (channelMasked(channel)) { NMI_SEND(x) }
#define NMI_WRAP(x) { if (nmiEnabled) { flagPin.write(HIGH); } x; }
#define NMI_MIDI_SEND(x) NMI_WRAP(MIDI.x)

#define NMI_CHANNEL_SEND(x) if (channelMasked(channel)) { NMI_MIDI_SEND(x) }

inline void handleNoteOn(byte channel, byte note, byte velocity) {
NMI_CHANNEL_SEND(sendNoteOn(note, velocity, channel))
Expand Down Expand Up @@ -178,39 +197,39 @@ inline void handlePitchBend(byte channel, int bend) {
}

inline void handleTimeCodeQuarterFrame(byte data) {
NMI_SEND(sendTimeCodeQuarterFrame(data))
NMI_MIDI_SEND(sendTimeCodeQuarterFrame(data))
}

inline void handleSongPosition(unsigned int beats) {
NMI_SEND(sendSongPosition(beats))
NMI_MIDI_SEND(sendSongPosition(beats))
}

inline void handleSongSelect(byte songnumber) {
NMI_SEND(sendSongSelect(songnumber))
NMI_MIDI_SEND(sendSongSelect(songnumber))
}

inline void handleTuneRequest() {
NMI_SEND(sendTuneRequest())
NMI_MIDI_SEND(sendTuneRequest())
}

inline void handleClock(void) {
NMI_SEND(sendClock())
NMI_MIDI_SEND(sendClock())
}

inline void handleStart(void) {
NMI_SEND(sendStart())
NMI_MIDI_SEND(sendStart())
}

inline void handleContinue(void) {
NMI_SEND(sendContinue())
NMI_MIDI_SEND(sendContinue())
}

inline void handleStop(void) {
NMI_SEND(sendStop())
NMI_MIDI_SEND(sendStop())
}

inline void handleSystemReset(void) {
NMI_SEND(sendSystemReset())
NMI_MIDI_SEND(sendSystemReset())
}

inline bool channelMasked(byte channel) {
Expand Down Expand Up @@ -329,21 +348,50 @@ inline void readCmdByte() {
if (*cmdByte > maxCmd) {
isrMode = ISR_INPUT;
} else {
isrMode = ISR_INPUT_CMD_DATA;
cmdLen = cmdLens + *cmdByte;
if (*cmdLen) {
isrMode = ISR_INPUT_CMD_DATA;
} else {
runCmd();
}
}
}

inline void versionCmd() {
NMI_WRAP({
for (byte i = 0; i < sizeof(versionStr); ++i) {
fs.write(versionStr[i]);
}})
flagPin.write(LOW);
}

inline void resetCmd() {
receiveChannelMask = 0;
nmiEnabled = false;
MIDI.turnThruOff();
resetWritePtrs();
}

inline void configCmd() {
receiveChannelMask = (inCmdBuf[2] << 8) + inCmdBuf[3];
nmiEnabled = inCmdBuf[4] & 1;
byte configFlags = inCmdBuf[4];
nmiEnabled = configFlags & 1;
if (configFlags & 2) {
MIDI.turnThruOn();
} else {
MIDI.turnThruOff();
}
}

inline void runCmd() {
isrMode = ISR_INPUT;
cmds[*cmdByte]();
}

inline void readCmdData() {
inCmdBuf[++inCmdBufReadPtr] = getByte();
if (inCmdBufReadPtr > *cmdLen) {
isrMode = ISR_INPUT;
cmds[*cmdByte]();
runCmd();
}
}

Expand Down Expand Up @@ -438,6 +486,7 @@ void setup() {
MIDI.setHandleTimeCodeQuarterFrame(handleTimeCodeQuarterFrame);
MIDI.setHandleSongPosition(handleSongPosition);
MIDI.setHandleSongSelect(handleSongSelect);
resetCmd();
attachInterrupt(digitalPinToInterrupt(C64_PC2), dummyIsr, RISING);
detachInterrupt(digitalPinToInterrupt(C64_PA2));
while (!inInputMode()) { };
Expand Down

0 comments on commit 3ec4d87

Please sign in to comment.