Program for operating the buttons on the EPS-103 unit (and controlling the motors)
It is great to be able to write any inscription on the display. EPS-103 can tell us what our program is doing. Another peripheral that needs to be mastered is the keyboard. The EPS-103 has a total of 16 switches. Above all, two large buttons Up and Down. Under the opening door we find buttons 0 to 9 and button E. And a three-position MODE switch, which actually works as three separate switches, one of which is always permanently closed.
The 16 switches are wired into a 4 x 4 wire matrix. The input to the matrix is connected to port A of circuit 8255A, i.e. at address 5800h. Only the bottom half of the port is active. The upper four bits are unused according to the scheme. Here would be the option to expand the keyboard with additional user buttons. The output from the matrix is directed to port B of the 8255A circuit, i.e. to address 5801h. Again, only the lower four bits are active. On the top half of the port, internal jumpers W1, W2 and W3 labeled Test are connected according to the diagram.
Using the matrix keyboard is quite simple. The binary number 1110 (1101, 1011, 0111) is written to the input and then the output is read from the matrix. According to its binary value (1111, 1110, 1101, 1011, 0111), we know which switch was activated. The value 1111 means that no button is pressed. The following table shows the values of each switch. An asterisk replaces bits that do not affect the function of the keyboard. The program usually overwrites them with all ones.
Write Address 5800h |
Read Address 5801h |
Button EPS-103 |
MyCode | ||||
bin | hex | bin | hex | 4600h | 4601h | ||
Group 0 | 1111 1110 | 0FE | **** 1110 | 0*E | 1 | 01h | |
**** 1101 | 0*D | 2/E | 02h | ||||
**** 1011 | 0*B | 3/U | 03h | ||||
**** 0111 | 0*7 | 4/W | 04h | ||||
Group 1 | 1111 1101 | 0FD | **** 1110 | 0*E | 5 | 05h | |
**** 1101 | 0*D | 6 | 06h | ||||
**** 1011 | 0*B | 7 | 07h | ||||
**** 0111 | 0*7 | 8/D | 08h | ||||
Group 2 | 1111 1011 | 0FB | **** 1110 | 0*E | 9 | 09h | |
**** 1101 | 0*D | 0 | 0Ah | ||||
**** 1011 | 0*B | E | 0Bh | ||||
**** 0111 | 0*7 | Not used | - | - | |||
Group 3 | 1111 0111 | 0F7 | **** 1110 | 0*E | PRG | 10h | |
**** 1101 | 0*D | TCH | 20h | ||||
**** 1011 | 0*B | RUN + Up | 30h | 0Ch | |||
**** 0111 | 0*7 | RUN + Down | 30h | 0Dh |
The RUN switch is wired a little differently than the other switches. It is in series with the Up and Down buttons. This means that using the PRG or TCH switch disables the Up and Down buttons. Therefore, it is necessary to first identify these three buttons and the Up and Down buttons with them in the first loop. Only subsequently in the second loop do we scan the remaining buttons. After identifying the pressed key, we can start an activity. For example, to write on the display which key is pressed.
This program will need RAM to run. We will use the space allocated to the stack. The stack starts at address 4700h and fills towards lower values. That's why I chose the addresses 4600h to 4603h. They're pretty close to the stack so the original firmware definitely doesn't use them for anything else. At the same time, they are far enough from the beginning of the stack that our program does not overwrite them with other data when using the stack.
At address 4600h, we write the code of the pressed key from the trio PRG, TCH, RUN. If any of the remaining keys is pressed, we write its code at address 4601h. It only makes sense to print the name of the pressed button on the display when it has changed. Therefore, control addresses 4602h and 4603h are also used. We copy the values of the main addresses 4600h and 4601h into them. By comparing the originals and copies, we can see if the status of the keyboard has changed.
Keyboard scanning can be part of the main program. But usually repeated actions are handled by some kind of interrupt. The EPS-103 has a generated HW interrupt RST7.5 from the NE555 pulse generator. The pulse period and thus the interruption repetition is 4 ms. This could be used to operate the keyboard. We just need to make sure that our handler finishes before the interrupt fires again. For example, clearing the LCD display takes 15ms according to the documentation. This means that we will not be able to operate the display during the interruption.
For study reasons, we will use the RST7.5 interrupt, but only to scan the keyboard. We write the result of the scan into RAM at addresses 4600h and 4601h. There will be a loop in the main program that will read these addresses and perform processing based on the contents, including writing to the display.
The introduction of the program is the same as the program Hello world. We first set the stack address in RAM to 4700h and then initialize the 8255A circuit. Then we need to set the interrupt correctly. RST7.5 needs to be enabled by writing 0 to bit 2 of the interrupt mask register. This is done by the SIM instruction. Then we can trigger the interrupt with the EI instruction. The infinite loop will repeatedly read addresses 4600h and 4601h and compare them to the copies at addresses 4602h and 4603h. If they differ, we first update the copies. Then we erase the entire display and write the setting of the MODE switch on it, followed by the name of the pressed button. The program does not handle the possibility of pressing multiple buttons at the same time.
In order for the program to have a specific function after all, we can activate the motors in the corresponding direction by pressing the 2/E, 3/U, 4W and 8/D buttons. After releasing the button, we stop the engine. If someone modifies the program to look for more simultaneous button presses, they have to think carefully about how to respond to them. For example, it's fine to run both the azimuth and elevation motors at the same time. It makes no sense to activate the rotation of one motor on both sides at the same time. Changing the direction of rotation while the motor is running would likely damage the motor.
The motors interface is connected to port C of circuit 8255A, i.e. at address 5802h. The motors are controlled by writing a control byte to this port. The meaning of individual bits is as follows:
Address bits 5802h | Function |
0 | Elevation direction: 0 = Up; 1 = Down |
1 | Elevation motor: 0 = Off; 1 = On |
2 | High Speed: 0 = Off; 1 = On |
3 | Azimuth direction: 0 = West; 1 = East |
4 | Azimuth motor: 0 = Off; 1 = On |
5 | Not used |
6 | Not used |
7 | Not used |
A corresponding program might look like this:
jmp Start
.ORG 003Ch
jmp IntRST75
.ORG 0040h
.dseg
nokey .text "and no other key is pressed"
key1 .text "and Key 1 is pressed"
key2 .text "and Key 2/E is pressed"
key3 .text "and Key 3/U is pressed"
key4 .text "and Key 4/W is pressed"
key5 .text "and Key 5 is pressed"
key6 .text "and Key 6 is pressed"
key7 .text "and Key 7 is pressed"
key8 .text "and Key 8/D is pressed"
key9 .text "and Key 9 is pressed"
keyA .text "and Key 0 is pressed"
keyE .text "and Key E is pressed"
keyup .text "and Key Up is pressed"
keydn .text "and Key Down is pressed"
keyprg .text "MODE PRG "
keytch .text "MODE TCH "
keyrun .text "MODE RUN "
.cseg
Start: lxi sp, 4700h ; Set Stack Address
mvi a, 82h ; CW
sta 5803h ; 8255A Write Control Word
xra a ; 0 -> a
sta 5802h ; 0 -> port C: stop motors
mvi a, 0Ch ; LCD Command: Display on, cursor off
call sub_1B70 ; Command -> LCD
mvi a, 00h
sta 4600h ; Initialization RAM
sta 4601h ; Initialization RAM
sta 4602h ; Initialization RAM
sta 4603h ; Initialization RAM
mvi a, 0Bh ; Mask interrupt, 0000 1011, 0 -> bit2
sim ; Set mask
ei ; Enable Interrupt
TestKey: lxi h, 4600h ; Source RAM
mov a, m ; value -> a
lxi h, 4602h
cmp m ; Compare the value with the copy
jnz TestCont ; if value 4600h <> value 4602h, jump
lxi h, 4601h ; Source RAM
mov a, m ; Compare the value with the copy
lxi h, 4603h
cmp m ; Compare the value with the copy
jz TestKey ; if value 4601h = value 4603h, nothing changed, test again
TestCont: lxi h, 4600h ; Source RAM
mov a, m ; value -> a
sta 4602h ; copy
lxi h, 4601h ; Source RAM
mov a, m ; value -> a
sta 4603h ; copy
mvi a, 01h ; LCD Command: Clear Display
call sub_1B70 ; Command -> LCD
lxi h, 4600h ; Mode switch MyCode
mov a, m ; Switch status
cpi 10h ; Is the PRG key?
jnz Test20 ; If not, jump
lxi h, keyprg ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
jmp Test01
Test20: lxi h, 4600h ; Mode switch MyCode
mov a, m ; Switch status
cpi 20h ; Is the TCH key?
jnz Test30 ; If not, jump
lxi h, keytch ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
jmp Test01
Test30: lxi h, keyrun ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
Test01: lxi h, 4601h
mov a, m ; Key MyCode
cpi 01h ; Is the 1 key?
jnz Test02 ; If not, jump
lxi h, key1 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test02: lxi h, 4601h
mov a, m ; Key MyCode
cpi 02h ; Is the 2/E key?
jnz Test03 ; If not, jump
lxi h, key2 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 18h ; 0001 1000 East = On
sta 5802h
jmp TestKey ; Neverending loop
Test03: lxi h, 4601h
mov a, m ; Key MyCode
cpi 03h ; Is the 3/U key?
jnz Test04 ; If not, jump
lxi h, key3 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 02h ; 0000 0010 Up = On
sta 5802h
jmp TestKey ; Neverending loop
Test04: lxi h, 4601h
mov a, m ; Key MyCode
cpi 04h ; Is the 4/W key?
jnz Test05 ; If not, jump
lxi h, key4 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 10h ; 0001 0000 West = On
sta 5802h
jmp TestKey ; Neverending loop
Test05: lxi h, 4601h
mov a, m ; Key MyCode
cpi 05h ; Is the 5 key?
jnz Test06 ; If not, jump
lxi h, key5 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test06: lxi h, 4601h
mov a, m ; Key MyCode
cpi 06h ; Is the 6 key?
jnz Test07 ; If not, jump
lxi h, key6 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test07: lxi h, 4601h
mov a, m ; Key MyCode
cpi 07h ; Is the 7 key?
jnz Test08 ; If not, jump
lxi h, key7 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test08: lxi h, 4601h
mov a, m ; Key MyCode
cpi 08h ; Is the 8/D key?
jnz Test09 ; If not, jump
lxi h, key8 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 03h ; 0000 0011 Down = On
sta 5802h
jmp TestKey ; Neverending loop
Test09: lxi h, 4601h
mov a, m ; Key MyCode
cpi 09h ; Is the 9 key?
jnz Test0A ; If not, jump
lxi h, key9 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0A: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Ah ; Is the 0 key?
jnz Test0B ; If not, jump
lxi h, keyA ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0B: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Bh ; Is the E key?
jnz Test0C ; If not, jump
lxi h, keyE ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0C: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Ch ; Is the Up key?
jnz Test0D ; If not, jump
lxi h, keyup ; Address of text
mvi b, 15h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0D: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Dh ; Is the Down key?
jnz TestNoK ; If not, jump
lxi h, keydn ; Address of text
mvi b, 17h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
TestNoK: lxi h, nokey ; Address of text
mvi b, 1Bh ; Number of characters
call WrStr ; Text -> LCD
mvi a, 00h
sta 5802h ; 0 -> PortC: stop motors
jmp TestKey ; Neverending loop
hlt ; For sure
; Write 1 byte command
sub_1B70: call sub_1B7E ; Wait for LCD ready
sta 4802h ; Write command byte
ret
; Write 1 byte data
sub_1B77: call sub_1B7E ; Wait for LCD ready
sta 4803h ; Write data byte
ret
; Wait for LCD ready
sub_1B7E: push psw
loc_1B7F: lda 4800h ; Read LCD status
ani 80h ; Test bit 7: 0 = ready
jnz loc_1B7F ; if not, repeat
pop psw
ret
; Write string on LCD
WrStr: mov a, m
call sub_1B77 ; Character -> LCD
inx h ; Address of next characters
dcr b ; Decrement counter
jnz WrStr
ret
; Interrupt handling RST7.5
IntRST75: push psw ; Save the contents of the registers to the stack
push b
push d
push h
mvi a, 0F7h
sta 5800h ; Scan Group 3
lda 5801h
ori 0F0h
cpi 0FEh ; Is the PRG key?
jnz TCH_20 ; If not, jump
mvi a, 10h ; The key is PRG, MyCode = 10h
sta 4600h
jmp ScanGr0 ; Go scan group 0
TCH_20: cpi 0FDh ; Is the TCH key?
jnz RUN_30 ; If not, jump
mvi a, 20h ; The key is TCH, MyCode = 20h
sta 4600h
jmp ScanGr0 ; Go scan group 0
RUN_30: cpi 0FBh ; Are the RUN + Up keys?
jnz RUN_31 ; If not, jump
mvi a, 30h ; The key is RUN, MyCode = 30h
sta 4600h
mvi a, 0Ch ; and the key is Up, MyCode = 0Ch
sta 4601h
jmp EndISR ; Finish scanning
RUN_31: cpi 0F7h ; Are the RUN + Down keys?
jnz RUN_32 ; If not, jump
mvi a, 30h ; The key is RUN, MyCode = 30h
sta 4600h
mvi a, 0Dh ; and the key is Up, MyCode = 0Dh
sta 4601h
jmp EndISR ; Finish scanning
RUN_32: mvi a, 30h ; The key must be RUN
sta 4600h
ScanGr0: mvi a, 0FEh ; Mask Group 0
sta 5800h ; Write mask
lda 5801h ; Scan Group 0
ori 0F0h
cpi 0FEh ; Is the 1 key?
jnz Gr0_2 ; If not, jump
mvi a, 01h ; The key is 1, MyCode = 01h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_2: cpi 0FDh ; Is the 2/E key?
jnz Gr0_3 ; If not, jump
mvi a, 02h ; The key is 2/E, MyCode = 02h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_3: cpi 0FBh ; Is the 3/U key?
jnz Gr0_4 ; If not, jump
mvi a, 03h ; The key is 3/U, MyCode = 03h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_4: cpi 0F7h ; Is the 4/W key?
jnz Gr1_1 ; If not, jump
mvi a, 04h ; The key is 4/W, MyCode = 04h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_1: mvi a, 0FDh ; Mask Group 1
sta 5800h ; Write mask
lda 5801h ; Scan Group 1
ori 0F0h
cpi 0FEh ; Is the 5 key?
jnz Gr1_2 ; If not, jump
mvi a, 05h ; The key is 5, MyCode = 05h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_2: cpi 0FDh ; Is the 6 key?
jnz Gr1_3 ; If not, jump
mvi a, 06h ; The key is 6, MyCode = 06h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_3: cpi 0FBh ; Is the 7 key?
jnz Gr1_4 ; If not, jump
mvi a, 07h ; The key is 7, MyCode = 07h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_4: cpi 0F7h ; Is the 8/D key?
jnz Gr2_1 ; If not, jump
mvi a, 08h ; The key is 8/D, MyCode = 08h
sta 4601h
jmp EndISR ; Finish scanning
Gr2_1: mvi a, 0FBh ; Mask Group 2
sta 5800h ; Write mask
lda 5801h ; Scan Group 2
ori 0F0h
cpi 0FEh ; Is the 9 key?
jnz Gr2_2 ; If not, jump
mvi a, 09h ; The key is 9, MyCode = 09h
sta 4601h
jmp EndISR ; Finish scanning
Gr2_2: cpi 0FDh ; Is the 0 key?
jnz Gr2_3 ; If not, jump
mvi a, 0Ah ; The key is 0, MyCode = 0Ah
sta 4601h
jmp EndISR ; Finish scanning
Gr2_3: cpi 0FBh ; Is the E key?
jnz NoKeyIs ; If not, jump
mvi a, 0Bh ; The key is E, MyCode = 0Bh
sta 4601h
jmp EndISR ; Finish scanning
NoKeyIs: mvi a, 00h ; no key is pressed, MyCode = 00h
sta 4601h
; Finish scanning
EndISR: pop h ; Restore the contents of the registers from the stack
pop d
pop b
pop psw
ei ; Enable Interrupt
ret
.END
The resulting binary file is loaded by the programmer into the erased EPROM memory and can be tested. I believe the information provided in this program will help anyone who would like to research the original EPS-103 firmware. You can download the source text of the mentioned program in the file KeyboardEPS103.asm..