First the sad news. I found out the other day that the SX line of processors is EOL. So this means that if I want to use something like a SX52 I have to switch to another line of micro-controllers. Which really bites because then I have to get another programmer.

Anyway I will still continue my projects using my SX28 for now.

I also will be attending University doing a BENG in EE in 2010.

Calculator

Now on to the calculator, I’m still working on.

I have tidied up the code. Went through a ruckus because my son a toddler pulled some of the wires out of the breadboard. Took some time to rewire it. I noticed that he knocked some of the resistors together that are used for the 7seg displays. I thought there was an error in the code and tried to debug it only to find out it was a hardware fault.

Design flaws

I have also realised that there are way too many wires the way it is wired up. I have seen cleaner designs, mine has each 7seg driven by its own chip when I could drive them all straight from the micro-controller and switch between them. Also the number of resistors used is way too many because I used common anode 7seg and the 7447 chip. Instead it would have been a cleaner design to use common cathode and 7448. Also then I would not need so many resistors and they could not touch each other. Common Cathode requires more current draw from the chip.

Another problem I have discovered is that neither the 48 or 47 chips can display a negative sign. Also I need extra lines from the micro-controller to get a decimal point.

Keypad

With the help of my Father I have worked out the correct circuit for a keypad. I wrote the code and it works. It took many hours of debugging to get right. The circuit pulls the line to +5V and sends a low to determine if there is a key press.

Option CarryX

After the keypad code worked I decided to work on the Math routines. I started with simple 2 Byte addition and subtraction. I came unstuck when I went to do the subtraction. The problem is that the carry is incorrectly set on the SX28 when you do sub. So when I did a subb the inverted carry made it do subtract when it should not, and not subtract when it should. Eventually I googled around and found a page talking about multi-byte math on the SX. That page uses the option carryX switch to make the SX always use carry during addition and subtraction, instead of manually via subb and addb. However when I added that switch to my code it affected my binary to BCD routines and I don’t know why.

Eventually I decided to remove the carryX option and manually invert the carry after sub. Currently my code is performing a 16 bit number – 8 bit number calculation without error. Later I will finish off the subtraction routine and make the calculator just do addition and subtraction before moving on to multiply and divide. But still I have a problem in that I don’t have a method for displaying a negative number.

Finally I have cleaned up the code for my SX28 binary to BCD conversion routine.

I link to the first post which explains how this works is here

SX28 code follows

binarytobcd ; rotate bits into registers
            bank math
            mov cnt1, #32
            clr bcd+0
            clr bcd+1
            clr bcd+2
            clr bcd+3

byte0       ; byte0     
            mov nbs, bcd+0
            call checknbs
            mov bcd+0, nbs
            ; byte1     
            mov nbs, bcd+1
            call checknbs
            mov bcd+1, nbs
            ; byte2     
            mov nbs, bcd+2
            call checknbs
            mov bcd+2, nbs
            ; byte3     
            mov nbs, bcd+3
            call checknbs
            mov bcd+3, nbs

            ; rotate after addition
            clc

rotate      rl bin+3
            rl bin+2
            rl bin+1
            rl bin+0

            rl bcd+3
            rl bcd+2
            rl bcd+1
            rl bcd+0

            ; loop back
            decsz cnt1
            jmp byte0
            ret

checknbs    ; check the 2 nibbles of a byte for the overflow
            ; data is passed in nbs and out in nbs

            ; check for > 5 in any 4 bits
            mov tmpl,nbs
            mov tmph,nbs

            and tmpl, #$0F
            and tmph, #$F0
            swap tmph

            cjb tmpl, #$05, :dontadd3l
            clc
            add tmpl, #$03
:dontadd3l
            cjb tmph, #$05, :dontadd3h
            clc
            add tmph, #$03
:dontadd3h
            swap tmph
            or tmpl, tmph
            mov nbs, tmpl

            ret

I have not yet cleaned up my code so I’m not going to post it here just explain it.

Code is now available here

How and Why

If you google for Binary to BCD conversion you will come up with the following algorithm

  1. Rotate the bits from the binary into the BCD
  2. In the BCD Check if 5 or higher
  3. If so add 3
  4. Go to step 1

So why are we adding 3 to 5 or higher
If we look at a sequence of binary representations between 0 and 15. And convert them from binary to BCD

================
HEX        BCD
0           0
1           1
2           2
3           3
4           4
5           5
6           6
7           7
8           8
9           9
A          10
B          11
C          12
D          13
E          14
F          15

You can see that there is a direct mapping between digits 0-9 in binary and BCD but digits A-F are a special case. So if we just copy a nibble from binary to BCD it works for 0-9 and not A-F. Further if we copy by rotation we can see this happen with A.

start
BCD is [0000 0000]
binary is [0000 1010]
rotate step 1  BCD [0000 0001] $01
rotate step 2  BCD [0000 0010] $02
rotate step 3  BCD [0000 0101] $05
rotate step 4  BCD [0000 1010] $0A

In step 3 we notice that in the BCD byte there is a value of $05. The next rotation of that byte will create $0A but we don’t want that we want $10 (BCD for 10) so we add 3 to the $05 and then rotate.

rotate step 3  BCD [0000 0101] $05
add 3          BCD [0000 1000] $08
rotate step 4  BCD [0001 0000] $10

Thus by adding the 3 prior to the rotation the next rotation creates the correct BCD value.

So a better algorithm may be

  1. for each nibble in the BCD
  2. check if the value is 5, if so add 3
  3. Rotate the bits from the binary into the BCD
  4. go to step 1

Further ponderings:

Question:

If the BCD result is actually 5 or above, how does the algorithm not add 3 and ruin the result?

Answer:

By doing the rotations after the summing, the last rotation results are not checked by the algorithm.

Question:

How does this work for multiple bytes, for example $50 needs 3 added to the high nibble is there

Answer:

Because this works only on a nibble basis it only manipulates one nibble. Therefore it does not matter where each nibble is in the sequence. It operates by manipulating the top bits of the nibble forcing the bit to be moved by the rotation, the sum itself never carries over into the next nibble.

Just removed the last bug in my Binary to BCD code converter and uploaded it to the SX28 but it does not work. Apparently I have yet to understand how banking works on the SX28.

There are 16 common bytes of RAM. Each of these is divided into 2, the lower 8 are the registers of the SX28 itself and the higher 8 are like global variables.

Now those 16 are mirrored by another 16 that are unique for each of the 8 banks.

So starting at location $20 is the same as $00 which is a register and $28 is the same as $08.

My code was overflowing the registers and memory so that all the results got corrupted I got garbage.

I corrected this oversight by changing my code to read like this

            ; each bank can store 16 bytes and there are 8 (10,30,50,70,90,B0,D0,F0)

            ; global variables "only 8 bytes"
            org $08
rtcc1       ds 1
digit12     ds 1
digit34     ds 1

            ; display variables
display     equ $10
            org $10
store2      ds 1
store1      ds 1
bit_count   ds 1
tempSP      ds 1

            ; math variables
math        equ $30
            org $30
cnt1        ds 1
tmpl        ds 1
tmph        ds 1
bin         ds 4
bcd         ds 4
bin2        ds 4

            ; input variables
input       equ $50
            org $50
col1        ds 1
col2        ds 1
col3        ds 1
col4        ds 1
col1reada   ds 1
col1readb   ds 1
col2reada   ds 1
col2readb   ds 1
col4reada   ds 1
col4readb   ds 1
col3reada   ds 1
col3readb   ds 1

To access the banks I used code like this in my program

            bank math
            mov bin2+0, #$00
            mov bin2+1, #$00
            mov bin2+2, #$00
            mov bin2+3, #$FF

Now I can continue to finish my calculator.

I created these pretty trees using python and pygame

Fractal Tree blue

Fractal Tree blue

Fractal Tree green to pink

Fractal Tree green to pink

""" 
Fractal trees by Richard James
"""

import math
import random

# PyGame Constants
import pygame
from pygame.locals import *
from pygame.color import THECOLORS

screenwidth = 1600
screenheight = 1200
# these are used for the centre of the cartesian plane
xmid = screenwidth / 2
ymid = screenheight / 2

div = 1.4 # change in length
angleDelta1 = 15 # change in angle
angleDelta2 = 45 # change in angle
totalIterations = 12 # number of iterations
greenDelta = -20 # change in colour green
redDelta = -50 # change in colour red
blueDelta = 20 # change in colour blue

def drawseg(screen, xc, yc, length, angle, iteration, rgb):
    # rotate
    theta = math.radians(-angle)
    xRotated = length * math.cos(theta)
    yRotated = length * math.sin(theta)
    # translate from the centre of the last lines endpoint
    x2 = xRotated + xc
    y2 = yRotated + yc
    # translate to the centre of the screen and draw
    pygame.draw.aaline(screen, rgb,(xc+xmid,yc+ymid), (x2+xmid,y2+ymid) )

    # alter colour
    red = rgb[0]+ redDelta
    if red < 0:
        red = 0
    if red > 255:
        red = 255
    green = rgb[1]+ greenDelta
    if green < 0:
        green = 0
    if green > 255:
        green = 255
    blue = rgb[2]+ blueDelta
    if blue < 0:
        blue = 0
    if blue > 255:
        blue = 255

    rgb = (red,green,blue) 

    # draw shoots
    iteration = iteration - 1
    if iteration > 0:
        randomAngle = random.randint(angleDelta1,angleDelta2)
        drawseg(screen,x2,y2,int(length/div),angle+randomAngle,iteration,rgb)
        drawseg(screen,x2,y2,int(length/div),angle-randomAngle,iteration,rgb)

    return

def main():
    pygame.init()
    screen = pygame.display.set_mode((screenwidth,screenheight))
    pygame.display.set_caption('fractal trees')

    screen.fill(THECOLORS["white"])

    length = 100
    angle = 90
    xc = 0
    yc = 0
    rgb = (255,255,255)

    drawseg(screen, xc, yc, length, angle, totalIterations,rgb)

    pygame.display.update()

    # The Main Event Loop
    done = False
    while not done:
        # Event Handling:
        events = pygame.event.get( )
        for e in events:
            if( e.type == QUIT ):
                done = True
                break
            elif (e.type == KEYDOWN):
                if( e.key == K_ESCAPE ):
                    done = True
                    break
                if( e.key == K_f ):
                    pygame.display.toggle_fullscreen()
    print ("Exiting!")

    return
if __name__=="__main__":
    main()


;=======================================================================
; Demo of a Clock
;=======================================================================
            device sx28l,oschs3
            device turbo,stackx,optionx
            IRC_CAL IRC_SLOW
            reset start_point
            freq 50000000 ; 50 MHz

            org 10
rtcc1       ds 1
store2      ds 1
store1      ds 1
bit_count   ds 1
tempSP      ds 1
digit1      ds 1
digit2      ds 1
digit3      ds 1
digit4      ds 1
            org 0
isr         ; interrupt handler
            inc rtcc1 ; this is the variable that the delay loop checks
            reti

start_point ; set RTCC to internal clock 1:256 ratio
            mov !option,#$87

            ; using port b as output to ser_>par shift registers
            mov !rb,#$00

            ; set the digits in the display to 0000
            mov digit1, #$00
            mov digit2, #$00
            mov digit3, #$00
            mov digit4, #$00

loop        ; clock demo
            inc digit1
            cjne digit1, #$0A, :skp_over
            mov digit1, #$00

            inc digit2
            cjne digit2, #$0A, :skp_over
            mov digit2, #$00

            inc digit3
            cjne digit3, #$0A, :skp_over
            mov digit3, #$00

            inc digit4
            cjne digit4,#$0A, :skp_over
            mov digit4, #$00

:skp_over
            call display
            call delay10ms
            jmp loop

display     ; Display all 4 digits
            and digit1,#$0F
            and digit2,#$0F
            and digit3,#$0F
            and digit4,#$0F
            mov store1, digit2 ; move digit2 into register 1
            swap store1
            or store1, digit1 ; place digit1 in bottom of byte

            mov store2, digit4  ; move digit4 into register 2
            swap store2
            or store2, digit3 ; place digit3 in bottom of byte

            ; move the bytes in the register
            call ser_par_1
            call ser_par_2

            ret

ser_par_2   ;move a byte into ser->par shift register 2

            mov bit_count, #$08
            mov tempSP, store2

            ; prepare register for data, clear current contents
            clrb rb.0 ; A
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2
            call cyc_clock2

:loop       rr tempSP
            jnc :store0
            ; store 1 and cycle clock
            setb rb.0 ; A
            call cyc_clock2
            djnz bit_count,:loop
            ret

:store0     ; store 0 and cycle clock
            clrb rb.0 ;A
            call cyc_clock2
            djnz bit_count,:loop
            ret

cyc_clock2  clrb rb.1 ; toggle clock to 0 and pause
            nop
            nop
            nop
            setb rb.1 ; toggle clock to 1 and pause
            nop
            nop
            nop
            ret

ser_par_1   ;move a byte into ser->par shift register 1

            mov bit_count, #$08
            mov tempSP, store1

            ; prepare register for data, clear current contents
            clrb rb.2 ; A
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1
            call cyc_clock1

:loop       rr tempSP
            jnc :store0
            ; store 1 and cycle clock 
            setb rb.2
            call cyc_clock1
            djnz bit_count,:loop
            ret

:store0     ; store 1 and cycle clock
            clrb rb.2
            call cyc_clock1
            djnz bit_count,:loop
            ret

cyc_clock1  clrb rb.3 ; toggle clock to 0 and pause
            nop
            nop
            nop
            setb rb.3 ; toggle clock to 1 and pause
            nop
            nop
            nop
            ret

delay10ms   clr rtcc
            clr rtcc1

:wait       mov w,#$07          ; 07A1 is 1953
            mov w,rtcc1-w
            jnz :wait
:wait0      mov w,#$A1
            mov w,rtcc-w
            jnz :wait0
            ret