-- Title: lcd_ks0108 - Library for KS0108 compatible LCD
-- Author: Serkan Ayyldz Copyright (c) 2006..2009, all rights reserved.
-- Adapted-by: Joep Suijs
-- Compiler: >=2.2
-- 
-- 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 lcd with 128x64 resolution.
-- 
--
-- Notes: 
--             

-- ----------------------------------------------------------------------------
-- technical (library internal) details
-- An 128x64 graphic LCD consists of 2 controllers (KS0108_LEFT and KS0108_RIGHT),
-- each take care of 64x64 bits.
--
-- A byte of data represents a column of 8 bits, so the vertical 64 bits are 
-- represented by 8 bytes.
-- 
--
-- ----------------------------------------------------------------------------

const byte GLCD_X_PIXELS = 128
const byte GLCD_Y_PIXELS = 64
const byte GLCD_COLOR_BITS = 1 -- black & white, monochrome
                       
const KS0108_LEFT          = 0              
const KS0108_RIGHT         = 1              
const KS0108_CMD_ON        = 0x3F    
const KS0108_CMD_OFF       = 0x3E    
const KS0108_CMD_PAGE      = 0xB8    
const KS0108_CMD_COLUMN    = 0x40    
const KS0108_CMD_TOP_RAM   = 0xC0    

-- constant colors
const byte GLCD_BLACK = 0x01
const byte GLCD_WHITE = 0x00

-- Default background color
var byte lcd_background_color = GLCD_WHITE
-- Default pen color
var byte glcd_pen_color = GLCD_BLACK
                                                                                                         
-- ----------------------------------------------------------------------------
-- _ks0108_write - Write a byte of data to the specified chip
-- ----------------------------------------------------------------------------
-- Inputs:     1) side - which chip to write the data to
--             2) data - the byte of data to write
-- ----------------------------------------------------------------------------
procedure _ks0108_write(byte in side, byte in data) is
   
   if side == 1 then          -- Choose which side to write to
      GLCD_CS2 = high
   else
      GLCD_CS1 = high
   end if
   GLCD_RW = low        -- Set for writing
   GLCD_DATAPRT = data     -- Put the data on the port
   GLCD_DATAPRT_DIR = all_output

   asm nop asm nop      -- delay_cycles(1)
   GLCD_E = high        -- Pulse the enable pin
   delay_2us()

   GLCD_E = low
   GLCD_CS1 = low       -- Reset the chip select lines
   GLCD_CS2 = low
   delay_2us()

end procedure

-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Purpose:    Reads a byte of data from the specified chip
-- Ouputs:     A byte of data read from the chip
-- ----------------------------------------------------------------------------
function _ks0108_read(byte in side) return byte  is
   var byte data                  -- Stores the data read from the LCD
   GLCD_DATAPRT_DIR = all_input           -- Set port d to input

   if side == 1 then     -- Choose which side to write to
      GLCD_CS2 = high
   else
      GLCD_CS1 = high
   end if
   GLCD_RW = high       -- Set for reading

   asm nop  asm nop     --  delay_cycles(1)
   GLCD_E = high        -- Pulse the enable pin
   
   delay_2us()
   data = GLCD_DATAPRT     -- Get the data from the display's output register
   GLCD_E = low

   GLCD_CS1 = low       -- Reset the chip select lines
   GLCD_CS2 = low
   delay_2us()
   
   return data          -- Return the read data
end function

-- ----------------------------------------------------------------------------
-- Purpose:    Turn the display on
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
procedure lcd_on() is
    _ks0108_write(KS0108_LEFT,  KS0108_CMD_ON)
    _ks0108_write(KS0108_RIGHT, KS0108_CMD_ON)
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Turn the display off
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
procedure lcd_off() is
    _ks0108_write(KS0108_LEFT,  KS0108_CMD_OFF)
    _ks0108_write(KS0108_RIGHT, KS0108_CMD_OFF)
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Set the page number
-- ----------------------------------------------------------------------------
-- Inputs:     A page number (0 - 7)
-- ----------------------------------------------------------------------------
procedure _ks0108_page(byte in side , byte in page) is  
   pragma inline
   _ks0108_write(side, KS0108_CMD_PAGE | page)
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Set the column address
-- ----------------------------------------------------------------------------
-- Inputs:     The column address (0 - 63)
-- ----------------------------------------------------------------------------
procedure _ks0108_column(byte in side, byte in column) is   
   pragma inline
   _ks0108_write(side, KS0108_CMD_COLUMN | column)
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Specify reads and writes are instructions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
procedure _ks0108_inst() is     
   GLCD_DI = low
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Specify reads and writes are data
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
procedure  _ks0108_data() is
   GLCD_DI = high
end procedure

-- ----------------------------------------------------------------------------
-- Purpose:    Turn a pixel on a graphic LCD on or off
-- ----------------------------------------------------------------------------
-- Inputs:     1) x - the x coordinate of the pixel
--             2) y - the y coordinate of the pixel
-- ----------------------------------------------------------------------------
procedure glcd_write_pixel(byte in x, byte in y) is  
   pragma inline 
   
   var byte data , yy
   var byte side = KS0108_LEFT   -- Stores which chip to use on the LCD
   
   if x > 63 then                -- Check for first or second display area
      x = x - 64
      side = KS0108_RIGHT
   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)      -- ignore; need two reads to get data at new address
   data = _ks0108_read(side)      -- actual data

   if glcd_pen_color == GLCD_BLACK then
      -- bit_set(data, y%8)    -- Turn the pixel on
      yy = y % 8
      data = data | ( 1 << yy )
   else                        -- or
      -- bit_clear(data, y%8)  -- turn the pixel off
      yy = y % 8
      data = data | !( 1 << yy )
   end if
   _ks0108_inst()              -- Set for instruction
   _ks0108_column(side,x)         -- Set the horizontal address
   _ks0108_data()              -- Set for data
   _ks0108_write(side, data)       -- Write the pixel data
end procedure

-- ---------------------------------------------------------------------------- 
-- ks0108_write_byte - write byte to display
-- ----------------------------------------------------------------------------
-- x = pixel column
-- y = pixel row (will be divided by 8)
-- ----------------------------------------------------------------------------
procedure ks0108_write_byte(byte in x, byte in y, byte in veri) is
   var byte side = KS0108_LEFT   -- Stores which chip to use on the LCD

   if x > 63 then                -- Check for first or second display area
      x = x - 64
      side = KS0108_RIGHT
   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, veri)    -- Write the pixel data
end procedure

-- ---------------------------------------------------------------------------- 
-- ks0108_read_byte - read byte to display
-- ----------------------------------------------------------------------------
-- x = pixel column
-- y = pixel row (will be divided by 8)
-- ----------------------------------------------------------------------------
function ks0108_read_byte(byte in x, byte in y) return byte is
   var byte side = KS0108_LEFT   -- Stores which chip to use on the LCD
   var byte data
   
   if x > 63 then                -- Check for first or second display area
      x = x - 64
      side = KS0108_RIGHT
   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)      -- ignore; need two reads to get data at new address
   data = _ks0108_read(side)      -- actual data    
    
   return data    
    
end function

-- ----------------------------------------------------------------------------
-- Purpose:    Fill the LCD screen with the passed in color
-- ----------------------------------------------------------------------------
-- Inputs:     ON  - turn all the pixels on
--             OFF - turn all the pixels off
-- ----------------------------------------------------------------------------
procedure lcd_fill(byte in data) is
   var byte i, j
   i = 0                               -- Loop through the vertical pages
   for 8 loop
      _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

      -- Loop through the horizontal sections (left to right
      for 64 loop
          _ks0108_write(KS0108_LEFT ,data)   -- Write the byte of data
          _ks0108_write(KS0108_RIGHT,data)
      end loop
      i = i + 1
   end loop
end procedure

-- ----------------------------------------------------------------------------
-- lcd_init - Initialize the graphic LCD.
-- ----------------------------------------------------------------------------
-- Call before using any other LCD function.
-- ----------------------------------------------------------------------------
procedure glcd_init() is      
   pragma inline
   
   -- Initialze some pins

   GLCD_DATAPRT = 0x00
   GLCD_DATAPRT_DIR = all_output
   
   GLCD_RW_DIRECTION  = output
   GLCD_CS1_DIRECTION = output
   GLCD_E_DIRECTION   = output
   GLCD_DI_DIRECTION  = output
   GLCD_RST_DIRECTION = output
   GLCD_CS2_DIRECTION = output
   
   GLCD_RST  = high
   GLCD_E    = low
   GLCD_CS1  = low
   
   _ks0108_inst()             -- Set for instruction
   _ks0108_write(KS0108_LEFT,  KS0108_CMD_TOP_RAM | 0)       -- First RAM line at the top of the screen
   _ks0108_write(KS0108_RIGHT, KS0108_CMD_TOP_RAM | 0)      -- First RAM line at the top of the screen
   _ks0108_page  (KS0108_LEFT ,0)        -- Set the page address to 0
   _ks0108_page  (KS0108_RIGHT,0)
   _ks0108_column(KS0108_LEFT ,0)        -- Set the column address to 0
   _ks0108_column(KS0108_RIGHT,0)
   lcd_on()                       -- Turn the display on

   lcd_fill(0)           -- Clear the display
end procedure


-- ---------------------------------------------------------------------------- 
-- _font_5x7_table[] - character table
-- ----------------------------------------------------------------------------
-- 91 * 5 bytes = 455
-- ----------------------------------------------------------------------------
const byte _font_5x7_table[] = {
   0x00, 0x00, 0x00, 0x00, 0x00,    -- space, 32
   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, 0x11, 0x11, 0x11, 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

}           

-- Writes an inverted character to the display using internal font definition.
-- note: the vertical position of chars is in 8-bit steps.
procedure ks0108_write_char(byte in x, byte in y, byte in ch ) is
  pragma inline
   var word indx = 0
   var byte cx
    
   if (ch <  32) then return end if
   if (ch > 122) then return end if
  
   indx = indx + 5 * word(ch - 32)

  
   for 5 loop
      cx = _font_5x7_table[indx]

      _ks0108_inst()                          -- Set for instruction          
      
      if (x < 64) then
         _ks0108_column(KS0108_LEFT,x)        -- Set the horizontal address
         _ks0108_page(KS0108_LEFT, (y / 8))   -- Set the page address
         _ks0108_data()                       -- Set for data
         _ks0108_write(KS0108_LEFT, cx)       -- Write the pixel data
      elsif (x < 128) then
         _ks0108_column(KS0108_RIGHT, x-64)   -- Set the horizontal address
         _ks0108_page(KS0108_RIGHT, (y / 8))  -- Set the page address
         _ks0108_data()                       -- Set for data
         _ks0108_write(KS0108_RIGHT, cx)      -- Write the pixel data
      end if
      -- x > 128 is also ignored (this enables us to write partial chars on the edge)

      indx = indx + 1
      x = x + 1
   end loop
end procedure


if defined(GLCD_FONT_USAGE) then
-- Same as ks0108_write_char, but can use external or user defined font
-- library.
procedure lcd_write_char(byte in x, byte in y, byte in ch ) is
   pragma inline
   var word indx = 0
   var byte cx
    
   if (ch <  32) then return end if
   if (ch > 122) then return end if
  
   indx = indx + _glcd_font_current_byte_per_char * word(ch - 32)

  
   for _glcd_font_current_byte_per_char loop
      cx = glcd_font_lookup(indx)

      _ks0108_inst()                          -- Set for instruction          
      
      if (x < 64) then
         _ks0108_column(KS0108_LEFT,x)        -- Set the horizontal address
         _ks0108_page(KS0108_LEFT, (y / 8))   -- Set the page address
         _ks0108_data()                       -- Set for data
         _ks0108_write(KS0108_LEFT, cx)       -- Write the pixel data
      elsif (x < 128) then
         _ks0108_column(KS0108_RIGHT, x-64)   -- Set the horizontal address
         _ks0108_page(KS0108_RIGHT, (y / 8))  -- Set the page address
         _ks0108_data()                       -- Set for data
         _ks0108_write(KS0108_RIGHT, cx)      -- Write the pixel data
      end if
      -- x > 128 is also ignored (this enables us to write partial chars on the edge)

      indx = indx + 1
      x = x + 1
   end loop
end procedure
end if
