Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
SIMPL- with EDSAC virtual machine grafted in!
// SIMPL Interpreter for MSP430 Launchpad MSP430G2553 - with EDSAC Simulator grafted on!
// An experiment in minimal computing simulated on an MSP430 launchpad
// Really a dreadful hack, done on a very hot day in Greece - I'll get my coat.......
// SIMPL provides a shell to allow basic communications with EDSAC simulator
// Teraterm is a convenient means of sending files to the SIMPL machine
// 0a resets the program counter to zero
// d dumps out the first 32 locations of the EDSAC memory
// Having set the program counter with 0a, you can then use Tera term to send a file of instructions
// but they are in reverse polish notation - so number first - then operator
/* Here's an example of an EDSAC asm file
36K Clear the accumulator - location 36 is trash
32A ADD in contents of location 32
33A ADD in contents of location 33
34K STORE in location 34
0E Jump back to location zero if ACC>=0
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
0A
2A
3A
5A
6A
0A
0A
0A
0A
0A
0A
*/
#define RXD BIT1 // Receive Data (RXD) at P1.1
#define TXD BIT2 // Transmit Data (TXD) at P1.2
#define RED 0x20 // Red LED is on Bit 6
#define GREEN 0x01 // Green LED is on Bit 0
#define POWER_PIN BIT0 // we power the 23K256 chip from one of our GPIO pins
#define SS_PIN BIT4 // CS , active low
#define DEBUG_PIN BIT0 // toggle on and off marking time to write
#define DUMMY_BYTE 0xFF // byte we send when we just want to read slave data
#define bufRead(addr) (*(unsigned char *)(addr))
#define bufWrite(addr, b) (*(unsigned char *)(addr) = (b))
#define bit0 0x01 // 1
#define bit1 0x02 // 2
#define bit2 0x04 // 4
#define bit3 0x08 // 8
#define bit4 0x10 // 16
#define bit5 0x20 // 32
#define bit6 0x40 // 64
#define bit7 0x80 // 128
static inline uint8_t RWData(uint8_t value);
void loop();
#define powerOn P1OUT |= POWER_PIN
#define powerOff P1OUT &= ~POWER_PIN
#define ssSelect P1OUT &= ~SS_PIN
#define ssDeselect P1OUT |= SS_PIN
#define delay_1ms __delay_cycles(16000)
#define SR_WRITE_STATUS 0x01
#define SR_WRITE 0x02
#define SR_READ 0x03
#define SR_READ_STATUS 0x05
#define BYTES_TO_STREAM 32768 // should be less <= 32768
#define PATTERN_BYTE_VALUE 65
int spi_rx_data = 0 ;
//--------------------------------------------------------------------------------
// 23K256 Serial Ram functions
uint8_t SR_getMode(void) { // Read the Mode of the 23K256
ssSelect; // select
RWData(SR_READ_STATUS); // 0x05
uint8_t mode = RWData(DUMMY_BYTE);
ssDeselect; // de-select
return mode;
}
void SR_setMode(uint8_t mode) { // Write Mode to 23K256
ssSelect;
RWData(SR_WRITE_STATUS); // 0x01
RWData(mode);
ssDeselect;
}
static inline void SR_writestream(uint16_t addr) { // Write a stream to 23K256
ssDeselect; // deselect if we are active
ssSelect;
RWData(0x02); // Send command
RWData(addr >> 8); // Send upper address
RWData(addr); // Send lower address
}
static inline void SR_readstream(uint16_t addr) { // Read a stream from 23K256
ssDeselect;
ssSelect;
RWData(0x03); // Send command
RWData(addr >> 8); // Send upper address
RWData(addr); // Send lower address
}
//-----------------------------------------------------------------
// SPI Send / Receive
static inline uint8_t RWData(uint8_t value)
{
UCB0TXBUF = value;
while (!(IFG2 & UCB0TXIFG)); // wait for buffer ready
{ }
while (!(IFG2 & UCB0RXIFG)); // USCI_A0 RX Received?
spi_rx_data = UCB0RXBUF; // Store received data
return spi_rx_data;
}
//-----------------------------------------------------------------
// This character array is used to hold the User's words
char array[2][8]; // Allocate a storage array in memory
/*
= { // Define a 26 x 48 array for the colon definitions
{"6d40{h1106ul1106u}"}, // Musical tones A - G
{"6d45{h986ul986u}"},
{"6d51{h929ul929u}"},
{"6d57{h825ul825u}"},
{"6d64{h733ul733u}"},
{"6d72{h690ul691u}"},
{"6d81{h613ul613u}"},
{"_Hello World, and welcome to SIMPL_"},
{"5{ABC}"},
{""},
{""},
{""},
{"_This is a test message - about 48 characters_"}
};
*/
int a = 0; // integer variables a,b,c,d
int b = 0;
int c = 0;
int p = 6; // p is used to denote the digital port pin for I/O operations
long x = 0; // Three gen purpose variables for stack & math operations
unsigned long y = 0;
unsigned int z = 0;
unsigned int ADC_value=0;
unsigned char in_byte;
int len = 32; // the max length of a User word
long old_millis=0;
long new_millis=0;
char name;
char* parray;
char buf[64];
char* addr;
unsigned int num = 0;
unsigned int num_val = 0;
long i;
long j;
long k;
char num_buf[11]; // long enough to hold a 32 bit long
int decade = 0;
char digit = 0;
int code = 0;
int mem = 0; // pointer to memory of EDSAC virtual machine
//---------------------------------------------------------------------------------
// Variables used in EDSAC CPU model
//MSP430 only has 512 bytes of RAM - so memory has to be limited to
unsigned short m[0x0080]; // 126 words of RAM = 256 bytes
static unsigned long time_gone = 0;
static unsigned short t;
static unsigned short A;
static unsigned short R;
static unsigned short n; //2nd item on stack
static unsigned short d[0x10]; // data stack
static unsigned short r[0x10]; // return stack
static unsigned short pc; // program counter, counts 16 bit words (cells)
static unsigned char dsp, rsp; // point to top entry
// static unsigned short* memory; // ram
static int sx[4] = { 0, 1, -2, -1 }; // 2-bit sign extension
unsigned int insn; // the 16 bit encoded instruction
unsigned int _t;
unsigned int _pc;
unsigned int target; // 13 bit target address for jumps and calls
unsigned int next_t;
long loop_count = 0;
int count = 0;
int murray = 0;
int instr = 0;
//---------------------------------------------------------------------------------
void setup()
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
// BCSCTL1 = CALBC1_1MHZ; // Set DCO
// DCOCTL = CALDCO_1MHZ;
P1DIR = BIT0 + BIT6; // P1.0 and P1.6 are the red+green LEDs
P1OUT = BIT0 + BIT6; // All LEDs off
uart_init(); // Initialise the UART for 96000 baud
spi_init();
P1SEL |= BIT3; // ADC input pin P1.3
ConfigureAdc();
setup_EDSAC(); // initialise the RAM and registers on EDSAC cpu model
parray = &array[0][0]; // parray is the pointer to the first element
uart_puts((char *)"MSP430 SIMPL EDSAC SIMULATOR\n\r"); // send banner message
}
//-------------------------------------------------------------------------------
void regs()
{
// Now print out the registers: PC, Instruction, Memory Address, Accumulator and Multiplier register
uart_puts((char *)"PC= ");
printlong(pc);
uart_puts((char *)" ");
uart_puts((char *)"INSN= ");
printlong(insn);
uart_puts((char *)" ");
uart_puts((char *)"ADDR= ");
printlong(n);
uart_puts((char *)" ");
uart_puts((char *)"MEM= ");
printlong(m[n]);
uart_puts((char *)" ");
uart_puts((char *)"ACC= ");
printlong(A);
uart_puts((char *)" ");
uart_puts((char *)"MLT= ");
printlong(R);
uart_puts((char *)" ");
}
void loop()
{
/*
execute(m[pc]) ; // get the next instruction and execute it using the cpu model
crlf();
for(j=0; j<=1000000; j++) // A delay of about 500mS to slow down output
{k = j;}
*/
//spi_mode();
textRead(buf, 64); // This is the endless while loop which implements the interpreter - just 3 simple functions
textChk(buf); // check if it is a : character for beginning a colon definition
textEval(buf);
}
// End of main loop
// ------------------------------------------------------------------------------
// Initialisation Routines
//------------------------------------------------------------------------------------
// UART Routines
void uart_init(void)
{
P1SEL = RXD + TXD;
P1SEL2 = RXD + TXD;
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 138; // 16MHz 115200
UCA0BR1 = 0; //
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // Initialize USCI state machine
}
void spi_init(void)
{
//----------------------------------------------------------------------------
// Configure the Clock for 16 MHz
BCSCTL1 = CALBC1_16MHZ;
DCOCTL = CALDCO_16MHZ;
//---------------------------------------------------------------------
// recommended procedure: set UCSWRST, configure USCI, configure ports, activate
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Set UCSWRST
UCB0CTL1 = UCSWRST;
//---------------------------------------------------------------------
// Configure USCI B0
UCB0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC; // 3-pin, 8-bit SPI master
UCB0CTL1 |= UCSSEL_2; // SMCLK
UCB0BR0 |= 2; // 8 MHz SPI-CLK
UCB0BR1 = 0;
//UCB0MCTL = 0;
//---------------------------------------------------------------------
// Configure Ports
P1SEL |= BIT5 + BIT6 + BIT7;
P1SEL2 |= BIT5 + BIT6 + BIT7;
P1DIR |= BIT0 + BIT4 + BIT5 | BIT7;
//---------------------------------------------------------------------
// activate
UCB0CTL1 &= ~UCSWRST;
}
unsigned char uart_getc()
{
while (!(IFG2&UCA0RXIFG)); // USCI_A0 RX buffer ready?
return UCA0RXBUF;
}
void uart_putc(unsigned char c)
{
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = c; // TX
}
void uart_puts(const char *str) // Output a string
{
while(*str) uart_putc(*str++);
}
// Print a 16 bit long int number
static void printlong(unsigned long num)
{
if (num / (unsigned short)10 != 0) printlong(num / (unsigned short)10);
uart_putc((char)(num % (unsigned short)10) + '0');
return;
}
// Print a CR-LF
void crlf(void) // send a crlf
{
uart_putc(10);
uart_putc(13);
}
//-------------------------------------------------------------------------------------
// ADC Configuration
// Function containing ADC set-up
void ConfigureAdc(void)
{
ADC10CTL1 = INCH_3 + ADC10DIV_3 ; // Channel 3, ADC10CLK/3
ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE; // Vcc & Vss as reference, Sample and hold for 64 Clock cycles, ADC on, ADC interrupt enable
ADC10AE0 |= BIT3; // ADC input enable P1.3
}
int ADC_Read(void)
{
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
ADC_value = ADC10MEM;
return ADC_value;
}
//-------------------------------------------------------------------------------------
void delay_mS(int j){
volatile unsigned long i;
while(j)
{
i = 42; // Delay
do (i--);
while (i != 0); // busy waiting (bad)
j--;
}
}
//-------------------------------------------------------------------------------
// Language Functions - Words
// ------------------------------------------------------------------------------
// Read the character into the buffer
void textRead (char *p, byte n) {
byte i = 0;
while (i < (n-1)) {
char ch = uart_getc();
if (ch == '\r' || ch == '\n') break;
if (ch >= ' ' && ch <= '~') {
*p++ = ch;
i++;
}
}
*p = 0;
}
// ---------------------------------------------------------------------------------------------------------
void textChk (char *buf) // Check if the text starts with a colon and if so store in user's word RAM array parray[]
{
if (*buf == ':') {
char ch;
int i =0;
while ((ch = *buf++)){
if (ch == ':') {
uart_putc(*buf); // get the name from the first character
uart_putc(10);
uart_putc(13);
name = *buf ;
buf++;
}
bufWrite((parray + (len*(name-65) +i)),*buf);
i++;
}
x = 1;
}
}
//------------------------------------------------------------------------------------------------
void setup_EDSAC(void) {
// Load up a simple count program into first 7 locations of memory
m[0] = 0x9824;
m[1] = 0x9824;
m[2] = 0x0021; // ADD (20)
m[3] = 0x0022; // ADD (21)
m[4] = 0x9822; // STORE in (22) and clear ACC
m[5] = 0x3820; // Load R with (20)
m[6] = 0xA822; // Multiply and add contents of 22
m[7] = 0x2000; // Jump to 0000
m[8] = 0x0000;
m[9] = 0x0000;
m[10] = 0xA000;
m[11] = 0xB000;
m[12] = 0xC000;
m[13] = 0xD000;
m[14] = 0xE000;
m[15] = 0xF000;
m[16] = 0x0100;
m[17] = 0x1100;
m[18] = 0x2100;
m[19] = 0x3100;
m[20] = 0x0003;
m[21] = 0x0004;
m[22] = 0x0002;
m[23] = 0x0000;
m[24] = 0x8100;
m[25] = 0x9100;
m[26] = 0xA100;
m[27] = 0xB100;
m[28] = 0xC100;
m[29] = 0xD100;
m[30] = 0xE100;
m[31] = 0xF100;
m[32] = 0x0002; // Use this location as a counter
m[33] = 0x0003;
m[34] = 0x0005;
m[35] = 0x0007;
m[36] = 0x0009;
m[37] = 0x5200;
m[38] = 0x6200;
m[39] = 0x0002;
m[40] = 0x0003;
//----------------------------------------------------------------------------------
// Clear the rest of the first 255 bytes of memory
for(i=41; i<=127; i++)
{
m[i] = 0; // clear memory
}
for(i=0; i<=16; i++) // clear data stack and return stack
{
d[i] = 0;
r[i] = 0;
}
pc = 0; // Clear program counter
A = 0 ; // Clear accumulator
R = 0 ; // Clear the multiplier register
}
// End of Setup_EDSAC
//-------------------------------------------------------------------------
// EDSAC CPU Model
// ------------------------------------------------------------------------
static void execute(int instruction) // This is the EDSAC CPU model
// The Alpha code is held in the top 5 bits of the instruction
// The bottom 11 bits hold the address
{
insn = (instruction & 0xF800)/2048 ;
_pc = pc + 1;
n = (instruction & 0x7FF); // n is the memory address field - lower 11 bits
switch (insn)
{
case 0: A += m[n] ; break; // ADD A
case 1: A -= m[n] ; break; // SUB Subtract
case 2: A += (m[n] & R) ; break; // COL C
case 3: m[n] = A ; break; // DEP Deposit D -no clear
case 4: if(A>=0) {_pc = n; } break; // JGT E
case 5: break; // VER F
case 6: if(A<0) {_pc = n; } ; break; // JLT E
case 7: R += m[n] ; break; // CPY H - Load R register
case 8: break; // INP I
case 9: A = m[n]>>1 ; break; // Right Shift
case 10: m[n] = A; A=0 ; break; // Transfer and clear
case 11: A = m[n]<<1 ; break; // LSH L
case 12: A += (m[n] * R) ; break; // Multiply and ADD
case 13: A -= m[n] * R ; break; // Multiply and Subtract- N
case 14: break; // OUT O
case 15: break; // PUT P
case 16: break;
case 17: A = m[n]>>1 ; break; // RHS R
case 18: A -= m[n] ; break; // SUB S
case 19: m[n] = A; A=0 ; break; // TRC T
case 20: m[n] = A ; break; // UPD U
case 21: A += (m[n] * R) ; break; // ML+ V
case 22: break;
case 23: break; // NOP X
case 24: break; // RND Y
case 25: break; // END Z
case 26: break;
case 27: break;
case 28: break;
case 29: break;
case 30: break;
case 31: break;
}
pc = _pc;
instruction = m[pc];
next_t = _t;
}
// End of CPU model
// ------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
void textEval (char *buf) {
char *loop;
char *start;
char ch;
unsigned long k = 0;
while ((ch = *buf++)) { // Is it a number?
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
x = ch - '0';
while (*buf >= '0' && *buf <= '9') {
x = x*10 + (*buf++ - '0'); // If a number store it in "x"
}
break;
//-------------------------------------------------------------------------------
// User Words - recreate the EDSAC instruction set
case 'A': // Point the interpreter to the array containing the words
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
// textEval(parray + (len*(ch-65))); // Evaluate and execute the User's expression from RAM
// printlong(ch);
// crlf();
code = ((ch-65)*2048)+x; // create the instruction
m[mem] = code; // store to memory
mem++; // next location
// printlong(code);
// crlf();
break;
//---------------------------------------------------------------------------------
// Primitive and User vocabulary defined in this section
// Timing & Printing Group
case 'p':
printlong(x);
break;
case 'q': // print integer with crlf
printlong(x);
crlf();
break;
case 'a':
mem = x; // set the memory location to start of mem
break;
case 'b':
// printlong(millis());
crlf();
break;
case 'c':
// printlong(micros());
crlf();
break;
case 'd': // dissassemble
for(i=0; i<=32; i++)
{
insn = ((m[i] & 0xF800)/2048)+65 ;
n = (m[i] & 0x7FF);
uart_putc(insn);
uart_puts((char *)" ");
uart_puts((char *)"ADDR= ");
printlong(n);
uart_puts((char *)" ");
crlf();
}
break;
case 's':
execute(m[pc]);
regs();
break;
case '_': // Print the string enclosed between underscores _Hello_
while ((ch = *buf++) && ch != '_') {
uart_putc(ch);
}
uart_putc(10);
break;
//----------------------------------------------------------
// Arithmetic Group
case '+':
x = x+y;
break;
case '-':
x = x-y;
break;
case '*':
// x = x*y;
break;
case '/':
// x = x/y;
break;
case '%':
// x = x%y;
break;
case 'x':
x = x + 1;
break;
case 'y':
y = y + 1;
break;
//--------------------------------------------------------------------
// Logical Group - provides bitwise logical function between x and y
case '&':
x = x&y; // Logical AND
break;
case '|':
x = x|y; // Logical OR
break;
case '^':
x = x^y; // Logical XOR
break;
case '~':
x = !x; // Complement x
break;
case ' ': // Transfer x into second variable y
k=y; // Transfer loop counter into k
y= x;
break;
case '$': // Load x with the ASCII value of the next character i.e. 5 = 35H or 53 decimal
x=*(buf-2);
break;
// ----------------------------------------------------------------------
// Memory Group
case '!': // store
y = x;
break;
case '@': // Fetch
x = y;
break;
/*
case 'r': // read a byte from RAM
bite = bufRead(x); // x = address
x = bite;
uart_putc(x); // print the character
break;
case 'q': // read a block of x bytes of RAM at address y
for (int i=0; i<x; i++) {
bite = bufRead(y+i); // read the array
uart_putc(bite); // print the character to the serial port
}
break;
case 'w': // write a byte to RAM address in y, data in x
bufWrite(y,x);
break;
*/
//--------------------------------------------------------------------
// Comparison Test and conditional Group
case '<':
if(x<y){x=1;} // If x<y x= 1 - can be combined with jump j
else x=0;
break;
case '>':
if(x>y){x=1;} // If x>y x= 1 - can be combined with jump j
else x=0;
break;
case 'j': // test if x = 1 and jump next instruction
if(x==1){*buf++;}
break;
//----------------------------------------------------------------------------------
// Print out the current word list
case '?': // Print out all the RAM
parray = &array[0][0]; // reset parray to the pointer to the first element
for (int j = 0; j<26; j++) {
uart_putc(j+65); // print the caps word name
uart_putc(32); // space
for (int i=0; i<len; i++) {
in_byte = bufRead( parray + (j *len )+i); // read the array
uart_putc(in_byte); // print the character to the serial port
}
crlf();
}
for(int i = 0; i <11; i++) // add some spaces to make it more legible on the page
{
crlf();
}
break;
//----------------------------------------------------------------------------------------------------
// Conditional Code branch
case '[': // The start of a condition test
k = x;
start = buf; // remember the start position of the test
while ((ch = *buf++) && ch != ']') { // get the next character into ch and increment the buffer pointer *buf - evaluate the code
}
case ']':
if (x) { // if x is positive - go around again
buf = start;
}
break;
//--------------------------------------------------------------------------
// Case Statement Selection
// Select some code from a list separated by commas
//5(0p,1p,2p,3p,4p,5p,6p) should select 5 and print it
case '(':
k = x; // copy x to use as the "phrase counter"
// decrement k to see whether to interpret or not
while (k)
{
ch = *buf++;
if (ch == ',')
{ k--;}
}
break;
case ',':
k--; //
while (k<0) // k < 0 so skip the remaining entries in the list
{
ch = *buf++; // skip the remaining characters
if (ch == ')') {break;}
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------
// Analogue and Digital Input and Output Group - these add heavily to total - need to be converted to MSP430
// case 's':
// x = ADC_Read(); // Adds 38 bytes
// break;
/*
case 'a':
analogWrite(d,x); // adds 340 bytes
break;
case 'i':
x = digitalRead(d); // adds 100 bytes
break;
case 'o':
digitalWrite(d, x%2); // adds 18 bytes
break;
*/
//-------------------------------------------------------------------
// Delays Group
case 'm':
delay_mS(x);
break;
case 'u':
// delayMicroseconds(x);
break;
//---------------------------------------------------------------------
case '{':
k = x;
loop = buf;
while ((ch = *buf++) && ch != '}') {
}
case '}':
if (k) {
k--;
buf = loop;
}
break;
case 'k':
x = k;
break;
// ----------------------------------------------------------------------
// Launchpad LED group
case 'w':
{
P1OUT |= BIT0;
}
break;
case 'r':
{
P1OUT &= ~BIT0;
}
break;
case 'h':
{
P1OUT |= BIT6;
}
break;
case 'l':
{
P1OUT &= ~BIT6;
}
break;
// ----------------------------------------------------------------------
}
}
}
//-----------------------------------------------------------------------------
// SPI RA Test routine
//---------------------------------------------------------------------
int spi_mode()
{
// toggle the power for the 23K256
powerOn;
ssDeselect;
delay_1ms;
while (1) {
uint8_t chipMode;
// make sure there is a 23K256 chip and that
// is wired properly and in sequential mode
chipMode = SR_getMode();
if (chipMode != 0x41) {
SR_setMode(0x41);
} else {
while (1) {
spi_test();
}
}
}
}
//loop - write a test pattern and read it back
void spi_test()
{
uint16_t i;
uint8_t storedValue = 0;
P1OUT |= DEBUG_PIN; // mark start of write for measurement with oscope
SR_writestream(0); // start writing at address 0
for (i = 0; i < BYTES_TO_STREAM; ++i) {
RWData(PATTERN_BYTE_VALUE);
}
P1OUT &= ~DEBUG_PIN; // mark end of write for measurement with oscope
// verify the bytes we wrote were stored and retreived properly
SR_readstream(0); // start reading at address 0
for (i = 0; i < BYTES_TO_STREAM; ++i) {
storedValue = RWData(DUMMY_BYTE);
uart_putc(storedValue);
// verify our test pattern
if (storedValue != PATTERN_BYTE_VALUE) {
// if values aren't the same an error occurred,
// turn off all leds, then sit and spin
P1OUT &= ~BIT6;
P1OUT &= ~DEBUG_PIN;
while (1) {
;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment