Created
January 18, 2017 18:05
-
-
Save anonymous/4af8e1d10e447fa1755873e8ce74f728 to your computer and use it in GitHub Desktop.
SIMPL in MSP430 assembly language 582 bytes -
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;------------------------------------------------------------------------------- | |
; MSP430 Assembler Code Template for use with TI Code Composer Studio | |
; | |
; | |
;------------------------------------------------------------------------------- | |
.cdecls C,LIST,"msp430.h" ; Include device header file | |
;------------------------------------------------------------------------------- | |
.def RESET ; Export program entry-point to | |
; make it known to linker. | |
;------------------------------------------------------------------------------- | |
; Variables | |
;------------------------------------------------------------------------------- | |
.sect "vars" | |
.bss parray, 256 | |
.bss x, 2 | |
.bss name, 2 | |
;------------------------------------------------------------------------------- | |
;------------------------------------------------------------------------------- | |
.text ; Assemble into program memory. | |
.retain ; Override ELF conditional linking | |
; and retain current section. | |
.retainrefs ; And retain any sections that have | |
; references to current section. | |
;------------------------------------------------------------------------------- | |
; This implements the SIMPL interpreter is MSP430 assembly Language | |
; Register Usage | |
; R0 MSP430 PC Program Counter | |
; R1 MSP430 SP Stack Pointer | |
; R2 MSP430 SR Status Register | |
; R3 MSP430 Constant Register | |
; R4 SIMPL - Top of Stack TOP | |
; R5 SIMPL - 2nd in Stack 2ND | |
; R6 | |
; R7 | |
; R8 | |
; R9 | |
; R10 SIMPL - Instruction Pointer IP | |
; R11 | |
; The input buffer - start is at 0x0300, which is pointed to by R14 | |
; R12 receives the character from the uart_get_c routine and puts in the buffer, pointed to by R14 | |
; R14 is the current character position in the input buffer | |
; R15 is a counter to ensure that we don't exceed 64 characters in input buffer | |
;------------------------------------------------------------------------------- | |
; textRead | |
; ------------------------------------------------------------------------------ | |
; Get a character from the UART and store it in the input buffer starting at 0x0300 | |
; Register Usage | |
; The input buffer - start is at 0x0300, which is pointed to by R14 | |
; R12 receives the character from the uart_get_c routine and puts in the buffer, pointed to by R14 | |
; R14 is the current character position in the input buffer | |
; R15 is a counter to ensure that we don't exceed 64 characters in input buffer | |
; 33 instructions | |
textRead: MOV.W #0x0200,R14 ; R14 = start of input buffer in RAM | |
CLR.B R15 ; i = 0 | |
getChar: CALL #uart_getc ; char ch = uart_getc() | |
CMP.B #0x000d,R12 ; is it carriage return? 0d | |
JEQ textEnd | |
CMP.B #0x000a,R12 ; Is it newline? 0a | |
JEQ textEnd | |
CMP.B #0x0020,R12 ; if (ch >= ' ' && ch <= '~') | |
JLO nonValid | |
CMP.B #0x007f,R12 | |
JHS nonValid | |
CMP.B #0x003A,R12 ; is it colon? 3A | |
JNE notColon | |
colon: ; If the input character is a colon | |
CALL #uart_getc ; char ch = uart_getc() - get the next character | |
; INC.W R14 ; increment the input buffer pointer to the next (2nd) character | |
MOV.B R12,R13 ; move the 1st character after the colon to "name" variable in R13 | |
$C$L13: MOV.B R13,R12 ; Copy the character into R13 and calculate the destination address | |
SUB.B #0x0041,R13 ; subtract 65 to remove offset of letter A | |
ADD.W R13,R13 ; Double R13 ; multiply by 2 | |
ADD.W R13,R13 ; Double R13 ; multiply by 4 | |
ADD.W R13,R13 ; Double R13 ; multiply by 8 | |
ADD.W R13,R13 ; Double R13 ; multiply by 16 | |
ADD.W R13,R13 ; Double R13 ; multiply by 32 | |
ADD.W R13,R14 ; Add (32*R13) to the index pointer R14 | |
ADD.W #0x020,R14 ; Add to array pointer 0x0220 | |
MOV.B R12,0xffff(R14) ; Store character at RAM buffer indexed by R14 | |
; R14 now contains the destination address | |
JMP incPointer | |
notColon: INC.W R14 ; Increment buffer pointer | |
MOV.B R12,0xffff(R14) ; Store character at RAM buffer indexed by R14 | |
incPointer: INC.B R15 ; Increment the input buffer pointer i++; | |
nonValid: CMP.B #0x003f,R15 ; If input pointer <64 loop back to start while (i < (63)) { | |
JLO getChar ; loop back and get next character | |
textEnd: CLR.B 0x0000(R14) ; Put a zero on the end of the buffer *p = 0; | |
RET | |
;------------------------------------------------------------------------------------------------------------------- | |
; We now come onto the textEval - where based on the value of the character we perform some action routine | |
; But first we need to determine whether the characers form part of a number - and these must be decoded separately | |
; and put on the stack | |
;------------------------------------------------------------------------------------------------------------------- | |
; Register Usage | |
; R10 - pointer to the current character in the input buffer | |
; R12 is the accumulator for the number - then stored in location #0x380 | |
; R13 Temporary - use in x10 multipication | |
; R14 | |
; R15 | |
; 24 Instructions | |
textEval: PUSH R10 | |
MOV.W #0x0200,R10 ; R10 = start of input buffer in RAM at address 0x0200 | |
next: MOV.B @R10+,R12 ; Get the next character from the instruction memory | |
MOV.B R12,R13 ; Copy into R13 | |
TST.B R12 ; is it end of string null? - then break | |
JEQ textEval_end | |
SUB.W #0x0030,R12 ; subtract 0x30 | |
CMP.W #0x000a,R12 ; and if it is < 10 it is a number | |
JHS notNumber | |
number: CMP.B #0x0030,0x0000(R10) ; >= '0' Is the next digit a number | |
JLO ($C$L20) ; break; | |
CMP.B #0x003a,0x0000(R10) ; <= '9' | |
JHS ($C$L20) ; break; | |
times_10: ; This multipies R12 by 10 | |
ADDC.W R12,R12 ; R12 = 2 * R12 | |
MOV.W R12,R13 ; R3 = 2 * R12 | |
ADDC.W R12,R12 ; R12 = 4 * R12 | |
ADDC.W R12,R12 ; R12 = 8 x R12 | |
ADDC.W R13,R12 ; R12 =10 x R12 | |
; | |
MOV.B @R10+,R14 ; Increment the instruction pointer fetching the next digit | |
SUB.W #0x0030,R14 | |
ADD.W R14, R12 ; Add in the next digit | |
JMP number | |
$C$L20: MOV.W R12,&0x380 ; final number x is stored in locaton 0x380 | |
MOV.W R12, R4 ; Put in R4 -the stack | |
JMP next ; process the next character | |
notNumber: MOV.W #0x0200,R14 ; Character is either a primitive or an alpha - so form CALL address | |
; Restore R14 to start of RAM buffer | |
; Get the current character location | |
; If it's a primitive between 0x20 and 0x3F - point to a look-up table and fetch it's code segment address | |
; If its an Alpha, or character >0x40 calculate it's code address from (char - 65)x32 | |
CMP.B #0x0040,R13 ; <= 'A' Is the next digit an Alpha or primitive | |
JLO primitive | |
; Character is in R13 so calculate the destination address | |
alpha: SUB.B #0x0041,R13 ; subtract 65 to remove offset of letter A | |
ADD.W R13,R13 ; Double R13 ; multiply by 2 | |
ADD.W R13,R13 ; Double R13 ; multiply by 4 | |
ADD.W R13,R13 ; Double R13 ; multiply by 8 | |
ADD.W R13,R13 ; Double R13 ; multiply by 16 | |
ADD.W R13,R13 ; Double R13 ; multiply by 32 | |
ADD.W R13,R14 ; Add (32*R13) to the index pointer R14 | |
ADD.W #0x020,R14 ; Add to array pointer 0x0220 | |
; R14 now contains the CALL address for the code | |
JMP next ; process the next character | |
primitive: MOV.W R13,R12 ; Get the command in R12 | |
; SUB.B #0x0020,R13 ; subtract 32 to remove offset of space | |
; ADD.W R13,R13 ; Double R13 ; multiply by 2 | |
; MOV.W #0x03C0,R14 ; index into look up table | |
; ADD.W R13,R14 ; Add (2*R13) to the index pointer R14 - this is the table address | |
; ------------------------------------------------------------------------------------------------------------------- | |
; Now we need to evaluate the primitives | |
space: CMP.B #0x0020,R12 ; Is it space? | |
JNE spaceEND | |
MOV.W R4,R5 ; Move a 2nd number onto the stack | |
JMP ($C$L18) | |
spaceEND: | |
fetch: CMP.B #0x0021,R12 ; Is it fetch? | |
JNE fetchEND | |
MOV.W (R4), R4 ; Use the address in R4 to fetch a word into R4 | |
JMP ($C$L18) | |
fetchEND: | |
dup: | |
CMP.B #0x0022,R12 ; Is it is it dup? | |
JNE dupEND | |
MOV.W R4,R5 | |
JMP ($C$L18) | |
dupEND: | |
lit: | |
CMP.B #0x0023,R12 ; Is it lit? | |
JNE litEND | |
JMP ($C$L18) | |
litEND: | |
swap: | |
CMP.B #0x0024,R12 ; Is it swap $ ? | |
JNE swapEND | |
MOV.W R4,R6 | |
MOV.W R5,R4 | |
MOV.W R6,R3 | |
JMP ($C$L18) | |
swapEND: | |
over: CMP.B #0x0025,R12 ; Is it over % ? | |
JNE overEND | |
JMP ($C$L18) | |
overEND: | |
and: CMP.B #0x0026,R12 ; Is it and & ? | |
JNE andEND | |
AND.W R5,R4 | |
JMP ($C$L18) | |
andEND: | |
drop: CMP.B #0x0027,R12 ; Is it drop ' ? | |
JNE dropEND | |
JMP ($C$L18) | |
dropEND: | |
left_par: CMP.B #0x0028,R12 ; Is it left_par ( ? | |
JNE left_parEND | |
JMP ($C$L18) | |
left_parEND: | |
right_par: CMP.B #0x0029,R12 ; Is it right_par ) ? | |
JNE right_parEND | |
JMP ($C$L18) | |
right_parEND: | |
mult: CMP.B #0x002A,R12 ; Is it mult * ? | |
JNE multEND | |
JMP ($C$L18) | |
multEND: | |
add: CMP.B #0x002B,R12 ; Is it add + ? | |
JNE addEND | |
ADD.W R5,R4 | |
JMP ($C$L18) | |
addEND: | |
push: CMP.B #0x002C,R12 ; Is it push ' ? | |
JNE pushEND | |
JMP ($C$L18) | |
pushEND: | |
sub: CMP.B #0x002D,R12 ; Is it sub - ? | |
JNE subEND | |
SUB.W R5,R4 | |
JMP ($C$L18) | |
subEND: | |
pop: CMP.B #0x002E,R12 ; Is it pop . ? | |
JNE popEND | |
JMP printNum ; go to decimal number print | |
JMP ($C$L18) | |
popEND: | |
div: CMP.B #0x002F,R12 ; Is it div / ? | |
JNE divEND | |
JMP ($C$L18) | |
divEND: | |
;------------------------------------------------------------------------------------------------- | |
printNum: ; Take the 16 bit value in R4 stack register and print to terminal as an integer | |
; do by repeated subtraction of powers of 10 | |
;------------------------------------------------------------------------------------------------- | |
MOV.W #10000,R14 ; R14 used as the decimation register | |
CLR.W R12 ; use R12 as a counter | |
CLR.W R11 ; Use R11 as scratch | |
CLR.W R13 | |
MOV.W R4,R12 ;copy the top of stack into R12 | |
CLRC ; clear the carry | |
sub10K: SUB.W R14,R12 | |
JLO end10K | |
add10K: ADD.B #1,R11 ; increments the digit count | |
add_zero: ADD.W R14,R13 ; R13 increases by the decimal value each time | |
JMP sub10K | |
end10K: ADD.B #0x30,R11 ; make it a number | |
MOV.W R11,R12 | |
CALL #uart_putc ; output character | |
SUB.W R13,R4 ; Decrement the stack count by n x 10 | |
CLR.W R11 ; Use R11 as scratch | |
CLR.W R13 | |
MOV.W R4,R12 | |
decimate: CMP.W #10000,R14 | |
JEQ use1K | |
CMP.W #1000,R14 | |
JEQ use100 | |
CMP.W #100,R14 | |
JEQ use10 | |
CMP.W #10,R14 | |
JEQ use1 | |
JMP crlf | |
use1K: MOV.W #1000,R14 | |
JMP sub10K | |
use100: MOV.W #100,R14 | |
JMP sub10K | |
use10: MOV.W #10,R14 | |
JMP sub10K | |
use1: MOV.W #1,R14 | |
JMP sub10K | |
crlf: MOV.W #0x0A, R12 | |
CALL #uart_putc ; output CR | |
MOV.W #0x0D, R12 | |
CALL #uart_putc ; output LF | |
printNum_end: | |
;------------------------------------------------------------------------------------------------- | |
$C$L18: ; CALL uart_putc ; emit the character as confirmation its been seen | |
JMP next | |
textEval_end: | |
POP.W R10 | |
RET | |
;------------------------------------------------------------------------------------------------------------------- | |
; Uses R12 to send receive chars via the UART at 115200 baud. | |
uart_getc: BIT.B #1,&IFG2 ; while (!(IFG2&UCA0RXIFG)) // USCI_A0 RX buffer ready? | |
JEQ (uart_getc) | |
MOV.B &UCA0RXBUF,R12 ; return UCA0RXBUF; | |
RET | |
uart_putc: BIT.B #2,&IFG2 ; while (!(IFG2&UCA0TXIFG)) // USCI_A0 TX buffer ready? | |
JEQ (uart_putc) | |
MOV.B R12,&UCA0TXBUF ; UCA0TXBUF = c; // TX | |
RET | |
;------------------------------------------------------------------------------- | |
; Main loop here | |
;------------------------------------------------------------------------------- | |
main: | |
; Implementing the initialisation, TextTead, TextChk, TextEval and UART routines in MSP430 assembly language | |
; Ken Boak December 23rd 2016 and more on 17th/18th Jan 2017 | |
;------------------------------------------------------------------------------- | |
RESET: mov.w #03E0h,SP ; Initialize stackpointer | |
StopWDT: mov.w #WDTPW|WDTHOLD,&WDTCTL ; Stop watchdog timer WDTCTL = WDTPW + WDTHOLD; Stop WDT | |
OSC_GPIO_init: | |
MOV.B &CALBC1_1MHZ,&BCSCTL1 ;BCSCTL1 = CALBC1_1MHZ; Set DCO | |
MOV.B &CALDCO_1MHZ,&DCOCTL ;DCOCTL = CALDCO_1MHZ; | |
SetupP1: bis.b #041h,&P1DIR ; P1.0 P1.6 output as P1.0 and P1.6 are the red+green LEDs | |
MOV.B #0x0041,&P1OUT ;P1OUT = BIT0 + BIT6; // All LEDs off | |
uart_init: MOV.B #0x0006,&P1SEL ;Initialise the UART for 115200 baud | |
MOV.B #0x0006,&P1SEL2 ;P1SEL2 = RXD + TXD; | |
BIS.B #0x0080,&UCA0CTL1 ;UCA0CTL1 |= UCSSEL_2// SMCLK | |
MOV.B #0x0068,&UCA0BR0 ;UCA0BR0 = 104 // 1MHz 9600 | |
CLR.B &UCA0BR1 ;UCA0BR1 = 0 // 1MHz 9600 | |
MOV.B #2,&UCA0MCTL ;UCA0MCTL = UCBRS0 // Modulation UCBRSx = 1 | |
BIC.B #1,&UCA0CTL1 ;UCA0CTL1 &= ~UCSWRST Initialize USCI state machine | |
MOV.W #0x4F, R12 | |
CALL #uart_putc ; output "O" | |
MOV.W #0x4B, R12 | |
CALL #uart_putc ; output "K" ; Print OK | |
;------------------------------------------------------------------------------- | |
interpreter: | |
CALL #textRead | |
; CALL #textChk | |
CALL #textEval | |
JMP interpreter | |
; Stack Pointer definition | |
;------------------------------------------------------------------------------- | |
.global __STACK_END | |
.sect .stack | |
;------------------------------------------------------------------------------- | |
; Interrupt Vectors | |
;------------------------------------------------------------------------------- | |
.sect ".reset" ; MSP430 RESET Vector | |
.short RESET | |
.end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment