Is SHIFT RIGHT (SFTR) implemented properly?

106 views
Skip to first unread message

Rolf Strand

unread,
May 22, 2021, 12:25:19 AMMay 22
to uKenbak-1
I am a newbie here, so maybe this was discussed before.

From the manual:
SHIFT RIGHT 1 
Original c7 c6 c5 c4 c3 c2 c1 c0
After      c7 c7 c6 c5 c4 c3 c2 c1

Shift right sign extends according to the manual

If I try this on nanoKENBAK-1:

023 377    LOAD A 377 
011            SFTR  A 1
034 200    STORE A OUTPUT
000            HALT

the output is 177  in stead of 377


Are there any other emulators I can try?

I have been working with this code and reporting issues:

I have this code that does 16bit math.
Running with some local patches to the IDE it seems to work.
If SFRT is on nanoKENBAK-1 does not sign extend shr1b will need to be modified
to emulate sign extension

; Simple thermostat with Anticipator cycler
; The control loop uses scaled 16 bit math
; The scale point is between the msb and lsb
;
; Psuedo code for the control
; antHeat is 4 
; relayState 0 or 1
; The anticipator cycler is a first order lag function.
; It converts the switch differential in to a proportional
; controller based on the proportional Perr. 
; With a switch differential of 1F, and antHeat of 4F
; the propotional band is 4F - 1F = 3F
; while (1)
; {
;    sense = getTemperature();
;    Perr = setpt - sense
;    ant += (antHeat * relayState - ant) >> 32
;    Cerr = Perr - ant
;    if (Cerr > 0) 
; relayState = 1;
;    else if (Cerr <= -1) 
; relayState = 0;
;    delay(cycleTime)
; }
;
; Hardware:
;
; The Temperature is provided in msb, lsb form from an external circuit
; to the INPUT of the KENBAK-1.  It is enabled by an external switch.
; b0 of the OUTPUT register selects: 1 = msb, 0 = lsb
;
; The control relay is connected to b2 of the OUTPUT
; antHeat is orged OUTPUT
; Anticipator heat is 4 degrees.
; This provides a short cut to antHeat * relayState:
; OUTPUT or antHeat*relaState is 4 when relay is on, 0 when off
; Application data
org 115
cycDly 1 ; 1 for debug
cycDlyh 0 ; 0 about 8ms per count
setpt 0
setpth 75
sense db
senseh db
Perr db
Perrh db
ant  db
anth db
Cerr db
Cerrh   db
loopCnt db ; Adjust org so this address is 127

org 128
antHeat db ; antHeat is b2 of OUTPUT


; Application program start
reset org 4 ; begining of program ram

control
; get the current temperature reading from INPUT
set 0,1,OUTPUT ; the input will now be the msb of the sensor
nop ; time to settle
;load B,INPUT
load B,73 ; debug at 73.5
set 0,0,OUTPUT ; the input will now be the lsb of the sensor
nop ; time to settle
;load A,INPUT
load A,128 ; debug at 73.5

; can optimize later to use neg16b and just add setpt
load X,#sense  ; point to sense
jmk store16b ; Save sensor reading for debug
; calc Perr = setpt - sense

load    X,#setpt ; point to spl
jmk load16b
load X,#sense ; point to sensl
jmk sub16b  ; Perr = setpt - sense
load X,#Perr ; point to Perr
jmk store16b ; save Perr

; update the anticipator cycler
; anticipator ant = ant + (HVAC - ant) / 32

load A,0 ; antHeat low is always 0
load B,antHeat ; BA = antHeat, note output all other OUTPUT pins assumed to be 0
load    X,#ant ; point to ant
jmk sub16b ; (antHeat - ant)
load X,5 ; shift 5 to divide by 32
jmk shr16b ; (antHeat - ant) >> 5
load X,#ant ; point to ant
jmk add16b ; ant += (antHeat - ant) >> 5
jmk store16b ; X still points to ant

; calculate the composite err
; cmperr = Perr - ant

jmk neg16b ; negate anticipator value in BA
load X,#Perr    ; point to Perr
jmk add16b ; Cerr = Perr - ant
load    X,#Cerr ; point to Cerr
jmk store16b ; store Cerr

; Update the relay state based on composite err
; if Cerr < 0 HVAC = 4 else if Cerr >= 1 HVAC = 0

jmp B,LT,chkOff ; Cerr > 0
rlyon set 2,1,OUTPUT ; Cerr > 0 so bit 2 relay output on
jmp cycleDelay
chkoff
load X,#S_ONE ; point to const scaled one
jmk add16b  ; Cerr + 1, if still positive then cerr >-1
jmp B,LT,rlyOff ; Cerr <= -1
jmp cycleDelay
rlyOff set 2,0,OUTPUT ; Cerr <= -1 so bit 2 relay output off

; anticiaptor time constant simulated here

cycleDelay
load A,cycDly ;delay constant for anticipator cycler
load B,cycDlyh ;delay constant for anticipator cycler
load X,#ONE    ;point to constant onel = 0x0001
delay jmk sub16b
jmp A,NE,delay
jmp B,NE,delay
load A,loopCnt
add A,1
store   A,loopCnt
jmp control


; -------------------- End Appplication ----------------------------------


; -------------------- 16 bit math routines ------------------------------
;
; The math routines are in upper half of RAM

org 0
lsb db ; used for 16bit math indexing
msb db ; used for 16bit math indexing

org 133 ;Skip over the registers

; 16 bit load, store, saveTmp
; on entry X points to value to load
; return msb = B, lsb = A

load16b db
load A,lsb+X
load B,msb+X
jmp (load16b)

store16b db
store   A,lsb+X
store   B,msb+X
jmp (store16b)

saveTmp db
store A,TMP16
store B,TMP16h
jmp (saveTmp)


; 16 bit math routines
; on entry
; B is msb A is lsb if first operand
; X is the address of the second operand
;
; result msb = B lsb = A 
; X is unchanged
; add16b
; sub16b
; neg16b
;

add16b db ; return address
add     A,lsb+X
skp     1,0,OCA ; skip if no carry 
add     B,1 ; adjust for carry
add     B,msb+X
jmp (add16b) ; return

sub16b  db ; return address
sub     A,lsb+X
skp     1,0,OCA ; skip if no carry
sub     B,1 ; adjust for carry
sub     B,msb+X
jmp (sub16b) ; return

neg16b db
store   X,TMPX ; save X
load X,#TMPN16    ; point to tmp
jmk store16b
load A,0 ; load 0 to BA
load B,0
jmk sub16b ; subtract input from 0
load    X,TMPX ; restore X
jmp (neg16b) ; return

; 16 bit div 2^n
; call with B = msb, A = lsb, X = places
; return B = msb, A = lsb, X = 0

shr16b  db
sloop   sft  A,R,1 ; shift right lsb
set    7,0,A ; clear lsb b7 
skp  0,0,B ; test b0 msb, skip it 0
set  7,1,A ; b0 lsb was 1 so set b7 lsb
sft  B,R,1 ; shift right msb, if negative sign extend
sub  X,1 ; dec place count
jmp  X,NE,sloop ; if not done again
jmp  (shr16b) ; return to caller

;Math system variables and constants
org 246
ZERO    0       ; math constant zero 0x0000
S_ONE 0 ; math constant scaled one low 0x0100
ONE  1 ;
ONEH 0 ; math constant one 0x0001
TMP16 db ; temporary 16b value
TMP16h db
TMPN16 db ; temp for neg16b
TMPN16h db
TMPX    db ; place to save X
RAM_END db ; If adding data items make sure this address is < 256

Raw octal dump:
The IDE assembler is consistent with itself
Not sure if it matches nanoKENBAK-1

000 000 000 004 102 200 200 123 111 002 200 200 023 200 223 167 
367 214 223 165 367 205 223 167 367 245 223 171 367 214 023 000 
124 200 223 173 367 245 223 005 367 301 223 173 367 232 367 214 
367 260 223 171 367 232 223 175 367 214 145 100 122 200 347 112 
223 367 367 232 145 110 347 112 022 200 024 163 124 164 223 370 
367 245 043 120 143 120 024 177 003 001 034 177 347 004 000 000 
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
000 000 000 001 000 000 113 000 000 000 000 000 000 000 000 000 
000 000 000 000 000 000 026 000 126 001 357 205 000 036 000 136 
001 357 214 000 034 372 134 373 357 223 000 006 000 212 201 103 
001 106 001 357 232 000 016 000 212 201 113 001 116 001 357 245 
000 234 376 223 374 367 214 023 000 123 000 367 245 224 376 357 
260 000 011 072 000 202 001 172 000 051 213 001 243 302 357 301 
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
000 000 000 000 000 000 000 000 001 000 000 000 000 000 000 000


Rolf Strand

unread,
May 22, 2021, 1:55:59 AMMay 22
to uKenbak-1
The emulator here sign extends on SFTR:

http://www.historicsimulations.com/kenbak1.html

Rolf Strand

unread,
May 22, 2021, 3:42:31 AMMay 22
to uKenbak-1

Above control converted to another assembler: (with some typos fixed)

# Simple thermostat with Anticipator cycler
# The control loop uses scaled 16 bit math
# The scale point is between the msb and lsb
#
# Psuedo code for the control
# antHeat is 4 
# relayState 0 or 1
# The anticipator cycler is a first order lag function.
# It converts the switch differential in to a proportional
# controler based on the proportional Perr. 
# With a switch differential of 1F, and antHeat of 4F
# the proportional band is 4F - 1F = 3F
# while (1)
# {
#    sense = getTemperature()#
#    Perr = setpt - sense
#    ant += (antHeat * relayState - ant) >> 32
#    Cerr = Perr - ant
#    if (Cerr > 0) 
# relayState = 1#
#    else if (Cerr <= -1) 
# relayState = 0#
#    delay(cycleTime)
# }
#
# Hardware:
#
# The Temperature is provided in msb, lsb form from an external circuit
# to the INPUT of the KENBAK-1.  It is enabled by an external switch.
# b0 of the OUTPUT register selects: 1 = msb, 0 = lsb
#
# The control relay is connected to b2 of the OUTPUT
# antHeat is orged OUTPUT
# Anticipator heat is 4 degrees.
# This provides a short cut to antHeat * relayState:
# OUTPUT or antHeat*relaState is 4 when relay is on, 0 when off
# Application data
        org    115
cycDly  db 1    # 1 for debug
cycDlyh db 0    # 0 about 8ms per count
setpt   db 0
setpth  db 75
sense   db 0
senseh  db 0
Perr    db 0
Perrh   db 0
ant     db 0
anth    db 0
Cerr    db 0
Cerrh   db 0
loopCnt db 0    # Adjust org so this address is 127
A       equ 0
B       equ 1
X       equ 2
PC      equ 3
OUTPUT  equ 128
OCA     equ 129
OCB     equ 130
OCX     equ 131
INPUT   equ 255

        org    128
antHeat db 0        # antHeat is b2 of OUTPUT


# Application program start
reset   org    4    # beginning of program ram
control
# get the current temperature reading from INPUT
        set1    OUTPUT,0        # the input will now be the msb of the sensor
        nop                     # time to settle
#        ldb    INPUT
        ldb     #73             # debug at 73.5
        set0    OUTPUT,0        # the input will now be the lsb of the sensor
        nop                     # time to settle
#        lda    INPUT    
        lda     #128            # debug at 73.5
# can optimize later to use neg16b and just add setpt
        ldx     #sense          # point to sense
        call    store16b        # Save sensor reading for debug
        
# calc Perr = setpt - sense

        ldx     #setpt          # point to spl
        call    load16b
        ldx     #sense          # point to sensl
        call    sub16b          # Perr = setpt - sense
        ldx     #Perr           # point to Perr
        call    store16b        # save Perr

# update the anticipator cycler
# anticipator ant = ant + (antHeat - ant) / 32

        lda     #0              # antHeat low is always 0
        ldb     antHeat         # BA = antHeat, note output all other OUTPUT pins assumed to be 0
        ldx     #ant            # point to ant
        call    sub16b          # (antHeat - ant)
        ldx     #5              # shift 5 to divide by 32
        call    shr16b          # (antHeat - ant) >> 5
        ldx     #ant            # point to ant
        call    add16b          # ant += (antHeat - ant) >> 5
        call    store16b        # X still points to ant
# calculate the composite err
# cmperr = Perr - ant

        call    neg16b          # negate anticipator value in BA
        ldx     #Perr           # point to Perr
        call    add16b          # Cerr = Perr - ant
        ldx     #Cerr           # point to Cerr
        call    store16b        # store Cerr

# Update the relay state based on composite err
# if Cerr < 0 HVAC = 4 else if Cerr >= 1 HVAC = 0

        jblt    chkOff          # Cerr > 0 
rlyon   set1    OUTPUT,2        # Cerr > 0 so bit 2 relay output on
        jmp     cycleDelay
chkoff
        ldx     #S_ONE          # point to const scaled one
        call    add16b          # Cerr + 1, if still positive then cerr >-1
        jblt    rlyOff          # Cerr <= -1
        jmp     cycleDelay
rlyOff  set0    OUTPUT,2        # Cerr <= -1 so bit 2 relay output off

# anticiaptor time constant simulated here

cycleDelay
        lda     cycDly          #delay constant for anticipator cycler
        ldb     cycDlyh         #delay constant for anticipator cycler
        ldx     #ONE            #point to constant onel = 0x0001
delay   call    sub16b
        jane    delay
        jbne    delay    
        lda     loopCnt
        adda    #1
        sta     loopCnt
        jmp     control 
# -------------------- End Appplication ----------------------------------


# -------------------- 16 bit math routines ------------------------------
#
# The math routines are in upper half of RAM

        org    0
lsb     db 0       # used for 16bit math indexing
msb     db 0       # used for 16bit math indexing

        org    133        #Skip over the registers

# 16 bit load, store, saveTmp
# on entry X points to value to load
# return msb = B, lsb = A

load16b db 0
        lda     lsb,x
        ldb     msb,x
        jmp     (load16b)

store16b db 0
        sta     lsb,X
        stb     msb,X
        jmp     (store16b)

saveTmp db 0
        sta     TMP16
        stb     TMP16h
        jmp     (saveTmp)

# 16 bit math routines
# on entry
#    B is msb A is lsb if first operand
#    X is the address of the second operand
#
#    result msb = B lsb = A 
#    X is unchanged
#    add16b
#    sub16b
#    neg16b
#    

add16b  db 0                # return address
        adda    lsb,X
        skp0    OCA,1       # skip if no carry 
        addb    #1         # adjust for carry
        addb    msb,X
        jmp     (add16b)    # return

sub16b  db 0                # return address
        suba    lsb,X
        skp0    OCA,1       # skip if no carry
        subb    #1         # adjust for carry
        subb    msb,X
        jmp     (sub16b)    # return

neg16b  db 0
        stx     TMPX        # save X
        ldx     #TMPN16     # point to tmp
        call    store16b
        lda     #0          # load 0 to BA
        ldb     #0
        call    sub16b      # subtract input    from 0
        ldx     TMPX        # restore X
        jmp     (neg16b)    # return

# 16 bit div 2^n
# call with B = msb, A = lsb, X = places
# return B = msb, A = lsb, X = 0

shr16b  db 0
sloop   shra     1          # shift right lsb
        set0     A,7        # clear lsb b7 
        skp0     B,0        # test b0 msb, skip it 0
        set1     A,7        # b0 lsb was 1 so set b7 lsb
        shrb     1          # shift right msb, if negative sign extend
        subx     #1         # dec place count
        jxne     sloop      # if not done again
        jmp      (shr16b)   # return to caller
        

#Math system variables and constants
        org 246
ZERO    db 0    # math constant zero 0x0000
S_ONE   db 0    # math constant scaled one low 0x0100
ONE     db 1    #
ONEH    db 0    # math constant one 0x0001
TMP16   db 0    # temporary 16b value
TMP16h  db 0    # 
TMPN16  db 0    # temp for neg16b
TMPN16h db 0    # 
TMPX    db 0    # place to save X
RAM_END db 0    # If adding data items make sure this address is < 256


Mark Wilson

unread,
May 22, 2021, 4:14:32 AMMay 22
to uKenbak-1
It does look like I missed that subtlety, I will investigate fixing it.
Potentially a breaking change, so I will probably add a flag for backward compatibility...

fcpr...@gmail.com

unread,
May 22, 2021, 10:15:13 AMMay 22
to uKenbak-1
I looked over my various contributed programs and this will break only one - SerialBlinkenLightsV2.1 (see µKenbak-1: Mindless serial port program). I will fix this to be independent of the shift-right instruction if and when Mark updates the firmware to sign-extend. I'll update that topic with the new version at that time.

The curious part is that I actually wrote this line of code, with exactly this comment:
        SHRB 1      # Make random number positive
I clearly made the same assumption without consulting the documentation; why I would have done that is beyond me because I am well aware that right shifts can be either zero-filled or sign-filled.

Nice catch to the OP!

Rolf Strand

unread,
May 22, 2021, 11:08:40 AMMay 22
to uKenbak-1
I only found it while implementing 16 bit shift right.  Needed the detail of the shift operator to implement it, so I had to read the manual.

MICHAEL GARDI

unread,
May 22, 2021, 12:27:28 PMMay 22
to uKenbak-1
Rolf is being modest. He is very thorough and has been a big help improving my KENBAK-1 IDE ( https://github.com/kidmirage/KENBAK-2-5-Build-Files) with bug reports and useful feature suggestions. Thanks Rolf.

Mark Wilson

unread,
May 22, 2021, 11:19:51 PMMay 22
to uKenbak-1
Thanks for finding that Rolf.
I have changed the right-shift instruction to preserve the high ("sign") bit.
In working on that, I found there was also a problem with the multi-bit roll left/right instructions so I fixed them too.
For example 004 rolled right 4x (0101) would give 0 rather than 0100
There is no compatibility flag but there is a #define in CPU.cpp (CPU_LEGACY_SHIFT_ROLL) which if defined, restores the "legacy" behaviour.
None of the built-in programs were broken by the change.
I have updated github.

fcpr...@gmail.com

unread,
May 23, 2021, 4:54:20 PMMay 23
to uKenbak-1
Thanks for the fix(es) Mark.

fcpr...@gmail.com

unread,
May 24, 2021, 10:57:32 AMMay 24
to uKenbak-1
On Saturday, May 22, 2021 at 11:19:51 PM UTC-4 funnypo...@gmail.com wrote:
None of the built-in programs were broken by the change.

Whoops, it seems that's not the case. The built-in program programCylon (STOP+BTN1) now only toggles bit 0. I didn't disassemble all of it, but I see a 011 in there (SHRA 1).

Mark Wilson

unread,
May 24, 2021, 3:02:25 PMMay 24
to uKenbak-1
Ah. I seached the assember versions but I think that's just raw bytes and I missed it. I will fix it shortly

Mark Wilson

unread,
May 24, 2021, 3:26:13 PMMay 24
to uKenbak-1
But really, it was just careless of me. I remember focusing on little test programs to check shift/roll, searching the asm source, running the clocks and Eratosthenes.  But I missed the most obvious program?
If I did that in my day job I'd be in trouble.
I apologize. I should be able to fix it tonight, NZ time.

Mark Wilson

unread,
May 25, 2021, 3:24:16 AMMay 25
to uKenbak-1
OK, Cylon is fixed on github.
Thanks for finding this.

Mark

fcpr...@gmail.com

unread,
May 25, 2021, 8:57:45 AMMay 25
to uKenbak-1
Great Mark! Re-flashed my µKenbak-1 and nanoKenbak-1 and all is well now.

Vicente González

unread,
May 26, 2021, 9:50:45 AMMay 26
to fcpr...@gmail.com, uKenbak-1
Thanks Rolf for your valuable work checking and validating the kenbakino firmware,
and more important, thanks Mark for keeping  your cool kenbakino up to date.
Vicente

--
You received this message because you are subscribed to the Google Groups "uKenbak-1" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ukenbak-1+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ukenbak-1/f83578bb-7dc2-413e-ba5c-e8031f99489dn%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages