COSMAC 1802 LCD Timer


Introduction to the COSMAC 1802 LCD Timer

In this section, we’ll build a COSMAC 1802 LCD Timer. I’m using the CDP1802 Microprocessor kit for this with an LCD display. The code will also display the value of the timer (up to 255) on the LED indicators. Basically, to run the watch, just go to memory location 8000, and press GO. In the future I’ll add new features to this stop watch using the EF1 and EF2 inputs for start and stop control. For now, we’ll just keep things simple. I’m just learning the COSMAC myself, so I’m sure there are better ways of doing this, but projects like this help us all to learn. Every programmer has different methods.

I’m using the A18 Assembler to give us an Intel Hex file. After that, we can load that hex file onto the COSMAC. I’m using the LST file below as an example, and you can use the object code if you prefer keypad entry.

For the LCD control, I used the CDP1802 Microprocessor Kit workbook as an example.

Create Aliases

Before we begin, there are a few things we need to do. We’ll create some aliases that will help us with the logic later on. Additionally, we need to set the files origin at 8000H. As I said before, you may need to manipulate the code for it to work on your own COSMAC unit.

   7200                 LCD_CWR:	EQU	7200H
   7201                 LCD_DWR:	EQU	7201H
   7202                 LCD_CRD:	EQU	7202h
   7203                 LCD_DRD:	EQU	7203H
                        
   8000                 	org	8000H
   0000                 R0	EQU	0
   0001                 R1	EQU	1
   0002                 R2	EQU	2
   0003                 R3	EQU	3
   0004                 R4	EQU	4
   0005                 R5	EQU	5
   0006                 R6	EQU	6
   0007                 R7	EQU	7
   0008                 R8	EQU	8
   0009                 R9	EQU	9
   000a                 RA	EQU	10
   000b                 RB	EQU	11
   000c                 RC	EQU	12
   000d                 RD	EQU	13
   000e                 RE	EQU	14
   000f                 RF	EQU	15

Initialize the Registers for the COSMAC 1802 LCD Timer

At this point, we need to put some initial values into the registers. That way, our display is sure to start at zero. At the end of this section, we’ll simply jump to the main loop.

   8000   f8 00         		LDI	00H
   8002   5b                            STR	RB
   8003   f8 70         		LDI	70H
   8005   ba            		PHI	RA
   8006   f8 00         		LDI	00H
   8008   aa            		PLO	RA
   8009   bb            		PHI     RB
   800a   ab            		PLO     RB
   800b   b7            		PHI	R7
   800c   a7            		PLO	R7
   800d   b8            		PHI	R8
   800e   a8            		PLO	R8
   800f   b9            		PHI	R9
   8010   a9            		PLO	R9
   8011   5a                            STR     RA
   8012   30 1c                         BR	MAIN

Create the LCD Subroutine

Next, we need a way to handle the LCD, and wait until it’s ready. Keep in mind that we are initially skipping over this subroutine. Later on, though, our logic will set the program counter to R3 to execute this routine. This subroutine is from the CDP1802 workbook. Basically, we enter the subroutine at memory cell 8015 We load R5, and AND this value with 80H. In other words, we check bit 3 to be high. If it’s not high, then the result is zero. Therefore, we continue to loop through this subroutine until the LCD is ready. Once it’s ready, then we branch up to 8014 where we set the program counter back to zero. This causes our logic to resume. This is a typical method in the COSMAC for calling subroutines.

  ;LCD DRIVERS
   8014   d0            RET_LCD1:	SEP	R0
   8015   05            LCD_READY:	LDN	R5
   8016   fa 80         		ANI	80H
   8018   3a 15         		BNZ     LCD_READY
   801a   30 14         		BR	RET_LCD1

Initialize the LCD Registers

Keep in mind that the COSMAC uses a lot of indirect addressing. We need to set some registers to point to the command and data registers for the LCD. Later on, when we store values with the STR command, those values do not go to the registers themselves. They go to the memory location the register contains. After setting up the registers to point to the correct memory locations, we place the value of 1 into register 4 to enable the LCD and set it up to receive data.

   801c   f8 72 b4 f8   MAIN:	LOAD	R4, LCD_CWR
   8020   00 a4         
   8022   f8 72 b5 f8   		LOAD	R5, LCD_CRD
   8026   02 a5         
   8028   f8 72 b6 f8   		LOAD    R6, LCD_DWR
   802c   01 a6         
   802e   f8 80 b3 f8   		LOAD	R3, LCD_READY
   8032   15 a3         
   8034   d3            		SEP	R3
   8035   f8 01         		LDI	01H
   8037   54            		STR	R4
   8038   d3            		SEP	R3

Write to the LCD

Later in this post, we’ll have counters in register 8 and 9. To optimize usage of the registers, I used both the high and low bytes to store each digit we need to send to the LCD. The R9 High byte contains the data for the most significant digit (on the left). After that, R9 Low, contains the next digit, R8 High contains the third digit, and R8 Low contains the last digit. This gives us the ability to count to 9999 seconds.

Because the LCD displays data in ASCII, we need to add 30H to each digit to get into the numerical section of the ASCII code. After we write each digit to the R6 register, we need to set the program counter back to R3. This will execute the subroutine that waits for the LCD to become ready again. Remember, once the LCD is ready, our routine sets the program counter back to R0. This allows the COSMAC to resume execution where we left off. After we write all four digits to the display, we’ll branch down to the TIMER logic.

   8039   99            		GHI	R9
   803a   fc 30                         ADI     30H
   803c   56            		STR	R6
   803d   d3            		SEP	R3
   803e   89            		GLO	R9
   803f   fc 30         		ADI     30H
   8041   56            		STR	R6
   8042   d3            		SEP	R3
   8043   98            		GHI	R8
   8044   fc 30         		ADI     30H
   8046   56            		STR	R6
   8047   d3            		SEP	R3
   8048   88            		GLO	R8
   8049   fc 30         		ADI     30H
   804b   56            		STR	R6
   804c   d3            		SEP	R3
   804d   30 4f         		BR	TIMER

Set up your Delays for the COSMAC 1802 LCD Timer

In order to increment our seconds, we need to keep the processor busy until one second passes. Here, I am creating 4 delay loops. I’m nesting three of the loops. By adjusting any one of the values in INIT0, INIT1, or INIT2, you are performing a “Course Adjust” to the time delay. The value of INIT3 is for a FINE adjust. If you find that you need even more fine tuning, you can simply add some NOP’s to the fine adjust loop. For very fine tuning, you can add NOPS after the loop. I could have used the 10ms tick as a timer, but not everyone will have that available. That’s why I chose to use the delay loops.

                        ;R8 is two LSDS
                        ;R9 is two MSDS
                        ;RA is for the GPIO
                        ;RB is the counter
                        ;RC is loop 0
                        ;RD is loop 1
                        ;RE is loop 2
                        ;RF is loop 3 fine tune
   804f   d0            TIMER:		SEP	R0
   8050   f8 10         INIT0:		LDI	010H
   8052   ac            DLY0:		PLO	RC
   8053   f8 10         INIT1:		LDI	010H
   8055   ad            DLY1:		PLO	RD
   8056   f8 de         INIT2:		LDI	0DEH
   8058   ae            DLY2:		PLO	RE
   8059   2e            		DEC	RE
   805a   8e            		GLO	RE
   805b   3a 58         		BNZ	DLY2
   805d   2d            		DEC	RD
   805e   8d            		GLO	RD
   805f   3a 55         		BNZ	DLY1
   8061   2c            		DEC	RC
   8062   8c            		GLO	RC
   8063   3a 52         		BNZ	DLY0
   8065   f8 f0         INIT3:	        LDI	0F0H
   8067   af            DLY3:	        PLO	RF
   8068   2f            		DEC	RF
   8069   8f            		GLO	RF
   806a   c4            		NOP
   806b   3a 67         		BNZ	DLY3

Separate out the Digits

Remember, for the LCD display, we need all of the digits to be separate. Keep in mind that R9 High/Low contain the most significant digits. R8 High/Low contain the least significant digits. To begin, we get the value of RB add 1, and store this value back to RB. The problem, though, is that we don’t have a DECIMAL mode like we have on the 6502. For this reason, after the ONES place gets to 0AH, we need to branch to the TENS. This will reset the ONES portion of R8, and increment the TENS portion. We repeat this for all four digits.

After we reach 9999 seconds, we’ll just have it roll over. You can extend this to as many digits as you need. In this case, four was enough for me.

After that, we go back to MAIN.

   806d   8b            		GLO	RB
   806e   fc 01         		ADI	01H
   8070   ab            		PLO	RB
   8071   5a            		STR	RA
   8072   88            ONES:		GLO	R8
   8073   fc 01         		ADI	01H
   8075   a8            		PLO	R8
   8076   88            		GLO	R8
   8077   fb 0a         		XRI	0AH
   8079   3a 1c         		BNZ	MAIN
   807b   30 7d         		BR	TENS
   807d   f8 00         TENS:		LDI	00H
   807f   a8            		PLO	R8
   8080   98            		GHI     R8
   8081   fc 01         		ADI	01H
   8083   b8            		PHI	R8
   8084   98            		GHI	R8
   8085   fb 0a         		XRI	0AH
   8087   3a 1c         		BNZ	MAIN
   8089   30 8b         		BR	HUNDREDS
   808b   f8 00         HUNDREDS:	LDI	00H
   808d   b8            		PHI	R8
   808e   89            		GLO	R9
   808f   fc 01         		ADI	01H
   8091   a9            		PLO	R9
   8092   89            		GLO	R9
   8093   fb 0a         		XRI	0AH
   8095   3a 1c         		BNZ	MAIN
   8097   30 99         		BR	THOUSANDS
   8099   f8 00         THOUSANDS:	LDI	00H
   809b   a9            		PLO	R9
   809c   99            		GHI	R9
   809d   fc 01         		ADI	01H
   809f   b9            		PHI	R9
   80a0   99            		GHI	R9
   80a1   fb 0a         		XRI	0AH
   80a3   3a 1c         		BNZ	MAIN
   80a5   30 a7         		BR	ZERO
   80a7   f8 00         ZERO:		LDI	00H
   80a9   a9            		PLO	R9
   80aa   30 1c         		BR	MAIN
   80ac                 		END

Run Your Code

Once you have the logic into the unit, you can go back to the programs starting address. In this case, that is 8000H. Press GO, and your timer should run!

For more information, check out the COSMAC Category page!

— Ricky Bryce

Leave a comment

Your email address will not be published. Required fields are marked *