A quick recap. The Zolatron 64 6502 homebrew machine is at the point where it can print messages to an LCD screen and send and receive messages via the serial port.
Most of the time.
The incoming messages are put in a buffer and then printed to the LCD. And a fat lot of use that is.
I wanted to move on to doing something a little more interesting. But first, I had a problem to solve.
Something’s come loose
Sometimes – actually, quite often – I’d make a minor change to the code, re-flash the EEPROM, fire up the Zolatron and … nothing. No message on the LCD, nothing coming down the serial connection. Nada.
It was driving me nuts. Why would an innocuous little change cause previously working code – code that had not been changed – to stop working? Well, the answer is obvious when you think about it – assuming you ever get around to thinking about it. And it’s something I mentioned in the previous post. Hardware problems.
The Zolatron is currently a breadboard computer using wire-wrapped connections (mostly). There’s a lot that can go physically wrong.
Out came the trusty multimeter, set to continuity checking mode. I started buzzing out all the connections. First I checked all the points where the data bus connects. Okay. Then the same for the address bus. Ditto. Also for the clock signal. No problem there.
Interestingly, a bit of probing with the oscilliscope – which I did during those times I got bored with buzzing out connections – suggested that the computer was actually running. At least, it was doing something, even if not the right thing.
Finally, I checked a connection from the CPU to the 6522 VIA that drives the LCD display. All the non-bus signal pins seemed fine. For the hell of it, I decided to check the data bus pins again. D0. Buzz. D1. Buzz. All the way to D7. Buz…
Hmm. It started buzzing, then stopped. Then started. Then stopped. Then started. I wiggled the wire. The buzzing stopped.
The wire running from D7 on the CPU to D7 on the VIA was buried too deep to easily remove. So I just ran a second one.
Haven’t had a lick of trouble since.
Having got over that hurdle I decided the next step was to make better use of the LCD. It’s a 16-character by 2-line display. But thus far I only had the ability to clear the screen and print to the first line. That’s a bit of a waste.
I’m used to having I2C backpacks on these displays. These come with their own firmware and you just send commands down the I2C bus to get the effects you want – such as moving the cursor. Reading the datasheet, I quickly realised that the display itself has relatively few commands that it understands. Clearly, the I2C firmware is doing a lot of work in combining these simple instructions to perform more complex operations. It’s a bit like the difference between Basic and assembler.
For now, all I wanted was the ability to position the cursor, so that I can use the second line.
Here’s how I did it.
.lcd_set_cursor ; assumes X & Y co-ords have been put in X and Y ; X should contain the X param in range 0-15. ; Y should be 0 or 1. lda #LCD_CURS_HOME ; send instruction to move the cursor to the home jsr lcd_cmd ; position. Doesn't affect existing text cpy #1 bcc lcd_move_curs ; Y is less than 1 txa ; otherwise, we want line 1. Put X value in A adc #39 ; add 39 tax ; store back in X .lcd_move_curs lda #LCD_CURS_R ; load A with 'move cursor right' instruction .lcd_curs_next_move cpx #0 beq lcd_set_curs_end ; end if X = 0 jsr lcd_cmd ; otherwise, executive the move cursor command dex ; decrement X jmp lcd_curs_next_move ; go round again .lcd_set_curs_end rts
A couple of things have already been defined elswhere. There’s an lcd_cmd subroutine that sends a command byte to the display. And at the beginning of the program we declare:
LCD_CURS_HOME = %00000010 ; return cursor to home position LCD_CURS_L = %00010000 ; shifts cursor to the left LCD_CURS_R = %00010100 ; shifts cursor to the right
Internally, the display actually has 40 cursor positions per line, even though it only displays 16. That’s useful because you can put text in those positions and then sideways scroll the whole display. I’m not going to get into that yet.
To get to the next line, you basically have to position text beyond the 40th position. So, in the code above, if the second line is specified (by having the value 1 in the Y register), then we add 39 to the X coordinate (naturally stored in the X register). Then, after sending the command to put the cursor at the ‘home’ position, we enter a loop to issue the ‘shift cursor right’ command a number of times corresponding to X.
So, to start printing from the beginning of the second line, you’d put 0 in the X register and 1 in the Y register. In the code above, X will be increased to 39.
The eagle-eyed may have spotted that I said 40 at one point and 39 at another. This confused me too for a long time. I mean, it works, but why?
I’m not entirely sure, but here’s how I have it in my head. The LCD display increments the counter for the cursor position at various times. When the cursor is ‘homed’, it internally sets the position to 1. (The LCD numbers from 1, I number from 0. [Update: Not true. More details to come]) In the example above, I add 39 to this, so now the counter is at 40. But the display increments the counter before printing a character. So when I come to actually print something, the counter gets incremented to 41 before the first character is printed. [Update: Also not quite right. More details to come in a future post.]
Anyway – like I said, it works, so let’s not mess with it.
So, here’s how I set the cursor.
ldx #0 : ldy #1 ; print version string on 2nd line of LCD jsr lcd_set_cursor
The code is on GitHub. It’s heavily commented, but if you’d like anything explained, just leave me a note below. I’ve now started to break it into multiple files, because I got tired of scrolling. Things like definitions of constants and subroutines have been broken out into files in the ‘include’ subdirectory.