Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/**
* Micro-Professor MPF-1 Tape File generator based on Arduino.
*
* Arduino Sketch: MPF_1_TapeFileGen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* File name (as a 16bit number)
*/
word fileName = 0xcafe; // Edit: the file name (number)
/**
* Start address (as a 16bit number), this is the RAM address of the program's first byte.
*/
word startAddr = 0x1800; // Edit: the program start address
/**
* Payload data byte array.
* TODO: find a smarter way to add the file data content here.
*/
//byte data[] = { 0x3e, 0x05,
// 0x06, 0x04,
// 0x80,
// 0x32, 0x30, 0x10,
// 0x76 };
// MPF-1B Manual, EXAMPLE 1: Display 'HELP US', HALT when [STEP] is pressed.
byte data[] = {
0xdd, 0x21, 0x20, 0x18,
0xcd, 0xfe, 0x05,
0xfe, 0x13,
0x20, 0xf9,
0x76,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xae, 0xb5, 0x1f, 0x85, 0x8f, 0x37
};
//// MPF-1B Manual, EXAMPLE 2: Flash 'HELP US'
//byte data[] = {
// 0x21, 0x26, 0x18,
// 0xe5,
// 0xdd, 0x21, 0x20, 0x18,
// 0xdd, 0xe3,
// 0x06, 0x32,
// 0xcd, 0x24, 0x06,
// 0x10, 0xfb,
// 0x18, 0xf5,
// 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xae, 0xb5, 0x1f, 0x85, 0x8f, 0x37,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
//};
/**
* txDataPin Data to be sent to MPF-1 Tape input
*/
int txDataPin = 12; // Edit: the pin number
/**
* 2kHz signal period [microseconds]
*/
int period2kHz = 500; // [us]
/**
* 1kHz signal period [microseconds]
*/
int period1kHz = 1000; // [us]
/**
* Calculate checksum over payload data.
* Algorithm: summarize all bytes, keep modulo 256 result only (8 bits, no carry out)
* @param dataStream Payload data bytes.
* @param nBytes Number of payload data bytes.
* @return Checksum byte.
*/
byte calcChkSum(byte* dataStream, int nBytes)
{
byte sum = 0;
for (int i = 0; i < nBytes; i++)
{
sum = (sum + dataStream[i]) % 256;
}
return sum;
}
/**
* Generate one square wave at the TX data pin (@see txDataPin) with the given period.
* _____
* | |_____
* ¦ period ¦
* ¦<--------->¦
*
* @parameter periodMicros Period of the square wave in [microseconds].
*/
void wave(int periodMicros)
{
for (int i = 0; i < 2; i++)
{
digitalWrite(txDataPin, ((i % 2) == 0) ? HIGH : LOW);
delayMicroseconds(periodMicros / 2);
}
}
/**
* Send one bit.
* Generate 8 2kHz cycles and 2 1kHz cycles for a 0, or
* generate 4 2kHz cycles and 4 1kHz cycles for a 1.
* @param value {0|1}
*/
void sendBit(boolean value)
{
for (int i=0; i < (value ? 4 : 8); i++)
{
wave(period2kHz);
}
for (int i=0; i < (value ? 4 : 2); i++)
{
wave(period1kHz);
}
}
/**
* Send Lead Sync signal (4000 1kHz cycles, 4secs).
*/
void sendLeadSync()
{
for (int i = 0; i < 4000; i++)
{
wave(period1kHz);
}
}
/**
* Send Mid Sync signal (4000 2kHz cycles, 2secs).
*/
void sendMidSync()
{
for (int i = 0; i < 4000; i++)
{
wave(period2kHz);
}
}
/**
* Send Tail Sync signal (4000 2kHz cycles, 2secs).
*/
void sendTailSync()
{
sendMidSync();
}
/**
* Send one data byte (including 1 start & 1 stop bit).
* Principle: LSB first, MSB last, using [@see sendBit()].
* @param dByte The data byte to be sent.
*/
void sendDataByte(byte dByte)
{
sendBit(false); // start bit
for (int i = 0; i < 8; i++)
{
sendBit((((dByte >> i) & 0x01) > 0) ? true : false);
}
sendBit(true); // stop bit
}
/**
* Send a number of bytes as a data stream.
* Principle: byte by byte using [@see sendDataByte()].
* @param sData Pointer to the first data byte.
* @param nBytes Number of bytes to be sent.
*/
void sendDataStream(byte* sData, int nBytes)
{
for (int i = 0; i < nBytes; i++)
{
sendDataByte(sData[i]);
}
}
/**
* Send one data word (16 bits).
* To be used for 16bit Addresses or File Number.
* Principle: Low Byte first, High byte last, using [@see sendDataStream()].
* @param dataWord The 16bit word to be sent.
*/
void sendWord(word dataWord)
{
byte dataStream[] = { (dataWord & 0x00ff), ((dataWord & 0xff00) >> 8) };
sendDataStream(dataStream, 2);
}
/**
* Send the whole file.
* Principle:
* - [@see sendLeadSync()]
* - send file name using [@see sendWord()]
* - send start address [@see startAddr]
* - send end address (calculated from start address and data length)
* - send checksum byte (calculated by [@see calcChkSum()] over the payload data)
* - [@see sendMidSync()]
* - send the payload data using [@see sendDataStream()]
* - [@see sendTailSync()]
*/
void sendFile()
{
int nbrOfDataBytes = sizeof(data) / sizeof(byte);
sendLeadSync();
sendWord(fileName);
sendWord(startAddr);
word endAddr = startAddr + nbrOfDataBytes - 1;
sendWord(endAddr);
byte chkSum = calcChkSum(data, nbrOfDataBytes);
sendDataStream(&chkSum, 1);
sendMidSync();
sendDataStream(data, nbrOfDataBytes);
sendTailSync();
}
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin Tx Data as an output.
pinMode(txDataPin, OUTPUT);
}
void loop()
{
// repeatedly send the file
sendFile();
delay(1000); // wait 1s
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment