I'm building a breadboard Z80-based computer. As now, I have the CPU hooked up to an EEPROM and an I/O device (an HD44780 character display) with appropriate decoding logic.
The ROM chip starts at address 0x0000 while the I/O device exposes two registers at addresses 0x00 and 0x01 when the IORQ pin is active (low).
I wrote the following program:
;HD44780 LCD test procedure
LCD_INSTR_REG: EQU %00000000
LCD_DATA_REG: EQU %00000001
;Reset procedure
ld a,%00111000
out (LCD_INSTR_REG),a
ld a,%00001000
out (LCD_INSTR_REG),a
ld a,%00000001
out (LCD_INSTR_REG),a
;Init procedure
ld a,%00111000
out (LCD_INSTR_REG),a
ld a,%00001110
out (LCD_INSTR_REG),a
;Write characters to display
ld a,%01000100
out (LCD_DATA_REG),a
ld a,%01100001
out (LCD_DATA_REG),a
ld a,%01101110
out (LCD_DATA_REG),a
ld a,%01101001
out (LCD_DATA_REG),a
ld a,%01100101
out (LCD_DATA_REG),a
ld a,%01101100
out (LCD_DATA_REG),a
ld a,%01100101
out (LCD_DATA_REG),a
ld a,%00100001
out (LCD_DATA_REG),a
halt
That, once compiled with GNU z80asm, translates to the following code:
You can see several 3E
instructions that load an immediate value to register A, interleaved by D3
instructions that load to I/O register 00 (and then 01) the value present in register A. The last instruction (76
) is the halt instruction.
The execution of the code causes the following bytes to appear on data bus (picked up with an Arduino used as a poor man's logic analyzer):
HD44780 debugger
DATA BUS HEX RS
00000000 0x00 I
00000000 0x00 I
00000000 0x00 I
00000000 0x00 I
00000000 0x00 I
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
00000000 0x00 D
Every line is the bus state at the IORQ descending edge (but I obtain the same result if I trigger my code on the rising edge).
You can see, from left to right: the value of the single data bus bits, the same value in hex, the value of A0, that select the I/O device register (instruction is LOW and data is HIGH).
As you can see, while the correct addresses are set, the CPU is writing all zeroes to the data bus. Did I misunderstand the way the OUT
instruction works? Otherwise, what could be the problem in my code?
Update
I decided to accept the answer from Spektre as it is complete and argumentative. By the way, note that in my case the problem was not due to timings, but two swapped lines in the address bus (so the CPU was reading in a whole different memory address, probably reading NOPs or garbage). My CPU was running at 140 Hz and I'd like to point out that, after fixing the addr bus pins, the LCD screen worked perfectly up to 14 kHz without needing to check the BUSY bit.
Over a certain frequency, BTW, adding delays as proposed by Spektre was not enough, as the signal was not kept on the data bus long enough for the LCD to read it. As far as I can understand, the only way to make the LCD work at frequencies in the MHz range is to use some kind of latched buffers/transceivers to decouple the LCD EN frequency from the CPU frequency.