i1T90

VMLAB Resurrection
~~~~~~~~~~~~~~~~~~

For some unidentified reasons, C:\VMLAB\Lessons\ as project folder works for both accounts Name.Surename and opilane.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Empty template.

007SPY.prj

  .MICRO "ATmega8"      ; ATMEL AVR 8-bit microcontroller
  .PROGRAM "007SPY.asm" ; Assembly is default language for VMLAB
  .TARGET "007SPY.hex"  ; Firmware to build
  .TRACE                ; Activate micro trace

  .POWER VDD=5 VSS=0    ; Power nodes
  .CLOCK 1meg           ; Micro clock
  .STORE 250m           ; Trace (micro+signals) storage time

  ; PINS: RESET,AREF,PB0-PB7,PC0-PC6,PD0-PD7,ACO,TIM1OVF,ADC6,ADC7

--------

007SPY.asm

  .include "C:\WinAVR\include\m8def.inc"

  start:
     nop

  forever:
     nop

  rjmp forever

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Graphical LCD 128x64 (KS0108) with ready made hex file.

GLCD.prj

  .MICRO "ATmega8"

  ; glcd1.hex contains a static image (map of the world)
  ; http://www.scienceprog.com
  ;      /controlling-graphical-128x64-lcd-based-on-ks0108/

  .program "glcd1.hex" ; try "glcd3.hex" "glcd5.hex"
  .POWER VDD=5 VSS=0   ; Power nodes
  .CLOCK 1meg          ; Micro clock

  ; PINS: RESET,AREF,PA0-PA7,PB0-PB7,PC0-PC7,PD0-PD7,ACO,TIM1OVF
  ; slider_1 adjusts backlight
  ; PERIPHERIALS

  Xglcd _graphiclcd pc0 pc1 pc2 pd7 pd6 pd5 pd4 pd3 pd2 pd1 pd0 pc4 pc3 vdd ledplus vss
  ;                 D/I RW  E   D7  D6  D5  D4  D3  D2  D1  D0  CS1 CS2 RST LED+    LED-
  ; AVRSimulatorIDE RS  RW  E   D7  D6  D5  D4  D3  D2  D1  D0  CS2 CS1 RST LED+    LED-

  V ledplus vss slider_1(0 5)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

One segment on each of 8 Displays:

7SEG.prj

  .MICRO "ATmega8"
  .TOOLCHAIN "GCC"     ; Bug*1
  .GCCPATH   "C:\WinAVR"
  .GCCMAKE   AUTO
  .TARGET    "7SEG.hex"
  .SOURCE    "7SEG.c"

  .TRACE              ; Activate micro trace

  .POWER VDD=5 VSS=0  ; Power nodes
  .CLOCK 1meg         ; Micro clock
  .STORE 250m         ; Trace (micro+signals) storage time

  ; PINS: RESET,AREF,PB0-PB7,PC0-PC6,PD0-PD7,ACO,TIM1OVF,ADC6,ADC7
  ; PERIPHERIALS

  ; 8 - digit LED 7 segment display
  ;
  ;           7   -   -   -   -   -   -   -
  ;          | | | 6 | | | | | | 2 | | | | |
  ;           -   -   -   -   -   -   1   -
  ;          | | | | | 5 | | 3 | | | | | | |
  ;           -.  -.  -.  4.  -.  -.  -.  -0

  X1 _led7cc VDD GND GND GND GND GND GND GND GND
  X2 _led7cc GND VDD GND GND GND GND GND GND GND
  X3 _led7cc GND GND VDD GND GND GND GND GND GND
  X4 _led7cc GND GND GND VDD GND GND GND GND GND
  X5 _led7cc GND GND GND GND VDD GND GND GND GND
  X6 _led7cc GND GND GND GND GND VDD GND GND GND
  X7 _led7cc GND GND GND GND GND GND VDD GND GND
  X8 _led7cc GND GND GND GND GND GND GND VDD GND

------
*1) I found solution. Copy this file:
  http://www.madwizard.org/download/electronics/msys-1.0-vista64.zip
to utils\bin directory (WinAVR)
  http://www.avrfreaks.net/forum/windows-81-compilation-error?page=all
------

7SEG.c

  int main(void) {
     while(1) {
     }
  }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MANHEX.prj

  .MICRO "ATmega8"
  .TOOLCHAIN "GCC" ;     Bug*
  ; http://www.avrfreaks.net/forum/windows-81-compilation-error?page=all
  .GCCPATH   "C:\WinAVR"
  .GCCMAKE   AUTO
  .TARGET    "manhex.hex"
  .SOURCE    "manhex.c"

  .TRACE              ; Activate micro trace

  .POWER VDD=5 VSS=0  ; Power nodes
  .CLOCK 1meg         ; Micro clock
  .STORE 250m         ; Trace (micro+signals) storage time

  ; Micro nodes: RESET, AREF, PB0-PB7, PC0-PC6, PD0-PD7, ACO, TIM1OVF, ADC6, ADC7
  ; Define here the hardware around the micro

  R1 PB0 VSS 330
  R2 PB1 VSS 330
  R3 PB2 VSS 330
  R4 PB3 VSS 330

  K0 PB0 S1 LATCHED
  K1 PB1 S2 LATCHED
  K2 PB2 S3 LATCHED
  K3 PB3 S4 LATCHED

  D1 VDD S1
  D2 VDD S2
  D3 VDD S3
  D4 VDD S4

  ; segA:

  RA0 PB0 nA2 1K
  RA1 PB1 nA7 1K
  RA2 PB2 nA1 1K
  RA3 PB3 nA3 1K

  ;   x3    nA3
  ;   x2    nA1
  ;   x1    nA7
  ;   x0    nA2
  ;   y     nAo

  XA1   ND2   nA1   nA1   nA4
  XA2   ND2   nA2   nA2   nA5
  XA3   ND2   nA3   nA3   nA6

  XA4   ND2   nA4   nA5   nA8
  XA5   ND2   nA6   nA7   nA9
  XA6   ND2   nA6   nA1   nAA
  XA7   ND2   nA3   nA4   nAB

  XA8   ND2   nA8   nA9   nAC
  XA9   ND2   nAA   nAA   nAD
  XAA   ND2   nAB   nAB   nAE
  XAB   ND2   nA7   nA7   nAF

  XAC   ND2   nAC   nAC   nAG
  XAD   ND2   nAD   nA2   nAH
  XAE   ND2   nA1   nA7   nAi
  XAF   ND2   nAE   nAF   nAJ

  XAG   ND2   nAG   nAH   nAK
  XAH   ND2   nAi   nAJ   nAL

  XAi   ND2   nAK   nAK   nAM
  XAJ   ND2   nAL   nAL   nAN

  XAK   ND2   nAM   nAN   nAo

  ; segB

  RB0 PB0 nB8 1K
  RB1 PB1 nB2 1K
  RB2 PB2 nB6 1K
  RB3 PB3 nB1 1K

  ;   x3    nB1
  ;   x2    nB6
  ;   x1    nB2
  ;   x0    nB8
  ;   y     nBT

  XB1   ND2   nB1   nB1   nB3
  XB2   ND2   nB2   nB2   nB4

  XB3   ND2   nB3   nB4   nB7

  XB4   ND2   nB6   nB6   nBA
  XB5   ND2   nB7   nB7   nBB
  XB6   ND2   nB8   nB8   nBC

  XB7   ND2   nB3   nBA   nBD
  XB8   ND2   nBB   nBC   nBE
  XB9   ND2   nB3   nB2   nBF
  XBA   ND2   nB1   nB4   nBG

  XBB   ND2   nBD   nBE   nBH
  XBC   ND2   nBF   nBF   nBJ
  XBD   ND2   nBG   nBG   nBK

  XBE   ND2   nBH   nBH   nBL
  XBF   ND2   nBA   nBC   nBM
  XBG   ND2   nBJ   nB8   nBN
  XBH   ND2   nBK   nB8   nBo

  XBi   ND2   nBL   nBM   nBP
  XBJ   ND2   nBN   nBo   nBQ

  XBK   ND2   nBP   nBP   nBR
  XBL   ND2   nBQ   nBQ   nBS

  XBM   ND2   nBR   nBS   nBT

  ; segC

  RC0 PB0 nC5 1K
  RC1 PB1 nC2 1K
  RC2 PB2 nC7 1K
  RC3 PB3 nC1 1K

  ;   x3    nC1
  ;   x2    nC7
  ;   x1    nC2
  ;   x0    nC5
  ;   y     nCH

  XC1   ND2   nC1   nC1   nC3
  XC2   ND2   nC2   nC2   nC4

  XC3   ND2   nC3   nC4   nCi
  XC4   ND2   nC3   nC5   nCJ

  XC5   ND2   nCi   nCJ   nC6
  XC6   ND2   nC7   nC7   nC8

  XC7   ND2   nC6   nC6   nC9
  XC8   ND2   nC4   nC5   nCA
  XC9   ND2   nC3   nC7   nCB
  XCA   ND2   nC1   nC8   nCC

  XCB   ND2   nC9   nCA   nCD
  XCC   ND2   nCB   nCC   nCE

  XCD   ND2   nCD   nCD   nCF
  XCE   ND2   nCE   nCE   nCG

  XCF   ND2   nCF   nCG   nCH

  ; segD

  RD0 PB0 nD7 1K
  RD1 PB1 nD9 1K
  RD2 PB2 nDX 1K
  RD3 PB3 nD1 1K

  ;   x3    nD1
  ;   x2    nDX
  ;   x1    nD5
  ;   x0    nD7
  ;   y     nDW

  XD1   ND2   nD1   nD1   nD3
  XD2   ND2   nDX   nDX   nD4

  XD3   ND2   nD3   nD4   nD6
  XD4   ND2   nD4   nD9   nD8

  XD5   ND2   nD6   nD6   nDA
  XD6   ND2   nD7   nD7   nDB
  XD7   ND2   nD8   nD8   nDC
  XD8   ND2   nD9   nD9   nDD

  XD9   ND2   nDA   nDB   nDE
  XDA   ND2   nDC   nD7   nDF
  XDB   ND2   nDX   nDD   nDG
  XDC   ND2   nDX   nD9   nDH

  XDD   ND2   nDE   nDF   nDN
  XDE   ND2   nDG   nDG   nDJ
  XDF   ND2   nDH   nDH   nDL

  XDG   ND2   nDN   nDN   nDo
  XDH   ND2   nDJ   nD7   nDP
  XDi   ND2   nDL   nDB   nDQ
  XDJ   ND2   nD1   nDD   nDR

  XDK   ND2   nDo   nDP   nDS
  XDL   ND2   nDQ   nDR   nDT

  XDM   ND2   nDS   nDS   nDU
  XDN   ND2   nDT   nDT   nDV

  XDo   ND2   nDU   nDV   nDW

  ; segE

  RE0 PB0 nE2 1K
  RE1 PB1 nE5 1K
  RE2 PB2 nE1 1K
  RE3 PB3 nE6 1K

  ;   x3    nE6
  ;   x2    nE1
  ;   x1    nE5
  ;   x0    nE2
  ;   y     nEF

  XE1   ND2   nE1   nE1   nE3
  XE2   ND2   nE2   nE2   nE4

  XE3   ND2   nE3   nE4   nE7
  XE4   ND2   nE5   nE4   nE8
  XE5   ND2   nE6   nE5   nE9
  XE6   ND2   nE6   nE1   nEA

  XE7   ND2   nE7   nE8   nEB
  XE8   ND2   nE9   nEA   nEC

  XE9   ND2   nEB   nEB   nED
  XEA   ND2   nEC   nEC   nEE

  XEB   ND2   nED   nEE   nEF

  ; segF

  RF0 PB0 nF7 1K
  RF1 PB1 nF2 1K
  RF2 PB2 nF5 1K
  RF3 PB3 nF1 1K

  ;   x3    nF1
  ;   x2    nF5
  ;   x1    nF2
  ;   x0    nF7
  ;   y     nFo

  XF1   ND2   nF1   nF1   nF3
  XF2   ND2   nF2   nF2   nF4

  XF3   ND2   nF3   nF4   nF6
  XF4   ND2   nF3   nF5   nF8

  XF5   ND2   nF6   nF6   nF9
  XF6   ND2   nF7   nF7   nFA
  XF7   ND2   nF8   nF8   nFB

  XF8   ND2   nF9   nFA   nFC
  XF9   ND2   nFB   nF4   nFD

  XFA   ND2   nFC   nFD   nFE
  XFB   ND2   nF5   nF5   nFF

  XFC   ND2   nFE   nFE   nFG
  XFD   ND2   nFA   nFB   nFH
  XFE   ND2   nF1   nFF   nFi
  XFF   ND2   nF1   nF2   nFJ

  XFG   ND2   nFG   nFH   nFK
  XFH   ND2   nFi   nFJ   nFL

  XFi   ND2   nFK   nFK   nFM
  XFJ   ND2   nFL   nFL   nFN

  XFK   ND2   nFM   nFN   nFo

  ; segG

  RG0 PB0 nG3 1K
  RG1 PB1 nG2 1K
  RG2 PB2 nG1 1K
  RG3 PB3 nG7 1K

  ;   x3    nG7
  ;   x2    nG1
  ;   x1    nG2
  ;   x0    nG3
  ;   y     nGG

  XG1   ND2   nG1   nG1   nG4
  XG2   ND2   nG2   nG2   nG5
  XG3   ND2   nG3   nG3   nG6

  XG4   ND2   nG4   nG2   nG8
  XG5   ND2   nG1   nG5   nG9
  XG6   ND2   nG1   nG6   nGA
  XG7   ND2   nG7   nG7   nGB

  XG8   ND2   nG8   nG9   nGC
  XG9   ND2   nGA   nGB   nGD

  XGA   ND2   nGC   nGC   nGE
  XGB   ND2   nGD   nGD   nGF

  XGC   ND2   nGE   nGF   nGG

  X1   _led7cc nAo nBT nCH nDW nEF nFo nGG GND  GND

  ;debugging
  ;X2   _led7cc nD1 nDX nD3 nD4 nD5 nD6 nD7 nD8  GND
  ;X3   _led7cc nD9 nDA nDB nDC nDD nDE nDF nDG  GND
  ;X4   _led7cc nDH nDi nDJ nDK nDL nDM nDN nDo  GND
  ;X5   _led7cc nDP nDQ nDR nDS nDT nDU nDV nDW  GND

------

MANHEX.c

  int main(void) {
     while(1) {
     }
  }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Stimulus K0..K3           ^^  |
                         //  -+-   100
                 VSS ---|>|--o o--/\/\/--- GND
FOUR.prj

  .MICRO "ATmega8"
  .TOOLCHAIN "GCC" ;     Bug*
  ; http://www.avrfreaks.net/forum/windows-81-compilation-error?page=all
  .GCCPATH   "C:\WinAVR"
  .GCCMAKE   AUTO
  .TARGET    "four.hex"
  .SOURCE    "four.c"

  .TRACE              ; Activate micro trace

  .POWER VDD=5 VSS=0  ; Power nodes
  .CLOCK 1meg         ; Micro clock
  .STORE 250m         ; Trace (micro+signals) storage time

  ; Micro nodes: RESET, AREF, PB0-PB7, PC0-PC6, PD0-PD7, ACO, TIM1OVF, ADC6, ADC7
  ; Define here the hardware around the micro

  R1 PB0 VSS 330
  R2 PB1 VSS 330
  R3 PB2 VSS 330
  R4 PB3 VSS 330

  K0 PB0 S1 LATCHED
  K1 PB1 S2 LATCHED
  K2 PB2 S3 LATCHED
  K3 PB3 S4 LATCHED

  D1 VDD S1
  D2 VDD S2
  D3 VDD S3
  D4 VDD S4

  R5 VDD  PB4 1K
  R6 VDD  PB5 1K
  R7 VDD  PB6 1K
  R8 VDD  PB7 1K

  R9  PB4 N4 10
  R10 PB5 N3 10
  R11 PB6 N2 10
  R12 PB7 N1 10

  ; 4511 ND2

  X001    ND2    N1    N1    N10  ; i3
  X002    ND2    N2    N2    NQ   ; i2
  X003    ND2    N3    N3    NP   ; i1
  X004    ND2    N4    N4    No   ; i0

  X005    ND2    N2    N3    N5
  X006    ND2    N2    N4    N6
  X007    ND2    N2    NP    N7
  X008    ND2    NQ    N3    N8
  X009    ND2    NP    N4    N9
  X010    ND2    NQ    N10   NA
  X011    ND2    N4    N3    NC
  X012    ND2    N3    NQ    N1C
  X013    ND2    No    NP    ND
  X014    ND2    NQ    N10   NE

  X015    ND2    N5    N5    NF
  X016    ND2    N6    N6    NG
  X017    ND2    N7    N7    NH
  X018    ND2    N8    N8    Ni
  X019    ND2    N9    N9    NJ
  X020    ND2    NA    NA    NR
  X021    ND2    NC    NC    NL
  X022    ND2    ND    ND    NM
  X023    ND2    NE    NE    NN

  X024    ND2    NF    No    NS
  X025    ND2    NG    NP    NT
  X026    ND2    NH    No    NU
  X027    ND2    Ni    No    NV
  X028    ND2    NV    NV    N18  ; c
  X029    ND2    NJ    NQ    NW
  X030    ND2    NJ    NR    NX
  X031    ND2    NL    N2    NY
  X032    ND2    NM    NN    NZ

  X033    ND2    NU    NS    N11
  X034    ND2    NS    NT    N12  ; b
  X035    ND2    NW    NU    N13
  X036    ND2    NU    No    N14  ; e
  X037    ND2    NX    NY    N15

  X038    ND2    N11   N11   N17
  X039    ND2    N13   N13   N19
  X040    ND2    N15   N15   N1B

  X041    ND2    N17   NX    N1E  ; a
  X042    ND2    N19   NY    N1F  ; d
  X043    ND2    N1B   N1C   N1G  ; f
  X044    ND2    N1B   NZ    N1H  ; g

  X1   _led7ca N1E N12 N18 N1F N14 N1G N1H VDD VDD

------------------------------------------------------------------------

FOUR.c

  #include <avr/io.h>
  #include <util/delay.h>

  unsigned char leds;

  void init(void) {
    DDRB=0xf0;
  }

  int main(void) {
    init();

    while(1) PORTB = PINB<<4 & 0xf0;
    return 0;
  }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

GLCD with arbitrary image: 6 source files project

          -- Почему в VMLab нет окна с деревом исходников на C,
             других используемых ресурсов (spice схемы, hex,..)
          -- А почему тебе этого хочется?
          -- Да видел вот в "Студиях".. Так почему?
          -- Достаточно одного (длинного) файла исходника на C.

prjektas.prj

  .MICRO "ATmega8"
  .TOOLCHAIN "GCC"    ; Bug:*
  ; http://www.avrfreaks.net/forum/windows-81-compilation-error?page=all

  .GCCPATH   "C:\WinAVR"
  .GCCMAKE   AUTO
  .TARGET    "prjektas.hex"
  .SOURCE    "prjektas.c"
  .TRACE              ; Activate micro trace

  .POWER VDD=5 VSS=0  ; Power nodes
  .CLOCK 1meg         ; Micro clock
  .STORE 250m         ; Trace (micro+signals) storage time

  ; PINS: RESET,AREF,PA0-PA7,PB0-PB7,PC0-PC7,PD0-PD7,ACO,TIM1OVF
  ; slider_1 adjusts backlight
  ; PERIPHERIALS

  Xglcd _graphiclcd pc0 pc1 pc2 pd7 pd6 pd5 pd4 pd3 pd2 pd1 pd0 pc4 pc3 vdd ledplus vss
  ;                 D/I RW  E   D7  D6  D5  D4  D3  D2  D1  D0  CS1 CS2 RST LED+    LED-
  ; AVRSimulatorIDE RS  RW  E   D7  D6  D5  D4  D3  D2  D1  D0  CS2 CS1 RST LED+    LED-
  ; VMLAB HEX succeeded with AVRSimulatorIDE

  V ledplus vss slider_1(0 5)

---------

prjektas.c

  #include <inttypes.h>
  #include <avr/io.h>
  #include <avr/pgmspace.h>

  /*
   * ks0108.h
   * Copyright:      Fabian Maximilian Thiele  mailto:me@apetech.de
   * Author:         Fabian Maximilian Thiele
   * Remarks:        this Copyright must be included
   * known Problems: none
   * Version:        1.1
   * Description:    Graphic Library for KS0108 based (and compatible) LCDs
   */

  // #include <inttypes.h>
  // #include <avr/pgmspace.h>

  #ifndef KS0108_H
  #define KS0108_H

  // Ports
  #define LCD_CMD_PORT      PORTC   // Command Output Register
  #define LCD_CMD_DIR       DDRC    // Data Direction Register
                                    // for Command Port

  #define LCD_DATA_IN       PIND    // Data Input Register
  #define LCD_DATA_OUT      PORTD   // Data Output Register
  #define LCD_DATA_DIR      DDRD    // Data Direction Register
                                    // for Data Port
  // Command Port Bits
  #define D_I               0x00    // D/I Bit Number
  #define R_W               0x01    // R/W Bit Number
  #define EN                0x02    // EN Bit Number
  #define CSEL1             0x03    // CS1 Bit Number
  #define CSEL2             0x04    // CS2 Bit Number

  // Chips
  #define CHIP1             0x00
  #define CHIP2             0x01

  // Commands
  #define LCD_ON            0x3F
  #define LCD_OFF           0x3E
  #define LCD_SET_ADD       0x40
  #define LCD_SET_PAGE      0xB8
  #define LCD_DISP_START    0xC0

  // Colors
  #define BLACK             0xFF
  #define WHITE             0x00

  // Font Indices
  #define FONT_LENGTH       0
  #define FONT_FIXED_WIDTH  2
  #define FONT_HEIGHT       3
  #define FONT_FIRST_CHAR   4
  #define FONT_CHAR_COUNT   5
  #define FONT_WIDTH_TABLE  6

  // Uncomment for slow drawing
  // #define DEBUG

  typedef struct {
    uint8_t x;
    uint8_t y;
    uint8_t page;
  } lcdCoord;

  typedef uint8_t (*ks0108FontCallback)(const uint8_t*);

  //
  // Function Prototypes
  //

  // Graphic Functions
  void ks0108DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
  void ks0108DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color);
  void ks0108DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color);
  void ks0108FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color);
  void ks0108InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height);
  void ks0108SetInverted(uint8_t invert);
  void ks0108SetDot(uint8_t x, uint8_t y, uint8_t color);

  #define ks0108DrawVertLine(x, y, length, color) {ks0108FillRect(x, y, 0, length, color);}
  #define ks0108DrawHoriLine(x, y, length, color) {ks0108FillRect(x, y, length, 0, color);}
  #define ks0108DrawCircle(xCenter, yCenter, radius, color) {ks0108DrawRoundRect(xCenter-radius, yCenter-radius, 2*radius, 2*radius, radius, color);}
  #define ks0108ClearScreen() {ks0108FillRect(0, 0, 127, 63, WHITE);}

  // Font Functions

  //Standard Read Callback
  uint8_t ks0108ReadFontData(const uint8_t* ptr);
  void ks0108SelectFont(const uint8_t* font, ks0108FontCallback callback, uint8_t color);
  int ks0108PutChar(unsigned char c);
  void ks0108Puts(char* str);
  void ks0108Puts_P(PGM_P str);
  uint8_t ks0108CharWidth(char c);
  uint16_t ks0108StringWidth(char* str);
  uint16_t ks0108StringWidth_P(PGM_P str);

  // Control Functions
  void ks0108GotoXY(uint8_t x, uint8_t y);
  void ks0108Init(uint8_t invert);
  uint8_t ks0108DoReadData(uint8_t first);
  uint8_t ks0108ReadData(void);
  void ks0108Enable(void);
  void ks0108WriteCommand(uint8_t cmd, uint8_t chip);
  void ks0108WriteData(uint8_t data);

  #endif KS0108_H
  // end of ks0108.h

  // bitmap.h
  // #include <avr/pgmspace.h>
  #ifndef BITMAP_H
  #define BITMAP_H
  #define LCD_WIDTH 128

  #define LCD_WIDTH 128

  static uint8_t IMAGE[] PROGMEM = {
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,
    0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0xFF,0xFF,
    0xFF,0xFF,0x7F,0x7F,0x3F,0xBF,0x3F,0x1F,
    0x3F,0x3F,0x3F,0x7F,0xFF,0xBF,0x1F,0x47,
    0x87,0x07,0x07,0x07,0x27,0x77,0x77,0x67,
    0xE7,0xE7,0xE7,0xC7,0x87,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x87,
    0xC7,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0x7F,
    0x7F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,
    0x3F,0x7F,0x7F,0x7F,0x3F,0x3F,0x3F,0x1F,
    0x1F,0x1F,0x1F,0x1F,0x0F,0x1F,0x1F,0x1F,
    0x3F,0x3F,0x3F,0x3F,0x3F,0x7F,0x7F,0x7F,
    0x7F,0x3F,0x3F,0x7F,0x7F,0x7F,0x7F,0x7F,
    0x7F,0xFF,0xFF,0xFF,0x7F,0x7F,0xFF,0xFF,
    0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xF8,0xF8,
    0xF0,0xF0,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,
    0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,
    0x0C,0x0E,0x1C,0x3F,0x0F,0x03,0x00,0x04,
    0x04,0x87,0xCF,0x9F,0xFF,0xFE,0xFC,0xF8,
    0xF8,0xFC,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFD,0xFF,0xFF,0xFF,0xDF,0xCF,0x1F,
    0x3F,0x3F,0x13,0x03,0x01,0x10,0x1C,0x0C,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x90,0xF8,0xF8,0xF8,0xF8,0xF8,0xCC,
    0xE0,0xF0,0xF8,0xF8,0xF8,0xFC,0xFC,0xFE,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xF8,0xF0,0xE0,0xE0,
    0xC0,0x80,0x00,0x00,0x00,0x80,0xC0,0xC0,
    0xE0,0xE0,0xC0,0xE0,0xF0,0xFC,0xFE,0xFE,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0x3F,0x1F,0x09,0x01,0x00,
    0x04,0x06,0x06,0x06,0x0E,0x1C,0x1E,0x18,
    0x1C,0x18,0x19,0x19,0x01,0x03,0x00,0x00,
    0x00,0x07,0x46,0x00,0x00,0x80,0x80,0x80,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x84,0xFC,0xF8,0xF4,0xFE,0xFE,
    0xF7,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFC,0xFC,0xFC,0xFD,0xF9,
    0xF9,0xF3,0xFF,0xFF,0x5F,0x0F,0x0F,0x0F,
    0x0F,0x0F,0x1F,0x3F,0x3F,0x3F,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xF0,0xE0,0xC0,0xC0,0xC0,0xC0,
    0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,
    0x88,0xC8,0xFC,0xFC,0xFE,0xFF,0xFF,0xFF,
    0xFF,0xFE,0xF0,0xE0,0xF8,0xFC,0xFE,0xFF,
    0xFE,0xFC,0xF8,0x30,0x30,0xE0,0xF6,0x7F,
    0x3F,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFD,0xF0,0xE0,0xC0,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x01,0x01,0xF1,0xFB,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFE,0x98,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x80,0xC0,0xFE,0x7F,
    0x1F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFE,0xFC,0xFF,0xFF,0xFE,
    0xFE,0x7F,0x7E,0x3F,0x1F,0x1F,0x0F,0x0F,
    0x3D,0x39,0x19,0x3B,0x7F,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,
    0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0xF0,0xF0,
    0xF0,0xF0,0xF8,0xFC,0xFF,0xFF,0xFF,0xFF,
    0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFC,0xF0,0xF0,0xF0,0xF8,0xF8,0xF8,0xF0,
    0xF0,0xE0,0xC0,0xC0,0xE0,0xF0,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0x7F,0xFF,0xDF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0xF8,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0x7F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,
    0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
    0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
    0x7F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x03,0x03,
    0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
    0x03,0x03,0x03,0x03,0x03,0x01,0x03,0x03,
    0x03,0x03,0x03,0x03,0x03,0x01,0x01,0x00,
    0x00,0x00,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x03,0x03,0x01,0x01,0x01,0x01,0x01,
    0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
    0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x01,0x05,0x05,0x07,0x07,0x07,0x07
  };

  void LoadBitmap(unsigned char *bitmap);

  #endif BITMAP_H

  void LoadBitmap(unsigned char *bitmap)
  {
    uint16_t i, j, by;
    for(i=0; i<64; i+=8)
      for(j=0; j<128; j++) {
        by=pgm_read_byte(bitmap++);
        ks0108GotoXY(j, i);
        ks0108WriteData(by);
      }
    return;
  }

  /*
   * Copyright:      Fabian Maximilian Thiele  mailto:me@apetech.de
   * Author:         Fabian Maximilian Thiele
   * Remarks:        this Copyright must be included
   * known Problems: none
   * Version:        1.1
   * Description:    Graphic Library for KS0108- (and compatible) based LCDs
   *
   */

  lcdCoord            ks0108Coord;
  uint8_t             ks0108Inverted=0;
  ks0108FontCallback  ks0108FontRead;
  uint8_t             ks0108FontColor;
  const uint8_t*      ks0108Font;

  inline uint8_t ks0108ReadData(void) {
    ks0108DoReadData(1);              // dummy read
    return ks0108DoReadData(0);       // "real" read
  }

  inline void ks0108Enable(void) {
    LCD_CMD_PORT |= 0x01 << EN;       // EN high level width: min. 450ns
    asm volatile("nop\n\t"
                 "nop\n\t"
                 "nop\n\t"
                 ::);
    LCD_CMD_PORT &= ~(0x01 << EN);
    // a little delay loop (faster than reading the busy flag)
    for(volatile uint8_t i=0; i<8; i++);
  }

  void ks0108DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
    uint8_t length, i, y, yAlt, xTmp, yTmp;
    int16_t m;

    //
    // vertical line
    //
    if(x1 == x2) {
      // x1|y1 must be the upper point
      if(y1 > y2) {
        yTmp = y1;
        y1 = y2;
        y2 = yTmp;
      }
      ks0108DrawVertLine(x1, y1, y2-y1, color);

    //
    // horizontal line
    //
    } else if(y1 == y2) {
      // x1|y1 must be the left point
      if(x1 > x2) {
        xTmp = x1;
        x1 = x2;
        x2 = xTmp;
      }
      ks0108DrawHoriLine(x1, y1, x2-x1, color);

    //
    // schiefe line :)
    //
    } else {
      // angle >= 45°
      if((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
        // x1 must be smaller than x2
        if(x1 > x2) {
          xTmp = x1;
          yTmp = y1;
          x1 = x2;
          y1 = y2;
          x2 = xTmp;
          y2 = yTmp;
        }

        length = x2-x1;    // not really the length :)
        m = ((y2-y1)*200)/length;
        yAlt = y1;

        for(i=0; i<=length; i++) {
          y = ((m*i)/200)+y1;

          if((m*i)%200 >= 100)
            y++;
          else if((m*i)%200 <= -100)
            y--;

          ks0108DrawLine(x1+i, yAlt, x1+i, y, color);

          if(length <= (y2-y1) && y1 < y2)
            yAlt = y+1;
          else if(length <= (y1-y2) && y1 > y2)
            yAlt = y-1;
          else
            yAlt = y;
        }

      // angle < 45°
      } else {
        // y1 must be smaller than y2
        if(y1 > y2) {
          xTmp = x1;
          yTmp = y1;
          x1 = x2;
          y1 = y2;
          x2 = xTmp;
          y2 = yTmp;
        }

        length = y2-y1;
        m = ((x2-x1)*200)/length;
        yAlt = x1;

        for(i=0; i<=length; i++) {
          y = ((m*i)/200)+x1;

          if((m*i)%200 >= 100)
            y++;
          else if((m*i)%200 <= -100)
            y--;

          ks0108DrawLine(yAlt, y1+i, y, y1+i, color);
          if(length <= (x2-x1) && x1 < x2)
            yAlt = y+1;
          else if(length <= (x1-x2) && x1 > x2)
            yAlt = y-1;
          else
            yAlt = y;
        }
      }
    }
  }

  void ks0108DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
    ks0108DrawHoriLine(x, y, width, color);        // top
    ks0108DrawHoriLine(x, y+height, width, color);    // bottom
    ks0108DrawVertLine(x, y, height, color);      // left
    ks0108DrawVertLine(x+width, y, height, color);    // right
  }

  void ks0108DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color) {
      int16_t tSwitch, x1 = 0, y1 = radius;
      tSwitch = 3 - 2 * radius;

    while (x1 <= y1) {
        ks0108SetDot(x+radius - x1, y+radius - y1, color);
        ks0108SetDot(x+radius - y1, y+radius - x1, color);

        ks0108SetDot(x+width-radius + x1, y+radius - y1, color);
        ks0108SetDot(x+width-radius + y1, y+radius - x1, color);

        ks0108SetDot(x+width-radius + x1, y+height-radius + y1, color);
        ks0108SetDot(x+width-radius + y1, y+height-radius + x1, color);

        ks0108SetDot(x+radius - x1, y+height-radius + y1, color);
        ks0108SetDot(x+radius - y1, y+height-radius + x1, color);

        if (tSwitch < 0) {
          tSwitch += (4 * x1 + 6);
        } else {
          tSwitch += (4 * (x1 - y1) + 10);
          y1--;
        }
        x1++;
    }

    ks0108DrawHoriLine(x+radius, y, width-(2*radius), color); // top
    ks0108DrawHoriLine(x+radius, y+height, width-(2*radius), color); // bottom
    ks0108DrawVertLine(x, y+radius, height-(2*radius), color); // left
    ks0108DrawVertLine(x+width, y+radius, height-(2*radius), color); // right
  }

  /*
   * Hardware-Functions
   */
  void ks0108FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
    uint8_t mask, pageOffset, h, i, data;
    height++;

    pageOffset = y%8;
    y -= pageOffset;
    mask = 0xFF;
    if(height < 8-pageOffset) {
      mask >>= (8-height);
      h = height;
    } else {
      h = 8-pageOffset;
    }
    mask <<= pageOffset;

    ks0108GotoXY(x, y);
    for(i=0; i<=width; i++) {
      data = ks0108ReadData();

      if(color == BLACK) {
        data |= mask;
      } else {
        data &= ~mask;
      }

      ks0108WriteData(data);
    }

    while(h+8 <= height) {
      h += 8;
      y += 8;
      ks0108GotoXY(x, y);

      for(i=0; i<=width; i++) {
        ks0108WriteData(color);
      }
    }

    if(h < height) {
      mask = ~(0xFF << (height-h));
      ks0108GotoXY(x, y+8);

      for(i=0; i<=width; i++) {
        data = ks0108ReadData();

        if(color == BLACK) {
          data |= mask;
        } else {
          data &= ~mask;
        }

        ks0108WriteData(data);
      }
    }
  }

  void ks0108InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
    uint8_t mask, pageOffset, h, i, data, tmpData;
    height++;

    pageOffset = y%8;
    y -= pageOffset;
    mask = 0xFF;
    if(height < 8-pageOffset) {
      mask >>= (8-height);
      h = height;
    } else {
      h = 8-pageOffset;
    }
    mask <<= pageOffset;

    ks0108GotoXY(x, y);
    for(i=0; i<=width; i++) {
      data = ks0108ReadData();
      tmpData = ~data;
      data = (tmpData & mask) | (data & ~mask);
      ks0108WriteData(data);
    }

    while(h+8 <= height) {
      h += 8;
      y += 8;
      ks0108GotoXY(x, y);

      for(i=0; i<=width; i++) {
        data = ks0108ReadData();
        ks0108WriteData(~data);
      }
    }

    if(h < height) {
      mask = ~(0xFF << (height-h));
      ks0108GotoXY(x, y+8);

      for(i=0; i<=width; i++) {
        data = ks0108ReadData();
        tmpData = ~data;
        data = (tmpData & mask) | (data & ~mask);
        ks0108WriteData(data);
      }
    }
  }

  void ks0108SetInverted(uint8_t invert) {
    if(ks0108Inverted != invert) {
      ks0108InvertRect(0,0,127,63);
      ks0108Inverted = invert;
    }
  }

  void ks0108SetDot(uint8_t x, uint8_t y, uint8_t color) {
    uint8_t data;

    ks0108GotoXY(x, y-y%8);     // read data from display memory
    data = ks0108ReadData();

    if(color == BLACK) {
      data |= 0x01 << (y%8);    // set dot
    } else {
      data &= ~(0x01 << (y%8)); // clear dot
    }

    ks0108WriteData(data);      // write data back to display
  }

  //
  // Font Functions
  //

  uint8_t ks0108ReadFontData(const uint8_t* ptr) {
    return pgm_read_byte(ptr);
  }

  void ks0108SelectFont(const uint8_t* font, ks0108FontCallback callback, uint8_t color) {
    ks0108Font = font;
    ks0108FontRead = callback;
    ks0108FontColor = color;
  }

  int ks0108PutChar(unsigned char c) {
         uint8_t width = 0;
         uint8_t height = ks0108FontRead(ks0108Font+FONT_HEIGHT);
         uint8_t bytes = (height+7)/8;
         uint8_t tooMuch = height % 8;

         uint8_t firstChar = ks0108FontRead(ks0108Font+FONT_FIRST_CHAR);
         uint8_t charCount = ks0108FontRead(ks0108Font+FONT_CHAR_COUNT);

         uint16_t index = 0;
         uint8_t x = ks0108Coord.x, y = ks0108Coord.y;

         if(c < firstChar || c >= (firstChar+charCount)) {
                 return 1;
         }
         c-= firstChar;

         // read width data, to get the index
         for(uint8_t i=0; i<c; i++)
                 index += ks0108FontRead(ks0108Font+FONT_WIDTH_TABLE+i);
         index = index*bytes+charCount+FONT_WIDTH_TABLE;
         width = ks0108FontRead(ks0108Font+FONT_WIDTH_TABLE+c);

         // last but not least, draw the character
         for(uint8_t i=0; i<bytes; i++)
         {
                 uint8_t page = i*width;
                 for(uint8_t j=0; j<width; j++)
                 {
                         uint8_t data = ks0108FontRead(ks0108Font+index+page+j);

                         if (i + 1 == bytes)
                         {
                                 uint8_t pageOld = (i - 1) * width;
                                 uint8_t dataOld = ks0108FontRead(ks0108Font+index+pageOld+j); //get last byte
                                 dataOld >>= tooMuch;
                                 data |= dataOld;
                         }

                         if(ks0108FontColor == BLACK)
                         {
                                 ks0108WriteData(data);
                         } else
                         {
                                 ks0108WriteData(~data);
                         }
                 }
                 // 1px gap between chars
                 if(ks0108FontColor == BLACK)
                 {
                         ks0108WriteData(0x00);
                 }
                 else
                 {
                         ks0108WriteData(0xFF);
                 }
                 if (i + 2 < bytes)
                         ks0108GotoXY(x, ks0108Coord.y+8);
                 else
                         ks0108GotoXY(x, ks0108Coord.y + tooMuch);
         }
         ks0108GotoXY(x+width+1, y);

         return 0;
  }

  void ks0108Puts(char* str) {
    int x = ks0108Coord.x;
    while(*str != 0) {
      if(*str == '\n') {
        ks0108GotoXY(x, ks0108Coord.y+ks0108FontRead(ks0108Font+FONT_HEIGHT));
      } else {
        ks0108PutChar(*str);
      }
      str++;
    }
  }

  void ks0108Puts_P(PGM_P str) {
    int x = ks0108Coord.x;
    while(pgm_read_byte(str) != 0) {
      if(pgm_read_byte(str) == '\n') {
        ks0108GotoXY(x, ks0108Coord.y+ks0108FontRead(ks0108Font+FONT_HEIGHT));
      } else {
        ks0108PutChar(pgm_read_byte(str));
      }
      str++;
    }
  }

  uint8_t ks0108CharWidth(char c) {
    uint8_t width = 0;
    uint8_t firstChar = ks0108FontRead(ks0108Font+FONT_FIRST_CHAR);
    uint8_t charCount = ks0108FontRead(ks0108Font+FONT_CHAR_COUNT);

    // read width data
    if(c >= firstChar && c < (firstChar+charCount)) {
      c -= firstChar;
      width = ks0108FontRead(ks0108Font+FONT_WIDTH_TABLE+c)+1;
    }

    return width;
  }

  uint16_t ks0108StringWidth(char* str) {
    uint16_t width = 0;

    while(*str != 0) {
      width += ks0108CharWidth(*str++);
    }

    return width;
  }

  uint16_t ks0108StringWidth_P(PGM_P str) {
    uint16_t width = 0;

    while(pgm_read_byte(str) != 0) {
      width += ks0108CharWidth(pgm_read_byte(str++));
    }

    return width;
  }

  void ks0108GotoXY(uint8_t x, uint8_t y) {
    uint8_t chip = CHIP1, cmd;

    if(x > 127) x = 0;       // ensure that coordinates are legal
    if(y > 63)  y = 0;

    ks0108Coord.x = x;       // save new coordinates
    ks0108Coord.y = y;
    ks0108Coord.page = y/8;

    if(x >= 64) {            // select the right chip
      x -= 64;
      chip = CHIP2;
    }
    cmd = LCD_SET_ADD | x;
    ks0108WriteCommand(cmd, chip); // set x address on active chip

    cmd = LCD_SET_PAGE | ks0108Coord.page; // set y address on both chips
    ks0108WriteCommand(cmd, CHIP1);
    ks0108WriteCommand(cmd, CHIP2);
  }

  void ks0108Init(uint8_t invert) {
    ks0108Coord.x = 0;
    ks0108Coord.y = 0;
    ks0108Coord.page = 0;

    ks0108Inverted = invert;

    LCD_CMD_DIR = 0xFF;                // command port is output
    ks0108WriteCommand(LCD_ON, CHIP1); // power on
    ks0108WriteCommand(LCD_ON, CHIP2);

    ks0108WriteCommand(LCD_DISP_START, CHIP1); // display start line = 0
    ks0108WriteCommand(LCD_DISP_START, CHIP2);
    ks0108ClearScreen();               // display clear
    ks0108GotoXY(0,0);
  }

  uint8_t ks0108DoReadData(uint8_t first) {
    uint8_t data;
    volatile uint8_t i;

    LCD_DATA_OUT = 0x00;
    LCD_DATA_DIR = 0x00;                      // data port is input

    if(ks0108Coord.x < 64) {
      LCD_CMD_PORT &= ~(0x01 << CSEL2);       // deselect chip 2
      LCD_CMD_PORT |= 0x01 << CSEL1;          // select chip 1
    } else if(ks0108Coord.x >= 64) {
      LCD_CMD_PORT &= ~(0x01 << CSEL1);       // deselect chip 1
      LCD_CMD_PORT |= 0x01 << CSEL2;          // select chip 2
    }
    if(ks0108Coord.x == 64 && first) {        // chip2 X-address = 0
      ks0108WriteCommand(LCD_SET_ADD, CHIP2); // wuff wuff
    }

    LCD_CMD_PORT |= 0x01 << D_I;              // D/I = 1
    LCD_CMD_PORT |= 0x01 << R_W;              // R/W = 1

    LCD_CMD_PORT |= 0x01 << EN;               // EN high level width:
                                              // min. 450ns
    asm volatile("nop\n\t"
                 "nop\n\t"
                 "nop\n\t"
                 ::);

    data = LCD_DATA_IN;                       // read Data

    LCD_CMD_PORT &= ~(0x01 << EN);
    // a little delay loop (faster than reading the busy flag)
    for(i=0; i<8; i++);

    LCD_DATA_DIR = 0xFF;

    ks0108GotoXY(ks0108Coord.x, ks0108Coord.y);

    if(ks0108Inverted)
      data = ~data;
    return data;
  }

  void ks0108WriteCommand(uint8_t cmd, uint8_t chip) {
    if(chip == CHIP1) {
      LCD_CMD_PORT &= ~(0x01 << CSEL2); // deselect chip 2
      LCD_CMD_PORT |= 0x01 << CSEL1;    // select chip 1
    } else if(chip == CHIP2) {
      LCD_CMD_PORT &= ~(0x01 << CSEL1); // deselect chip 1
      LCD_CMD_PORT |= 0x01 << CSEL2;    // select chip 2
    }

    LCD_CMD_PORT &= ~(0x01 << D_I);     // D/I = 0
    LCD_CMD_PORT &= ~(0x01 << R_W);     // R/W = 0
    LCD_DATA_DIR = 0xFF;                // data port is output
    LCD_DATA_OUT = cmd;                 // write command
    ks0108Enable();                     // enable
    LCD_DATA_OUT = 0x00;
  }

  void ks0108WriteData(uint8_t data) {
    uint8_t displayData, yOffset, cmdPort;

  #ifdef DEBUG
    volatile uint16_t i;
    for(i=0; i<5000; i++);
  #endif

    if(ks0108Coord.x >= 128)
      return;

    if(ks0108Coord.x < 64) {
      LCD_CMD_PORT &= ~(0x01 << CSEL2);  // deselect chip 2
      LCD_CMD_PORT |= 0x01 << CSEL1;     // select chip 1
    } else if(ks0108Coord.x >= 64) {
      LCD_CMD_PORT &= ~(0x01 << CSEL1);  // deselect chip 1
      LCD_CMD_PORT |= 0x01 << CSEL2;     // select chip 2
    }
    if(ks0108Coord.x == 64)              // chip2 X-address = 0
      ks0108WriteCommand(LCD_SET_ADD, CHIP2);

    LCD_CMD_PORT |= 0x01 << D_I;         // D/I = 1
    LCD_CMD_PORT &= ~(0x01 << R_W);      // R/W = 0
    LCD_DATA_DIR = 0xFF;                 // data port is output

    yOffset = ks0108Coord.y%8;
    if(yOffset != 0) {
      // first page
      cmdPort = LCD_CMD_PORT;            // save command port
      displayData = ks0108ReadData();

      LCD_CMD_PORT = cmdPort;            // restore command port
      LCD_DATA_DIR = 0xFF;               // data port is output

      displayData |= data << yOffset;
      if(ks0108Inverted)
        displayData = ~displayData;
      LCD_DATA_OUT = displayData;        // write data
      ks0108Enable();                    // enable

      // second page
      ks0108GotoXY(ks0108Coord.x, ks0108Coord.y+8);

      displayData = ks0108ReadData();

      LCD_CMD_PORT = cmdPort;            // restore command port
      LCD_DATA_DIR = 0xFF;               // data port is output

      displayData |= data >> (8-yOffset);
      if(ks0108Inverted)
        displayData = ~displayData;
      LCD_DATA_OUT = displayData;        // write data
      ks0108Enable();                    // enable

      ks0108GotoXY(ks0108Coord.x+1, ks0108Coord.y-8);
    } else {
      if(ks0108Inverted)
        data = ~data;
      LCD_DATA_OUT = data;               // write data
      ks0108Enable();                    // enable
      ks0108Coord.x++;
    }
    LCD_DATA_OUT = 0x00;
  }

  // end ks0108.c

  int main(void) {
    for(volatile uint16_t i=0; i<15000; i++);
    ks0108Init(0);
    ks0108ClearScreen();
    LoadBitmap(IMAGE);
    while(1);
  }

------------------------------------------------------------------------

Links:
......

  1. Download VMLAB
     (and install to C:\VMLAB)

    http://www.amctools.com/download.htm

  2. Download WINAVR C
     (and install to C:\WinAVR)

    https://sourceforge.net/projects/winavr/files/

  3. Arduino assembler fans

    http://www.avrfreaks.net/forum/atmega328p-compiler-write-assembler

  4. Controlling graphical 128×64 LCD based on KS0108

    https://scienceprog.com
      /controlling-graphical-128x64-lcd-based-on-ks0108/

Annex A. GLCD5.hex
..................

GLCD5.hex

:1000000012C221C220C21FC21EC21DC21CC21BC2FC
:100010001AC219C218C217C216C215C214C213C21C
:1000200012C211C210C2FFFFFFFFFFFF7F7F7F7F61
:100030007F7F7F7FFFFFFFFF7F7F3FBF3F1F3F3FF0
:100040003F7FFFBF1F478707070727777767E7E7E8
:10005000E7C7870707070707070707070787C7F7E0
:10006000FFFFFFFFFFFFFFFFFFFFFFEFEF7F7F7F40
:100070007FFFFFFFFFFFFFFFFFBFFFFFFFFF3F7F90
:100080007F7F3F3F3F1F1F1F1F1F0F1F1F1F3F3F30
:100090003F3F3F7F7F7F7F3F3F7F7F7F7F7F7FFF30
:1000A000FFFF7F7FFFFFFEFEFEFFFFFFF8F8F0F08F
:1000B000F8F8F8F8F8F8F0E0C08000000000000060
:1000C00000000000000C0C0E1C3F0F03000404870E
:1000D000CF9FFFFEFCF8F8FCFEFEFFFFFFFFFFFDD9
:1000E000FFFFFFDFCF1F3F3F130301101C0C000079
:1000F0000000000000000080800000000000000000
:1001000000000000000000000000000000000000EF
:100110000000000000000090F8F8F8F8F8CCE0F0DB
:10012000F8F8F8FCFCFEFFFFFFFFFFFFFFFFFFFFFB
:10013000FFFFFFFFFFFFFFFFFFFFF8F0E0E0C080E1
:1001400000000080C0C0E0E0C0E0F0FCFEFEFFFF69
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
:10016000FF3F1F090100040606060E1C1E181C187E
:10017000191901030000000746000080808000007C
:10018000000000000000000000000000000000006F
:1001900084FCF8F4FEFEF7FBFEFFFFFFFFFFFFFF0E
:1001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
:1001B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001C000FFFCFCFCFDF9F9F3FFFF5F0F0F0F0F0FB2
:1001D0001F3F3F3FFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001E000F0E0C0C0C0C0C0C0C000000000000000FF
:1001F00000000003060088C8FCFCFEFFFFFFFFFEB6
:10020000F0E0F8FCFEFFFEFCF83030E0F67F3F3F08
:10021000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE
:10022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
:10023000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE
:10024000FFFFFFFFFFFFFFFFFFFDF0E0C00000002A
:100250000000000000000101F1FBFFFFFFFFFFFFB6
:10026000FFFFFFFFFFFFFFFFFFFE98000000000001
:10027000000080C0FE7F1FDFFFFFFFFFFFFFFFFFCB
:10028000FFFFFFFFFFFFFFFFFFFEFCFFFFFEFE7F04
:100290007E3F1F1F0F0F3D39193B7FFFFFFFFFFF01
:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E
:1002C000FFFFFFFFFFFFFFFFFFFFFFFF0F000080AB
:1002D000C0E0F0F8FCFEFFFFFFFFFFFFFFFFFFFFA6
:1002E000FFFFFFFFFFFFFFFFFFFFFFFCF0F0F0F05D
:1002F000F8FCFFFFFFFFFEFFFFFFFFFFFFFFFFFF19
:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF00F
:10031000F0F0F8F8F8F0F0E0C0C0E0F0FFFFFFFF09
:10032000FFFF7FFFDFFFFFFFFFFFFFFFFFFFFFFF7D
:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD
:10034000FFFFFFFFFFFFFFFFFFFFFFFFF8F8FFFFCB
:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD
:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
:10037000FFFFFFFFFFFFFF7F7F7FFFFFFFFFFFFF0D
:10038000FFFFFFFF7F7F7F7F7F7F7F7F7F7F7F7F7D
:100390007F7F7F7F7F7F7F7F7FFFFFFFFFFFFFFFED
:1003A000FFFFFFFFFFFF070707070707070707070D
:1003B00007070707030303030303030303030303FD
:1003C000030303010303030303030301010000000C
:1003D00007070707070707070707070707070703B1
:1003E00003010101010101010101010101010101FB
:1003F00001000000000000000000000000000101FA
:1004000000000000000000000000000000000000EC
:1004100000000000000000000000000000000001DB
:1004200005050707070711241FBECFE5D4E0DEBF8F
:10043000CDBF10E0A0E6B0E001C01D92A936B10723
:10044000E1F702D0E3C6DCCD0F93DF93CF9300D06A
:10045000CDB7DEB71A82198205C089819A810196CB
:100460009A83898389819A8188599A43B0F380E07D
:100470005CD480E060E04FE72FE300E0CAD386E27F
:1004800090E076D6FFCFFC018491089590936800A8
:1004900080936700709366006093650040936100ED
:1004A00008950F931F93082F809167009091680023
:1004B000E0916500F091660004960995182F8091EF
:1004C000670090916800E0916500F09166000596E4
:1004D0000995482F0117D8F0202F30E0812F90E0A8
:1004E000840F911D2817390794F4011B202F30E049
:1004F0002A5F3F4F8091670090916800E09165000E
:10050000F0916600820F931F09958F5F01C080E014
:100510001F910F9108950F931F93CF93DF938C0139
:10052000C0E0D0E005C00F5F1F4FBBDFC80FD11D7B
:10053000F80180818823B9F7CE01DF91CF911F9117
:100540000F9108950F931F93CF93DF93EC0100E079
:1005500010E006C02196F9018491A3DF080F111D58
:100560009E01FE0184918823A9F7C801DF91CF91F4
:100570001F910F910895DF93CF930F92CDB7DEB700
:10058000982F662319F4AC98AB9A04C0613011F42B
:10059000AB98AC9AA898A9988FEF81BB92BBAA9A06
:1005A000000000000000AA98198203C089818F5FB3
:1005B000898389818830D0F312BA0F90CF91DF916F
:1005C00008951F9387FD80E0603408F060E0809319
:1005D000620060936300669566956695609364001B
:1005E000803410F460E002C0805461E08064C3DFB6
:1005F00010916400186B812F60E0BDDF812F61E0F6
:10060000BADF1F9108951F93DF93CF930F92CDB759
:10061000DEB712BA11BA90916200903418F4AC9817
:10062000AB9A02C0AB98AC9A903429F4882319F0A5
:1006300080E461E0A0DFA89AA99AAA9A00000000CD
:10064000000010B3AA98198203C089818F5F898343
:1006500089818830D0F38FEF81BB809162006091F7
:100660006300AFDF8091600081111095812F0F90A2
:10067000CF91DF911F910895DF92EF92FF920F9338
:100680001F93DF93CF930F92CDB7DEB7F82E8091F3
:10069000620087FD84C0803418F4AC98AB9A02C025
:1006A000AB98AC9A803411F461E065DFA89AA99800
:1006B0000FEF01BB10916300177009F458C0D5B259
:1006C00081E0A1DF80E09FDFD5BA01BBEF2CFF24E2
:1006D000012F10E09701002E02C0220F331F0A9451
:1006E000E2F7982F922B809160008111909592BB38
:1006F000AA9A000000000000AA98198203C089810C
:100700008F5F898389818830D0F360916300685F4F
:100710008091620056DF81E076DF80E074DFD5BA39
:100720009FEF91BB28E030E0201B310B02C0F59415
:10073000E7942A95E2F7282F2E29809160008111F5
:10074000209522BBAA9A000000000000AA981982F6
:1007500003C089818F5F898389818830D0F380913C
:1007600062006091630068508F5F2BDF17C080913B
:1007700060008111F094F2BAAA9A00000000000013
:10078000AA98198203C089818F5F89838981883003
:10079000D0F3809162008F5F8093620012BA0F9055
:1007A000CF91DF911F910F91FF90EF90DF9008950F
:1007B0002F923F924F925F926F927F928F929F9271
:1007C000AF92BF92CF92DF92EF92FF920F931F935F
:1007D000DF93CF9300D000D000D0CDB7DEB7082F85
:1007E0008091670090916800E0916500F09166004B
:1007F00003960995C82E282F27702A8380916700B9
:1008000090916800E0916500F0916600049609956A
:10081000182F8091670090916800E0916500F09139
:10082000660005960995D82E3090620030916300DD
:100830003983011708F4C1C0202F30E0812F90E0E8
:100840008D0D911D281739070CF0B7C0B02EB11AC5
:10085000EE24FF2400E056E0852E912C0AC0C90149
:10086000880D991D0995E80EF11C0F5F0894811CF5
:10087000911C2091670030916800E0916500F09133
:1008800066000B1560F38C2D90E0079643E095957C
:1008900087954A95E1F7282E082F10E0E09EC001C9
:1008A000E19E900DF09E900D1124CD2CDD2446E0AC
:1008B00050E0C40ED51EC80ED91E8B2D90E00696B2
:1008C000820F931F0995E82E682E6194AA24BB24F9
:1008D00077241C830B8366C02601460C511C8601BD
:1008E000070D111D1E821D8245010894811C911C5B
:1008F00038C08091670090916800E0916500F091A8
:100900006600800F911F0995F82E8B819C818816B7
:100910009906B9F42D813E81240D351D8091670023
:1009200090916800E0916500F0916600820F931F3E
:10093000099590E00A8002C0959587950A94E2F7A0
:10094000F82A809161008F3F11F48F2D02C08F2D06
:10095000809592DEED81FE813196FE83ED830F5FFF
:100960001F4FFD81FE1528F2809161008F3F11F429
:1009700080E001C08FEF80DEC50102962B813C81B3
:100980008217930724F460916300685F04C060914C
:1009900063003A81630F832D14DE0894A11CB11CFF
:1009A0006E0C7E0CA21408F497CF3394832D8E0D19
:1009B000698107DE20E030E002C021E030E0C901BB
:1009C00026960FB6F894DEBF0FBECDBFCF91DF9154
:1009D0001F910F91FF90EF90DF90CF90BF90AF905D
:1009E0009F908F907F906F905F904F903F902F904F
:1009F00008950F931F93CF93DF93EC010091620052
:100A000015C08A3089F4109163008091670090913D
:100A10006800E0916500F091660003960995180F53
:100A2000802F612FCEDD01C0C3DE2196FE018491AF
:100A3000882339F7DF91CF911F910F9108950F937C
:100A40001F93CF93DF93EC010091620015C08A30B1
:100A500089F4109163008091670090916800E091A3
:100A60006500F091660003960995180F802F612F9D
:100A7000A8DD01C09DDE21968881882341F7DF91A2
:100A8000CF911F910F9108950F931F93162F042F4D
:100A9000687F97DD81E0B7DD80E0B5DD482F212F4D
:100AA00030E00F3F59F42770307081E090E002C0D1
:100AB000880F991F2A95E2F7842B0BC0277030709E
:100AC00081E090E002C0880F991F2A95E2F7809597
:100AD0008423D2DD1F910F910895AF92BF92CF92E0
:100AE000DF92EF92FF920F931F93CF93DF93B82E75
:100AF000A42EE22EE394462F4770F62EF41ACE2D44
:100B0000D0E0642F70E028E030E0C901861B970B2D
:100B1000C817D9075CF42C1B3D0B8FEF90E002C087
:100B2000959587952A95E2F70E2D03C008E0041BE2
:100B30008FEF90E002C0880F991F6A95E2F7D82ED8
:100B40008B2D6F2D3EDD10E0CD2CC09481E05BDD60
:100B500080E059DD982F8C2D892390959D21892B3C
:100B60008BDD1F5FA11690F710C088E0F80E8B2D6B
:100B70006F2D27DD10E081E046DD80E044DD8095CB
:100B80007BDD1F5FA116B8F7085F202F30E0C90199
:100B900007968C179D074CF30E1508F5C21BD30B57
:100BA0008FEF90E002C0880F991FCA95E2F7082FD7
:100BB000009588E0F80E8B2D6F2D03DD10E0F02EF0
:100BC000F09481E020DD80E01EDD982F8F2D8923B9
:100BD00090959023892B50DD1F5FA11690F7DF9130
:100BE000CF911F910F91FF90EF90DF90CF90BF902A
:100BF000AF9008951F93182F80916000811739F0EE
:100C000080E060E04FE72FE368DF109360001F9102
:100C100008959F92AF92BF92CF92DF92EF92FF9290
:100C20000F931F93CF93DF93B82E942EE22EA02E16
:100C3000E394462F4770F62EF41ACE2DD0E0642FA1
:100C400070E028E030E0C901861B970BC817D90770
:100C50005CF42C1B3D0B8FEF90E002C095958795BF
:100C60002A95E2F70E2D03C008E0041B8FEF90E0F9
:100C700002C0880F991F6A95E2F7D82E8B2D6F2D31
:100C8000A0DC10E0CD2CC09481E0BDDC80E0BBDCBA
:100C90009FEFA91611F48D2901C08C21EDDC1F5F97
:100CA000911690F70CC088E0F80E8B2D6F2D89DC23
:100CB00010E08A2DE1DC1F5F9116D8F7085F202F26
:100CC00030E0C90107968C179D076CF30E1508F5E7
:100CD000C21BD30B8FEF90E002C0880F991FCA95FB
:100CE000E2F7E82EE09498E0F90E8B2D6F2D69DC89
:100CF00010E0FE2CF09481E086DC80E084DC9FEF45
:100D0000A91611F48E2901C08F21B6DC1F5F911640
:100D100090F7DF91CF911F910F91FF90EF90DF90AF
:100D2000CF90BF90AF909F9008950F931092620064
:100D30001092630010926400809360008FEF84BB78
:100D40008FE360E018DC8FE361E015DC80EC60E0AD
:100D500012DC80EC61E00FDC80E060E04FE72FE325
:100D600000E057DF80E060E02CDC0F9108952F92C7
:100D70003F924F925F926F927F928F929F92AF922B
:100D8000BF92CF92DF92EF92FF920F931F93DF9368
:100D9000CF93CDB7DEB72C970FB6F894DEBF0FBE5A
:100DA000CDBF8F83688749872A87C02EDD24C6017F
:100DB000880F991F22273327281B390B3E832D8349
:100DC0002D5F3F4F3C872B873F80300E262E200E15
:100DD000342F8F81380F3B83301B3C839A85960FCD
:100DE0009983901B9A83432C522C632E792EAA242C
:100DF000BB24882499241C2D022D0C19842D602FCE
:100E00004E2D42DEF32CFC188F2D652D4E2D3CDE31
:100E1000862D602F4E2D38DE0C810C0D802F652D18
:100E20004E2D32DEFA811F0F862D612F4E2D2CDEC6
:100E3000802F672D4E2D28DE842D612F4E2D24DE30
:100E40008F2D672D4E2D20DE2B853C8537FF07C06B
:100E50002A5F3F4F280D391D3C872B8715C08B8596
:100E60009C850A969C878B87C5018C199D09880FE4
:100E7000991F880F991FEB85FC85E80FF91FFC87E8
:100E8000EB870894C108D1080894A11CB11C4A94AE
:100E90005A946394739424E030E0820E931ECA1433
:100EA000DB040CF0A8CF1D81F984F10E832D688539
:100EB0004F2D20E00E2DADDE832D69814F2D20E0DA
:100EC000A8DE3A85130F8F81622D40E0212FA1DE2D
:100ED0008B81622D40E0212F9CDE2C960FB6F8947A
:100EE000DEBF0FBECDBFCF91DF911F910F91FF905D
:100EF000EF90DF90CF90BF90AF909F908F907F90BA
:100F00006F905F904F903F902F900895CF92DF9217
:100F1000EF92FF920F931F93182FF62ED42EC22E0E
:100F200020E077DE6C2D6F0D812F4D2D20E071DEDE
:100F3000812F6F2D40E02C2D6CDE8D2D810F6F2DBC
:100F400040E02C2D66DE1F910F91FF90EF90DF9017
:100F5000CF9008954F925F926F927F928F929F925F
:100F6000AF92BF92CF92DF92EF92FF920F931F93B7
:100F7000CF93DF93D82EC62EF42EE22E502E841758
:100F800051F4261710F4E62EC22EEC188D2D6C2D80
:100F900040E02E2D0BC0621761F4481710F4FD2CB1
:100FA000D42EFD188D2D6C2D4F2D20E0052D31DE1A
:100FB000CEC0622F70E04C2D50E02F2D30E0281B6A
:100FC0003109CB01841B950B8217930734F4461B20
:100FD000570B421753070CF45EC0FD1430F48F2DED
:100FE000FD2CD82E8E2DEC2CC82E4F2C4D18EE2D0E
:100FF000F0E02C2D30E04F01821A930AA42CBB2480
:1010000088EC90E0AC01849EC001859E900D949E7A
:10101000900D1124B501D3D0EB010C2DFF24390123
:101020006E1A7F0A8F2D90E08C9F90018D9F300D5E
:101030009C9F300D1124C90168EC70E0C0D0162FC0
:101040001C0DC90168EC70E0BAD08436910514F02B
:101050001F5F05C05FEF8D3995070CF411504F2DC0
:101060004D0D842F602F212F052D74DF8A149B04D2
:101070002CF0CE1418F4012F0F5F09C06A147B0402
:101080002CF0EC1418F4012F015001C0012FF3943F
:101090004F1440F65CC0EC1428F48F2DFD2CD82E94
:1010A000EC2CC62E4E2C4C18EF2DF0E02D2D30E000
:1010B0004F01821A930AA42CBB2488EC90E0AC0167
:1010C000849EC001859E900D949E900D1124B501C3
:1010D00076D0EB014D2DEE2439016E1A7F0A8E2D4C
:1010E00090E08C9F90018D9F300D9C9F300D1124BE
:1010F000C90168EC70E063D0162F1D0DC90168ECC2
:1011000070E05DD08436910514F01F5F05C05FEF7D
:101110008D3995070CF411502E2D2C0D842F622F34
:10112000412F052D17DF8A149B042CF0DF1418F4CF
:10113000412F4F5F09C06A147B042CF0FD1418F492
:10114000412F415001C0412FE3944E1440F6DF91EE
:10115000CF911F910F91FF90EF90DF90CF90BF90B4
:10116000AF909F908F907F906F905F904F90089579
:10117000EF92FF920F931F93CF93DF938C01FF2485
:1011800014C0F801EC0FFD1FE4908C2F6F2D19DABD
:101190008E2D72DA2196C038D10599F700581F4F6D
:1011A00088E0F80E80E4F81619F0C0E0D0E0E9CF4E
:1011B000DF91CF911F910F91FF90EF90089597FBD2
:1011C000092E07260AD077FD04D00CD006D00020C7
:1011D0001AF4709561957F4F0895F6F79095819573
:1011E0009F4F0895AA1BBB1B51E107C0AA1FBB1F3D
:1011F000A617B70710F0A61BB70B881F991F5A95A3
:10120000A9F780959095BC01CD010895F894FFCF82
:00000001FF

Annex B: From "controlling graphical 128×64 LCD based on KS0108"
----------------------------------------------------------------

  https://scienceprog.com
  /controlling-graphical-128x64-lcd-based-on-ks0108/

Here is the correct pin-out of LCD (HQM1286404):

  Article                                  VMLab GLCD Source C++

  1 VCC           +5V                      DIGITAL_IN(RS, 1);     D/I*1
  2 GND           GND                      DIGITAL_IN(RW, 2);     --
  3 VCON*2        RP1                      DIGITAL_IN(E, 3);      --
  4-11 DB0-DB7    PD0-PD7
  12 CS2          -                        DIGITAL_BID(D7, 4);
  13 CS1          -                        DIGITAL_BID(D6, 5);
  14 RESET        N1                       DIGITAL_BID(D5, 6);
  15 R/W          -                        DIGITAL_BID(D4, 7);
  16 D/I*3        -                        DIGITAL_BID(D3, 8);
  17 EN           -                        DIGITAL_BID(D2, 9);
  18 VEE Out*4    RP1                      DIGITAL_BID(D1, 10);
  19 LEDA Anode   +5V                      DIGITAL_BID(D0, 11);
  20 LEDK Kathode R2
                       ;        | VCON     DIGITAL_IN(CS1, 12);   --
  RP1 VEE VCON GND 10K ; VEE --/\/-- GND   DIGITAL_IN(CS2, 13);   --
  R2  LEDK  GND 100
  R3  RESET VCC 10K                        DIGITAL_IN(Reset, 14); +5
  C1  RESET GND 100nF
                                           ANALOG_IN(LEDPos, 15); +5
                                           ANALOG_IN(LEDNeg, 16); GND100
------
*1) Exclude all known pins, the only suggestion
*0) VCON -- V contrast in
*1) D/I -- Data/Instruction RS - Receive/Send
*2) External contrast adjustment
------

KS0108 Library.:

  https://www.scienceprog.com/wp-content/uploads/2007/07/glcd_ks0108.zip

Just download GLCDFontCreator2 and with this java program you can fetch fonts from you windows system and automatically convert them to character arrays and save them in .h format ready to include to project.

  http://www.mikrocontroller.net/attachment/22095/GLCDFontCreator2.zip

I convert bmp files to character arrays with fastlcd.zip program

  https://web.archive.org/web/20070803034955/
    http://www.fastavr.com/fastlcd.zip

Here is my example which displays simple graphics on LCD: Bitmap display demo program

  https://www.scienceprog.com/wp-content/uploads/2007/07/glcd1.zip

is ready to be compiled with latest WinAVR and flashed to Atmega8 via avrdude.

Annex C. VMLab components
.........................

Resistor      Rm        n1  n2  value
Capacitor     Cm        n   VSS value
LED diode     D1-8      VDD n
Key           K0-15     n1  n2        [MONOSTABLE(t_delay), LATCHED]
Opamp         Xm  OPAMP i+  i-  out
Comparator    Xm  COMP  i+  i-  out
D/A converter Xm  D2A8  i7 i6 i5 i4 i3 i2 i1 i0  out
NAND 2        Xm  ND2   i1  i2  out
V Sinewave    Vm        n   VSS SIN(v_offset v_amplitude frequency)
V Pulse       Vm        n   VSS PULSE(v_init v_fin t_delay t_rise t_fall t_width t_period)
V Slider      Vm        VSS SLIDER_x(v_low v_high) ;x: Panel sliders 1 -- 3
V Digital NRZ Pm        NRZ(t_step) n
LCD module    Xm  LCD(chars lines oscil_freq) RS RW E D7 D6 D5 D4 D3 D2 D1 D0
Keypad 4x4    Xm KEY4x4 row0 row1 row2 row3 col0 col1 col2 col3
7-SEG-CC      Xm _led7cc <A> <B> <C> <D> <E> <F> <G> <DP> <CATHODE>
7-SEG-CA      Xm _led7ca <A> <B> <C> <D> <E> <F> <G> <DP> <ANODE>
GLCD          Xm _graphiclcd RS RW E D7 D6 D5 D4 D3 D2 D1 D0 CS1 CS2 RST LED+ LED-

OSCILLOSCOPE  .plot v(n1) .. v(nk)

Annex D. Miscellaneous
......................

1.  Atmel Studio6.0 on Windows XP

       C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel
         \AVRAssembler\2.1.51.64\avrassembler\include\m328Pdef.inc

2.  Build average bitmap for KS0108.
     Google: silhouette (ee: siluett ru: силуэт) -> Pictures
     See link below:

       http://www.scienceprog.com
         /controlling-graphical-128x64-lcd-based-on-ks0108/

     Try to convert with

       https://sourceforge.net/projects/bmp2glcd/

A simple atoi() function (quick fix to bmp2glcd):

  https://stackoverflow.com/questions/28919861
        /atoi-implementation-in-c

  int atoi(char *str) {
    int res = 0;
    for (int i=0; str[i] != '\0'; ++i) res = res*10 + str[i] - '0';
    return res;
  }

You may need:

  set PATH=%PATH%;C:\WinAVR\bin
  set PATH=%PATH%;C:\WinAVR\utils\bin

Annex E. HEX from 7SEG
......................

    aaa
   f   b
   f   b
    ggg
   e   c
   e   c
    ddd

  hex x3 x2 x1 x0   a b c d e f g

  0   0  0  0  0    1 1 1 1 1 1 0
  1   0  0  0  1    0 1 1 0 0 0 0
  2   0  0  1  0    1 1 0 1 1 0 1
  3   0  0  1  1    1 1 1 1 0 0 1
  4   0  1  0  0    0 1 1 0 0 1 1
  5   0  1  0  1    1 0 1 1 0 1 1
  6   0  1  1  0    1 0 1 1 1 1 1
  7   0  1  1  1    1 1 1 0 0 0 0
  8   1  0  0  0    1 1 1 1 1 1 1
  9   1  0  0  1    1 1 1 1 0 1 1
  A   1  0  1  0    1 1 1 0 1 1 1
  b   1  0  1  1    0 0 1 1 1 1 1
  c   1  1  0  0    0 0 0 1 1 0 1
  d   1  1  0  1    0 1 1 1 1 0 1
  E   1  1  1  0    1 0 0 1 1 1 1
  F   1  1  1  1    1 0 0 0 1 1 1

  a = ~x2 ~x0 + ~x3 x1 + ~x3 x2 x0 + x2 x1 + x3 ~x2 ~x1
  b = ~x3 ~x2 + ~x3 ~x1 ~x0 + ~x2 ~x0 + ~x3 x1 x0 + x3 ~x1 x0
  c = ~x1 + ~x3 x0 + ~x3 x2 + x3 ~x2
  d = ~x3 ~x2 ~x0 + ~x2 x1 x0 + x2 ~x1 x0 + x2 x1 ~x0 + x3 ~x1
  e = ~x2 ~x0 + x1 ~x0 + x3 x1 + x3 x2
  f = ~x3 ~x1 ~x0 + ~x3 x2 ~x1 + ~x3 x2 ~x0 + x3 ~x2 + x3 x1
  g = ~x2 x1 + x2 ~x1 + x2 ~x0 + x3

------------

Links: AVR

  1. Top AVR C++ downloads

    http://avr-c1.software.informer.com/downloads/
