/Z80_LCD_128x64_Graphics_Library

Full native Z80 Graphics Library for the 128x64 Pixel LCD Screen

MIT LicenseMIT

Z80 LCD 128x64 Graphics Library

Full native Z80 Graphics Library for the 128x64 Pixel LCD Screen

The above image demonstrates most of the graphical routines. This includes line drawing, circles, filled shapes, text and pixel drawing.


Files in this repository

  • lcd_128x64_glib.z80 - Z80 Graphics library for the ST7920 Controller
  • lcd_3d_demo.z80 - 3D Frame rotation program
  • lcd_mad_program.z80 - MAD Magazine face drawing
  • lcd_maze_gen.z80 - Maze Generation program
  • ST7920.pdf - ST7920 Datasheet
  • QC12864B.pdf - LCD Screen Datasheet
  • 3D_Fundamentals.JPG - Amstrad Basic 3D Frame article
  • add_on_PCB directory - Add-on Board for the TEC-1 Computer

LCD Screens

There are a few variants of these LCD screens, but all typically use the ST7920 LCD Controller. The LCD Screen that I used is the QC12864B. This screen has two ST7921 Panels (128 x 32) stacked one above the other. Other LCD boards might not do this. If so, the PLOT_TO_LCD function will need to be modified. (future work)

These screens have Graphics Display RAM (GDRAM) and Display Data RAM (DDRAM) areas. GDRAM is for drawing pixels and DDRAM is for displaying text or characters. Both RAM areas __can be__ displayed at the same time.

The Pinout and connection to the Z80 for the QC12864B board is as follows:

Pin Name Description Serial1 Parallel
1 VSS Ground GND GND
2 VDD Power 5v 5v
3 V0 Contrast N/A N/A
4 D/I IR/DR (CS) 5v A74
5 R/W R/W (SID) D0 RD (inverted)2
6 E Enable (SCLK) D1 Port 7 4 (inverted)2
7 DB0 Data N/A D0
8 DB1 Data N/A D1
9 DB2 Data N/A D2
10 DB3 Data N/A D3
11 DB4 Data N/A D4
12 DB5 Data N/A D5
13 DB6 Data N/A D6
14 DB7 Data N/A D7
15 PSB Serial/Para GND 5v
16 NC
17 RST Reset RST RST
18 VEE LCD Drive N/A N/A
19 A Backlight 5v/NC 5v/NC
20 K Backlight GND/NC GND/NC

1 Three communication lines are need for Serial SPI transfer. Pin 4 is Chip Select (CS). As there is only one peripheral connected, this can just be tied high at 5v. Pin 5 is Serial Input Data (SID) and Pin 6 is Serial Clock (SCLK). Pin 5 and 6 are to be latched via a 74HC74 Dual D-Flip Flop. The inputs to the latch are D0 (SID) and D1 (SCLK). Have a look at the schematic below.

2 A CMOS CD4049 Inverter Buffer is needed to invert the input

3 Some 128x64 LCD Screens have different Pinouts than in the table above.

4 I use Port 7 and A7 to connect the LCD screen to the TEC computer. You will need to modify these two constants if connecting the LCD in a different way. See the header part of the lcd_128x64_glib.z80 file to modify these values.

Example Code

To see the library working, have a look at the example code provided. The LDC_3D_demo.z80 has a DISPLAY_MENU label that displays the above picture. Look at this piece of code for better understanding.

Also, see the library and these files in action on YouTube and YouTube!

Thanks to PCBWay for sponsoring this video. ** For $5 off your first order at PCBWay, click here **. PCBWay Printed Circuit Boards the Easy Way.

Programming Guide

The Graphics Library has routines that interact with the LCD Screen. Routines are called via a Jumpblock. These routines are at the start of the Graphics Library. By default the library is placed at location 0x3000. If this location is changed, the start locations of the functions are also to be changed.

For example, to initalise the LCD, do something like this:

    CALL 3000H

Or to make it more readable create a label and do this:

    G_INIT_LCD:	EQU 3000H
    CALL G_INIT_LCD

In summary, here are the routines and their default address locations:

Address Routine Description
3000H INIT_LCD Initalise the LCD
3003H CLEAR_GBUF Clear the Graphics Buffer
3006H CLEAR_GR_LCD Clear the Graphics LCD Screen
3009H CLEAR_TXT_LCD Clear the Text LCD Screen
300CH SET_GR_MODE Set Graphics Mode
300FH SET_TXT_MODE Set Text Mode
3012H DRAW_BOX Draw a rectangle between two points
3015H DRAW_LINE Draw a line between two points
3018H DRAW_CIRCLE Draw a circle from Mid X,Y to Radius
301BH DRAW_PIXEL Draw one pixel at X,Y
301EH FILL_BOX Draw a filled rectangle between two points
3021H FILL_CIRCLE Draw a filled circle from Mid X,Y to Radius
3024H PLOT_TO_LCD Display the Graphics Buffer to the LCD Screen
3027H PRINT_STRING Print Text on the screen in a given row
302AH PRINT_CHARS Print Characters on the screen in a given row and column
302DH DELAY_US Microsecond delay for LCD updates
3030H DELAY_MS Millisecond delay for LCD updates
3033H SET_BUF_CLEAR Clear the Graphics buffer on after Plotting to the screen
3036H SET_BUF_NO_CLEAR Retain the Graphics buffer on after Plotting to the screen
3039H CLEAR_PIXEL Clear one pixel at X,Y
303CH FLIP_PIXEL Invert one pixel at X,Y
303FH LCD_INST Send a parallel or serial instruction to LCD
3042H LCD_DATA Send a parallel or serial data to LCD
3045H SER_SYNC Send serial synchronize byte to LCD

Here is the detailed routine descriptions with examples

INIT_LCD

Initalise the LCD Screen. Needed to be called before any other routine

  • Entry: No conditions
  • Exit: All registers corrupt
    CALL INIT_LCD       ;Initalise the LCD Screen

CLEAR_GBUF

Clear the Graphics Buffer. The Graphics Buffer or GBUF is the internal memory area that contains pixel data for the LCD. The drawing routines write to the GBUF. Once all pixels are set, this buffer is then plotted to the LCD with the PLOT_TO_LCD Routine. Clearing the GBUF is a good way to ensure the pixel area is empty.

  • Entry: No conditions
  • Exit: All registers corrupt
    CALL CLEAR_GBUF     ;Clears the Graphics Buffer

CLEAR_GR_LCD

Clear the Graphics LCD Screen. This routine clears the GDRAM or Graphics screen on the LCD.

  • Entry: No conditions
  • Exit: All registers corrupt
    CALL CLEAR_GR_LCD     ;Clears the Graphics LCD Screen

CLEAR_TXT_LCD

Clear the Text LCD Screen. This routine clears the DDRAM or Text screen on the LCD.

  • Entry: No conditions
  • Exit: All registers corrupt
    CALL CLEAR_TXT_LCD    ;Clear the Text LCD Screen

SET_GR_MODE

Set the LCD to Graphics Mode. This routine puts the LCD in Graphics mode (Extended Instructions) and any further instructions to the LCD will be for the graphics screen. It only needs to be called once if multiple graphics routines are used.

  • Entry: No conditions
  • Exit: AF, DE corrupt
    CALL SET_GR_MODE      ;Set Graphics Mode

SET_TXT_MODE

Set the LCD to Text Mode. This routine puts the LCD in Text mode (Basic Instructions) and any further instructions to the LCD will be for the text screen. It only needs to be called once if multiple text routines are used.

  • Entry: No conditions
  • Exit: AF, DE corrupt
    CALL SET_TXT_MODE     ;Set Text Mode

DRAW_BOX

Draws a single-line box between two points X1,Y1 and X2,Y2.

  • Entry:
    • B = X1-coordinate (0-127)
    • C = Y1-coordinate (0-63)
    • D = X2-coordinate (0-127)
    • E = Y2-coordinate (0-63)
  • Exit: AF, HL corrupt
    LD BC, 0020H 	;X0, Y0
    LD DE, 7F3FH 	;X1, Y1
    CALL DRAW_BOX ;Draw a outline box from X0,Y0 to X1,Y1

DRAW_LINE

Draws a straight line between X1,Y1 and X2,Y2. Uses Bresenham Line drawing algorithm

  • Entry:
    • B = X1-coordinate (0-127)
    • C = Y1-coordinate (0-63)
    • D = X2-coordinate (0-127)
    • E = Y2-coordinate (0-63)
  • Exit: All registers corrupt
    LD BC, 0010H 	;X0, Y0
    LD DE, 7F30H 	;X1, Y1
    CALL DRAW_LINE

DRAW_CIRCLE

Draws a circle from a mid point to a radius. Uses Bresenham Circle drawing algorithm

  • Entry:
    • B = Mid-X-coordinate (0-127)
    • C = Mid-Y-coordinate (0-63)
    • E = Radius (1-63)
  • Exit: All registers corrupt
    LD BC, 0818H 	;Mid X, Mid Y
    LD E, 08H 		;Radius
    CALL DRAW_CIRCLE

DRAW_PIXEL

Draws a single Pixel.

  • Entry:
    • B = X-coordinate (0-127)
    • C = Y-coordinate (0-63)
  • Exit: AF, HL corrupt
    LD BC, 4020H 	;X,Y
    CALL DRAW_PIXEL

FILL_BOX

Draws a filled box between X1,Y1 and X2,Y2.

  • Entry:
    • B = X1-coordinate (0-127)
    • C = Y1-coordinate (0-63)
    • D = X2-coordinate (0-127)
    • E = Y2-coordinate (0-63)
  • Exit: AF, HL corrupt
    LD BC, 0020H 	;X0, Y0
    LD DE, 7F3FH 	;X1, Y1
    CALL FILL_BOX ;Draw a filled box from X0,Y0 to X1,Y1

FILL_CIRCLE

Draws a filled circle from a mid point to a radius. This routine iteratively calls the DRAW_CIRCLE routine increasing the radius until is equals the register E. There might be gaps in the filled circle, but hey it looks just like what you get on a BASIC program.

  • Entry:
    • B = Mid-X-coordinate (0-127)
    • C = Mid-Y-coordinate (0-63)
    • E = Radius (0-63)
  • Exit: All registers corrupt
    LD BC, 1018H 	;Mid X, Mid Y
    LD E, 08H 		;Radius
    CALL FILL_CIRCLE

PLOT_TO_LCD

This routine draws the Graphics Buffer or GBUF to the Graphics LDC screen. It is usually called after one of the drawing routines is called.

  • Entry: No conditions
  • Exit: All registers corrupt
    CALL PLOT_TO_LCD     ;Display the Graphics Buffer to the LCD Screen

PRINT_STRING

Prints ASCII text on the screen on a given row. There are 4 text rows on the LCD screen. The text to be displayed is to be defined directly after the CALL routine and is to be terminated with a zero.

Here are the 128 characters that are available. Conveniently, Alphanumeric characters align with the ASCII table.

  • Entry:
    • A = row number (0-3)
    • Text = "String" on the next line, terminate with 0
  • Exit: All registers corrupt
    LD A,2
    CALL PRINT_STRING
    DB 02H, " This Text ", 1BH ,00H

    ; This will display a smiley face, "This Text" 
    ; and a left arrow on row 2 of the LCD screen.

PRINT_CHARS

Print Characters on the screen in a given row and column. This routine is similar to the one above but character row and column placement can be made. Characters to be printed are to be terminated with a zero.

Note: Even though there are 16 columns, only every second column can be written to and two characters are to be printed. IE: if one character is to be printed in column 2, then set B=0 and print " x", putting a space before the character.

  • Entry:
    • B = column (0-7)
    • C = row (0-3)
    • HL = start address of text data
  • Exit: All registers corrupt. HL will be at the end of the text data table.
    LD HL, MENU_DATA ;Load HL with Menu data table
    LD BC, 0102H 	;Column 1, Row 2
    CALL PRINT_CHARS ;Display Text
    ...
    MENU_DATA:
    DB "Hello!",0

DELAY_US

Delay loop for LCD to complete its instruction. Every time a command is sent to the LDC, it requires a small amount of time to complete that operation. IE: setting extended instruction mode. Time needed for most operations is defined in the LDC specification. It is usually around 72us. This routine is used internally, but can also be used here. The delay time depends on how fast the CPU is running at. A variable V_DELAY_US can be changed to suit your LDC setup. I found that my LDC can handle a delay of 0004H.

This routine will delay the cpu by V_DELAY_US and can be used if outputting instructions to the LDC directly.

  • Entry: No conditions
  • Exit: DE, AF corrupt
    LD A, 02H       ;Home command
    OUT (LCD_IR), A ;Move the cursor to the top of LCD
    CALL DELAY_US   ;Delay the CPU by V_DELAY_US

DELAY_MS:

This is the same as the above routine, but the delay can be software controlled.

  • Entry: DE = delay value
  • Exit: DE, AF corrupt
    LD A, 01H       ;Clear command
    OUT (LCD_IR), A ;Clear the LCD screen
    LD DE, 0050H    ;Longer Delay
    CALL DELAY_MS   ;Delay the CPU by DE

SET_BUF_CLEAR

On every PLOT_TO_LCD, Clear the graphics buffer GBUF. Calling this routine will clear the graphics buffer on every draw to LCD. This is useful if doing animation that require a new drawing to be display on every plot or frame.

  • Entry: No conditions
  • Exit: AF corrupt
    CALL SET_BUF_CLEAR    ;Clear GBUF on plot

SET_BUF_NO_CLEAR

Do not clear the graphics buffer on every PLOT_TO_LCD. Calling this routine will not clear the graphics buffer on every draw to LCD. This is useful adding graphics data to an existing drawing. This is used in the MAD example, where lines are being continually drawn to the screen until the whole picture is complete.

  • Entry: No conditions
  • Exit: AF corrupt
    CALL SET_BUF_NO_CLEAR    ;Do not clear GBUF on plot

CLEAR_PIXEL

Removes or clears a single Pixel.

  • Entry:
    • B = X-coordinate (0-127)
    • C = Y-coordinate (0-63)
  • Exit: AF, HL corrupt
    LD BC, 4020H 	;X,Y
    CALL CLEAR_PIXEL

FLIP_PIXEL

Inverts a single Pixel. If the Pixel is on, it will turn off and if the Pixel is off, it will turn on.

  • Entry:
    • B = X-coordinate (0-127)
    • C = Y-coordinate (0-63)
  • Exit: AF, HL corrupt
    LD BC, 4020H 	;X,Y
    CALL FLIP_PIXEL

LCD_INST

Send an instruction to the LCD. This routine will send the value of Register A to the Instruction Register of the LCD Screen. The routine is universal regardless of Serial or Parallel connection. For Serial connection, this will also send the Synchronization Byte.

  • Entry:
    • A = Value to send
  • Exit: AF, DE (Parallel only) corrupt
    LD A, 38H   ; Clear Screen 
    CALL LCD_INST

LCD_DATA

Send data to the LCD. This routine will send the value of Register A to the Data Register of the LCD Screen. The routine is universal regardless of Serial or Parallel connection.
For Serial connection, a call to SER_SYNC is to be done prior. This is because only one Synchronization Byte is needed for multiple (up to 256) Data bytes.

  • Entry:
    • A = Value to send
  • Exit: AF, DE (Parallel only) corrupt
    ;Parallel example
    LD A, "B"   ; The letter B
    CALL LCD_DATA

SER_SYNC

Send a Serial Synchronization Byte to the LCD. This routine is only needed for Serial connection AND prior to an LCD_DATA call. The Register A must be 02H

  • Entry:
    • A = 02H
  • Exit: AF corrupt
    LD A, 02H 
    CALL SER_SYNC   ;Data Block Sync
    LD A,  "T"      ;Letter T
    CALL LCD_DATA
    LD A,  "E"      ;Letter E
    CALL LCD_DATA
    LD A,  "C"      ;Letter C
    CALL LCD_DATA

DRAW_GRAPHIC

Draw an ASCII character or Sprite to the LCD at the current cursor. ASCII characters are 6x6 or 5x5 Pixels and most have a gap to the left and bottom for spacing. PLOT_TO_LCD is still required to be called after all graphics have been drawn.

Graphics data is in the format of up to 16 bytes across and 64 bytes down, where a BIT set will indicate a pixel to be drawn. If graphics is less than 8 bits wide, then bits are read from the least significant bit.

  • Entry:
    • A = ASCII number or if A=0 Then
      • HL = Address of graphic data
      • B = width of graphic in pixels (1-128)
      • C = height of graphic in pixels (1-64)
  • Exit: All Corrupt
    LD A, 00H         ;Custom graphic
    LD HL, PICTURE    ;Data table address
    LD B, 16          ;B=16 pixels wide
    LD C, 08          ;C=8 pixels down
    CALL DRAW_GRAPHIC ;Draw the GLCD Buffer
    CALL PLOT_TO_LCD  ;Display on GLCD

PICTURE:
    .db 10000011b,11000001b
    .db 10000100b,00100001b
    .db 10001010b,01010001b
    .db 10001000b,00010001b
    .db 10001010b,01010001b
    .db 10001001b,10010001b
    .db 10000100b,00100001b
    .db 10000011b,11000001b

This example will display this image from the current cursor position

INV_GRAPHIC

Inverse graphics printing. Calling this routine will TOGGLE the inverse drawing flag. The initial state is normal. If in inverse mode, a pixel drawn using the DRAW_GRAPHIC routine is displayed if a BIT is not set.

  • Exit: AF corrupt
    CALL INV_GRAPHIC    ;Toggle inverse flag

INIT_TERMINAL

Initialize the LCD for terminal emulation. This routine is to be called before any TERMINAL routine is called. It will set up the graphics and scroll buffers. It also Clears the GBUF, sets cursor to top left and displays cursor. This routine will as call INIT_LCD.

  • Exit: All Corrupt
    CALL INIT_TERMINAL   ;Start terminal emulation

SEND_CHAR_TO_GLCD

Send or handle ASCII characters to the GLCD screen. This routines displays ASCII characters to the GLCD screen and handles some special control characters. It also handles scrolling history of 10 lines. Characters are drawn at the current cursor position. The Cursor will increment when a character is drawn. Character will automatically be displayed on the LCD.

Some special characters are:

  • CR / 0DH = will move the cursor down and reset it column

  • LF / 0AH = is ignored

  • BS / 08H = will delete the character at the cursor and move cursor back one

  • HT / 09H = will TAB 4 spaces

  • UP / 05H = will scroll up one line if any

  • DN / 06H = will scroll down one line if any

  • Entry:

    • A = ASCII character to send to the GLCD screen. OR
    • A = 0, draw the cursor only
  • Exit: All Corrupt

    LD A,65     ;ASCII 'A'
    CALL SEND_CHAR_TO_GLCD    ;Display on LCD
    LD A,0DH    ;Carriage Return
    CALL SEND_CHAR_TO_GLCD    ;Do a CR on the LCD

SEND_STRING_TO_GLCD

Send a string of characters to the GLCD. Prints a string pointed by DE. It stops printing and returns when either a CR is printed or when the next byte is the same as what is in register A.

  • Entry:
    • DE = address of string to print
    • A = character to stop printing.
  • Exit: All Corrupt
    LD DE,TEXT      ;Text to display 
    XOR A           ;Terminate with 0
    CALL SEND_STRING_TO_GLCD

TEXT: .db "Hello World!",0

SEND_A_TO_GLCD

Display the register A in ASCII on the GLCD at the current cursor

  • Entry:
    • A = value to convert and display
    LD A,7BH        ;Set A=7BH
    CALL SEND_A_TO_GLCD ;Display "7B" on LCD

SEND_HL_TO_GLCD ;Send register HL to the LCD

Display the register HL in ASCII on the GLCD at the current cursor

  • Entry:
    • HL = value to convert and display
    LD HL,0A6C0H         ;Set HL=0A6C0H
    CALL SEND_HL_TO_GLCD ;Display "A6C0" on LCD

SET_CURSOR

Set the Graphic cursor position for Terminal Emulation. Update is ignored if either X,Y input is out of bounds.

  • Entry:
    • B = X position in pixels (0-127)
    • C = Y position in pixels (0-63)
  • Exit: AF corrupt
    LD BC,4020H     ;Set cursor to middle of screen
    CALL SET_CURSOR

GET_CURSOR

Get the current cursor position

  • Exit:
    • B = X position in pixels (0-127)
    • C = Y position in pixels (0-63)
    CALL GET_CURSOR

DISPLAY_CURSOR

Turn the cursor ON or OFF. Default is Cursor ON

  • Entry:
    • A = 0, Turn cursor on, A = non zero, Turn cursor off
    LD A,1    
    CALL DISPLAY_CURSOR ;Turn cursor off

Future Work

Displaying Text on this screen is clumsy as text can only be placed at defined locations. I plan to make a graphics font of 128 characters that is smaller in size, possibly 5x7 and can be placed anywhere on the screen.

Some LCD screens have a slightly different way data is written to them in terms of how the screens are laid out. The QC12864B as two ST9721 screens placed on top of each other, but are connected in series. Plotting to the LCD routine will need to be changed for other LCD screen layouts.