OledDisplaySSD1306

This is a library is for the monchroom Oled Display SSD1306. The module got his own graphic routines and make use of a framebuffer before it is send to the display. This is a array of 1023 byte.

To test the module

Include "Oled.inc"
Include "Bitmap.inc"
//Include "Arial.bas"
Include "BookAntiqua.bas"

Dim x1  As Byte
Dim y1  As Byte
Dim z1  As Byte

Oled.Init()
Oled.ClearBuffer(0)

Oled.VLine(0, 0, 10, 1)
Oled.HLine(0, 0, 10, 1)

//DrawRectangle(5,5,122,60,1)
//Oled.DrawRectangle(10, 10, 20, 20, 1)
//Oled.FillRectangle(20, 20, 100, 40, 1)
//Oled.DrawCircle(20, 20, 10, 1)
//Oled.DrawCircle(18, 18, 10, 1)
//Oled.DrawCircle(16, 16, 10, 1)
//FilledCircle(70, 30, 10, 1)
LoadImage(30, 0, 1, BatteryImage)

SetFont(BookAntiqua)
Print(80,0,"Testrun")

x1 = 20
y1 = 15
z1 = 0
While true
  Oled.DrawCircle(x1, y1, 10, 0)
  Oled.DrawCircle(x1+2, y1+2, 10, 0)
  Oled.DrawCircle(x1+4, y1+4, 10, 0)
  If z1 = 0 Then
    Inc(x1)
    Inc(y1)
  EndIf
  If z1 = 1 Then
    Dec(x1)
    Dec(y1)
  EndIf
  If x1 = 20 Then z1 = 0 EndIf
  If x1 = 50 Then z1 = 1 EndIf

  Oled.DrawCircle(x1, y1, 10, 1)
  Oled.DrawCircle(x1+2, y1+2, 10, 1)
  Oled.DrawCircle(x1+4, y1+4, 10, 1)

  If z1 = 0 Then
    If x1 = 25 Then DrawRectangle(80,20,100,30,1) EndIf
    If x1 = 30 Then DrawRectangle(80,20,100,30,0) EndIf
    If x1 = 30 Then FillRectangle(80,20,100,30,1) EndIf
    If x1 = 35 Then FillRectangle(80,20,100,30,0) EndIf
    If x1 = 35 Then DrawCircle(90,30,10,1) EndIf
    If x1 = 40 Then DrawCircle(90,30,10,0) EndIf
    If x1 = 40 Then FilledCircle(90,30,10,1) EndIf
    If x1 = 45 Then FilledCircle(90,30,10,0) EndIf
  EndIf
  Oled.UpdateDisplay()  
Wend

module bitmap

module Bitmap

public Const BatteryImage() As Byte = 
  (1,11,6,0, 
  $00,$00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF, 
  $00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$FF,
  $00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$00, 
  $00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$00, 
  $00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$00,$FF, 
  $00,$00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF) 

Oled module

'******************************************************************************
'*  Name    : Oled Library for the SSD1306 display                            *
'*  Author  : Frank Sloep (Yoepie)                                            *
'*  Notice  : This source code is provided 'as is'                            *
'*          :                                                                 *
'*          :                                                                 *
'*  Date    : 06/01/16                                                        *
'*  Version :                                                                 *
'*  Notes   : 0.3 Initial release for SwordFish                               *
'*  Notes   : 0.2 add ImageFromCode                                           *
'*  Notes   : 0.1 Initial release For Proton (never released)                 *
'******************************************************************************
'  The most inportend thing about Oled-displays you can not write directly to
'  the cords of the display. This Oled module works fromout a buffer of 1024bytes
'  for a 128x64 display. Everytime you do a write to the display, it will go to
'  the buffer. You have to update the buffer, to see somthing on the display, 
'  with the commands UpdateDisplay() or UpdateRow(Row). 
'
'  Oled Initialize        : Oled.Init()
'  Clear Buffer           : Oled.ClearBuffer(Color (0/1))
'  Clear Row              : Oled.ClearRow(Row (0/7), Color (0/1))
'  Update Display         : Oled.UpdateDisplay()
'  Update Row             : Oled.UpdateRow(Row (0/7))
'  Draw horizontal line   : Oled.Hline(startX , StartY , Length, Color (0/1))
'  Draw Vertical line     : Oled.Vline(startX , StartY , Length, Color (0/1))
'  Draw Rectangle         : Oled.DrawRectangle(StartX , StartY, EndX , EndY, Color (0/1))
'  Draw filled Rectangle  : Oled.DrawRectangle(StartX , StartY, EndX , EndY, Color (0/1))
'  Draw Circle            : Oled.DrawCircle(StartX, StartY, Radius (in pixel), Color (0/1))
'  Draw filled Circle     : Oled.DrawCircle(StartX, StartY, Radius (in pixel), Color (0/1))
'  Display on / off       : Oled.Power(0/1)
'  SetFont                : Oled.SetFont(fontname)
'  Print string           : Olet.Print(StartX, StartY, string)
'  Load Image from mem    : Oled.LoadImage(startX,StartY,Invert,Imagename)
'
'  ----------------------------------------------------------------------------------------
'  The Oled work at the moment only with I2C.
'  Oled_Hardware = I2C
'  Oled_MODEL = SSD1306/?? 
'  LetterSpacing = On/Off
'  Debug = On/Off

Module Oled

Include "Convert.bas"
//Include "SPI.bas"
Include "i2c.bas"
// output in debug
Include "USART.bas"

#option Oled_MODEL = SSD1306
// SSD1306 
#option Oled_Hardware = I2C
// i2C - SPI
#option LetterSpacing = On
// LetterSpacing = On - 1 pixel row after each character
// LetterSpacing = Off - No pixel row after each character.
#option Debug = Off 

'==== Change to your needs ==============================================================================='
Const 
  Adres                     = $78,                                            // I2C adress, Oled display
  Buffer                    = 1024,                                           // maxX * MaxY / 8 Pixels 
  OledMaxX                  = 128,                                            // max Horizontal display size
  OledMaxY                  = 64                                              // max vertical display size

'========================================================================================================='
Private Dim
  Framebuffer(Buffer)       As Byte,                                          // Oled display buffer
  OledRow                   As Byte,                                          // Oled row 0/7
  OledIndex                 As Word,                                          //
  OledData                  As Byte,                                          //
  Oledi                     As Word,                                          // counter
  Oledii                    As Word,                                          // counter
  Olediii                   As Word,                                          // counter
  Temp                      As Byte

// Font structure...
Public Structure TFont
   Header                   As Word
   Data                     As Word
   Width                    As Word
   Height                   As Byte
   RealWidth                As Byte
   IsFixed                  As Header.Booleans(0)
   IsVariable               As Header.Booleans(1)
End Structure

// Image structure...
Public Structure TImage
   Width                    As Word
   Height                   As Byte
   Size                     As Byte
End Structure

Public Dim
  Font                      As TFont,
  Image                     As TImage

// local variables...
Dim
   FFontTableOffset         As Byte,
   FFontTableWidth          As Byte

(*********************************************************************
* Start and Write command to oled display 
* Input1    : Command                                   
* Output    : none                                                   
**********************************************************************)
Sub WriteCommand(pCommand As Byte)

#if Oled_Hardware = I2C
  I2C.Start()
  I2C.WriteByte(Adres)                                                        
  I2C.WriteByte($00)                                                          
  I2C.WriteByte(pCommand)                                                     
  I2C.Stop()
#endif

#if Oled_Hardware = SPI


#endif
End Sub

(*********************************************************************
* Start and Write data to the oled display 
* Input1    :                                    
* Output    : none                                                   
**********************************************************************)
Sub WriteStart(pData As Bit)
#if Oled_Hardware = I2C
  I2C.Start()
  I2C.WriteByte(Adres) 
  If pData = 0 Then
    I2C.WriteByte($80)
  Else
    I2C.WriteByte($40)
  EndIf
#endif

#if Oled_Hardware = SPI


#endif
End Sub

(*********************************************************************
* Helper routine - Write data to the oled display
* Input1    :                                    
* Output    : none                                                   
**********************************************************************)
Sub WriteDataWord(pData As Byte)
#if Oled_Hardware = I2C
  I2C.WriteByte(pData)
#endif

#if Oled_Hardware = SPI


#endif
End Sub

(*********************************************************************
* Initialize for the oled display SSD1306
* Input1    : none                                   
* Output    : none                                                   
**********************************************************************)
Public Sub Init()
#if Oled_Hardware = I2C
  I2C.Initialize(I2C_400_KHZ)
#endif
#if Oled_Hardware = SPI

#endif


#if Oled_MODEL = SSD1306
  WriteCommand($AE)                                                           // \Turn display off
  WriteCommand($00)                                                           // /
  WriteCommand($10)                                                           //
  WriteCommand($40)                                                           //
  WriteCommand($B0)                                                           // Set COM Output Scan Direction
  WriteCommand($81)                                                           // set Low column address  
  WriteCommand($CF)                                                           // set High column address
  WriteCommand($A1)                                                           // set Start line address
  WriteCommand($A6)                                                           // set contrast control register
  WriteCommand($A8)                                                           // set normal display
  WriteCommand($3F)                                                           // set segment re-map 0 To 127
  WriteCommand($c8)                                                           // set normal display
  WriteCommand($d3)                                                           // set multiplex ratio(1 To 64)
  WriteCommand($00)                                                           // 
  WriteCommand($d5)                                                           // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  WriteCommand($80)                                                           // set display offset
  WriteCommand($d9)                                                           // Not offset
  WriteCommand($f1)                                                           // set display Clock divide ratio/oscillator frequency
  WriteCommand($da)                                                           // set divide ratio
  WriteCommand($12)                                                           // set pre-charge period
  WriteCommand($db)                                                           //
  WriteCommand($40)                                                           // set com pins hardware configuration
  WriteCommand($8d)                                                           // set vcomh
  WriteCommand($14)                                                           // 0x20,0.77xVcc
  WriteCommand($AF)                                                           // set DC-DC Enable
  WriteCommand($AF)                                                           // Set COM Output Scan Direction 64 To 0
  DelayMS(10)
#endif
End Sub

(*********************************************************************
* Turn display on / off 
* Input1    : 0 = off | 1 = on                                  
* Output    : None                                                   
**********************************************************************)
Public Sub Power(pData As Bit)

  If pData = 0 Then
    WriteCommand($AE)    
    WriteCommand($00)
  ElseIf pdata = 1 Then    
    WriteCommand($AF)    
    WriteCommand($01)
  EndIf
End Sub 

(*********************************************************************
* Clear Buffer 
* Input1    : color 0=off 1=on                                   
* Output    : none                                                   
**********************************************************************)
Public Sub ClearBuffer(pColor As Byte)
  If pColor = 0 Then OledData = $00 EndIf
  If pColor = 1 Then OledData = $FF EndIf

  Oledi = 0
  For Oledi = 0 To Buffer
    Framebuffer(Oledi) = OledData
  Next
End Sub

(*********************************************************************
* Clear a row in the Buffer 
* Input1    : row 0/7                                   
* Input2    : color 0=off 1=on                                   
* Output    : none                                                   
**********************************************************************)
Public Sub ClearRow(pRow As Byte, pColor As Byte)

  If pColor = 0 Then OledData = $00 EndIf
  If pColor = 1 Then OledData = $FF EndIf
  If pRow = 0 Then Oledii = 0 EndIf
  If pRow = 1 Then Oledii = 128 EndIf
  If pRow = 2 Then Oledii = 256 EndIf
  If pRow = 3 Then Oledii = 384 EndIf
  If pRow = 4 Then Oledii = 512 EndIf
  If pRow = 5 Then Oledii = 640 EndIf
  If pRow = 6 Then Oledii = 768 EndIf
  If pRow = 7 Then Oledii = 896 EndIf

  Oledi = Oledii
  For Oledi = Oledii To Oledii + 127
    Framebuffer(Oledi) = OledData
  Next  
End Sub

(*********************************************************************
* Update display 
* Input1    : none                                   
* Output    : none                                                   
**********************************************************************)
Public Sub UpdateDisplay()
  Oledi = 0
  For OledRow = $b0 To $b7
    WriteCommand(OledRow)                                                   // 
    WriteCommand($00)                                                       // Low column start address
    WriteCommand($10)                                                       // High column start address

    WriteStart(1)
    For Oledii = 0 To OledMaxX -1
      WriteDataWord(Framebuffer(Oledi))
      Inc(Oledi)
    Next
    I2C.Stop()
  Next 
End Sub

(*********************************************************************
* Update display with one row 0/7
* Input1    : row 0/7                                   
* Output    : none                                                   
**********************************************************************)
Public Sub UpdateRow(pRow As Byte)

  WriteCommand($B0 + pRow)                                                    // Row0 .. Row7
  WriteCommand($00)                                                           // Low column Start address
  WriteCommand($10)                                                           // High column Start address

  WriteStart(1)
  For Oledi = 0 To OledMaxX -1
    OledIndex = pRow * 128 + Oledi
    WriteDataWord(Framebuffer(OledIndex))
  Next
  I2C.Stop()
End Sub

(*********************************************************************
* Place Graphics to buffer
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : Color 0=off 1=on                                  
* Output    : Framebuffer(buffer)                                                   
**********************************************************************)
Public Sub DrawPixel(pXPixel As Byte,pYPixel As Byte,pColor As Byte)

  Dim OledOffset As Byte 
  Dim PreData As Byte 

  OledRow = pYpixel / 8
  OledOffset = pYpixel Mod 8
  OledIndex = OledRow * 128 + pXpixel
  PreData = Framebuffer(OledIndex)

  Oledi = 1 << OledOffset
  If pColor = 1 Then
    Framebuffer(OledIndex) = PreData Or Oledi  
  Else
    Framebuffer(OledIndex) = PreData And (Not Oledi)  
  EndIf

#if debug = On  
  USART.Write("_SETLED_", 13)
  USART.Write("X      = ", DecToStr(pXpixel), 13)
  USART.Write("Y      = ", DecToStr(pYpixel),13) 
  USART.Write("Value  = ", DecToStr(pColor), 13) 
  USART.Write("Row    = ", DecToStr(OledRow), 13) 
  USART.Write("Offset = ", DecToStr(OledOffset), 13) 
  USART.Write("Index  = ", DecToStr(OledIndex), 13) 
  USART.Write("Pre    = ", DecToStr(PreData), 13) 
  USART.Write("Buffer = ", DecToStr(Framebuffer(OledIndex)), 13) 
  USART.Write("Oledi  = ", DecToStr(Oledi), 13) 
  USART.Write(13)
#endif
End Sub

(*********************************************************************
* draw Horizontal line on display
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : Line length 0-127                                  
* Output    : to DrawPixel                                                   
**********************************************************************)
Public Sub HLine(StartXpos As Word, StartYpos As Byte, pLength As Word, pColor As Byte)

  Repeat
    DrawPixel((StartXpos + pLength), StartYpos, pColor)
    Dec(pLength)
  Until pLength = 0
End Sub

(*********************************************************************
* draw vertical line on display
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : Line length 0-63                                  
* Output    : to DrawPixel                                                   
**********************************************************************)
Public Sub VLine(StartXpos As Word, StartYpos As Byte, pLength As Word, pColor As Byte)

  Repeat
    DrawPixel(StartXpos, (StartYpos + pLength), pColor)
    Dec(pLength)
  Until pLength = 0
End Sub

(*********************************************************************
* Helper routine for drawpixel to buffer
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : X end cords 0-127                                  
* Input4    : Y end cords 0-63                                  
* Input5    : Color 0=off - 1=on                                  
* Output    : to DrawPixel                                                   
**********************************************************************)
Private Sub DrawLine(StartXpos As Word, StartYpos As Byte, EndXpos As Word, EndYpos As Byte, pColor As Byte)
  Dim DX As Byte
  Dim DY As Byte

  If StartXpos = EndXpos And StartYpos = EndYpos Then 
    Exit
  EndIf

  If StartXpos > EndXpos Then     
    Temp = EndXpos
    EndXpos = StartXpos
    StartXpos = Temp
  EndIf   

  If StartYpos > EndYpos Then     
    Temp = EndYpos
    EndYpos = StartYpos
    StartYpos = Temp   
  EndIf

  DX = EndXpos - StartXpos       
	DY = EndYpos - StartYpos       

  If DX = 0 Then
    Temp = StartYpos    
    Repeat
      DrawPixel(StartXpos, Temp, pColor)  
      Inc(Temp)
    Until Temp = EndYpos 
    Exit 
  EndIf

  If DY = 0 Then
    Temp = StartXpos     
    Repeat
      DrawPixel(Temp, StartYpos, pColor)
      Inc(Temp)
    Until Temp = EndXpos + 1
    Exit
  EndIf

  If DX > DY Then
    Temp = (2 * DY) - DX                
    While StartXpos <> EndXpos
      DrawPixel(StartXpos, StartYpos, pColor)  
      Inc(StartXpos)
      If Temp > 0 Then               
        Inc(StartYpos)                    
        Temp = 2 * DY - 2 * DX
      Else         
        Temp = 2 * DY          
		    DrawPixel(StartXpos, StartYpos, pColor)
      EndIf
    Wend
  Else
    Temp = (2 * DX) - DY                     
    While StartYpos <> EndYpos 
      DrawPixel(StartXpos, StartYpos, pColor)     
      Inc(StartYpos)
      If  Temp > 0 Then           
        Inc(StartXpos)               
        Temp = (2 * DY) - (2 * DX) 
      Else
		    Temp = 2 * DY
		    DrawPixel(StartXpos, StartYpos, pColor)
      EndIf
    Wend
  EndIf
End Sub

(*********************************************************************
* Draw Rectangle on the display 
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : X end cords 0-127                                  
* Input4    : Y end cords 0-63                                  
* Input5    : Color 0=off - 1=on                                  
* Output    : to drawline                                                   
**********************************************************************)
Public Sub DrawRectangle(StartXpos As Word, StartYpos As Byte, EndXpos As Word, EndYpos As Byte, pColor As Byte)
  DrawLine(StartXpos, StartYpos, EndXpos  , StartYpos, pColor)
  DrawLine(StartXpos, EndYpos  , EndXpos  , EndYpos  , pColor)
  DrawLine(StartXpos, StartYpos, StartXpos, EndYpos  , pColor)
  DrawLine(EndXpos  , StartYpos, EndXpos  , EndYpos  , pColor)
End Sub

(*********************************************************************
* Draw Filled Rectangle on the display 
* Input1    : X start cords 0-127                                   
* Input2    : Y start cords 0-63                                   
* Input3    : X end cords 0-127                                  
* Input4    : Y end cords 0-63                                  
* Input5    : Color 0=off - 1=on                                  
* Output    : to drawline                                                    
**********************************************************************)
Public Sub FillRectangle(StartXpos As Word, StartYpos As Byte, EndXpos As Word, EndYpos As Byte, pColor As Byte)

  If StartXpos > EndXpos Then
    Temp = EndXpos
    EndXpos = StartXpos
    StartXpos = Temp
  EndIf

  If StartYpos > EndYpos Then
    Temp = EndYpos
    EndYpos = StartYpos
    StartYpos = Temp   
  EndIf

  Repeat
    DrawLine(StartXpos, StartYpos, EndXpos, StartYpos, pColor)
    Inc(StartYpos)
  Until StartYpos = EndYpos
End Sub

(*********************************************************************
* Draw Circle on the display 
* Input1    : X cords 0-127                                   
* Input2    : Y cords 0-63                                   
* Input3    : Radius in pixels                                  
* Input4    : Color 0=off - 1=on                                  
* Output    : to DrawPixel                                                   
* Note      : Radius is the center of the circle
**********************************************************************)
Public Sub DrawCircle(OledX0 As Byte, OledY0 As Byte, pRadius As Byte, pColor As Byte) 
  Dim OledparL As Byte
  Dim OledparR As Byte
  Dim Oled_X  As Byte
  Dim Oled_Y  As Byte
  Dim OledCircle As LongInt

  OledCircle = -pRadius
  Oled_X   = pRadius
  Oled_Y   = 0
  While Oled_X >= Oled_Y 
    OledparL = OledX0 + Oled_X
    OledparR = OledY0 + Oled_Y
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 - Oled_X
    OledparR = OledY0 + Oled_Y
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 + Oled_X
    OledparR = OledY0 - Oled_Y
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 - Oled_X
    OledparR = OledY0 - Oled_Y
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 + Oled_Y
    OledparR = OledY0 + Oled_X
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 - Oled_Y
    OledparR = OledY0 + Oled_X
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 + Oled_Y
    OledparR = OledY0 - Oled_X
    DrawPixel(OledparL , OledparR, pColor) 
    OledparL = OledX0 - Oled_Y
    OledparR = OledY0 - Oled_X
    DrawPixel(OledparL , OledparR, pColor)

    OledCircle = OledCircle + Oled_Y
    Oled_Y = Oled_Y + 1
    OledCircle = OledCircle + Oled_Y
    If OledCircle >= 0 Then
      Oled_X = Oled_X - 1
      OledCircle = OledCircle - Oled_X
      OledCircle = OledCircle - Oled_X
    EndIf
  Wend
End Sub

(*********************************************************************
* Draw filled Circle on the display 
* Input1    : X cords 0-127                                   
* Input2    : Y cords 0-63                                   
* Input3    : Radius in pixels                                  
* Input4    : Color 0=off - 1=on                                  
* Output    : to DrawPixel                                                   
* Note      : Radius is the center of the circle
**********************************************************************)
Public Sub FilledCircle(OledX0 As Byte, OledY0 As Byte, pRadius As Byte, pColor As Byte) 
  Dim OledcX0 As Byte
  Dim OledcY0 As Byte
  Dim OledcX1 As Byte
  Dim OledcY1 As Byte
  Dim Oled_X  As Byte
  Dim Oled_Y  As Byte
  Dim OledCircle As LongInt

  OledCircle  = -pRadius
  Oled_X  = pRadius
  Oled_Y  = 0         
  While Oled_X >= Oled_Y 
    OledcX0 = OledX0 - Oled_X
    OledcY0 = OledY0 + Oled_Y
    OledcX1 = OledX0 + Oled_X
    OledcY1 = OledY0 + Oled_Y
    DrawLine(OledcX0, OledcY0, OledcX1, OledcY1, pColor)
    OledcX0 = OledX0 - Oled_X
    OledcY0 = OledY0 - Oled_Y
    OledcX1 = OledX0 + Oled_X
    OledcY1 = OledY0 - Oled_Y
    DrawLine(OledcX0, OledcY0, OledcX1, OledcY1, pColor)
    OledcX0 = OledX0 - Oled_Y
    OledcY0 = OledY0 + Oled_X
    OledcX1 = OledX0 + Oled_Y
    OledcY1 = OledY0 + Oled_X
    DrawLine(OledcX0, OledcY0, OledcX1, OledcY1, pColor)
    OledcX0 = OledX0 - Oled_Y
    OledcY0 = OledY0 - Oled_X
    OledcX1 = OledX0 + Oled_Y
    OledcY1 = OledY0 - Oled_X
    DrawLine(OledcX0, OledcY0, OledcX1, OledcY1, pColor)

    OledCircle = OledCircle + Oled_Y
    Oled_Y = Oled_Y + 1
    OledCircle = OledCircle + Oled_Y
    If OledCircle >= 0 Then
      Oled_X =  Oled_X - 1
      OledCircle = OledCircle - Oled_X
      OledCircle = OledCircle - Oled_X
    EndIf
  Wend
End Sub

(*********************************************************************
* Helper routine - Read byte from the table(name)
* Input1    : none                                   
* Output    : position in the table                                                   
**********************************************************************)
Public Inline Function ReadFromTable() As Byte
   Asm-
      TBLRD *+
      movff TABLAT, result
   End Asm
End Function

Function ReadByte(pIndex As Byte) As Byte
   TABLEPTR = Font.Data + pIndex
Asm-
   TBLRD *+
   movff TABLAT, Result
End Asm
End Function  

Function ReadWord(pIndex As Word) As Word
   TABLEPTR = Font.Data + pIndex * 2
Asm-
   TBLRD *+
   movff TABLAT, Result
   TBLRD *+
   movff TABLAT, Result + 1
End Asm
End Function 

(*********************************************************************
* Place fontname in system for simple accses to print characters
* Input1    : Name of font                                  
* Output    : None                                                   
**********************************************************************)
Public Sub SetFont(ByRefConst pFontTable() As Byte)
  Dim FontHeaderSize As Byte

  Font.Data = @pFontTable                                             // address of font table
  Font.Header = ReadWord(0)                                           // font type, number of characters
  Font.Width = ReadByte(2)                                            // character width
  Font.Height = ReadByte(3)                                           // character height
  FontHeaderSize = 4                                                  // default header size
  FFontTableOffset = 32                                               // default character offset

  // calculate fixed font table width...
  If Font.IsFixed Then
    FFontTableWidth = Byte(Font.Height + 7) / 8 * Font.Width
  EndIf

  // skip over header information and point to font table...
  Inc(Font.Data, FontHeaderSize)

  #if debug = On Then
    USART.Write("** Font **",13)
    USART.Write("Font.Data + 4    ", DecToStr(Font.Data),13)
    USART.Write("Font.Header      ", DecToStr(Font.Header),13)
    USART.Write("Font.Width       ", DecToStr(Font.Width),13)
    USART.Write("Font.Height      ", DecToStr(Font.Height),13)
    USART.Write("FontHeaderSize   ", DecToStr(FontHeaderSize),13)
    USART.Write("FFontTableOffset ", DecToStr(FFontTableOffset),13)
    USART.Write("FFontTableWidth  ", DecToStr(FFontTableWidth),13)
    USART.Write("IsFixed          ", DecToStr(Font.IsFixed),13)
    USART.Write("IsVariable       ", DecToStr(Font.IsVariable),13)
  #endif
End Sub

(*********************************************************************
* Helper routine to print character to buffer 
* Input1    : x coords                                  
* Input2    : y coords                                  
* Input3    : Character                                  
* Output    : None                                                   
**********************************************************************)
Sub PrintChar(pXpos, pYpos, pChar As Byte)
  Dim HeightCount, WidthCount, PixelRowCount, pixels As Byte
  Dim X1, y1 As Byte

  HeightCount = Font.Height
  PixelRowCount = 0
  X1 = pXpos
  y1 = pYpos

  Dec(pChar, FFontTableOffset)
  TABLEPTR = Font.Data + ReadWord(pChar)

  If Font.IsVariable Then
    Font.Width = ReadFromTable()
  EndIf
  WidthCount = Font.Width

  Repeat
    Repeat
      If PixelRowCount = 0 Then
        PixelRowCount = 8
        pixels = ReadFromTable()
      EndIf
      If pixels.0 = 0 Then                                           // do we need to set a pixel
        DrawPixel(X1, y1, 0)                                         // no
      ElseIf pixels.0 = 1 Then
        DrawPixel(X1, y1, 1)                                         // yes
      EndIf
      pixels = pixels >> 1                                           // Move each pixel into bit-0
      Inc(Y1)
      Dec(PixelRowCount)
      Dec(HeightCount)
    Until HeightCount = 0  
    PixelRowCount = 0
    Inc(X1)
    y1 = pYpos
    HeightCount = Font.Height
    Dec(WidthCount)
  Until WidthCount = 0
  Font.RealWidth = Font.RealWidth + Font.Width                       // 
  #if LetterSpacing = On Then
    Inc(Font.RealWidth)
  #EndIf 
End Sub

(*********************************************************************
* Print string routine 
* Input1    : x coords                                  
* Input2    : y coords                                  
* Input3    : String                                  
* Output    : Single character + coords                                                  
**********************************************************************)
Public Sub Print(pXpos, pYpos As Byte, pText As String)
  Dim CharIndex, CharData As Byte

  CharIndex = 0
  CharData = pText(0)
  Font.RealWidth = pXpos
  While CharData <> 0
    PrintChar(Font.RealWidth, pYpos, CharData)
    Inc(CharIndex)
    CharData = pText(CharIndex)
  Wend
End Sub

(*********************************************************************
* Load image from mem to display 
* Input1    : X cords 0-127                                 
* Input2    : y Cords 0-63                                 
* Input3    : Invert the pixel                                 
* Input4    : ImageName                                 
* Output    : None                                                   
**********************************************************************)
Public Sub LoadImage(pXpos, pYpos As Byte, pInvert As Bit, ByRefConst pImageTable() As Byte)
  Dim Result As Byte

  TABLEPTR = @pImageTable
  ReadFromTable
  Image.Width = ReadFromTable
  Image.Height = ReadFromTable
  ReadFromTable
  Image.Size = Image.Width * Image.Height
  Temp = Image.Size

  Olediii = 0
  Oledii = pXpos
  Repeat
    Result = ReadFromTable
    If Result = 0 Then
      If pInvert = 0 Then
        DrawPixel(Oledii, pYpos, 0)
      Else
        DrawPixel(Oledii, pYpos, 1)
      EndIf
    ElseIf Result > 0 Then
      If pInvert = 0 Then
        DrawPixel(Oledii, pYpos, 1)
      Else
        DrawPixel(Oledii, pYpos, 0)
      EndIf
    EndIf

    Inc(Olediii)  
    Inc(Oledii)
    If Olediii = Image.Width Then 
      Olediii = 0
      Oledii = pXpos
      Inc(pYpos)
    EndIf  
    Dec(Image.Size)
  Until Image.Size = 0

  #if debug = On Then
    USART.Write("** Image **",13)
    USART.Write("ImageWidth       ", DecToStr(Image.Width),13)
    USART.Write("ImageHeight      ", DecToStr(Image.Height),13)
    USART.Write("ImageSize        ", DecToStr(Temp),13)
  #endif
End Sub