From 800972fa3e4b81f29c9fb1f30271b690a6aa83c3 Mon Sep 17 00:00:00 2001
From: gbouille <45919242+gbouille@users.noreply.github.com>
Date: Mon, 31 Dec 2018 18:02:14 +0100
Subject: [PATCH] A little improvment on SSD refresh by using the ability to
send up to 15 bytes in one single data command. Should increase the speed to
refresh the screen.
---
src/SSD1306.cpp | 641 +++++++++++++++++++++++++-----------------------
src/SSD1306.h | 293 +++++++++++-----------
2 files changed, 479 insertions(+), 455 deletions(-)
diff --git a/src/SSD1306.cpp b/src/SSD1306.cpp
index c96fc33..89931bd 100644
--- a/src/SSD1306.cpp
+++ b/src/SSD1306.cpp
@@ -1,309 +1,332 @@
-// Pi1541 - A Commodore 1541 disk drive emulator
-// Copyright(C) 2018 Stephen White
-//
-// This file is part of Pi1541.
-//
-// Pi1541 is free software : you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Pi1541 is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Pi1541. If not, see .
-
-#include "SSD1306.h"
-#include "debug.h"
-#include
-#include "Petscii.h"
-
-extern "C"
-{
-#include "xga_font_data.h"
-}
-
-extern unsigned char* CBMFont;
-
-SSD1306::SSD1306(int BSCMaster, u8 address, unsigned width, unsigned height, int flip, LCD_MODEL type)
- : BSCMaster(BSCMaster)
- , address(address)
- , type(type)
- , flip(flip)
- , contrast(127)
- , width(width)
- , height(height)
-{
- sizeof_frame = width*height/8;
- frame = (unsigned char *)malloc(sizeof_frame);
- oldFrame = (unsigned char *)malloc(sizeof_frame);
- RPI_I2CInit(BSCMaster, 1);
- InitHardware();
-}
-
-void SSD1306::InitHardware()
-{
- // SSD1306 data sheet configuration flow
- SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
-
- SendCommand(SSD1306_CMD_MULTIPLEX_RATIO); // 0xA8
- SendCommand(height-1); // SSD1306_LCDHEIGHT - 1
-
- SendCommand(SSD1306_CMD_SET_DISPLAY_OFFSET); // 0xD3 Vertical scroll position
- SendCommand(0x00); // no Offset
-
- SendCommand(SSD1306_CMD_SET_START_LINE | 0x0); // 0x40
-
- if (flip) {
- SendCommand(0xA0); // No Segment Re-Map
- SendCommand(0xC0); // No COM Output Scan Direction
- } else {
- SendCommand(0xA1); // Set Segment Re-Map (horizontal flip)
- SendCommand(0xC8); // Set COM Output Scan Direction (vertical flip)
- }
-
- SendCommand(SSD1306_CMD_SET_COM_PINS); // 0xDA Layout and direction
- if (type == LCD_1306_128x32)
- SendCommand(0x02);
- else
- SendCommand(0x12);
-
- SetContrast(GetContrast());
-
- SendCommand(SSD1306_CMD_TEST_DISPLAY_OFF); // 0xA4 - DONT force entire display on
-
- SendCommand(SSD1306_CMD_NORMAL_DISPLAY); // 0xA6 = non inverted
-
- SendCommand(SSD1306_CMD_SET_PRE_CHARGE_PERIOD); // 0xD9
- SendCommand(0xF1);
-
- SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL); // 0xDB
- SendCommand(0x40);
-
- SendCommand(SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO); // 0xD5
- SendCommand(0x80); // upper nibble is rate, lower nibble is divisor
-
- SendCommand(SSD1306_ENABLE_CHARGE_PUMP); // 0x8D Enable charge pump regulator
- SendCommand(0x14); // external = 0x10 internal = 0x14
-
- SendCommand(SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE); // 0x20 Set Memory Addressing Mode
- SendCommand(0x10); // 10 - Page Addressing Mode for SH1106 compatibility
-
- Home();
-
- if (type != LCD_1106_128x64)
- SendCommand(SSD1306_CMD_DEACTIVATE_SCROLL); // 0x2E
-}
-
-void SSD1306::SendCommand(u8 command)
-{
- char buffer[2];
-
- buffer[0] = SSD1306_CONTROL_REG;
- buffer[1] = command;
-
- RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
-}
-
-void SSD1306::SendData(u8 data)
-{
- char buffer[2];
-
- buffer[0] = SSD1306_DATA_REG;
- buffer[1] = data;
-
- RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
-}
-
-void SSD1306::Home()
-{
- SetDataPointer(0, 0);
-}
-
-void SSD1306::SetDataPointer(u8 page, u8 col)
-{
- if (col > width-1) { col = width-1; }
- if (page > height/8-1) { page = height/8-1; }
-
- if (type == LCD_1106_128x64)
- col += 2; // sh1106 uses columns 2..129
-
- SendCommand(SSD1306_CMD_SET_PAGE | page); // 0xB0 page address
- SendCommand(SSD1306_CMD_SET_COLUMN_LOW | (col & 0xf)); // 0x00 column address lower bits
- SendCommand(SSD1306_CMD_SET_COLUMN_HIGH | (col >> 4)); // 0x10 column address upper bits
-}
-
-void SSD1306::RefreshScreen()
-{
- unsigned i;
- for (i = 0; i < height/8; i++)
- {
- RefreshPage(i);
- }
-}
-
-// assumes a text row is 8 bit high
-void SSD1306::RefreshTextRows(u32 start, u32 amountOfRows)
-{
- unsigned int i;
-
- //start <<= 1;
- //amountOfRows <<= 1;
- for (i = start; i < start+amountOfRows; i++)
- {
- RefreshPage(i);
- }
-}
-
-// Some very basic optimisation is implemented.
-// it scans the page to work out the first (new_start) and last (new_end) changed bytes
-// Only update that window on the OLED
-// If someone is keen, a smarter algorithm could work out a series of ranges to update
-void SSD1306::RefreshPage(u32 page)
-{
- if (page >= height/8)
- return;
-
- // x32 displays use lower half (pages 2 and 3)
- if (type == LCD_1306_128x32)
- {
- page = page+4; // 0,1,2,3 -> 4,5,6,7
- page = page%4; // and wrap it so 4,5 -> 0,1
- }
-
- int i;
- int start = page*width;
- int end = start + width;
-
- int new_start = -1;
- int new_end = -1;
- for (i = start; i < end; i++)
- {
- if (oldFrame[i] ^ frame[i])
- {
- if (new_start == -1)
- new_start = i;
- new_end = i;
- }
- }
-
- if (new_start >= 0)
- {
- SetDataPointer(page, new_start-start);
- for (i = new_start; i <= new_end; i++)
- {
- SendData(frame[i]);
- oldFrame[i] = frame[i];
- }
- }
-}
-
-void SSD1306::ClearScreen()
-{
- memset(frame, 0, sizeof_frame);
- memset(oldFrame, 0xff, sizeof_frame); // to force update
- RefreshScreen();
-}
-
-void SSD1306::DisplayOn()
-{
- SendCommand(SSD1306_CMD_DISPLAY_ON); // 0xAF
-}
-
-void SSD1306::DisplayOff()
-{
- SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
-}
-
-void SSD1306::SetContrast(u8 value)
-{
- contrast = value;
- SendCommand(SSD1306_CMD_SET_CONTRAST_CONTROL);
- SendCommand(value);
- if (type != LCD_1106_128x64) // dont fiddle vcomdeselect on 1106 displays
- SetVCOMDeselect( value >> 8);
-}
-
-void SSD1306::SetVCOMDeselect(u8 value)
-{
- SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL);
- SendCommand( (value & 7) << 4 );
-}
-
-void SSD1306::PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse)
-{
-// assumes 16 character width
- int i;
- i = 0;
- while (str[i] && x < 16)
- {
- PlotCharacter(useCBMFont, petscii, x++, y, str[i++], inverse);
- }
-}
-
-// Pg 143 - 145. Transposing an 8x8 bit matrix. Hacker's Delight
-void transpose8(unsigned char* B, const unsigned char* A, bool inverse)
-{
- unsigned x, y, t;
- x = (A[7] << 24) | (A[6] << 16) | (A[5] << 8) | A[4];
- y = (A[3] << 24) | (A[2] << 16) | (A[1] << 8) | A[0];
- if (inverse)
- {
- x = ~x;
- y = ~y;
- }
-
- t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
- t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
-
- t = (x ^ (x >> 14)) & 0x0000CCCC; x = x ^ t ^ (t << 14);
- t = (y ^ (y >> 14)) & 0x0000CCCC; y = y ^ t ^ (t << 14);
-
- t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
- y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
- x = t;
-
-
- B[0] = x >> 24; B[1] = x >> 16; B[2] = x >> 8; B[3] = x;
- B[4] = y >> 24; B[5] = y >> 16; B[6] = y >> 8; B[7] = y;
-}
-
-void SSD1306::PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char c, bool inverse)
-{
- unsigned char a[8], b[8];
- if (useCBMFont && CBMFont)
- {
- if (! petscii)
- c = ascii2petscii(c);
- c = petscii2screen(c);
- transpose8(a, CBMFont + ((c+256) * 8), inverse); // 256 byte shift to use the maj/min bank
- memcpy(frame + (y * 128) + (x * 8), a, 8);
- }
- else
- {
- transpose8(a, avpriv_vga16_font + (c * 16), inverse);
- transpose8(b, avpriv_vga16_font + (c * 16) + 8, inverse);
- memcpy(frame + (y * 256) + (x * 8), a, 8);
- memcpy(frame + (y * 256) + (x * 8) + 128, b, 8);
- }
-
-}
-
-void SSD1306::PlotPixel(int x, int y, int c)
-{
- switch (c)
- {
- case 1: frame[x+ (y/8)*width] |= (1 << (y&7)); break;
- case 0: frame[x+ (y/8)*width] &= ~(1 << (y&7)); break;
- case -1: frame[x+ (y/8)*width] ^= (1 << (y&7)); break;
- }
-}
-
-// expects source in ssd1306 native vertical byte format
-void SSD1306::PlotImage(const unsigned char * source)
-{
- memcpy (frame, source, SSD1306_128x64_BYTES);
-}
+// Pi1541 - A Commodore 1541 disk drive emulator
+// Copyright(C) 2018 Stephen White
+//
+// This file is part of Pi1541.
+//
+// Pi1541 is free software : you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Pi1541 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Pi1541. If not, see .
+
+#include "SSD1306.h"
+#include "debug.h"
+#include
+#include "Petscii.h"
+
+extern "C"
+{
+#include "xga_font_data.h"
+}
+
+extern unsigned char* CBMFont;
+
+SSD1306::SSD1306(int BSCMaster, u8 address, unsigned width, unsigned height, int flip, LCD_MODEL type)
+ : BSCMaster(BSCMaster)
+ , address(address)
+ , type(type)
+ , flip(flip)
+ , contrast(127)
+ , width(width)
+ , height(height)
+{
+ sizeof_frame = width*height/8;
+ frame = (unsigned char *)malloc(sizeof_frame);
+ oldFrame = (unsigned char *)malloc(sizeof_frame);
+ RPI_I2CInit(BSCMaster, 1);
+ InitHardware();
+}
+
+void SSD1306::InitHardware()
+{
+ // SSD1306 data sheet configuration flow
+ SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
+
+ SendCommand(SSD1306_CMD_MULTIPLEX_RATIO); // 0xA8
+ SendCommand(height-1); // SSD1306_LCDHEIGHT - 1
+
+ SendCommand(SSD1306_CMD_SET_DISPLAY_OFFSET); // 0xD3 Vertical scroll position
+ SendCommand(0x00); // no Offset
+
+ SendCommand(SSD1306_CMD_SET_START_LINE | 0x0); // 0x40
+
+ if (flip) {
+ SendCommand(0xA0); // No Segment Re-Map
+ SendCommand(0xC0); // No COM Output Scan Direction
+ } else {
+ SendCommand(0xA1); // Set Segment Re-Map (horizontal flip)
+ SendCommand(0xC8); // Set COM Output Scan Direction (vertical flip)
+ }
+
+ SendCommand(SSD1306_CMD_SET_COM_PINS); // 0xDA Layout and direction
+ if (type == LCD_1306_128x32)
+ SendCommand(0x02);
+ else
+ SendCommand(0x12);
+
+ SetContrast(GetContrast());
+
+ SendCommand(SSD1306_CMD_TEST_DISPLAY_OFF); // 0xA4 - DONT force entire display on
+
+ SendCommand(SSD1306_CMD_NORMAL_DISPLAY); // 0xA6 = non inverted
+
+ SendCommand(SSD1306_CMD_SET_PRE_CHARGE_PERIOD); // 0xD9
+ SendCommand(0xF1);
+
+ SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL); // 0xDB
+ SendCommand(0x40);
+
+ SendCommand(SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO); // 0xD5
+ SendCommand(0x80); // upper nibble is rate, lower nibble is divisor
+
+ SendCommand(SSD1306_ENABLE_CHARGE_PUMP); // 0x8D Enable charge pump regulator
+ SendCommand(0x14); // external = 0x10 internal = 0x14
+
+ SendCommand(SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE); // 0x20 Set Memory Addressing Mode
+ SendCommand(0x10); // 10 - Page Addressing Mode for SH1106 compatibility
+
+ Home();
+
+ if (type != LCD_1106_128x64)
+ SendCommand(SSD1306_CMD_DEACTIVATE_SCROLL); // 0x2E
+}
+
+void SSD1306::SendCommand(u8 command)
+{
+ char buffer[2];
+
+ buffer[0] = SSD1306_CONTROL_REG;
+ buffer[1] = command;
+
+ RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
+}
+
+void SSD1306::SendData(u8 data)
+{
+ char buffer[2];
+
+ buffer[0] = SSD1306_DATA_REG;
+ buffer[1] = data;
+
+ RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
+}
+
+//We can send up to 16 bytes to the i2c bus
+void SSD1306::SendDataLong(void* data, u8 length)
+{
+ char buffer[15];
+
+ buffer[0] = SSD1306_DATA_REG;
+ memcpy(&buffer[1], data, length);
+
+ RPI_I2CWrite(BSCMaster, address, buffer, ++length);
+}
+
+void SSD1306::Home()
+{
+ SetDataPointer(0, 0);
+}
+
+void SSD1306::SetDataPointer(u8 page, u8 col)
+{
+ if (col > width-1) { col = width-1; }
+ if (page > height/8-1) { page = height/8-1; }
+
+ if (type == LCD_1106_128x64)
+ col += 2; // sh1106 uses columns 2..129
+
+ SendCommand(SSD1306_CMD_SET_PAGE | page); // 0xB0 page address
+ SendCommand(SSD1306_CMD_SET_COLUMN_LOW | (col & 0xf)); // 0x00 column address lower bits
+ SendCommand(SSD1306_CMD_SET_COLUMN_HIGH | (col >> 4)); // 0x10 column address upper bits
+}
+
+void SSD1306::RefreshScreen()
+{
+ unsigned i;
+ for (i = 0; i < height/8; i++)
+ {
+ RefreshPage(i);
+ }
+}
+
+// assumes a text row is 8 bit high
+void SSD1306::RefreshTextRows(u32 start, u32 amountOfRows)
+{
+ unsigned int i;
+
+ //start <<= 1;
+ //amountOfRows <<= 1;
+ for (i = start; i < start+amountOfRows; i++)
+ {
+ RefreshPage(i);
+ }
+}
+
+// Some very basic optimisation is implemented.
+// it scans the page to work out the first (new_start) and last (new_end) changed bytes
+// Only update that window on the OLED
+// If someone is keen, a smarter algorithm could work out a series of ranges to update
+void SSD1306::RefreshPage(u32 page)
+{
+ if (page >= height/8)
+ return;
+
+ // x32 displays use lower half (pages 2 and 3)
+ if (type == LCD_1306_128x32)
+ {
+ page = page+4; // 0,1,2,3 -> 4,5,6,7
+ page = page%4; // and wrap it so 4,5 -> 0,1
+ }
+
+ int i;
+ int start = page*width;
+ int end = start + width;
+
+ int new_start = -1;
+ int new_end = -1;
+ for (i = start; i < end; i++)
+ {
+ if (oldFrame[i] ^ frame[i])
+ {
+ if (new_start == -1)
+ new_start = i;
+ new_end = i;
+ }
+ }
+
+ if (new_start >= 0)
+ {
+ SetDataPointer(page, new_start-start);
+ new_end++;
+ while (new_start < new_end)
+ {
+ i = (new_end-new_start < 15) ? new_end-new_start : 15;
+ if (i == 1)
+ {
+ SendData(frame[new_start]);
+ oldFrame[new_start] = frame[new_start];
+ }
+ else // Use the ability to send up to 16 byte in a row
+ // (1 for command and 15 for data)
+ {
+ SendDataLong(&frame[new_start],i);
+ memcpy(&oldFrame[new_start], &frame[new_start],i);
+ }
+ new_start += i;
+ }
+ }
+}
+
+void SSD1306::ClearScreen()
+{
+ memset(frame, 0, sizeof_frame);
+ //memset(oldFrame, 0xff, sizeof_frame); // to force update
+ RefreshScreen();
+}
+
+void SSD1306::DisplayOn()
+{
+ SendCommand(SSD1306_CMD_DISPLAY_ON); // 0xAF
+}
+
+void SSD1306::DisplayOff()
+{
+ SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
+}
+
+void SSD1306::SetContrast(u8 value)
+{
+ contrast = value;
+ SendCommand(SSD1306_CMD_SET_CONTRAST_CONTROL);
+ SendCommand(value);
+ if (type != LCD_1106_128x64) // dont fiddle vcomdeselect on 1106 displays
+ SetVCOMDeselect( value >> 8);
+}
+
+void SSD1306::SetVCOMDeselect(u8 value)
+{
+ SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL);
+ SendCommand( (value & 7) << 4 );
+}
+
+void SSD1306::PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse)
+{
+// assumes 16 character width
+ int i;
+ i = 0;
+ while (str[i] && x < 16)
+ {
+ PlotCharacter(useCBMFont, petscii, x++, y, str[i++], inverse);
+ }
+}
+
+// Pg 143 - 145. Transposing an 8x8 bit matrix. Hacker's Delight
+void transpose8(unsigned char* B, const unsigned char* A, bool inverse)
+{
+ unsigned x, y, t;
+ x = (A[7] << 24) | (A[6] << 16) | (A[5] << 8) | A[4];
+ y = (A[3] << 24) | (A[2] << 16) | (A[1] << 8) | A[0];
+ if (inverse)
+ {
+ x = ~x;
+ y = ~y;
+ }
+
+ t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
+ t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
+
+ t = (x ^ (x >> 14)) & 0x0000CCCC; x = x ^ t ^ (t << 14);
+ t = (y ^ (y >> 14)) & 0x0000CCCC; y = y ^ t ^ (t << 14);
+
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+
+ B[0] = x >> 24; B[1] = x >> 16; B[2] = x >> 8; B[3] = x;
+ B[4] = y >> 24; B[5] = y >> 16; B[6] = y >> 8; B[7] = y;
+}
+
+void SSD1306::PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char c, bool inverse)
+{
+ unsigned char a[8], b[8];
+ if (useCBMFont && CBMFont)
+ {
+ if (! petscii)
+ c = ascii2petscii(c);
+ c = petscii2screen(c);
+ transpose8(a, CBMFont + ((c+256) * 8), inverse); // 256 byte shift to use the maj/min bank
+ memcpy(frame + (y * 128) + (x * 8), a, 8);
+ }
+ else
+ {
+ transpose8(a, avpriv_vga16_font + (c * 16), inverse);
+ transpose8(b, avpriv_vga16_font + (c * 16) + 8, inverse);
+ memcpy(frame + (y * 256) + (x * 8), a, 8);
+ memcpy(frame + (y * 256) + (x * 8) + 128, b, 8);
+ }
+
+}
+
+void SSD1306::PlotPixel(int x, int y, int c)
+{
+ switch (c)
+ {
+ case 1: frame[x+ (y/8)*width] |= (1 << (y&7)); break;
+ case 0: frame[x+ (y/8)*width] &= ~(1 << (y&7)); break;
+ case -1: frame[x+ (y/8)*width] ^= (1 << (y&7)); break;
+ }
+}
+
+// expects source in ssd1306 native vertical byte format
+void SSD1306::PlotImage(const unsigned char * source)
+{
+ memcpy (frame, source, SSD1306_128x64_BYTES);
+}
diff --git a/src/SSD1306.h b/src/SSD1306.h
index b1ffb62..07d1293 100644
--- a/src/SSD1306.h
+++ b/src/SSD1306.h
@@ -1,146 +1,147 @@
-// Pi1541 - A Commodore 1541 disk drive emulator
-// Copyright(C) 2018 Stephen White
-//
-// This file is part of Pi1541.
-//
-// Pi1541 is free software : you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Pi1541 is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Pi1541. If not, see .
-
-#ifndef SSD1306_H
-#define SSD1306_H
-
-#include
-#include "types.h"
-extern "C"
-{
-#include "rpi-i2c.h"
-}
-
-// 8 pages * (128 columns * 8 bits)
-//0 127 0 127
-//0 127 0 127
-//0 127 0 127
-//________________________________________________________
-//7777777 | 7
-//6666666 | 6
-//5555555 | 5
-//4444444 PG(ROW)0 | PG(ROW)1 4
-//3333333 | 3
-//2222222 | 2
-//1111111 | 1
-//0000000 | 0
-//________________________________________________________
-//7777777 | 7
-//6666666 | 6
-//5555555 | 5
-//4444444 PG(ROW)2 | PG(ROW)3 4
-//3333333 | 3
-//2222222 | 2
-//1111111 | 1
-//0000000 | 0
-//________________________________________________________
-//7777777 | 7
-//6666666 | 6
-//5555555 | 5
-//4444444 PG(ROW)4 | PG(ROW)5 4
-//3333333 | 3
-//2222222 | 2
-//1111111 | 1
-//0000000 | 0
-//________________________________________________________
-//7777777 | 7
-//6666666 | 6
-//5555555 | 5
-//4444444 PG(ROW)6 | PG(ROW)7 4
-//3333333 | 3
-//2222222 | 2
-//1111111 | 1
-//0000000 | 0
-//________________________________________________________
-
-#define SSD1306_128x64_BYTES ((128 * 64) / 8)
-
-class SSD1306
-{
-public:
- // 128x32 0x3C
- // 128x64 0x3D or 0x3C (if SA0 is grounded)
- SSD1306(int BSCMaster = 1, u8 address = 0x3C, unsigned width = 128, unsigned height = 64, int flip = 0, LCD_MODEL type=LCD_UNKNOWN);
-
- void PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char ascii, bool inverse);
- void PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse);
-
- void InitHardware();
- void DisplayOn();
- void DisplayOff();
- void SetContrast(u8 value);
- u8 GetContrast() { return contrast; }
- void SetVCOMDeselect(u8 value);
-
- void ClearScreen();
- void RefreshScreen();
- void RefreshPage(u32 page);
- void RefreshTextRows(u32 start, u32 amountOfRows);
- void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2);
- void PlotPixel(int x, int y, int c);
- void PlotImage(const unsigned char * source);
-
-protected:
- void SendCommand(u8 command);
- void SendData(u8 data);
-
- void Home();
- void SetDataPointer(u8 row, u8 col);
-
-// unsigned char frame[SSD1306_128x64_BYTES];
-// unsigned char oldFrame[SSD1306_128x64_BYTES];
- unsigned char * frame;
- unsigned char * oldFrame;
- unsigned sizeof_frame;
-
- int BSCMaster;
- u8 address;
- int type;
- int flip;
- int contrast;
- unsigned width;
- unsigned height;
-};
-#endif
-
-
-#define SSD1306_CMD_SET_COLUMN_LOW 0x00
-#define SSD1306_CMD_SET_COLUMN_HIGH 0x10
-#define SSD1306_CMD_SET_PAGE 0xB0
-#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20
-#define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21
-#define SSD1306_CMD_SET_PAGE_ADDRESS 0x22
-#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E
-#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F
-#define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0
-#define SSD1306_ENABLE_CHARGE_PUMP 0x8D
-#define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4
-#define SSD1306_CMD_TEST_DISPLAY_ON 0xA5
-#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel
-#define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel
-#define SSD1306_CMD_DISPLAY_OFF 0xAE
-#define SSD1306_CMD_DISPLAY_ON 0xAF
-#define SSD1306_CMD_MULTIPLEX_RATIO 0xA8
-#define SSD1306_CMD_SET_START_LINE 0x40
-#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
-#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
-#define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9
-#define SSD1306_CMD_SET_COM_PINS 0xDA
-#define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
-#define SSD1306_CONTROL_REG 0x00
-#define SSD1306_DATA_REG 0x40
+// Pi1541 - A Commodore 1541 disk drive emulator
+// Copyright(C) 2018 Stephen White
+//
+// This file is part of Pi1541.
+//
+// Pi1541 is free software : you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Pi1541 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Pi1541. If not, see .
+
+#ifndef SSD1306_H
+#define SSD1306_H
+
+#include
+#include "types.h"
+extern "C"
+{
+#include "rpi-i2c.h"
+}
+
+// 8 pages * (128 columns * 8 bits)
+//0 127 0 127
+//0 127 0 127
+//0 127 0 127
+//________________________________________________________
+//7777777 | 7
+//6666666 | 6
+//5555555 | 5
+//4444444 PG(ROW)0 | PG(ROW)1 4
+//3333333 | 3
+//2222222 | 2
+//1111111 | 1
+//0000000 | 0
+//________________________________________________________
+//7777777 | 7
+//6666666 | 6
+//5555555 | 5
+//4444444 PG(ROW)2 | PG(ROW)3 4
+//3333333 | 3
+//2222222 | 2
+//1111111 | 1
+//0000000 | 0
+//________________________________________________________
+//7777777 | 7
+//6666666 | 6
+//5555555 | 5
+//4444444 PG(ROW)4 | PG(ROW)5 4
+//3333333 | 3
+//2222222 | 2
+//1111111 | 1
+//0000000 | 0
+//________________________________________________________
+//7777777 | 7
+//6666666 | 6
+//5555555 | 5
+//4444444 PG(ROW)6 | PG(ROW)7 4
+//3333333 | 3
+//2222222 | 2
+//1111111 | 1
+//0000000 | 0
+//________________________________________________________
+
+#define SSD1306_128x64_BYTES ((128 * 64) / 8)
+
+class SSD1306
+{
+public:
+ // 128x32 0x3C
+ // 128x64 0x3D or 0x3C (if SA0 is grounded)
+ SSD1306(int BSCMaster = 1, u8 address = 0x3C, unsigned width = 128, unsigned height = 64, int flip = 0, LCD_MODEL type=LCD_UNKNOWN);
+
+ void PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char ascii, bool inverse);
+ void PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse);
+
+ void InitHardware();
+ void DisplayOn();
+ void DisplayOff();
+ void SetContrast(u8 value);
+ u8 GetContrast() { return contrast; }
+ void SetVCOMDeselect(u8 value);
+
+ void ClearScreen();
+ void RefreshScreen();
+ void RefreshPage(u32 page);
+ void RefreshTextRows(u32 start, u32 amountOfRows);
+ void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2);
+ void PlotPixel(int x, int y, int c);
+ void PlotImage(const unsigned char * source);
+
+protected:
+ void SendCommand(u8 command);
+ void SendData(u8 data);
+ void SendDataLong(void* data, u8 length);
+
+ void Home();
+ void SetDataPointer(u8 row, u8 col);
+
+// unsigned char frame[SSD1306_128x64_BYTES];
+// unsigned char oldFrame[SSD1306_128x64_BYTES];
+ unsigned char * frame;
+ unsigned char * oldFrame;
+ unsigned sizeof_frame;
+
+ int BSCMaster;
+ u8 address;
+ int type;
+ int flip;
+ int contrast;
+ unsigned width;
+ unsigned height;
+};
+#endif
+
+
+#define SSD1306_CMD_SET_COLUMN_LOW 0x00
+#define SSD1306_CMD_SET_COLUMN_HIGH 0x10
+#define SSD1306_CMD_SET_PAGE 0xB0
+#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20
+#define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21
+#define SSD1306_CMD_SET_PAGE_ADDRESS 0x22
+#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E
+#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F
+#define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0
+#define SSD1306_ENABLE_CHARGE_PUMP 0x8D
+#define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4
+#define SSD1306_CMD_TEST_DISPLAY_ON 0xA5
+#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel
+#define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel
+#define SSD1306_CMD_DISPLAY_OFF 0xAE
+#define SSD1306_CMD_DISPLAY_ON 0xAF
+#define SSD1306_CMD_MULTIPLEX_RATIO 0xA8
+#define SSD1306_CMD_SET_START_LINE 0x40
+#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
+#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
+#define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9
+#define SSD1306_CMD_SET_COM_PINS 0xDA
+#define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
+#define SSD1306_CONTROL_REG 0x00
+#define SSD1306_DATA_REG 0x40