-- Title: glcd_ks0108 - Library for KS0108 compatible graphic LCDs
-- Author: Serkan Ayyldz Copyright (c) 2006..2009, all rights reserved.
-- Adapted-by: Sebastien Lelong, Rob Hamerling.
-- Compiler: 2.4o
-- Revision: $Revision: 2871 $
--
-- This file is part of jallib (http://jallib.googlecode.com)
-- Released under the ZLIB license (http://www.opensource.org/licenses/zlib-license.html)
--
-- Sources:
--
-- Description: Library for KS0108 compatible graphic lcds with 128x64 resolution
--              for example the GDM12864A.
--
-- Notes: Modifications by Rob Hamerling (nov/dec 2011):
--        - Minor timing changes in _ks0108_read() and _ks0108_write()
--          according to datasheet schemes for MPU Write timing and Read timing.
--          Replaced hard coded ASM NOPs by _usec_delay()
--        - Private procedures/function prefixed with underscore
--        - Added procedure glcd_clear_screen() and use it in glcd_init().
--          Uses the reset line to set Z-address to zero.
--        - Modified procedures to be able to write characters with
--          y-coordinates NOT on page (8-bit) boundaries.
--          Works probably only with max 8 bits high font (5x7 and 6x8)
--        - Replaced absolute values for loops and offsets by symbolic values
--        - Code optimisations, incl add/remove of some 'pragma inline'
--        - Fixed some bugs with external fonts
--        - Comments extended, corrected, aligned (for JALAPI docs)
-- .
--        Technical (library internal) details
--        - A 128x64 graphic LCD consists with 2 controllers (KS0108_LEFT and
--          KS0108_RIGHT), each take care of 64x64 bits.
--        - Addressing: upper left corner is coordinate (0,0)
--        - A byte of data represents a column of 8 pixels, so the vertical
--          64 pixels are represented by 8 bytes (a row of 8 bits is called
--          'page' in the datasheet).
--
-- ----------------------------------------------------------------------------

const byte GLCD_X_PIXELS       = 128               -- display x-dimension
const byte GLCD_Y_PIXELS       = 64                -- display y-dimension
const byte GLCD_COLOR_BITS     = 1                 -- monochrome display

const bit  KS0108_LEFT         = 0                 -- select left part of display
const bit  KS0108_RIGHT        = 1                 -- select right part of display
const byte KS0108_CMD_ON       = 0x3F              -- command to enable the display
const byte KS0108_CMD_OFF      = 0x3E              -- command to disable the display
const byte KS0108_CMD_PAGE     = 0xB8              -- command to select page (y-coord / 8)
const byte KS0108_CMD_COLUMN   = 0x40              -- command to select column (x-coord)
const byte KS0108_CMD_TOP_RAM  = 0xC0              -- command to set startline

-- constant colors
const byte GLCD_BLACK          = 0x01              -- normal pen color
const byte GLCD_WHITE          = 0x00              -- alternate pen color

-- data/instruction selection of DI line
const byte GLCD_DI_DATA        = high              -- select data to display
const byte GLCD_DI_INST        = low               -- select command

var byte glcd_background_color = GLCD_WHITE        -- default background color
var byte glcd_pen_color        = GLCD_BLACK        -- default pen color


-- ----------------------------------------------------------------------------
-- Purpose:   Low level write a byte to the specified chip
-- Arguments: - side: which chip to write the data to: 0 = left, 1 = right)
--            - data: the byte of data to write)
-- Notes:     The 'data' byte may be display data or a command.
--            When it is data to be displayed its coordinates must
--            have been been set on beforehand (column,page).
--            GLCD_E is set low for 1/4 cycle upon call and kept low
--            for 1/4 cycle before returning such that successive calls
--            to read or write have GLCD_E low for at least 1/2 cycle
--            between calls.
-- ----------------------------------------------------------------------------
procedure _ks0108_write(bit in side, byte in data) is

   GLCD_E = low                                    -- start cycle in low state
   _usec_delay(1)                                  -- Twl/2
   if side == KS0108_LEFT then
      GLCD_CS1 = high                              -- select left half
   else
      GLCD_CS2 = high                              -- select right half
   end if
   GLCD_RW = low                                   -- clear for writing
   _usec_delay(1)                                  -- Twl/2 (Tasu)
   GLCD_DATAPRT = data                             -- data on the port
   GLCD_DATAPRT_DIR = all_output                   -- set port to output
   GLCD_E = high                                   -- set to high state
   _usec_delay(2)                                  -- Twh (Tdsu)
   GLCD_E = low                                    -- data to display
   _usec_delay(1)                                  -- Tah
   GLCD_CS1 = low                                  -- deselect both halves
   GLCD_CS2 = low                                  -- (Tdhw)

end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Low level read of a byte from the specified chip.
-- Arguments: side: which chip to read, 0 = left, 1 = right)
-- Returns:   A byte of data read from the chip (row of 8 vertical pixels).
--            The coordinates (column,page) must have been been set on
--            beforehand.
-- Notes:     GLCD_E is set low for 1/4 cycle upon call and kept low
--            for 1/4 cycle before returning such that successive calls
--            to read or write have GLCD_E low for at least 1/2 cycle
--            between calls.
-- ----------------------------------------------------------------------------
function _ks0108_read(bit in side) return byte  is

   var byte data                                   -- for the data from the display

   GLCD_E = low                                    -- start cycle in low state
   _usec_delay(1)                                  -- Twl/2
   if side == KS0108_LEFT then                     -- select left half
      GLCD_CS1 = high
   else                                            -- select right half
      GLCD_CS2 = high
   end if
   GLCD_RW = high                                  -- set for reading
   _usec_delay(1)                                  -- Twl/2 (Tasu)
   GLCD_E = high                                   -- make E high
   _usec_delay(2)                                  -- Twh (Td)
   GLCD_E = low                                    -- latch/read
   _usec_delay(1)                                  -- Twl/2 (Td)
   GLCD_DATAPRT_DIR = all_input                    -- Set port to input
   data = GLCD_DATAPRT                             -- latch/read data
   GLCD_CS1 = low                                  -- deselect both halves
   GLCD_CS2 = low

   return data                                     -- return the data
end function


-- ----------------------------------------------------------------------------
-- Purpose:   Turn the display on
-- Arguments: none
-- Notes:     both sides are activated
-- ----------------------------------------------------------------------------
procedure _ks0108_on() is
   _ks0108_write(KS0108_LEFT,  KS0108_CMD_ON)
   _ks0108_write(KS0108_RIGHT, KS0108_CMD_ON)
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Turn the display off
-- Arguments: none
-- Notes:     both sides are de-activated
-- ----------------------------------------------------------------------------
procedure _ks0108_off() is
   _ks0108_write(KS0108_LEFT,  KS0108_CMD_OFF)
   _ks0108_write(KS0108_RIGHT, KS0108_CMD_OFF)
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Set the page number
-- Arguments: - side
--            - page number (y-coordinate) 0..7
-- ----------------------------------------------------------------------------
procedure _ks0108_page(bit in side, byte in page) is
   pragma inline
   _ks0108_write(side, KS0108_CMD_PAGE | (page & 0x07))
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Set the column number
-- Arguments: - cide
--            - column number (x-coordinate) 0..63
-- ----------------------------------------------------------------------------
procedure _ks0108_column(bit in side, byte in column) is
   pragma inline
   _ks0108_write(side, KS0108_CMD_COLUMN | (column & 0x3F))
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Specify subsequent reads and writes are instructions
-- Arguments: none
-- ----------------------------------------------------------------------------
procedure _ks0108_inst() is
   pragma inline
   GLCD_DI = GLCD_DI_INST                          -- select instruction / command
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Specify subsequent reads and writes are data
-- Arguments: none
-- ----------------------------------------------------------------------------
procedure  _ks0108_data() is
   pragma inline
   GLCD_DI = GLCD_DI_DATA                          -- select data
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:    Write (modify) a single pixel of the display
-- Arguments:  - x-coordinate of the pixel
--             - y-coordinate of the pixel
-- ----------------------------------------------------------------------------
procedure glcd_write_pixel(byte in x, byte in y) is

   var byte data
   var bit  side = KS0108_LEFT                     -- default: select left half

   if x >= (GLCD_X_PIXELS / 2) then                -- check for right half
      x = x - (GLCD_X_PIXELS / 2)                  -- correct offset
      side = KS0108_RIGHT                          -- select right half
   end if

   _ks0108_inst()                                  -- set for instruction
   _ks0108_column(side, x)                         -- set the horizontal address
   _ks0108_page(side, y / 8)                       -- set the page address
   _ks0108_data()                                  -- set for data
   data = _ks0108_read(side)                       -- latch (no data!)
   data = _ks0108_read(side)                       -- read data

   if glcd_pen_color == GLCD_BLACK then
      data = data |   (1 << (y % 8))               -- set pixel
   else
      data = data & ! (1 << (y % 8))               -- clear pixel
   end if
   _ks0108_inst()                                  -- set for instruction
   _ks0108_column(side,x)                          -- set the horizontal address
   _ks0108_page(side, y / 8)                       -- set the page address
   _ks0108_data()                                  -- set for data
   _ks0108_write(side, data)                       -- write the pixel data
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Write byte (column of 8 pixels) to display
-- Arguments: - x-coordinate of the pixel
--            - y-coordinate of the pixel (not necessarily on page boundary)
--            - pattern of 8 vertically aligned pixels
-- Notes:     When data on page boundary (y-coordinate multiple of 8): single write
--            When the data is crossing page boundary: two read-modify-write
--            operations have to be performed, which is considerably slower.
-- ----------------------------------------------------------------------------
procedure _ks0108_write_byte(byte in x, byte in y, byte in data) is
   var bit side = KS0108_LEFT                      -- Select left display half
   if x >= (GLCD_X_PIXELS / 2) then                -- check if second display half
      x = x - (GLCD_X_PIXELS / 2)                  -- adapt x-coordinate
      side = KS0108_RIGHT                          -- select right display half
   end if
   _ks0108_inst()                                  -- set for instruction
   _ks0108_column(side,x)                          -- set the horizontal address
   _ks0108_page(side, y / 8)                       -- page address (rounded down)
   _ks0108_data()                                  -- set for data
   if ((y % 8) == 0) then                          -- y is on on page boundary
      _ks0108_write(side, data)                    -- write the pixel pattern
   else                                            -- NOT on page boundary
      var byte d1                                  -- intermediate pattern
      d1 = _ks0108_read(side)                      -- latch (no data!)
      d1 = _ks0108_read(side)                      -- read data
      d1 = d1 & (0xFF >> (8 - (y % 8)))            -- keep low order bits
      d1 = d1 | (data << (y % 8))                  -- replace high order bits with data
      _ks0108_inst()                               -- set for instruction
      _ks0108_column(side, x)                      -- set horizontal address
      _ks0108_page(side, y / 8)                    -- set page
      _ks0108_data()                               -- set for data
      _ks0108_write(side, d1)                      -- write the pattern

      _ks0108_inst()                               -- Set for instruction
      _ks0108_column(side,x)                       -- Set the horizontal address
      _ks0108_page(side, y / 8 + 1)                -- Page address rounded up
      _ks0108_data()                               -- Set for data
      d1 = _ks0108_read(side)                      -- latch (no data)
      d1 = _ks0108_read(side)                      -- read data
      d1 = d1 & (0xFF << (y % 8))                  -- keep high order bits
      d1 = d1 | (data >> (8 - (y % 8)))            -- replace high order bits with data (aligned)
      _ks0108_inst()                               -- set for instruction
      _ks0108_column(side, x)                      -- set horizontal address
      _ks0108_page(side, y / 8 + 1)                -- set page
      _ks0108_data()                               -- set for data
      _ks0108_write(side, d1)                      -- write the pattern
   end if
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:    read byte (column of pixels) from display
-- Arguments:  x = pixel column
--             y = pixel row (not necessarily on page boundary)
-- Returns:    byte, row of pixels on current location
-- ----------------------------------------------------------------------------
function _ks0108_read_byte(byte in x, byte in y) return byte is
   var byte d1,d2
   var bit  side = KS0108_LEFT                     -- Select left half
   if x >= (GLCD_X_PIXELS / 2) then                -- Check right half
      x = x - (GLCD_X_PIXELS / 2)
      side = KS0108_RIGHT
   end if
   _ks0108_inst()                                  -- Set for instruction
   _ks0108_column(side,x)                          -- Set the horizontal address
   _ks0108_page(side, y / 8)                       -- Page address rounded down
   _ks0108_data()                                  -- Set for data
   d1 = _ks0108_read(side)                         -- latch (no data)
   d1 = _ks0108_read(side)                         -- read data
   if ((y % 8) == 0) then                          -- when y is on page page: ready!
     return d1                                     -- return pixel pattern
   end if
                                                   -- crossing page boundary:
   d1 = d1 >> (y % 8)                              -- keep only relevant pixels
   _ks0108_inst()                                  -- Set for instruction
   _ks0108_column(side,x)                          -- Set the horizontal address
   _ks0108_page(side, y / 8 + 1)                   -- Page address rounded up
   _ks0108_data()                                  -- Set for data
   d2 = _ks0108_read(side)                         -- latch (no data)
   d2 = _ks0108_read(side)                         -- read data
   d2 = d2 << (8 - (y % 8))                        -- keep only relevant pixels
   return (d2 | d1)                                -- return combined pattern

end function


-- ----------------------------------------------------------------------------
-- Purpose:    Fill the whole GLCD with a pixel pattern (horizontal lines!)
-- Arguments:  bit pattern (8 pixel column, usually 0x00 or 0xFF)
-- Notes:      Uses auto-increment of x-coordinate for fast write of rows
-- ----------------------------------------------------------------------------
procedure glcd_fill(byte in data) is
   var byte i                                      -- loop counter
   for GLCD_Y_PIXELS / 8 using i loop              -- all pages
      _ks0108_inst()                               -- set for instruction
      _ks0108_page(KS0108_LEFT, i)                 -- set page address
      _ks0108_page(KS0108_RIGHT, i)
      _ks0108_column(KS0108_LEFT, 0)               -- set horizontal address to 0
      _ks0108_column(KS0108_RIGHT, 0)
      _ks0108_data()                               -- set for data
      for GLCD_X_PIXELS loop                       -- all columns (auto-increment)
         _ks0108_write(KS0108_LEFT, data)          -- left half
         _ks0108_write(KS0108_RIGHT, data)         -- right half
      end loop
   end loop
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:    Clear the screen
-- Arguments:  none
-- Notes:      Resets the display with the RST line
--             Uses current glcd_background_color setting as fill character
-- ----------------------------------------------------------------------------
procedure glcd_clear_screen() is
   GLCD_RST = low                                  -- hardware reset
   _usec_delay(2)                                  -- Trs
   GLCD_RST = high                                 -- hardware enable
   if (glcd_background_color == GLCD_WHITE) then   -- current background is white
      glcd_fill(0b0000_0000)                       -- row of 'white' pixels
   else
      glcd_fill(0b1111_1111)                       -- row of 'black' pixels
   end if
   _ks0108_inst()                                  -- Set for instruction
   _ks0108_page(KS0108_LEFT,0)                     -- Set the left page address to 0
   _ks0108_page(KS0108_RIGHT,0)                    --  "   "  right  "     "
   _ks0108_column(KS0108_LEFT,0)                   -- Set the left column address to 0
   _ks0108_column(KS0108_RIGHT,0)                  --  "   "  right   "      "
   _ks0108_on()                                    -- turn the display on
end procedure


-- ----------------------------------------------------------------------------
-- Purpose:   Initialize the graphic LCD.
-- Arguments: none
-- Notes:     to be called before using any other GLCD function.
-- ----------------------------------------------------------------------------
procedure glcd_init() is

   GLCD_DATAPRT = 0b0000_0000                      -- all zero
   GLCD_DATAPRT_DIR = all_output                   -- output

   GLCD_DI   = high                                -- set for data
   GLCD_RW   = low                                 -- set read mode
   GLCD_E    = high                                -- quisce
   GLCD_CS1  = low                                 -- deselect both halves
   GLCD_CS2  = low                                 --
   GLCD_RST  = high                                -- enable

   GLCD_RW_DIRECTION  = output
   GLCD_DI_DIRECTION  = output
   GLCD_E_DIRECTION   = output
   GLCD_CS1_DIRECTION = output
   GLCD_CS2_DIRECTION = output
   GLCD_RST_DIRECTION = output

   glcd_clear_screen()                             -- reset and clear the display

end procedure


-- ----------------------------------------------------------------------------
-- _font_5x7_table[] - character table
-- ----------------------------------------------------------------------------
const byte _font_5x7_table[] = {
   0x00, 0x00, 0x00, 0x00, 0x00,    -- space, 32 (decimal)
   0x00, 0x00, 0x2f, 0x00, 0x00,    -- !,  33
   0x00, 0x07, 0x00, 0x07, 0x00,    --     34
   0x14, 0x7f, 0x14, 0x7f, 0x14,    -- #,  35
   0x24, 0x2a, 0x7f, 0x2a, 0x12,    -- $,  36
   0xc4, 0xc8, 0x10, 0x26, 0x46,    -- %,  37
   0x36, 0x49, 0x55, 0x22, 0x50,    -- &,  38
   0x00, 0x05, 0x03, 0x00, 0x00,    -- ,  39
   0x00, 0x1c, 0x22, 0x41, 0x00,    -- (,  40
   0x00, 0x41, 0x22, 0x1c, 0x00,    -- ),  41
   0x14, 0x08, 0x3E, 0x08, 0x14,    -- *,  42
   0x08, 0x08, 0x3E, 0x08, 0x08,    -- +,  43
   0x00, 0x00, 0x50, 0x30, 0x00,    -- ,,  44
   0x10, 0x10, 0x10, 0x10, 0x10,    -- -,  45
   0x00, 0x60, 0x60, 0x00, 0x00,    -- .,  46
   0x20, 0x10, 0x08, 0x04, 0x02,    -- /,  47
   0x3E, 0x51, 0x49, 0x45, 0x3E,    -- 0,  48
   0x00, 0x42, 0x7F, 0x40, 0x00,    -- 1,  49
   0x42, 0x61, 0x51, 0x49, 0x46,    -- 2,  50
   0x21, 0x41, 0x45, 0x4B, 0x31,    -- 3,  51
   0x18, 0x14, 0x12, 0x7F, 0x10,    -- 4,  52
   0x27, 0x45, 0x45, 0x45, 0x39,    -- 5,  53
   0x3C, 0x4A, 0x49, 0x49, 0x30,    -- 6,  54
   0x01, 0x71, 0x09, 0x05, 0x03,    -- 7,  55
   0x36, 0x49, 0x49, 0x49, 0x36,    -- 8,  56
   0x06, 0x49, 0x49, 0x29, 0x1E,    -- 9,  57
   0x00, 0x36, 0x36, 0x00, 0x00,    -- :,  58
   0x00, 0x56, 0x36, 0x00, 0x00,    -- ;,  59
   0x08, 0x14, 0x22, 0x41, 0x00,    -- <,  60
   0x14, 0x14, 0x14, 0x14, 0x14,    -- =,  61
   0x00, 0x41, 0x22, 0x14, 0x08,    -- >,  62
   0x02, 0x01, 0x51, 0x09, 0x06,    -- ?,  63
   0x32, 0x49, 0x59, 0x51, 0x3E,    -- @,  64
   0x7E, 0x01, 0x01, 0x01, 0x7E,    -- A,  65
   0x7F, 0x49, 0x49, 0x49, 0x36,    -- B,  66
   0x3E, 0x41, 0x41, 0x41, 0x22,    -- C,  67
   0x7F, 0x41, 0x41, 0x22, 0x1C,    -- D,  68
   0x7F, 0x49, 0x49, 0x49, 0x41,    -- E,  69
   0x7F, 0x09, 0x09, 0x09, 0x01,    -- F,  70
   0x3E, 0x41, 0x49, 0x49, 0x7A,    -- G,  71
   0x7F, 0x08, 0x08, 0x08, 0x7F,    -- H,  72
   0x00, 0x41, 0x7F, 0x41, 0x00,    -- I,  73
   0x20, 0x40, 0x41, 0x3F, 0x01,    -- J,  74
   0x7F, 0x08, 0x14, 0x22, 0x41,    -- K,  75
   0x7F, 0x40, 0x40, 0x40, 0x40,    -- L,  76
   0x7F, 0x02, 0x0C, 0x02, 0x7F,    -- M,  77
   0x7F, 0x04, 0x08, 0x10, 0x7F,    -- N,  78
   0x3E, 0x41, 0x41, 0x41, 0x3E,    -- O,  79
   0x7F, 0x09, 0x09, 0x09, 0x06,    -- P,  80
   0x3E, 0x41, 0x51, 0x21, 0x5E,    -- Q,  81
   0x7F, 0x09, 0x19, 0x29, 0x46,    -- R,  82
   0x46, 0x49, 0x49, 0x49, 0x31,    -- S,  83
   0x01, 0x01, 0x7F, 0x01, 0x01,    -- T,  84
   0x3F, 0x40, 0x40, 0x40, 0x3F,    -- U,  85
   0x1F, 0x20, 0x40, 0x20, 0x1F,    -- V,  86
   0x3F, 0x40, 0x38, 0x40, 0x3F,    -- W,  87
   0x63, 0x14, 0x08, 0x14, 0x63,    -- X,  88
   0x07, 0x08, 0x70, 0x08, 0x07,    -- Y,  89
   0x61, 0x51, 0x49, 0x45, 0x43,    -- Z,  90
   0x00, 0x7F, 0x41, 0x41, 0x00,    -- [,  91
   0x55, 0x2A, 0x55, 0x2A, 0x55,    -- 5,  92
   0x00, 0x41, 0x41, 0x7F, 0x00,    -- ],  93
   0x04, 0x02, 0x01, 0x02, 0x04,    -- ^,  94
   0x40, 0x40, 0x40, 0x40, 0x40,    -- _,  95
   0x00, 0x01, 0x02, 0x04, 0x00,    -- ',  96
   0x20, 0x54, 0x54, 0x54, 0x78,    -- a,  97
   0x7F, 0x48, 0x44, 0x44, 0x38,    -- b,  98
   0x38, 0x44, 0x44, 0x44, 0x20,    -- c,  99
   0x38, 0x44, 0x44, 0x48, 0x7F,    -- d, 100
   0x38, 0x54, 0x54, 0x54, 0x18,    -- e, 101
   0x08, 0x7E, 0x09, 0x01, 0x02,    -- f, 102
   0x0C, 0x52, 0x52, 0x52, 0x3E,    -- g, 103
   0x7F, 0x08, 0x04, 0x04, 0x78,    -- h, 104
   0x00, 0x44, 0x7D, 0x40, 0x00,    -- i, 105
   0x20, 0x40, 0x44, 0x3D, 0x00,    -- j, 106
   0x7F, 0x10, 0x28, 0x44, 0x00,    -- k, 107
   0x00, 0x41, 0x7F, 0x40, 0x00,    -- l, 108
   0x7C, 0x04, 0x18, 0x04, 0x78,    -- m, 109
   0x7C, 0x08, 0x04, 0x04, 0x78,    -- n, 110
   0x38, 0x44, 0x44, 0x44, 0x38,    -- o, 111
   0x7C, 0x14, 0x14, 0x14, 0x08,    -- p, 112
   0x08, 0x14, 0x14, 0x18, 0x7C,    -- q, 113
   0x7C, 0x08, 0x04, 0x04, 0x08,    -- r, 114
   0x48, 0x54, 0x54, 0x54, 0x20,    -- s, 115
   0x04, 0x3F, 0x44, 0x40, 0x20,    -- t, 116
   0x3C, 0x40, 0x40, 0x20, 0x7C,    -- u, 117
   0x1C, 0x20, 0x40, 0x20, 0x1C,    -- v, 118
   0x3C, 0x40, 0x30, 0x40, 0x3C,    -- w, 119
   0x44, 0x28, 0x10, 0x28, 0x44,    -- x, 120
   0x0C, 0x50, 0x50, 0x50, 0x3C,    -- y, 121
   0x44, 0x64, 0x54, 0x4C, 0x44     -- z, 122
}

-- ----------------------------------------------------------------------------
-- Purpose:   Write character to the display using internal font definition.
-- Arguments: - x- and y-coordinates (upper left corner of character pattern)
--            - character to display
-- Notes:     For characters the display is 8 lines, each 8 bits high;
--            specify for y-coordinate a multiple of 8.
-- ----------------------------------------------------------------------------
procedure _ks0108_write_char(byte in x, byte in y, byte in ch) is
   var word indx
   var byte cx
   if (ch <  32) | (ch > 122) then                 -- only supported characters
      ch = " "                                     -- replace it by a space
   end if
   indx = 5 * word(ch - 32)                        -- non proportional font, 5 pixels wide
   for 5 loop                                      -- 5 colums per character
      if (x >= GLCD_X_PIXELS) then                 -- check for maximum x value
         exit loop                                 -- clip text beyond display
      end if
      cx = _font_5x7_table[indx]                   -- obtain bit pattern column
      if glcd_pen_color == GLCD_WHITE then         -- white on black
         cx = !cx                                  -- invert bit pattern
      end if
      _ks0108_write_byte(x, y, cx)                 -- write vertical pixel pattern
      indx = indx + 1                              -- next byte in font table
      x = x + 1                                    -- next display column
   end loop
end procedure



if defined(GLCD_FONT_USAGE) then                   -- when using a font library

-- ----------------------------------------------------------------------------
-- Purpose:   Same as _ks0108_write_char, but uses external font.
-- Arguments: - x-coordinate (in pixels) of left most pixel columm (range 0..127)
--            - y-coordinate (in pixels) of upper most pixel-row (range 0..63)
--            - character (binary value in range 32..122)
-- Notes:     When y-coordinate is a multiple of 8 (0,8,16,etc.): single write,
--            otherwise two read-modify-write cycles needed: slower!
-- ----------------------------------------------------------------------------
procedure glcd_write_char(byte in x, byte in y, byte in ch ) is
   var word indx                                   -- position of bit pattern
   var byte cx                                     -- bit pattern
   if (ch <  32) | (ch > 122) then                 -- unsupported char
      ch = " "                                     -- replace it by a space
   end if
   indx = _glcd_font_current_width * word(ch - 32)  -- non proportional font
   for _glcd_font_current_width loop               -- whole character
      if (x >= GLCD_X_PIXELS) then                 -- check for maximum x value
         exit loop                                 -- clip text beyond display
      end if
      cx = glcd_font_lookup(indx)                  -- obtain bit pattern
      if glcd_pen_color == GLCD_WHITE then         -- white pen
         cx = !cx                                  -- invert bit pattern
      end if
      _ks0108_write_byte(x, y, cx)                 -- write vertical pixel pattern
      indx = indx + 1                              -- next byte in font table
      x = x + 1                                    -- position of next pixel row
   end loop
end procedure

end if     -- (using a font library)

