Previously as part of this year’s Retro Challenge, I learnt how to build and run Z80 assembly language programs on my RC2014 Classic 2 computer.
I now want to recreate the BASIC program that moves LEDs using Z80 Assembly Language.
I am using IO address $DE for my Rotary Encoder module. I also have the Digital I/O module using IO address $03. I can define these as constants so I can easily change them if necessary.
I need to store values for the input, the output, and the last value of the CLK pin. The input will just be the value from the IN operation. The output value will be the byte we want to show on the LED output. I will set this to be %00000001 initially. When I turn the encoder, I want this to shift to either the left or right. If it reaches the edge, I want it to wrap. The Z80 operations RLCA and RRCA will do this for me.
To check if a bit is high or low, I can use an AND operation to mask out out other values. For example, to check if the SW1 is being pressed, I can do the following.
SW1 EQU %00000100
INPUT_PORT EQU $DE
in a,(INPUT_PORT)
and SW1
cp SW1
jp z, end ; jump to end if SW is high.
I can repeat this logic to check the values of CLK1 and DT1.
This is the code I have come up with.
OUTPUT rotaryencoderledcontrol.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
jp z, end
; now see if clk1 matches the lastclk. If it does loop
ld a,(input)
and CLK1
ld (lastclk),a
cp b
jp z, loop
; now work out what direction we are moving
check_case1:
ld a,(input)
and CLK1
cp CLK1
jr nz, check_case2
ld a,(input)
and DT1
cp DT1
jr z, left ; if both CLK and DT are high then left
check_case2:
ld a, (input)
and CLK1
cp 0
jr nz, right
ld a, (input)
and DT1
cp 0
jr nz, right
; 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
; 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
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
The LED successfully moves left and right depending on how I turn the rotary encoder. However, because I am checking in both high and low states of CLK1, it is moving two steps per turn. This will be too senstive to use in an application, so my next job is to change this to check once per turn.