RC2024 – Part 8 – Refining The Rotary Encoder Z80 Assembly Language Program

RC2014 with Rotary Encoder and Digital IO modules

In my last Retro Challenge post I was able to read the rotary encoder from Z80 assembly language on my RC2014 Classic 2 computer. However, the code was too sensitive. This was because I was looking for all changes to the CLK1 signal, instead of just the transition from low to high.

This turned out to be simpler than I though, and I was able to remove a large(ish) block of code. The old code I had a check_case1 and check_case2 that can be removed and replaced with the following.

; now work out what direction we are moving.
; if CLK1 is 1 then we can can check DT1 to get the
; direction of rotation. If it's 0, we need to go 
; back to the start of the loop.
    ld  a,(input)          
    and CLK1
    cp  CLK1 
    jr  nz, loop            

; this is where we check DT1. If 1 we are turning left.
    ld  a, (input)
    and DT1
    cp  0 
    jr  nz, left

In the first part I’m loading the input data from memory. I then mask off everything but the CLK1 bit and see if it is high. If it’s not, I go back to the start of the program.

In the second part I do the same for the DT1 bit, but check if it’s low. If high, I jump to the turning left code. If it is low, I carry on the to the turning right code, which directly follows.

The completed code looks like this.

    OUTPUT rotaryencoderledcontrol2.z80

; On the RC2014 Classic 2 running from BASIC the Z80
; code runs from address $9000. 
    ORG $9000

; The input and output ports to use.
INPUT_PORT  EQU $DE
OUTPUT_PORT EQU $03

; The input bits from the rotary encoder.
CLK1    EQU %00000001
DT1     EQU %00000010
SW1     EQU %00000100

; show the inital led value
    ld  a,(output)
    out (OUTPUT_PORT),a

loop:
; load the last clk value into register b
    ld  a,(lastclk)
    ld  b,a

; read the input port and store in "input"
    in  a,(INPUT_PORT)
    ld  (input),a

; now check if the switch on first rotary encode has been
; pressed. If it has jump to end
    and SW1
    cp  SW1
    jr  z, end

; now see if clk1 matches the lastclk. If it does loop
    ld  a,(input)
    and CLK1
    ld  (lastclk),a
    cp  b
    jr  z, loop

; now work out what direction we are moving.
; if CLK1 is 1 then we can can check DT1 to get the
; direction of rotation. If it's 0, we need to go 
; back to the start of the loop.
    ld  a,(input)          
    and CLK1
    cp  CLK1 
    jr  nz, loop            

; this is where we check DT1. If 1 we are turning left.
    ld  a, (input)
    and DT1
    cp  0 
    jr  nz, left

; we must be turning right so rotate the output to the right
; and store it before going back to the start of the loop.
right:
    ld  a,(output)
    rrca
    out (OUTPUT_PORT),a
    ld  (output),a

    jr  loop

; we must be turning left so rotate the output to the left
; and store it before going back to the start of the loop.
left:
    ld  a,(output)
    rlca
    out (OUTPUT_PORT),a
    ld  (output),a

    jp  loop

; the switch has been pressed, so we clear the output
; and exit.
end:
    ld  a,0
    out (OUTPUT_PORT),a

    ret

input:
    db  0
output:
    db  %00000001
lastclk:
    db  0

I also swapped the JP instructions to JR instructions because the code is small. This saves a few bytes.

This is only a small refinement, but it makes the Rotary Encoder Module so much more usable.