COSMAC Processor Stack Operation


Introduction to the COSMAC Processor Stack Operation

The COSMAC Processor Stack Operation will allow us to store data on the fly that we need to retrieve later. This will usually be an address contained in a register. Examples would be a program counter, or a memory location that contains data for a subroutine to operate on. Technically, we are really just emulating a stack in the COSMAC by the use of it’s existing instructions.

By default, the stack pointer is at register #2. On my CDP1802 unit, register 2 points to $FFD4 by default. We can change this. In this post, I’ll change this address to $9120. Be sure the stack pointer is high enough as to not interfere with your program. As we perform stack operations we will decrease, or increase this pointer. As we add data to the stack (push data), we’ll usually decrease this pointer. Likewise as we pull data from the stack (pop), we will increase the stack pointer. In other words, the stack operates from the top down.

In those post, we’ll perform several experiments to see the stack data, and the stack pointer.

Initial values for the COSMAC Processor Stack Operation

At this point, we’ll take a look at some of the registers. I’ve declared the following aliases at the beginning of each of these examples. Assume that these aliases are at the top of every project in this post.

STACKP:     EQU 9120H
GPIO1:      EQU 7000H
BREAK:      EQU 2756H


	org	8000H		;have to start somewhere ...	
R0	EQU	0
R1	EQU	1
R2	EQU	2
R3	EQU	3
R4	EQU	4
R5	EQU	5
R6	EQU	6
R7	EQU	7
R8	EQU	8
R9	EQU	9
RA	EQU	10
RB	EQU	11
RC	EQU	12
RD	EQU	13
RE	EQU	14
RF	EQU	15

At this point, let’s enter some simple logic to see what the initial value of the stack pointer is. It’s important to realize that I’m using the COSMAC CDP1802 Microprocessor kit. The break routine will store the values of each register to a memory location where I can access their values. I can only access scratchpad registers R3 to RF (R15), though. So let’s just load the value of Register 2 into register 3. Remeber, Register 2 is our Stack Pointer. After that, we’ll break, and see what the value of register 3 contains. Additionally, we’ll take a look at the memory locations in the vicinity of the cell the stack pointer points to.

        LOAD    R2, STACKP
START:  GLO     R2
        PLO     R3
        GHI     R2
        PHI     R3
        LBR     BREAK
        END

Remember each scratchpad register is 16 bits wide. Since this is an 8 bit processor, we will move 8 bits at a time (1 byte).

With this code, we’ll load STACKP into R2. Remember, this is $9120. Next, we’ll get the low byte of the stack pointer (R2) with the GLO (Get Low) instruction. We place that into the low byte of R3 with the PLO (Put Low) instruction. Likewise, we get the high byte of R2, and place this into the high byte of R3.

I’m going to use the A18 assembler to convert this code to an Intel Hex file. After that, I’ll send the file to the unit. Keep in mind that we need to connect to our processor through a terminal for this. The character and line delays are at 10ms.

Here is the output of the assembler in Intel hex file format:

:0D800000F891B2F820A282A392B3C02756D7
:00800D0172

At this point, I’ll run the code, and see what our initial values are:

Register 3 contains the value of the stack pointer, which is currently at $9120 (Copied from R2).

Now, let’s look at the values in the vicinity of this address:

$911C  FF
$911D  FE
$911E  EF
$911F  0F
$9120  00
$9121  FB  

As you can see, this is basically random data.

Pushing a Register onto the Stack

At this point, let’s modify our program. We’ll start by loading register RE with GPIO1. Recall from our aliases at the top of this post that GPIO1 resides at address $7000. We’ll do this by using the STXD instruction. This will store data to the stack, and decrement the stack pointer. Remember, each register contains 16 bits of data. Our memory locations, however, are only 8 bits. Therefore, we’ll need to do this in 2 steps.

Let’s look at our code:

        LOAD    R2, STACKP
        LOAD    RE, GPIO1
START:  GHI     RE
        STXD
        GLO     RE
        STXD
        
        GLO     R2
        PLO     R3
        GHI     R2
        PHI     R3
        LBR     BREAK
        END
:17800000F891B2F820A2F870BEF800AE9E738E7382A392B3C02756EF
:0080170168

As you can see, at first, we are loading register RE with GPIO1 ($7000). After that, we get the high byte from register RE. Then we store this to the stack, and decrement the stack pointer. We do the same thing with the low byte. At this point, we’ll load register R2 into register R3, so we can see what location the stack pointer is at.

Once again, I’ll send this code to the processor, and we’ll see where our values are.

Register R2, our stack pointer is at $911E.  Decreased by 2

$911C  FF (UNCHANGED)
$911D  FE (UNCHANGED)
$911E  EF (UNCHANGED)
$911F  00 (LOW BYTE OF RE) 
$9120  70 (HIGH BYTE OF RE)
$9121  FB (UNCHANGED)

As you can see, we load the high byte first (70H), and decrement the address in R2. Then we load the low byte to the stack, and decrement the pointer again. At this point, the stack holds the current value of RE. I’ve heard of a bug in the 1802 processor where STXD does not decrement the pointer, but I’m not seeing this bug happen here.

Popping a Value from the Stack

At this point, we’ll pull (pop) the register address from the stack. To demonstrate, we’ll place this value into register (RF). Before we begin, we need to execute the IRX instruction. This advances the stack pointer to where the data is. Let’s see what happens to the stack pointer when we use this instruction:

        LOAD    R2, STACKP
        LOAD    RE, GPIO1
START:  GHI     RE
        STXD
        GLO     RE
        STXD
        IRX
        
        GLO     R2
        PLO     R3
        GHI     R2
        PHI     R3
        LBR     BREAK
        END
:18800000F891B2F820A2F870BEF800AE9E738E736082A392B3C027568E
:0080180167

As you can see, the stack pointer was copied into R3, and is currently at $911F, which is the beginning of our data.

At this point, we’ll execute the LDXA command, and LDX. LDXA will pull data from the stack, and advance the stack pointer. When we pull the high byte from the stack at $9120, we are at the top of the stack. We do not need to advance the pointer beyond this, so we just use the LDX command.

Let’s add these commands, and re-run our logic.

        LOAD    R2, STACKP
        LOAD    RE, GPIO1
START:  GHI     RE
        STXD
        GLO     RE
        STXD
        IRX
        LDXA
        PLO     RF
        LDX
        PHI     RF
        
        GLO     R2
        PLO     R3
        GHI     R2
        PHI     R3
        LBR     BREAK
        END
:1C800000F891B2F820A2F870BEF800AE9E738E736072AFF0BF82A392B3C02756BA
:00801C0163

At this point, let’s take a look at our registers:

R3 contains $9120
RF contains $7000

The “Stack” contains the same values as before, however, since we’ve already recovered this data, we are free to overwrite it.

For more information, visit the COSMAC category page!

— Ricky Bryce

Leave a comment

Your email address will not be published. Required fields are marked *