Assembly #1: Reverse a string

Intro

This intro provides a little fun background on why I started all this assembly on the PC-1360. You can skip ahead to the Getting Started section below if you wish to skip the intro.

I was talking with a coworker, Bill, one day, about how I liked the idea of writing assembly language programs, and how I wanted to use it for my robot projects. Assembly to me, has always been the ultimate programming language. There’s just something about scraping your knuckles on the bare hardware, working directly with the CPU and it’s hardware. I get all giddy just thinking about it.  

Bill had done a lot of assembly back in the day, and expressed he had no desire to ever do it again. He described it as painful and tedious. So he set a challenge for me, maybe to prove how much of a pain assembly is, and shut me up. “Why on earth would he want to write assembly code?” The challenge was to write a routine in assebly to reverse a string.

At the time, I was learning to use an AVR chip, which is a tiny RISC based CPU available in a number of pin sizes. The smallest of which is just 8 pins; no bigger then a 555 timer. Then I found the Arduino, and fell in love with how easy was to write code for it. Unfortunatly, I found it too convenient to use C on the Arduino, and never bothered with assembly on the AVR chip.

Fast forward 2 years, and I am looking through some old boxes, and I happen upon my Sharp PC-1360. “Cool!” I say, and I tapped out a quick BASIC program. It was then that I was reminded how slow BASIC was, and I was curious if I could write assembly language programs for it. I discovered both Aldweb and simons pocket computer reference site, and discovered that yes, I could write assembly for it! The string reverser project was born. I am actually glad I did the project on the PC-1360. Turns out to be a very nice platform to work on. You have a screen, keyboard, and the ability to look at and change bytes in memory.

Conventions

A couple convention examples. When you see something like X – 1 -> X you are saying X=X-1 or –X. When you see A -> [DP] you are copying the value of A register to the location pointed to by DP. In C it would look like *ptr = 123. X -> DP is like saying ptr = 0xC030. If you need more info, see the Machine Language Quick Manual on aldweb.

&C030 is an example of a hex value. 0xC030 is also, and is used in C/C++ and Java. When working with the pocket computers, you will need to use &C030 form. I may slip in 0xC030 now and then since that is what I use outside of pockets computers.

Getting Started

The purpose of this project is to write a simple assembly language program, just to get the hang of things.

What I learned while writing this was what registers are used for what. I came to the conclusion that X is typically used when reading data. I came to this conclusion because you can see the IXL instruction will increment itself, copy it’s value to DP and load the A register with the data at [DP]; all in one instruction. Similarly, the Y register is great for storing data. IYS will increment Y, copy to DP, and store A to [DP]. I also got a better understanding of PUSH and how to use LOOP.

Lastly, I finally came to terms with the P register. I figured out that it is used to point to registers you can’t directly access. For example, you can’t directly load the X and Y registers. Instead you have to do it indirectly by pointing the P register to first XH then XL. For example, the following code will point X to SOURCE:

        ORG     &C030

SOURCE: DB      48 49 50 51 52 53 54

        # load X with SOURCE
        LP      XH_REG          # point P to XH
        LIA     HB@SOURCE       # A contains &C0
        EXAM                    # A -> [P]
        LP      XL_REG          # point P to XL
        LIA     LB@SOURCE       # A contains &30
        EXAM                    # X now points SOURCE

 

You will find similar instances where you have to point P to a register to perform an operation. CPMA is a perfect example. If you wanted to compare the registers A and B, you’ll discover there are no instructions to do that. Instead you point P to the B register, then call CPMA which will compare [P] with A. That in effect will compare B with A. Sneaky isn’t it?

The Task of Reversing

I broke this task into two parts.

  1.  The string reverser assembly routine, which does the actual work.
  2. A BASIC program to get input from, and display the result, to the user.

I did this to keep the assembly part simple. We will just let BASIC read from the keyboard and display to the screen, since it’s great for that. Actually, you will discover that we really don’t really even need to have the BASIC part, but it does make the whole process a little easier to test results, and show the results to friends.

The assembly program will have a predefined location for the source string (the string to reverse), and a location to store the reversed string. We also need to store the length of the source string.

Note that the term assembly language typically refers to the source, and machine language or machine code refers to the compiled version of the assembly language. I may use the terms interchangbly.

The BASIC program will:

  1. Read a string from the user
  2. POKE that string into the predefined source location
  3. POKE the length of the string
  4. Call the machine language program
  5. Use PEEK to get the reversed string
  6. Display the string to the user

 The assembly program will simply:

  1. Read from the source
  2. Look at how many bytes long the source is
  3. Copy the source to the destination in reverse order

 

 

Assembly Source

Compile and load this at address 49200 (&C030) via CLOAD M on the pocket computer and . Once loaded, you can use PEEK to look at addresses 4920749209 and see they contain the value 35. Which his what we have defined at DEST in the source below.

 

Before we load up the BASIC program, lets run the machine code via CALL 49215, and again PEEK at addresses 4920749209. You will notice it now contains 50, 49, 48. It read the data from SOURCE and put the reverse in DEST, but only did it for LENGTH bytes.

# Reverse a number of bytes
# X register points to the source data
# Y register points to the destination data

# We point X to the last character of the input string,
# and Y to the first of the destination. Then just loop
# the correct number of times and copy data along the way.
# When we are done, the reversed sting is stored in DEST.

# Important instructions:
# IXL loads  A from [X] (via DP)
# IYS stores A to [++Y] 
# DYS stores A to [--Y]

# http://www.axorion.com


        $JR
        org     $C030
        
I_REG   EQU     0               # Internal Registers
J_REG   EQU     1
A_REG   EQU     2
B_REG   EQU     3
XL_REG  EQU     4
XH_REG  EQU     5
YL_REG  EQU     6
YH_REG  EQU     7
K_REG   EQU     8
L_REG   EQU     9
M_REG   EQU     10
N_REG   EQU     11
PORTC   EQU     95

SOURCE: DB      48 49 50 51 52 53 54
DEST:   DB      35 35 35 35 35 35 35
LENGTH: DB      3

# Copy SOURCE to DEST in the reverse order, copying LENGTH bytes.
START1:
        # load X with source
        LP      XH_REG
        LIA     HB@SOURCE
        EXAM                    # A -> [P]
        LP      XL_REG
        LIA     LB@SOURCE
        EXAM                    # X now points SOURCE
        DX                      # needed because DXL is a little odd
        
        # Move the source to the end of the string
        LIDP    LENGTH          # point to length byte
        LDD                     # [DP] -> A
        CPIA    0               # check if string length is 0
        JPZ     DONE            # length is 0
        PUSH                    # used in loop later
        LP      I_REG           # point P to I register
        EXAM                    # A -> [P], I reg now contains length
INCXPTR:
        IX                      # ++X
        LOOP    INCXPTR         # loop A times

        # load Y with dest
        LP      YH_REG
        LIA     HB@DEST
        EXAM
        LP      YL_REG
        LIA     LB@DEST
        EXAM                    # Y points to dest
        DY                      # one less for ++Y later

LOOP1:
        DXL                     # [--X] -> A
        IYS                     # A -> [++Y]
        DECI
        JPNZ    LOOP1   
        
DONE:
        RTN

 

BASIC Source

The BASIC program assumes you have already loaded the machine code. Load this in PRO mode via CLOAD. Appologies for not renumbering the code. I haven’t tried the tool that does it on my Mac, and can’t send data from the 1360 to my Mac yet.

Once loaded, if you type RUN it will dump the contents of memory addresses 49200-49214, to show you both the source and destination values. Use DEF S to start the real program (at line 100.) Once I find a renumbering tool, I’ll update the listing.

 

You’ll notice at line 127 IF L = 0 THEN 155, I check if the user entered a zero length string, and skip poking the contends of the string. You’ll also noticed that I will still call the assembly program, as it will handle a zero length string.

5 S=49200 : A=S
6 WAIT 0
7 GOSUB 10
8 END
10 FOR I=1 TO 14
15 IF I=14 WAIT
20 PRINT CHR$ PEEK a;" ";
25 A=A+1
26 if I=7 THEN PRINT "-"
30 NEXT I
40 RETURN
100 "S"
101 S$=""
104 WAIT 0
105 A=49200
110 INPUT "ENTER STRING (MAX 7)? ";S$
120 IF LEN(S$) > 7 THEN PRINT "TOO LONG" : GOTO 110
124 PRINT "USING [";S$;"]"
125 REM STORE ENTRY FOR ML PROGRAM TO ACCESS
126 L = LEN(S$) : POKE 49214,L
127 IF L = 0 THEN 155
130 FOR I=1 TO L
135 V = ASC(MID$(S$,I,1))
136 PRINT "POKING ";V;"INTO ";A
140 POKE A,V
145 A=A+1
150 NEXT I
155 PRINT "CALLING REVERSER"
160 CALL 49215
165 S$=""
170 PRINT "RESULT"
180 A=49207
182 IF L=0 THEN 215
185 FOR I=1 TO L
190 S$=S$+CHR$ PEEK A
200 A=A+1
210 NEXT I
215 PRINT "RESULT [";S$;"]"
220 A=49200
230 GOSUB 10
240 END

 

Feel free to drop me a line at eightbit at axorion. You can also start a thread on Simons Pocket Computer Forum. Best to let me know you did though, in case I don't see your forum entry right away.

Where to go next? See my next project, Assembly #2 String Compare.