# Morse Code in Assembly Language

In this sample assembly language solution, our tutor is demonstrating the creation of Morse Code. Here, students are required to make use of STDIN (Standard Input) data stream which is assigned to the keyboard of the computer.  Data is retrieved from STDIN buffer using the read syscall. Here data is used from STDIN to convert Characters and Numbers into Morse Code.

SOLUTION : –

.global     _start

_start:                                   @ we should cycle back to the start until

@ the user tells us they are done

LDR         r1, =prompt       @ place the address string in r1

BL          _sPrint           @ use library function to display string

/*    Step 1 read stream into memory

*

*    Get input from the user using the _sInput function then

*    print a newline before displaying morse code of input.

*/

LDR         r1, =userInput    @ point to the space allocated for input

MOV         r2, #40                 @ set the limit of character to read

BL          _sInput                 @ get the input

LDR         r1, =newline      @ move to the next line (make it pretty)

BL          _sPrint                 @ print a ‘\n’

/*    Step 2 prepare the input for parsing

*

*    Our morse code generator will ignore all characters except letters,

*    numbers, and punctuation (which simply prints a newline).

*    To make our job easier we will convert all letters to capitals.

*

*    Additionally we will check if the user entered a “/q” to indicate

*    they would like to quit the program.

*

*  NOTE: Comment the following code to demonstrat that you understand it.

*

*/

LDR         r1, =userInput  @ point to the input string

BL          _strUpper       @ convert input string to uppercase

LDR         r1, =userInput  @ point to input string

LDRB  r0, [r1], #1    @ load first character from input, advance pointer

CMP         r0, #92                 @ see if the first character is a slash ‘\’

BNE         morseCoder      @ if not, go to morse coder

LDRB  r0, [r1]        @ else, load second character from input string

CMP         r0, #81         @ see if the second character is a ‘Q’

BEQ         _end            @ if the string was \Q, end the program

/*    Step 3      stream parser

*

*    This code examines each character in our prepared input stream

*    and does the following:

*    – prints letters as morse code using a lookup table

*    – prints numbers as morse code using a lookup table

*    – prints periods and commas as newline characters

*    – prints a space for a space character

*    – ignores all other characters.

*

*    NOTE: You will write the parsing engine here. It needs to move through the

*    user input and perform the above actions.

*

*/

morseCoder:

processChar:

LDRB    r0, [r1], #1        @ load a character from the string

CMP     r0, #0              @ see if we reached the end of string

BEQ     endOfInput          @ if end of string, end input

PUSH    {r1}                @ save r1 in the stack

CMP     r0, #46             @ see if it’s a period

BEQ     printNL             @ if period, print newline

CMP     r0, #44             @ see if it’s a comma

BEQ     printNL             @ if comma, print newline

CMP     r0, #32             @ see if it’s a space

BEQ     printSpace          @ if space, print spaces

CMP     r0, #65             @ see if it’s above or equal to letter ‘A’

BLT     testDigit           @ if not a letter, test if it’s a digit

CMP     r0, #90             @ see if it’s below or equal to letter ‘Z’

printLetter:

SUB     r0, r0, #65         @ convert ascii ‘A’ to ‘Z’ to a numeric value 0 to 25

MOV     r1, #6              @ load number 6 in r1 for multiplication

MUL     r0, r1, r0          @ mulyiply value*6

LDR     r1, =letters        @ point ot start of letter table

ADD     r1, r1, r0          @ add value*6 to point to letter entry in table

BL      _sPrint             @ print morse conversion

BAL     nextChar            @ repeat to process next character

testDigit:

CMP     r0, #48             @ see if it’s above or equal to digit ‘0’

CMP     r0, #57             @ see if it’s below or equal to digit ‘9’

printDigit:

SUB     r0, r0, #48         @ convert ascii ‘0’ to ‘9’ to a numeric value

MOV     r1, #7              @ load number 7 in r1 for multiplication

MUL     r0, r1, r0          @ mulyiply value*7

LDR     r1, =numbers        @ point ot start of number table

ADD     r1, r1, r0          @ add value*7 to point to digit entry in table

BL      _sPrint             @ print morse conversion

BAL     nextChar            @ repeat to process next character

printNL:

LDR         r1, =newline          @ point to string to print

BL          _sPrint                     @ print a ‘\n’

BAL     nextChar            @ repeat to process next character

printSpace:

LDR         r1, =space            @ point to string to print

BL          _sPrint                     @ print a series of spaces

nextChar:

POP     {r1}                @ restore value of r1

BAL     processChar         @ repeat to process next character

endOfInput:

LDR         r1, =newline      @ move to the next line (make it pretty)

BL          _sPrint                 @ print a ‘\n’

BAL         _start                  @ do not end the program unless user types \q

_end:

MOV         r7, #1                  @ set EXIT syscall

SWI         0                       @ execute syscall

.data

prompt:     .asciz      “Please enter text to translate\nMC> ”

newline:    .asciz      “\n”

space:            .asciz      ”   ”

userInput:  .space      44

.align

@ Note that the following are aligned so that each letter takes up

@ exactly six bytes, therefore letters+6bytes gives us the second letter

@ and so on.

letters:    .ascii      “.- ”       @ A

.byte 0x00, 0x00, 0x00

.ascii “-… ”          @ B

.byte 0x00

.ascii “-.-. ”          @ C

.byte       0x00

.ascii “-.. ”           @ D

.byte 0x00, 0x00

.ascii “. ”             @ E

.byte 0x00, 0x00, 0x00, 0x00

.ascii “..-. ”          @ F

.byte       0x00

.ascii “–. ”           @ G

.byte 0x00, 0x00

.ascii “…. ”          @ H

.byte       0x00

.ascii “.. ”            @ I

.byte 0x00, 0x00, 0x00

.ascii “.— ”          @ J

.byte 0x00

.ascii “-.- ”           @ K

.byte 0x00, 0x00

.ascii “.-.. ”          @ L

.byte       0x00

.ascii “– ”            @ M

.byte 0x00, 0x00, 0x00

.ascii “-. ”            @ N

.byte       0x00, 0x00, 0x00

.ascii “— ”           @ O

.byte 0x00, 0x00

.ascii “.–. ”          @ P

.byte 0x00

.ascii “–.- ”          @ Q

.byte 0x00

.ascii “.-. ”           @ R

.byte 0x00, 0x00

.ascii “… ”           @ S

.byte 0x00, 0x00

.ascii “- ”             @ T

.byte       0x00, 0x00, 0x00, 0x00

.ascii “..- ”           @ U

.byte 0x00, 0x00

.ascii “…- ”          @ V

.byte 0x00

.ascii “.– ”           @ W

.byte 0x00, 0x00

.ascii “-..- ”          @ X

.byte       0x00

.ascii “-.– ”          @ Y

.byte 0x00

.asciz “–.. ”          @ Z

.align

@ Note that the following data is for numbers (0-9) and you can choose

@ to use the data as is or simply produce the numbers on the fly

@ using the relationship between the number and the dots and dashes.

@ Also note that in ASCII the 0 comes first, in morse code 0 is last.

@ How do you account for this?

@

numbers:    .asciz      “—– “, “.—- “, “..— “, “…– “, “….- ”

.asciz  “….. “, “-…. “, “–… “, “—.. “, “—-. ”

@     Declarations of functions in library

.equ        STDOUT, 1   @ set a constant for the standard output

.equ        STDIN,      0     @ set a constant for the standard input

.global           _strLen           @ returns the length of an array in bytes

.global           _strCat           @ concatonates two bchar arrays

.global           _strUpper   @ replaces all lowercase letters with uncials

.global           _sPrint           @ prints a string onto the STDOUT

.global           _sInput           @ takes in a string from the STDIN flushing buffer

/* String Length Function

*

*    This function takes the address of a null terminated array of bytes

*    in register 1 and returns the number of characters in the array in

*    register 2.

*

*/

_strLen:

MOV         r2, #0                  @ start the element counter at 0

findEnd:

LDRB  r0, [r1], #1      @ r1 contains the address of the byte to

ADD         r2, r2, #1        @ increment the byte counter

CMP         r0, #0                  @ set the status register by comparing the

@ value in r0 with null (#0)

BNE         findEnd                 @ if r0 is not null continue looping

SUB         r2, r2, #1        @ fix that we counted the null character

MOV         pc, lr                  @ restore control to calling code

/* String to Upper Case Function

*

* Takes the addresses of a null terminated string and converts any

* lower case letters to their uncial (upper case) equivalent.

*

*/

_strUpper:

LDRB  r0, [r1]                @ load the next byte of array 1

CMP         r0, #97                       @ compare byte to ‘a’

BLT         writeChar               @ preserve non-lowercase letter

CMP         r0, #122                @ compare to ‘z’

SUBLE r0, r0, #32             @ change to uppercase

writeChar:

STRB  r0, [r1], #1            @ if not end of array 1 store value

CMP         r0, #0                        @ look for end of string

BNE         _strUpper               @ loop until end of string

/* String Printer

*

* Takes the address of a null terminated string array and outputs

* that string to STDOUT (console). String address in register 1

*

*/

_sPrint:

MOV   r0, #STDOUT             @ set the output to the console

PUSH  {r0-r1, lr}             @ save the working registers and LR

BL          _strLen                       @ set register 2 to string length

POP         {r0-r1, lr}             @ restore working registers and LR

MOV         r7, #4                        @ set the syscall to WRITE

SWI         0                             @ make the syscall

/* String Inputer

*

* Takes the address for a string in register 1 and the number of

* characters to read in register 2. Makes the syscall to READ from

* STDIN and then flushes any remaining characters from the buffer.

*

*/

_sInput:

PUSH  {r1-r2, lr}             @ preserve values for function

MOV   r0, #STDIN              @ set the input to the console

MOV         r7, #3                        @ set the syscall to READ

SWI   0                             @ make the syscall

POP         {r1-r2, lr}             @ restore values sent to function

sInputStrStart:

CMP         r2, #0                        @ memory bounds checker

MOVLE r0, #0                        @ if no more characters put null in R1

STRLEB      r0, [r1]                @ if no more characters store null in array

BLE         sInputEnd               @ if no more characters exit

LDRB  r0, [r1]                @ load in the next byte of sent array

CMP         r0, #10                       @ check for newline character

MOVEQ r0, #0                        @ if end put null charater in R1

STRB  r0, [r1], #1            @ write byte out at end of array 1

SUB         r2, r2, #1              @ decrement letter counter

BNE         sInputStrStart          @ loop until newline

sInputEnd: