From e292de11791ebbaf24d472c95dfbc438397d27d0 Mon Sep 17 00:00:00 2001 From: Stephen White Date: Sun, 3 Jun 2018 18:11:58 +1000 Subject: [PATCH] Added support for ssd1306 128x64 Options.txt needs the line;- LCDName = ssd1306_128x64 and depending upon how you want the keyboard's pgup and pgdown keys to work;- keyboardBrowseLCDScreen = 1 Splits lines connect SDA to GPIO 2 pin 3 SCL to GPIO 3 pin 5 Non-split lines SDA to GPIO 0 pin 27 SCL to GPIO 1 pin 28 --- Makefile | 4 +- src/DiskCaddy.cpp | 102 ++++++++-- src/DiskCaddy.h | 4 +- src/FileBrowser.cpp | 449 ++++++++++++++++++++++++++++-------------- src/FileBrowser.h | 82 +++++--- src/InputMappings.cpp | 20 +- src/InputMappings.h | 18 ++ src/SSD1306.cpp | 251 +++++++++++++++++++++++ src/SSD1306.h | 99 ++++++++++ src/Screen.cpp | 10 +- src/Screen.h | 41 +--- src/ScreenBase.h | 97 +++++++++ src/ScreenLCD.cpp | 100 ++++++++++ src/ScreenLCD.h | 58 ++++++ src/main.cpp | 20 +- src/options.cpp | 6 + src/options.h | 9 + src/rpi-i2c.c | 186 +++++++++++++++++ src/rpi-i2c.h | 11 ++ 19 files changed, 1337 insertions(+), 230 deletions(-) create mode 100644 src/SSD1306.cpp create mode 100644 src/SSD1306.h create mode 100644 src/ScreenBase.h create mode 100644 src/ScreenLCD.cpp create mode 100644 src/ScreenLCD.h create mode 100644 src/rpi-i2c.c create mode 100644 src/rpi-i2c.h diff --git a/Makefile b/Makefile index 152051a..8df1180 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ OBJS = armc-start.o armc-cstartup.o armc-cstubs.o armc-cppstubs.o \ - exception.o main.o rpi-aux.o rpi-mailbox-interface.o rpi-mailbox.o \ + exception.o main.o rpi-aux.o rpi-i2c.o rpi-mailbox-interface.o rpi-mailbox.o \ rpi-gpio.o rpi-interrupts.o cache.o ff.o interrupt.o Keyboard.o \ Pi1541.o DiskImage.o iec_bus.o iec_commands.o m6502.o m6522.o \ - Drive.o gcr.o prot.o lz.o emmc.o diskio.o options.o Screen.o \ + Drive.o gcr.o prot.o lz.o emmc.o diskio.o options.o Screen.o SSD1306.o ScreenLCD.o \ Timer.o FileBrowser.o DiskCaddy.o ROMs.o InputMappings.o xga_font_data.o SRCDIR = src diff --git a/src/DiskCaddy.cpp b/src/DiskCaddy.cpp index e6ea56b..1775e1e 100644 --- a/src/DiskCaddy.cpp +++ b/src/DiskCaddy.cpp @@ -34,6 +34,8 @@ static u32 red = RGBA(0xff, 0, 0, 0xff); bool DiskCaddy::Insert(const FILINFO* fileInfo, bool readOnly) { + int x; + int y; bool success; FIL fp; FRESULT res = f_open(&fp, fileInfo->fname, FA_READ); @@ -41,13 +43,28 @@ bool DiskCaddy::Insert(const FILINFO* fileInfo, bool readOnly) { if (screen) { - int x = screen->ScaleX(screenPosXCaddySelections); - int y = screen->ScaleY(screenPosYCaddySelections); + x = screen->ScaleX(screenPosXCaddySelections); + y = screen->ScaleY(screenPosYCaddySelections); snprintf(buffer, 256, "Loading %s\r\n", fileInfo->fname); screen->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red); } + if (screenLCD) + { + RGBA BkColour = RGBA(0, 0, 0, 0xFF); + screenLCD->Clear(BkColour); + x = 0; + y = 0; + + snprintf(buffer, 256, "Loading"); + screenLCD->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), BkColour); + y += 16; + snprintf(buffer, 256, "%s ", fileInfo->fname); + screenLCD->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red); + screenLCD->SwapBuffers(); + } + u32 bytesRead; SetACTLed(true); f_read(&fp, DiskImage::readBuffer, READBUFFER_SIZE, &bytesRead); @@ -146,12 +163,14 @@ bool DiskCaddy::InsertNBZ(const FILINFO* fileInfo, unsigned char* diskImageData, void DiskCaddy::Display() { + unsigned numberOfImages = GetNumberOfImages(); + unsigned caddyIndex; + int x; + int y; if (screen) { - unsigned numberOfImages = GetNumberOfImages(); - unsigned caddyIndex; - int x = screen->ScaleX(screenPosXCaddySelections); - int y = screen->ScaleY(screenPosYCaddySelections); + x = screen->ScaleX(screenPosXCaddySelections); + y = screen->ScaleY(screenPosYCaddySelections); snprintf(buffer, 256, "Emulating\r\n"); screen->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), red); @@ -168,17 +187,71 @@ void DiskCaddy::Display() y += 16; } } - - ShowSelectedImage(0); } + + if (screenLCD) + { + RGBA BkColour = RGBA(0, 0, 0, 0xFF); + screenLCD->Clear(BkColour); + } + ShowSelectedImage(0); } void DiskCaddy::ShowSelectedImage(u32 index) { - u32 x = screen->ScaleX(screenPosXCaddySelections) - 16; - u32 y = screen->ScaleY(screenPosYCaddySelections) + 16 + 16 * index; - snprintf(buffer, 256, "*"); - screen->PrintText(false, x, y, buffer, white, red); + u32 x; + u32 y; + if (screen) + { + x = screen->ScaleX(screenPosXCaddySelections) - 16; + y = screen->ScaleY(screenPosYCaddySelections) + 16 + 16 * index; + snprintf(buffer, 256, "*"); + screen->PrintText(false, x, y, buffer, white, red); + } + if (screenLCD) + { + unsigned numberOfImages = GetNumberOfImages(); + unsigned caddyIndex; + + if (screenLCD) + { + RGBA BkColour = RGBA(0, 0, 0, 0xFF); + //screenLCD->Clear(BkColour); + x = 0; + y = 0; + + snprintf(buffer, 256, "Emulating %d/%d ", index + 1, numberOfImages); + screenLCD->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), RGBA(0xff, 0xff, 0xff, 0xff)); + y += 16; + + if (numberOfImages > 3 && index > 2) + { + if (numberOfImages - index < 3) + caddyIndex = numberOfImages - 3; + else + caddyIndex = index; + } + else + { + caddyIndex = 0; + } + + for (; caddyIndex < numberOfImages; ++caddyIndex) + { + DiskImage* image = GetImage(caddyIndex); + const char* name = image->GetName(); + if (name) + { + snprintf(buffer, 256, "%d %s ", caddyIndex + 1, name); + screenLCD->PrintText(false, x, y, buffer, RGBA(0xff, 0xff, 0xff, 0xff), caddyIndex == index ? RGBA(0xff, 0xff, 0xff, 0xff) : BkColour); + y += 16; + } + if (y >= screenLCD->Height()) + break; + } + screenLCD->SwapBuffers(); + } + } } bool DiskCaddy::Update() @@ -198,6 +271,11 @@ bool DiskCaddy::Update() ShowSelectedImage(oldCaddyIndex); } + if (screenLCD) + { + + } + return true; } return false; diff --git a/src/DiskCaddy.h b/src/DiskCaddy.h index 40a8a4d..081cc85 100644 --- a/src/DiskCaddy.h +++ b/src/DiskCaddy.h @@ -29,10 +29,11 @@ public: DiskCaddy() : selectedIndex(0) , screen(0) + , screenLCD(0) { } - void SetScreen(Screen* screen) { this->screen = screen; } + void SetScreen(Screen* screen, ScreenBase* screenLCD) { this->screen = screen; this->screenLCD = screenLCD; } void Empty() { @@ -106,6 +107,7 @@ private: u32 oldCaddyIndex; Screen* screen; + ScreenBase* screenLCD; }; #endif \ No newline at end of file diff --git a/src/FileBrowser.cpp b/src/FileBrowser.cpp index 9ea5bbb..98aedf7 100644 --- a/src/FileBrowser.cpp +++ b/src/FileBrowser.cpp @@ -21,7 +21,6 @@ #include #include #include -#include "Screen.h" #include "debug.h" #include "Keyboard.h" #include "options.h" @@ -33,8 +32,6 @@ extern "C" #include "rpi-gpio.h" } -extern Screen screen; - #define PNG_WIDTH 320 #define PNG_HEIGHT 200 @@ -75,6 +72,205 @@ static const u32 palette[] = RGBA(0x9F, 0x9F, 0x9F, 0xFF) }; +void FileBrowser::BrowsableListView::Refresh() +{ + char buffer1[128] = { 0 }; + char buffer2[128] = { 0 }; + u32 index; + u32 entryIndex; + u32 x = positionX; + u32 y = positionY; + u32 colour; + RGBA BkColour = RGBA(0, 0, 0, 0xFF); //palette[VIC2_COLOUR_INDEX_BLUE]; + + // Ensure the current selection is visible + if (list->currentIndex - offset >= rows) + { + //DEBUG_LOG("CI= %d O = %d R = %d\r\n", list->currentIndex, offset, rows); + offset = list->currentIndex - rows + 1; + if ((int)offset < 0) offset = 0; + } + + for (index = 0; index < rows; ++index) + { + entryIndex = offset + index; + + if (entryIndex < list->entries.size()) + { + FileBrowser::BrowsableList::Entry* entry = &list->entries[entryIndex]; + if (screen->IsMonocrome()) + { + if (entry->filImage.fattrib & AM_DIR) + { + snprintf(buffer2, columns + 1, "[%s]", entry->filImage.fname); + } + else + { + if (entry->caddyIndex != -1) + snprintf(buffer2, columns + 1, "%d>%s", entry->caddyIndex, entry->filImage.fname); + else + snprintf(buffer2, columns + 1, "%s", entry->filImage.fname); + } + } + else + { + snprintf(buffer2, columns + 1, "%s", entry->filImage.fname); + } + memset(buffer1, ' ', columns); + buffer1[127] = 0; + strncpy(buffer1, buffer2, strlen(buffer2)); + if (/*showSelected && */list->currentIndex == entryIndex) + { + if (entry->filImage.fattrib & AM_DIR) + { + screen->PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], RGBA(0xff, 0xff, 0xff, 0xff)); + } + else + { + colour = RGBA(0xff, 0, 0, 0xff); + if (entry->filImage.fattrib & AM_RDO) + colour = palette[VIC2_COLOUR_INDEX_RED]; + + screen->PrintText(false, x, y, buffer1, colour, RGBA(0xff, 0xff, 0xff, 0xff)); + } + } + else + { + if (entry->filImage.fattrib & AM_DIR) + { + screen->PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], BkColour); + } + else + { + colour = palette[VIC2_COLOUR_INDEX_LGREY]; + if (entry->filImage.fattrib & AM_RDO) + colour = palette[VIC2_COLOUR_INDEX_PINK]; + screen->PrintText(false, x, y, buffer1, colour, BkColour); + } + } + } + else + { + memset(buffer1, ' ', 80); + screen->PrintText(false, x, y, buffer1, BkColour, BkColour); + } + y += 16; + } + + screen->SwapBuffers(); +} + +bool FileBrowser::BrowsableListView::CheckBrowseNavigation(bool pageOnly) +{ + InputMappings* inputMappings = InputMappings::Instance(); + bool dirty = false; + u32 numberOfEntriesMinus1 = list->entries.size() - 1; + + if (inputMappings->BrowseDown()) + { + if (list->currentIndex < numberOfEntriesMinus1) + { + if (!pageOnly) + { + list->currentIndex++; + list->current = &list->entries[list->currentIndex]; + } + if (list->currentIndex >= (offset + rows) && (list->currentIndex < list->entries.size())) + offset++; + dirty = true; + } + } + if (inputMappings->BrowseUp()) + { + if (list->currentIndex > 0) + { + if (!pageOnly) + { + list->currentIndex--; + list->current = &list->entries[list->currentIndex]; + } + if ((offset > 0) && (list->currentIndex < offset)) + offset--; + dirty = true; + } + } + if ((lcdPgUpDown && inputMappings->BrowsePageDownLCD()) || (!lcdPgUpDown && inputMappings->BrowsePageDown())) + { + u32 rowsMinus1 = rows - 1; + + if (list->currentIndex == offset + rowsMinus1) + { + // Need to move the screen window down so that the currentIndex is now at the top of the screen + offset = list->currentIndex; + + // Current index now becomes the bottom one + if (offset + rowsMinus1 > numberOfEntriesMinus1) + list->currentIndex = numberOfEntriesMinus1; // Not enough entries to move another page just move to the last entry + else // Move the window down a page + list->currentIndex = offset + rowsMinus1; + } + else + { + // Need to move to list->offset + rowsMinus1 + if (offset + rowsMinus1 > numberOfEntriesMinus1) + list->currentIndex = numberOfEntriesMinus1; // Run out of entries before we hit the bottom. Just move to the bottom. + else + list->currentIndex = offset + rowsMinus1; // Move the bottom of the screen + } + list->current = &list->entries[list->currentIndex]; + dirty = true; + } + if ((lcdPgUpDown && inputMappings->BrowsePageUpLCD()) || (!lcdPgUpDown && inputMappings->BrowsePageUp())) + { + if (list->currentIndex == offset) + { + // If the cursor is already at the top of the window then page up + int offsetInWindow = (int)list->currentIndex - (int)rows; + if (offsetInWindow < 0) offset = 0; + else offset = (u32)offsetInWindow; + list->currentIndex = offset; + } + else + { + list->currentIndex = offset; // Move the cursor to the top of the window + } + list->current = &list->entries[list->currentIndex]; + dirty = true; + } + + return dirty; +} + +void FileBrowser::BrowsableList::ClearSelections() +{ + u32 entryIndex; + + for (entryIndex = 0; entryIndex < entries.size(); ++entryIndex) + { + entries[entryIndex].caddyIndex = -1; + } +} + +void FileBrowser::BrowsableList::RefreshViews() +{ + u32 index; + for (index = 0; index < views.size(); ++index) + { + views[index].Refresh(); + } +} + +bool FileBrowser::BrowsableList::CheckBrowseNavigation() +{ + bool dirty = false; + u32 index; + for (index = 0; index < views.size(); ++index) + { + dirty |= views[index].CheckBrowseNavigation(index != 0); + } + return dirty; +} + FileBrowser::BrowsableList::Entry* FileBrowser::BrowsableList::FindEntry(const char* name) { int index; @@ -89,18 +285,38 @@ FileBrowser::BrowsableList::Entry* FileBrowser::BrowsableList::FindEntry(const c return 0; } -FileBrowser::FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons) +FileBrowser::FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons, ScreenBase* screenMain, ScreenBase* screenLCD) : state(State_Folders) - , maxOnScreen(38) , diskCaddy(diskCaddy) , selectionsMade(false) , roms(roms) , deviceID(deviceID) , displayPNGIcons(displayPNGIcons) + , screenMain(screenMain) + , screenLCD(screenLCD) { - maxOnScreen = (int)(38.0f * screen.GetScaleY()); - if (maxOnScreen < 1) - maxOnScreen = 1; + u32 columns = screenMain->ScaleX(80); + u32 rows = (int)(38.0f * screenMain->GetScaleY()); + u32 positionX = 0; + u32 positionY = 17; + + if (rows < 1) + rows = 1; + + folder.AddView(screenMain, columns, rows, positionX, positionY, false); + + positionX = screenMain->ScaleX(1024 - 320); + caddySelections.AddView(screenMain, columns, rows, positionX, positionY, false); + + + + columns = 128 / 8; + rows = 4; + positionX = 0; + positionY = 0; + + if (screenLCD) + folder.AddView(screenLCD, columns, rows, positionX, positionY, true); } u32 FileBrowser::Colour(int index) @@ -204,6 +420,7 @@ void FileBrowser::DisplayRoot() FolderChanged(); } +/* void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* browsableList, int xOffset, bool showSelected) { char buffer1[128] = { 0 }; @@ -219,7 +436,7 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow if (terminal) printf("\E[2J\E[f"); - u32 maxCharacters = screen.ScaleX(80); + u32 maxCharacters = screenMain->ScaleX(80); for (index = 0; index < maxOnScreen; ++index) { @@ -238,7 +455,7 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow { if (terminal) printf("\E[34;47m%s\E[0m\r\n", buffer1); - screen.PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], RGBA(0xff, 0xff, 0xff, 0xff)); + screenMain->PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], RGBA(0xff, 0xff, 0xff, 0xff)); } else { @@ -246,7 +463,7 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow if (entry->filImage.fattrib & AM_RDO) colour = palette[VIC2_COLOUR_INDEX_RED]; - screen.PrintText(false, x, y, buffer1, colour, RGBA(0xff, 0xff, 0xff, 0xff)); + screenMain->PrintText(false, x, y, buffer1, colour, RGBA(0xff, 0xff, 0xff, 0xff)); if (terminal) printf("\E[31;47m%s\E[0m\r\n", buffer1); } @@ -255,7 +472,7 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow { if (entry->filImage.fattrib & AM_DIR) { - screen.PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], BkColour); + screenMain->PrintText(false, x, y, buffer1, palette[VIC2_COLOUR_INDEX_LBLUE], BkColour); if (terminal) printf("\E[34m%s\E[0m\r\n", buffer1); } @@ -264,7 +481,7 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow colour = palette[VIC2_COLOUR_INDEX_LGREY]; if (entry->filImage.fattrib & AM_RDO) colour = palette[VIC2_COLOUR_INDEX_PINK]; - screen.PrintText(false, x, y, buffer1, colour, BkColour); + screenMain->PrintText(false, x, y, buffer1, colour, BkColour); if (terminal) printf("\E[0;m%s\E[0m\r\n", buffer1); } @@ -273,13 +490,14 @@ void FileBrowser::RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* brow else { memset(buffer1, ' ', 80); - screen.PrintText(false, x, y, buffer1, BkColour, BkColour); + screenMain->PrintText(false, x, y, buffer1, BkColour, BkColour); if (terminal) printf("%s\r\n", buffer1); } y += 16; } } +*/ void FileBrowser::RefeshDisplay() { @@ -289,13 +507,16 @@ void FileBrowser::RefeshDisplay() u32 textColour = Colour(VIC2_COLOUR_INDEX_LGREEN); u32 bgColour = Colour(VIC2_COLOUR_INDEX_GREY); - screen.ClearArea(0, 0, (int)screen.Width(), 17, bgColour); - screen.PrintText(false, 0, 0, buffer, textColour, bgColour); + screenMain->ClearArea(0, 0, (int)screenMain->Width(), 17, bgColour); + screenMain->PrintText(false, 0, 0, buffer, textColour, bgColour); } - u32 offsetX = screen.ScaleX(1024 - 320); - RefeshDisplayForBrowsableList(&folder, 0); - RefeshDisplayForBrowsableList(&caddySelections, offsetX, false); + //u32 offsetX = screenMain->ScaleX(1024 - 320); + //RefeshDisplayForBrowsableList(&folder, 0); + //RefeshDisplayForBrowsableList(&caddySelections, offsetX, false); + + folder.RefreshViews(); + caddySelections.RefreshViews(); DisplayPNG(); DisplayStatusBar(); @@ -349,7 +570,7 @@ void FileBrowser::DisplayPNG(FILINFO& filIcon, int x, int y) if (image && (w == PNG_WIDTH && h == PNG_HEIGHT)) { //DEBUG_LOG("Opened PNG %s w = %d h = %d cif = %d\r\n", fileName, w, h, channels_in_file); - screen.PlotImage((u32*)image, x, y, w, h); + screenMain->PlotImage((u32*)image, x, y, w, h); } else { @@ -368,8 +589,8 @@ void FileBrowser::DisplayPNG() if (displayPNGIcons && folder.current) { FileBrowser::BrowsableList::Entry* current = folder.current; - u32 x = screen.ScaleX(1024 - 320); - u32 y = screen.ScaleY(666) - 240; + u32 x = screenMain->ScaleX(1024 - 320); + u32 y = screenMain->ScaleY(666) - 240; DisplayPNG(current->filIcon, x, y); } } @@ -409,11 +630,6 @@ void FileBrowser::UpdateInput() } } -FileBrowser::Folder* FileBrowser::GetCurrentFolder() -{ - return &folder; -} - bool FileBrowser::FillCaddyWithSelections() { if (caddySelections.entries.size()) @@ -450,6 +666,7 @@ bool FileBrowser::AddToCaddy(FileBrowser::BrowsableList::Entry* current) } if (canAdd) { + current->caddyIndex = caddySelections.entries.size(); caddySelections.entries.push_back(*current); added = true; } @@ -464,7 +681,7 @@ void FileBrowser::UpdateInputFolders() if (folder.entries.size() > 0) { - u32 numberOfEntriesMinus1 = folder.entries.size() - 1; + //u32 numberOfEntriesMinus1 = folder.entries.size() - 1; bool dirty = false; if (inputMappings->BrowseSelect()) @@ -531,7 +748,7 @@ void FileBrowser::UpdateInputFolders() } else if (inputMappings->Exit()) { - caddySelections.Clear(); + ClearSelections(); dirty = true; } else if (inputMappings->BrowseInsert()) @@ -560,71 +777,7 @@ void FileBrowser::UpdateInputFolders() } } - if (inputMappings->BrowseDown()) - { - if (folder.currentIndex < numberOfEntriesMinus1) - { - folder.currentIndex++; - folder.current = &folder.entries[folder.currentIndex]; - if (folder.currentIndex >= (folder.offset + maxOnScreen) && (folder.currentIndex < folder.entries.size())) - folder.offset++; - dirty = true; - } - } - if (inputMappings->BrowseUp()) - { - if (folder.currentIndex > 0) - { - folder.currentIndex--; - folder.current = &folder.entries[folder.currentIndex]; - if ((folder.offset > 0) && (folder.currentIndex < folder.offset)) - folder.offset--; - dirty = true; - } - } - if (inputMappings->BrowsePageDown()) - { - u32 maxOnScreenMinus1 = maxOnScreen - 1; - - if (folder.currentIndex == folder.offset + maxOnScreenMinus1) - { - // Need to move the screen window down so that the currentIndex is now at the top of the screen - folder.offset = folder.currentIndex; - - // Current index now becomes the bottom one - if (folder.offset + maxOnScreenMinus1 > numberOfEntriesMinus1) - folder.currentIndex = numberOfEntriesMinus1; // Not enough entries to move another page just move to the last entry - else // Move the window down a page - folder.currentIndex = folder.offset + maxOnScreenMinus1; - } - else - { - // Need to move to folder.offset + maxOnScreenMinus1 - if (folder.offset + maxOnScreenMinus1 > numberOfEntriesMinus1) - folder.currentIndex = numberOfEntriesMinus1; // Run out of entries before we hit the bottom. Just move to the bottom. - else - folder.currentIndex = folder.offset + maxOnScreenMinus1; // Move the bottom of the screen - } - folder.current = &folder.entries[folder.currentIndex]; - dirty = true; - } - if (inputMappings->BrowsePageUp()) - { - if (folder.currentIndex == folder.offset) - { - // If the cursor is already at the top of the window then page up - int offset = (int)folder.currentIndex - (int)maxOnScreen; - if (offset < 0) folder.offset = 0; - else folder.offset = (u32)offset; - folder.currentIndex = folder.offset; - } - else - { - folder.currentIndex = folder.offset; // Move the cursor to the top of the window - } - folder.current = &folder.entries[folder.currentIndex]; - dirty = true; - } + dirty = folder.CheckBrowseNavigation(); } if (dirty) RefeshDisplay(); @@ -642,45 +795,41 @@ bool FileBrowser::SelectLST(const char* filenameLST) //DEBUG_LOG("Selected %s\r\n", filenameLST); if (DiskImage::IsLSTExtention(filenameLST)) { - FileBrowser::Folder* currentFolder = GetCurrentFolder(); - if (currentFolder) + DiskImage::DiskType diskType; + FIL fp; + FRESULT res; + res = f_open(&fp, filenameLST, FA_READ); + if (res == FR_OK) { - DiskImage::DiskType diskType; - FIL fp; - FRESULT res; - res = f_open(&fp, filenameLST, FA_READ); - if (res == FR_OK) + u32 bytesRead; + SetACTLed(true); + f_read(&fp, FileBrowser::LSTBuffer, FileBrowser::LSTBuffer_size, &bytesRead); + SetACTLed(false); + f_close(&fp); + + TextParser textParser; + + textParser.SetData((char*)FileBrowser::LSTBuffer); + char* token = textParser.GetToken(true); + while (token) { - u32 bytesRead; - SetACTLed(true); - f_read(&fp, FileBrowser::LSTBuffer, FileBrowser::LSTBuffer_size, &bytesRead); - SetACTLed(false); - f_close(&fp); - - TextParser textParser; - - textParser.SetData((char*)FileBrowser::LSTBuffer); - char* token = textParser.GetToken(true); - while (token) + //DEBUG_LOG("LST token = %s\r\n", token); + diskType = DiskImage::GetDiskImageTypeViaExtention(token); + if (diskType == DiskImage::D64 || diskType == DiskImage::G64 || diskType == DiskImage::NIB || diskType == DiskImage::NBZ) { - //DEBUG_LOG("LST token = %s\r\n", token); - diskType = DiskImage::GetDiskImageTypeViaExtention(token); - if (diskType == DiskImage::D64 || diskType == DiskImage::G64 || diskType == DiskImage::NIB || diskType == DiskImage::NBZ) + FileBrowser::BrowsableList::Entry* entry = folder.FindEntry(token); + if (entry && !(entry->filImage.fattrib & AM_DIR)) { - FileBrowser::BrowsableList::Entry* entry = currentFolder->FindEntry(token); - if (entry && !(entry->filImage.fattrib & AM_DIR)) - { - bool readOnly = (entry->filImage.fattrib & AM_RDO) != 0; - if (diskCaddy->Insert(&entry->filImage, readOnly)) - validImage = true; - } + bool readOnly = (entry->filImage.fattrib & AM_RDO) != 0; + if (diskCaddy->Insert(&entry->filImage, readOnly)) + validImage = true; } - else - { - roms->SelectROM(token); - } - token = textParser.GetToken(true); } + else + { + roms->SelectROM(token); + } + token = textParser.GetToken(true); } } } @@ -717,17 +866,25 @@ void FileBrowser::UpdateInputDiskCaddy() void FileBrowser::DisplayStatusBar() { u32 x = 0; - u32 y = screen.ScaleY(STATUS_BAR_POSITION_Y); + u32 y = screenMain->ScaleY(STATUS_BAR_POSITION_Y); char bufferOut[128]; snprintf(bufferOut, 256, "LED 0 Motor 0 Track 00.0 ATN 0 DAT 0 CLK 0"); - screen.PrintText(false, x, y, bufferOut, RGBA(0, 0, 0, 0xff), RGBA(0xff, 0xff, 0xff, 0xff)); + screenMain->PrintText(false, x, y, bufferOut, RGBA(0, 0, 0, 0xff), RGBA(0xff, 0xff, 0xff, 0xff)); } void FileBrowser::ClearScreen() { u32 bgColour = palette[VIC2_COLOUR_INDEX_BLUE]; - screen.Clear(bgColour); + screenMain->Clear(bgColour); +} + +void FileBrowser::ClearSelections() +{ + selectionsMade = false; + caddySelections.Clear(); + + folder.ClearSelections(); } void FileBrowser::ShowDeviceAndROM() @@ -735,10 +892,10 @@ void FileBrowser::ShowDeviceAndROM() char buffer[256]; u32 textColour = RGBA(0, 0, 0, 0xff); u32 bgColour = RGBA(0xff, 0xff, 0xff, 0xff); - u32 y = screen.ScaleY(STATUS_BAR_POSITION_Y); + u32 y = screenMain->ScaleY(STATUS_BAR_POSITION_Y); snprintf(buffer, 256, "Device %d %s \r\n", deviceID, roms->ROMNames[roms->currentROMIndex]); - screen.PrintText(false, 43 * 8, y, buffer, textColour, bgColour); + screenMain->PrintText(false, 43 * 8, y, buffer, textColour, bgColour); } void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForIcon) @@ -754,7 +911,7 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI char name[17] = { 0 }; unsigned char buffer[260] = { 0 }; int charIndex; - u32 fontHeight = screen.GetCBMFontHeight(); + u32 fontHeight = screenMain->GetFontHeight(); u32 x = 0; u32 y = 0; char bufferOut[128] = { 0 }; @@ -763,7 +920,7 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI u32 usedColour = palette[VIC2_COLOUR_INDEX_RED]; u32 freeColour = palette[VIC2_COLOUR_INDEX_LGREEN]; - u32 BAMOffsetX = screen.ScaleX(400); + u32 BAMOffsetX = screenMain->ScaleX(400); ClearScreen(); @@ -798,12 +955,12 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI if (!used) { snprintf(bufferOut, 128, "%c", screen2petscii(87)); - screen.PrintText(true, x, y, bufferOut, usedColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, usedColour, bgColour); } else { snprintf(bufferOut, 128, "%c", screen2petscii(81)); - screen.PrintText(true, x, y, bufferOut, freeColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, freeColour, bgColour); } x += 8; bits <<= 1; @@ -816,7 +973,7 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI for (int bit = 0; bit < DiskImage::SectorsPerTrack[bamTrack]; bit++) { snprintf(bufferOut, 128, "%c", screen2petscii(87)); - screen.PrintText(true, x, y, bufferOut, usedColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, usedColour, bgColour); x += 8; } y += fontHeight; @@ -824,10 +981,10 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI x = 0; y = 0; snprintf(bufferOut, 128, "0"); - screen.PrintText(true, x, y, bufferOut, textColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, textColour, bgColour); x = 16; snprintf(bufferOut, 128, "\"%s\" %c%c%c%c%c%c", name, buffer[162], buffer[163], buffer[164], buffer[165], buffer[166], buffer[167]); - screen.PrintText(true, x, y, bufferOut, bgColour, textColour); + screenMain->PrintText(true, x, y, bufferOut, bgColour, textColour); x = 0; y += fontHeight; @@ -876,10 +1033,10 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI //DEBUG_LOG("%d name = %s %x\r\n", blocks, name, fileType); snprintf(bufferOut, 128, "%d", blocks); - screen.PrintText(true, x, y, bufferOut, textColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, textColour, bgColour); x += 5 * 8; snprintf(bufferOut, 128, "\"%s\"", name); - screen.PrintText(true, x, y, bufferOut, textColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, textColour, bgColour); x += 19 * 8; char modifier = 0x20; if ((fileType & 0x80) == 0) @@ -887,7 +1044,7 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI else if (fileType & 0x40) modifier = screen2petscii(60); snprintf(bufferOut, 128, "%s%c", fileTypes[fileType & 7], modifier); - screen.PrintText(true, x, y, bufferOut, textColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, textColour, bgColour); y += fontHeight; } entryOffset += 32; @@ -903,7 +1060,7 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI x = 0; //DEBUG_LOG("%d blocks free\r\n", blocksFree); snprintf(bufferOut, 128, "%d BLOCKS FREE.\r\n", blocksFree); - screen.PrintText(true, x, y, bufferOut, textColour, bgColour); + screenMain->PrintText(true, x, y, bufferOut, textColour, bgColour); y += fontHeight; } @@ -914,8 +1071,8 @@ void FileBrowser::DisplayDiskInfo(DiskImage* diskImage, const char* filenameForI FILINFO filIcon; if (CheckForPNG(filenameForIcon, filIcon)) { - x = screen.ScaleX(1024) - 320; - y = screen.ScaleY(0); + x = screenMain->ScaleX(1024) - 320; + y = screenMain->ScaleY(0); DisplayPNG(filIcon, x, y); } } @@ -938,7 +1095,7 @@ void FileBrowser::AutoSelectTestImage() if (index != maxEntries) { - caddySelections.Clear(); + ClearSelections(); caddySelections.entries.push_back(*current); selectionsMade = FillCaddyWithSelections(); } diff --git a/src/FileBrowser.h b/src/FileBrowser.h index 107b74e..7e314d0 100644 --- a/src/FileBrowser.h +++ b/src/FileBrowser.h @@ -25,6 +25,7 @@ #include "DiskImage.h" #include "DiskCaddy.h" #include "ROMs.h" +#include "ScreenBase.h" #define VIC2_COLOUR_INDEX_BLACK 0 #define VIC2_COLOUR_INDEX_WHITE 1 @@ -51,51 +52,88 @@ class FileBrowser { public: + class BrowsableList; + + class BrowsableListView + { + public: + BrowsableListView(BrowsableList* list, ScreenBase* screen, u32 columns, u32 rows, u32 positionX, u32 positionY, bool lcdPgUpDown) + : list(list) + , screen(screen) + , columns(columns) + , rows(rows) + , positionX(positionX) + , positionY(positionY) + , lcdPgUpDown(lcdPgUpDown) + { + } + + void Refresh(); + bool CheckBrowseNavigation(bool pageOnly); + + BrowsableList* list; + u32 offset; + + ScreenBase* screen; + u32 columns; + u32 rows; + u32 positionX; + u32 positionY; + bool lcdPgUpDown; + }; + class BrowsableList { public: BrowsableList() : current(0) , currentIndex(0) - , offset(0) { } void Clear() { + u32 index; entries.clear(); current = 0; currentIndex = 0; - offset = 0; + for (index = 0; index < views.size(); ++index) + { + views[index].offset = 0; + } } + void AddView(ScreenBase* screen, u32 columns, u32 rows, u32 positionX, u32 positionY, bool lcdPgUpDown) + { + BrowsableListView view(this, screen, columns, rows, positionX, positionY, lcdPgUpDown); + views.push_back(view); + } + + void ClearSelections(); + struct Entry { + Entry() : caddyIndex(-1) + { + } FILINFO filImage; FILINFO filIcon; + int caddyIndex; }; Entry* FindEntry(const char* name); + void RefreshViews(); + bool CheckBrowseNavigation(); + std::vector entries; Entry* current; u32 currentIndex; - u32 offset; + + std::vector views; }; - class Folder : public BrowsableList - { - public: - Folder() - : BrowsableList() - , name(0) - { - } - - FILINFO* name; - }; - - FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons); + FileBrowser(DiskCaddy* diskCaddy, ROMs* roms, unsigned deviceID, bool displayPNGIcons, ScreenBase* screenMain, ScreenBase* screenLCD); void AutoSelectTestImage(); void DisplayRoot(); @@ -111,7 +149,7 @@ public: bool SelectionsMade() { return selectionsMade; } const char* LastSelectionName() { return lastSelectionName; } - void ClearSelections() { selectionsMade = false; caddySelections.Clear(); } + void ClearSelections(); void ShowDeviceAndROM(); @@ -119,8 +157,6 @@ public: void SetDeviceID(u8 id) { deviceID = id; } - Folder* GetCurrentFolder(); - static const long int LSTBuffer_size = 1024 * 8; static unsigned char LSTBuffer[]; @@ -138,7 +174,7 @@ private: void UpdateInputFolders(); void UpdateInputDiskCaddy(); - void RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* browsableList, int xOffset, bool showSelected = true); + //void RefeshDisplayForBrowsableList(FileBrowser::BrowsableList* browsableList, int xOffset, bool showSelected = true); bool FillCaddyWithSelections(); @@ -153,8 +189,7 @@ private: State_DiskCaddy } state; - Folder folder; - u32 maxOnScreen; + BrowsableList folder; DiskCaddy* diskCaddy; bool selectionsMade; const char* lastSelectionName; @@ -164,6 +199,9 @@ private: BrowsableList caddySelections; + ScreenBase* screenMain; + ScreenBase* screenLCD; + char PNG[FILEBROWSER_MAX_PNG_SIZE]; }; #endif diff --git a/src/InputMappings.cpp b/src/InputMappings.cpp index e09a880..6ca7164 100644 --- a/src/InputMappings.cpp +++ b/src/InputMappings.cpp @@ -32,6 +32,7 @@ unsigned InputMappings::directDiskSwapRequest = 0; //unsigned InputMappings::escapeSequenceIndex = 0; InputMappings::InputMappings() + : keyboardBrowseLCDScreen(false) { } @@ -136,6 +137,7 @@ bool InputMappings::CheckKeyboardBrowseMode() keyboardFlags = 0; + // TODO: add KEY_HOME and KEY_END if (keyboard->KeyHeld(KEY_ESC)) SetKeyboardFlag(ESC_FLAG); else if (keyboard->KeyHeld(KEY_ENTER)) @@ -149,11 +151,25 @@ bool InputMappings::CheckKeyboardBrowseMode() else if (keyboard->KeyHeld(KEY_UP)) SetKeyboardFlag(UP_FLAG); else if (keyboard->KeyHeld(KEY_PAGEUP) || keyboard->KeyHeld(KEY_LEFT)) - SetKeyboardFlag(PAGEUP_FLAG); + { + if (keyboardBrowseLCDScreen) + SetKeyboardFlag(PAGEUP_LCD_FLAG); + else + SetKeyboardFlag(PAGEUP_FLAG); + } else if (keyboard->KeyHeld(KEY_DOWN)) SetKeyboardFlag(DOWN_FLAG); else if (keyboard->KeyHeld(KEY_PAGEDOWN) || keyboard->KeyHeld(KEY_RIGHT)) - SetKeyboardFlag(PAGEDOWN_FLAG); + { + if (keyboardBrowseLCDScreen) + SetKeyboardFlag(PAGEDOWN_LCD_FLAG); + else + SetKeyboardFlag(PAGEDOWN_FLAG); + } + //else if (keyboard->KeyHeld(KEY_HOME)) + // SetKeyboardFlag(PAGEUP_LCD_FLAG); + //else if (keyboard->KeyHeld(KEY_END)) + // SetKeyboardFlag(PAGEDOWN_LCD_FLAG); else { unsigned index; diff --git a/src/InputMappings.h b/src/InputMappings.h index f17a48d..6e5f99f 100644 --- a/src/InputMappings.h +++ b/src/InputMappings.h @@ -34,6 +34,9 @@ #define INSERT_FLAG (1 << 10) #define NUMBER_FLAG (1 << 11) +#define PAGEDOWN_LCD_FLAG (1 << 12) +#define PAGEUP_LCD_FLAG (1 << 13) + class InputMappings : public Singleton { protected: @@ -42,6 +45,8 @@ protected: unsigned keyboardFlags; unsigned buttonFlags; + bool keyboardBrowseLCDScreen; + //inline void SetUartFlag(unsigned flag) { uartFlags |= flag; } //inline bool UartFlag(unsigned flag) { return (uartFlags & flag) != 0; } inline void SetKeyboardFlag(unsigned flag) { keyboardFlags |= flag; } @@ -64,6 +69,11 @@ public: buttonFlags = 0; } + void SetKeyboardBrowseLCDScreen(bool value) + { + keyboardBrowseLCDScreen = value; + } + inline bool Exit() { return KeyboardFlag(ESC_FLAG)/* | UartFlag(ESC_FLAG)*/ | ButtonFlag(ESC_FLAG); @@ -103,6 +113,10 @@ public: { return KeyboardFlag(PAGEUP_FLAG)/* | UartFlag(PAGEUP_FLAG)*/; } + inline bool BrowsePageUpLCD() + { + return KeyboardFlag(PAGEUP_LCD_FLAG); + } inline bool BrowseDown() { @@ -113,6 +127,10 @@ public: { return KeyboardFlag(PAGEDOWN_FLAG)/* | UartFlag(PAGEDOWN_FLAG)*/; } + inline bool BrowsePageDownLCD() + { + return KeyboardFlag(PAGEDOWN_LCD_FLAG); + } inline bool BrowseInsert() { diff --git a/src/SSD1306.cpp b/src/SSD1306.cpp new file mode 100644 index 0000000..19ea061 --- /dev/null +++ b/src/SSD1306.cpp @@ -0,0 +1,251 @@ +// 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 + +extern "C" +{ +#include "xga_font_data.h" +} + + +#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20 + +#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_ENTIRE_DISPLAY_ON 0xA4 +#define SSD1306_CMD_ENTIRE_DISPLAY_OFF 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 + +unsigned char frame[SSD1306_128x64_BYTES]; + +SSD1306::SSD1306(int BSCMaster, u8 address) + : BSCMaster(BSCMaster) + , address(address) +{ + RPI_I2CInit(BSCMaster, 1); + + // SSD1306 data sheet configuration flow + SendCommand(SSD1306_CMD_DISPLAY_OFF); + SendCommand(SSD1306_CMD_MULTIPLEX_RATIO); + SendCommand(0x3F); // SSD1306_LCDHEIGHT - 1 + + SendCommand(SSD1306_CMD_SET_DISPLAY_OFFSET); + SendCommand(0x00); // no Offset + + SendCommand(SSD1306_CMD_SET_START_LINE | 0x0); + + SendCommand(0xA1); // Set Segment Re-Map + + SendCommand(0xC8); // Set COM Output Scan Direction + + SendCommand(SSD1306_CMD_SET_COM_PINS); // Layout and direction + SendCommand(0x12); + + SendCommand(SSD1306_CMD_SET_CONTRAST_CONTROL); + SendCommand(0x7F); + + SendCommand(SSD1306_CMD_ENTIRE_DISPLAY_ON); + + SendCommand(SSD1306_CMD_NORMAL_DISPLAY); + SendCommand(0xD5); + SendCommand(0x80); + + SendCommand(SSD1306_CMD_SET_PRE_CHARGE_PERIOD); + SendCommand(0xF1); + + SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL); + SendCommand(0x40); + + SendCommand(SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO); + SendCommand(0x80); // upper nibble is rate, lower nibble is divisor + + SendCommand(SSD1306_ENABLE_CHARGE_PUMP); // Enable charge pump regulator + SendCommand(0x14); // external = 0x10 internal = 0x14 + + + + + SendCommand(0x00); // Set Lower Column Start Address + + SendCommand(0x10); // Set Higher Column Start Address + + SendCommand(0xB0); // Set Page Start Address for Page Addressing Mode + + SendCommand(SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE); // Set Memory Addressing Mode + SendCommand(0x00); // 00 - Horizontal Addressing Mode + + SendCommand(0x21); // Set Column Address (only for horizontal or vertical mode) + SendCommand(0x00); + SendCommand(0x7F); + + SendCommand(SSD1306_CMD_SET_PAGE_ADDRESS); + SendCommand(0x00); + SendCommand(0x07); + + SendCommand(SSD1306_CMD_DEACTIVATE_SCROLL); +} + +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() +{ + SendCommand(0x21); // column range + SendCommand(0x00); // set start to 0 + SendCommand(0x7F); // set end to 0x7F + SendCommand(0x22); // row range + SendCommand(0x00); // set start to 0 + SendCommand(0x07); // set end to 0x07 +} + +void SSD1306::MoveCursorByte(u8 row, u8 col) +{ + if (col > 127) { col = 127; } + if (row > 7) { row = 7; } + + SendCommand(0x21); // set column + SendCommand(col); // start = col + SendCommand(0x7F); // end = col max + SendCommand(0x22); // set row + SendCommand(row); // start = row + SendCommand(0x07); // end = row max +} + +void SSD1306::MoveCursorCharacter(u8 row, u8 col) +{ + if (col > 15) { col = 15; } + if (row > 7) { row = 7; } + + MoveCursorByte(row, col << 3); +} + +void SSD1306::RefreshScreen() +{ + int i; + Home(); + for (i = 0; i < SSD1306_128x64_BYTES; i++) + { + SendData(frame[i]); + } +} + +void SSD1306::ClearScreen() +{ + memset(frame, 0, sizeof(frame)); + RefreshScreen(); +} + +void SSD1306::DisplayOn() +{ + SendCommand(SSD1306_CMD_DISPLAY_ON); + ClearScreen(); +} + +void SSD1306::DisplayOff() +{ + ClearScreen(); + SendCommand(SSD1306_CMD_DISPLAY_OFF); +} + +void SSD1306::Plottext(int x, int y, char* str, bool inverse) +{ + int i; + i = 0; + while (str[i] && x < 16) + { + PlotCharacter(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(int x, int y, char c, bool inverse) +{ + unsigned char a[8], b[8]; + 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); +} + diff --git a/src/SSD1306.h b/src/SSD1306.h new file mode 100644 index 0000000..ec17f63 --- /dev/null +++ b/src/SSD1306.h @@ -0,0 +1,99 @@ +// 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 "types.h" +extern "C" +{ +#include "rpi-i2c.h" +} + +// 8 pages * (128 columns * 8 bits) +//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); + + void PlotCharacter(int x, int y, char ascii, bool inverse); + void Plottext(int x, int y, char* str, bool inverse); + + void DisplayOn(); + void DisplayOff(); + + void ClearScreen(); + void RefreshScreen(); + +protected: + void SendCommand(u8 command); + void SendData(u8 data); + + void Home(); + void MoveCursorByte(u8 row, u8 col); + void MoveCursorCharacter(u8 row, u8 col); + + unsigned char frame[SSD1306_128x64_BYTES]; + + int BSCMaster; + u8 address; +}; +#endif diff --git a/src/Screen.cpp b/src/Screen.cpp index d5564b6..b1c6314 100644 --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -148,14 +148,6 @@ void Screen::PlotPixel8(u32 pixel_offset, RGBA Colour) framebuffer[pixel_offset++] = RED(Colour); } -void Screen::ClipRect(u32& x1, u32& y1, u32& x2, u32& y2) -{ - if (x1 > width) x1 = width; - if (y1 > height) y1 = height; - if (x2 > width) x2 = width; - if (y2 > height) y2 = height; -} - void Screen::ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour) { ClipRect(x1, y1, x2, y2); @@ -195,7 +187,7 @@ void Screen::Clear(RGBA colour) ClearArea(0, 0, width, height, colour); } -u32 Screen::GetCBMFontHeight() +u32 Screen::GetFontHeight() { if (CBMFont) return 8; diff --git a/src/Screen.h b/src/Screen.h index 9655e9f..f5ceee7 100644 --- a/src/Screen.h +++ b/src/Screen.h @@ -19,28 +19,14 @@ #ifndef SCREEN_H #define SCREEN_H -#include "types.h" +#include "ScreenBase.h" -typedef u32 RGBA; - -#define RED(colour) ( (u8)(((u32)colour) & 0xFF) ) -#define GREEN(colour) ( (u8)(((u32)colour >> 8) & 0xFF) ) -#define BLUE(colour) ( (u8)(((u32)colour >> 16) & 0xFF) ) -#define ALPHA(colour) ( (u8)(((u32)colour >> 24) & 0xFF) ) - -#define RGBA(r, g, b, a) ( ((u32)((u8)(r))) | ((u32)((u8)(g)) << 8) | ((u32)((u8)(b)) << 16) | ((u32)((u8)(a)) << 24) ) - -class Screen +class Screen : public ScreenBase { public: Screen() - : opened(false) - , width(0) - , height(0) - , bpp(0) - , pitch(0) - , framebuffer(0) + : ScreenBase() { } @@ -61,37 +47,26 @@ public: void PlotImage(u32* image, int x, int y, int w, int h); - u32 Width() const { return width; } - u32 Height() const { return height; } - - u32 GetCBMFontHeight(); - float GetScaleX() const { return scaleX; } float GetScaleY() const { return scaleY; } u32 ScaleX(u32 x) { return (u32)((float)x * scaleX); } u32 ScaleY(u32 y) { return (u32)((float)y * scaleY); } + u32 GetFontHeight(); + + void SwapBuffers() {} private: typedef void (Screen::*PlotPixelFunction)(u32 pixel_offset, RGBA Colour); + PlotPixelFunction plotPixelFn; + void PlotPixel32(u32 pixel_offset, RGBA Colour); void PlotPixel24(u32 pixel_offset, RGBA Colour); void PlotPixel16(u32 pixel_offset, RGBA Colour); void PlotPixel8(u32 pixel_offset, RGBA Colour); - void ClipRect(u32& x1, u32& y1, u32& x2, u32& y2); - - PlotPixelFunction plotPixelFn; - - bool opened; - u32 width; - u32 height; - u32 bpp; - u32 pitch; - volatile u8* framebuffer; - float scaleX; float scaleY; }; diff --git a/src/ScreenBase.h b/src/ScreenBase.h new file mode 100644 index 0000000..a6c7a60 --- /dev/null +++ b/src/ScreenBase.h @@ -0,0 +1,97 @@ +// 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 SCREENBASE_H +#define SCREENBASE_H + +#include "types.h" + +typedef u32 RGBA; + +#define RED(colour) ( (u8)(((u32)colour) & 0xFF) ) +#define GREEN(colour) ( (u8)(((u32)colour >> 8) & 0xFF) ) +#define BLUE(colour) ( (u8)(((u32)colour >> 16) & 0xFF) ) +#define ALPHA(colour) ( (u8)(((u32)colour >> 24) & 0xFF) ) + +#define RGBA(r, g, b, a) ( ((u32)((u8)(r))) | ((u32)((u8)(g)) << 8) | ((u32)((u8)(b)) << 16) | ((u32)((u8)(a)) << 24) ) + +class ScreenBase +{ + +public: + ScreenBase() + : opened(false) + , width(0) + , height(0) + , bpp(0) + , pitch(0) + , framebuffer(0) + { + } + + virtual void ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour) = 0; + virtual void Clear(RGBA colour) = 0; + + virtual void ScrollArea(u32 x1, u32 y1, u32 x2, u32 y2) = 0; + + virtual void WriteChar(bool petscii, u32 x, u32 y, unsigned char c, RGBA colour) = 0; + virtual u32 PrintText(bool petscii, u32 xPos, u32 yPos, char *ptr, RGBA TxtColour = RGBA(0xff, 0xff, 0xff, 0xff), RGBA BkColour = RGBA(0, 0, 0, 0xFF), bool measureOnly = false, u32* width = 0, u32* height = 0) = 0; + virtual u32 MeasureText(bool petscii, char *ptr, u32* width = 0, u32* height = 0) = 0; + + virtual void PlotPixel(u32 x, u32 y, RGBA colour) = 0; + + virtual void PlotImage(u32* image, int x, int y, int w, int h) = 0; + + u32 Width() const { return width; } + u32 Height() const { return height; } + + virtual float GetScaleX() const { return 1; } + virtual float GetScaleY() const { return 1; } + + virtual u32 ScaleX(u32 x) { return x; } + virtual u32 ScaleY(u32 y) { return y; } + + virtual u32 GetFontHeight() = 0; + + virtual void SwapBuffers() = 0; + + bool IsMonocrome() const { return bpp == 1; } + +protected: + + //typedef void (ScreenBase::*PlotPixelFunction)(u32 pixel_offset, RGBA Colour); + + //PlotPixelFunction plotPixelFn; + + void ClipRect(u32& x1, u32& y1, u32& x2, u32& y2) + { + if (x1 > width) x1 = width; + if (y1 > height) y1 = height; + if (x2 > width) x2 = width; + if (y2 > height) y2 = height; + } + + bool opened; + u32 width; + u32 height; + u32 bpp; + u32 pitch; + u8* framebuffer; +}; + +#endif \ No newline at end of file diff --git a/src/ScreenLCD.cpp b/src/ScreenLCD.cpp new file mode 100644 index 0000000..b07deba --- /dev/null +++ b/src/ScreenLCD.cpp @@ -0,0 +1,100 @@ +// 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 "ScreenLCD.h" +#include +#include +#include +#include "debug.h" + +void ScreenLCD::Open(u32 widthDesired, u32 heightDesired, u32 colourDepth, int BSCMaster) +{ + bpp = 1; + + if (widthDesired < 128) + widthDesired = 128; + if (heightDesired < 64) + heightDesired = 64; + if (widthDesired > 128) + widthDesired = 128; + if (heightDesired > 64) + heightDesired = 64; + + width = widthDesired; + height = heightDesired; + + DEBUG_LOG("BSCMaster = %d\r\n"); + ssd1306 = new SSD1306(BSCMaster); + ssd1306->DisplayOn(); + ssd1306->Plottext(5, 1, "Pi1541", false); + ssd1306->RefreshScreen(); + + opened = true; +} + +void ScreenLCD::ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour) +{ + ClipRect(x1, y1, x2, y2); +} + +void ScreenLCD::ScrollArea(u32 x1, u32 y1, u32 x2, u32 y2) +{ +} + +void ScreenLCD::Clear(RGBA colour) +{ + ssd1306->ClearScreen(); +} + +void ScreenLCD::WriteChar(bool petscii, u32 x, u32 y, unsigned char c, RGBA colour) +{ + if (opened) + { + } +} + +void ScreenLCD::PlotPixel(u32 x, u32 y, RGBA colour) +{ +} + +void ScreenLCD::PlotImage(u32* image, int x, int y, int w, int h) +{ +} + +u32 ScreenLCD::PrintText(bool petscii, u32 x, u32 y, char *ptr, RGBA TxtColour, RGBA BkColour, bool measureOnly, u32* width, u32* height) +{ + int len = 0; + ssd1306->Plottext(x >> 3, y >> 4, ptr, (BkColour & 0xffffff) != 0); + return len; +} + +u32 ScreenLCD::MeasureText(bool petscii, char *ptr, u32* width, u32* height) +{ + return PrintText(petscii, 0, 0, ptr, 0, 0, true, width, height); +} + +u32 ScreenLCD::GetFontHeight() +{ + return 16; +} + +void ScreenLCD::SwapBuffers() +{ + ssd1306->RefreshScreen(); +} + diff --git a/src/ScreenLCD.h b/src/ScreenLCD.h new file mode 100644 index 0000000..598d9f9 --- /dev/null +++ b/src/ScreenLCD.h @@ -0,0 +1,58 @@ +// 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 SCREENLCD_H +#define SCREENLCD_H + +#include "ScreenBase.h" +#include "SSD1306.h" + +class ScreenLCD : public ScreenBase +{ + +public: + ScreenLCD() + : ScreenBase() + , ssd1306(0) + { + } + + void Open(u32 width, u32 height, u32 colourDepth, int BSCMaster); + + void ClearArea(u32 x1, u32 y1, u32 x2, u32 y2, RGBA colour); + void Clear(RGBA colour); + + void ScrollArea(u32 x1, u32 y1, u32 x2, u32 y2); + + void WriteChar(bool petscii, u32 x, u32 y, unsigned char c, RGBA colour); + u32 PrintText(bool petscii, u32 xPos, u32 yPos, char *ptr, RGBA TxtColour = RGBA(0xff, 0xff, 0xff, 0xff), RGBA BkColour = RGBA(0, 0, 0, 0xFF), bool measureOnly = false, u32* width = 0, u32* height = 0); + u32 MeasureText(bool petscii, char *ptr, u32* width = 0, u32* height = 0); + + void PlotPixel(u32 x, u32 y, RGBA colour); + + void PlotImage(u32* image, int x, int y, int w, int h); + + u32 GetFontHeight(); + + void SwapBuffers(); + +private: + SSD1306* ssd1306 = 0; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 39a79d7..c4c8e2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,12 +18,14 @@ #include "defs.h" #include +#include #include "Timer.h" #include "ROMs.h" #include "stb_image.h" extern "C" { #include "rpi-aux.h" +#include "rpi-i2c.h" #include "rpi-gpio.h" #include "startup.h" #include "cache.h" @@ -37,12 +39,13 @@ extern "C" #include "diskio.h" #include "Pi1541.h" #include "FileBrowser.h" +#include "ScreenLCD.h" #include "logo.h" #include "sample.h" unsigned versionMajor = 1; -unsigned versionMinor = 2; +unsigned versionMinor = 3; // When the emulated CPU starts we execute the first million odd cycles in non-real-time (ie as fast as possible so the emulated 1541 becomes responsive to CBM-Browser asap) // During these cycles the CPU is executing the ROM self test routines (these do not need to be cycle accurate) @@ -84,6 +87,7 @@ DiskCaddy diskCaddy; Pi1541 pi1541; CEMMCDevice m_EMMC; Screen screen; +ScreenLCD* screenLCD = 0; Options options; const char* fileBrowserSelectedName; u8 deviceID = 8; @@ -570,8 +574,8 @@ void emulator() roms.lastManualSelectedROMIndex = 0; - diskCaddy.SetScreen(&screen); - fileBrowser = new FileBrowser(&diskCaddy, &roms, deviceID, options.DisplayPNGIcons()); + diskCaddy.SetScreen(&screen, screenLCD); + fileBrowser = new FileBrowser(&diskCaddy, &roms, deviceID, options.DisplayPNGIcons(), &screen, screenLCD); fileBrowser->DisplayRoot(); pi1541.Initialise(); @@ -598,6 +602,7 @@ void emulator() selectedViaIECCommands = false; inputMappings->Reset(); + inputMappings->SetKeyboardBrowseLCDScreen(screenLCD && options.KeyboardBrowseLCDScreen()); fileBrowser->ShowDeviceAndROM(); @@ -1145,6 +1150,15 @@ extern "C" CheckOptions(); + if (strcasecmp(options.GetLCDName(), "ssd1306_128x64") == 0) + { + screenLCD = new ScreenLCD(); + screenLCD->Open(128, 64, 1, options.SplitIECLines() ? 1 : 0); + } + else + { + } + IEC_Bus::SetSplitIECLines(options.SplitIECLines()); IEC_Bus::SetInvertIECInputs(options.InvertIECInputs()); IEC_Bus::SetInvertIECOutputs(options.InvertIECOutputs()); diff --git a/src/options.cpp b/src/options.cpp index 627765d..c7b2af8 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -135,6 +135,7 @@ Options::Options(void) , ignoreReset(0) , screenWidth(1024) , screenHeight(768) + , keyboardBrowseLCDScreen(0) { strcpy(ROMFontName, "chargen"); starFileName[0] = 0; @@ -186,10 +187,15 @@ void Options::Process(char* buffer) ELSE_CHECK_DECIMAL_OPTION(ignoreReset) ELSE_CHECK_DECIMAL_OPTION(screenWidth) ELSE_CHECK_DECIMAL_OPTION(screenHeight) + ELSE_CHECK_DECIMAL_OPTION(keyboardBrowseLCDScreen) else if ((strcasecmp(pOption, "StarFileName") == 0)) { strncpy(starFileName, pValue, 255); } + else if ((strcasecmp(pOption, "LCDName") == 0)) + { + strncpy(LCDName, pValue, 255); + } else if ((strcasecmp(pOption, "ROM") == 0) || (strcasecmp(pOption, "ROM1") == 0)) { strncpy(ROMName, pValue, 255); diff --git a/src/options.h b/src/options.h index 58d9ce3..f1d1ea9 100644 --- a/src/options.h +++ b/src/options.h @@ -66,6 +66,12 @@ public: inline unsigned int ScreenWidth() const { return screenWidth; } inline unsigned int ScreenHeight() const { return screenHeight; } + // Page up and down will jump a different amount based on the maximum number rows displayed. + // Perhaps we should use some keyboard modifier to the the other screen? + inline unsigned int KeyboardBrowseLCDScreen() const { return keyboardBrowseLCDScreen; } + + const char* GetLCDName() const { return LCDName; } + static unsigned GetDecimal(char* pString); private: @@ -87,7 +93,10 @@ private: unsigned int screenWidth; unsigned int screenHeight; + unsigned int keyboardBrowseLCDScreen; + char starFileName[256]; + char LCDName[256]; char ROMFontName[256]; char ROMName[256]; diff --git a/src/rpi-i2c.c b/src/rpi-i2c.c new file mode 100644 index 0000000..4072ab4 --- /dev/null +++ b/src/rpi-i2c.c @@ -0,0 +1,186 @@ +#include +#include "rpi-aux.h" +#include "rpi-base.h" +#include "rpi-gpio.h" +#include "startup.h" +#include "stdlib.h" + +#include "rpiHardware.h" + +/* Define the system clock frequency in MHz for the baud rate calculation. +This is clearly defined on the BCM2835 datasheet errata page: +http://elinux.org/BCM2835_datasheet_errata */ +#define FALLBACK_SYS_FREQ 250000000 + +#define I2C_BSC0_BASE (PERIPHERAL_BASE + 0x205000) +#define I2C_BSC1_BASE (PERIPHERAL_BASE + 0x804000) + +#define GetBaseAddress(BSCMaster) (BSCMaster == 0 ? I2C_BSC0_BASE : I2C_BSC1_BASE) + +#define I2C_BSC_C 0x00 // Control register +#define I2C_BSC_S 0x04 // Statis register +#define I2C_BSC_DLEN 0x08 +#define I2C_BSC_A 0x0C // Slave address +#define I2C_BSC_FIFO 0x10 +#define I2C_BSC_DIV 0x14 +#define I2C_BSC_DEL 0x18 +#define I2C_BSC_CLKT 0x1C + +#define CONTROL_BIT_READ (1 << 0) // READ Read Transfer +#define CONTROL_BIT_CLEAR0 (1 << 4) // CLEAR FIFO 00 = No action. x1 = Clear FIFO. One shot operation. 1x = Clear FIFO. +#define CONTROL_BIT_CLEAR1 (1 << 5) +#define CONTROL_BIT_ST (1 << 7) // ST Start Transfer +#define CONTROL_BIT_INTD (1 << 8) // INTD Interrupt on DONE +#define CONTROL_BIT_INTT (1 << 9) // INTT Interrupt on TX +#define CONTROL_BIT_INTR (1 << 10) // INTR Interrupt on RX +#define CONTROL_BIT_I2CEN (1 << 15) // I2C Enable + + +#define STATUS_BIT_TA (1 << 0) // Transfer Active +#define STATUS_BIT_DONE (1 << 1) // Done +#define STATUS_BIT_TXW (1 << 2) // FIFO needs Writing (full) +#define STATUS_BIT_RXR (1 << 3) // FIFO needs Reading (full) +#define STATUS_BIT_TXD (1 << 4) // FIFO can accept Data +#define STATUS_BIT_RXD (1 << 5) // FIFO contains Data +#define STATUS_BIT_TXE (1 << 6) // FIFO Empty +#define STATUS_BIT_RXF (1 << 7) // FIFO Full +#define STATUS_BIT_ERR (1 << 8) // ERR ACK Error +#define STATUS_BIT_CLKT (1 << 9) // CLKT Clock Stretch Timeout + +#define FIFO_SIZE 16 + +void RPI_I2CSetClock(int BSCMaster, int clock_freq) +{ + _data_memory_barrier(); + + int sys_freq = get_clock_rate(CORE_CLK_ID); + + if (!sys_freq) { + sys_freq = FALLBACK_SYS_FREQ; + } + + unsigned short divider = (unsigned short)(sys_freq / clock_freq); + write32(GetBaseAddress(BSCMaster) + I2C_BSC_DIV, divider); + + _data_memory_barrier(); +} + +void RPI_I2CInit(int BSCMaster, int fast) +{ + if (BSCMaster == 0) + { + RPI_SetGpioPinFunction(RPI_GPIO0, FS_ALT0); + RPI_SetGpioPinFunction(RPI_GPIO1, FS_ALT0); + } + else + { + RPI_SetGpioPinFunction(RPI_GPIO2, FS_ALT0); + RPI_SetGpioPinFunction(RPI_GPIO3, FS_ALT0); + } + + RPI_I2CSetClock(BSCMaster, fast != 0 ? 400000 : 100000); +} + +int RPI_I2CRead(int BSCMaster, unsigned char slaveAddress, void* buffer, unsigned count) +{ + int success = 0; + if (slaveAddress < 0x80) + { + if (count > 0) + { + unsigned baseAddress = GetBaseAddress(BSCMaster); + unsigned char* data = (unsigned char*)buffer; + + write32(baseAddress + I2C_BSC_A, slaveAddress); + write32(baseAddress + I2C_BSC_C, CONTROL_BIT_CLEAR1); + write32(baseAddress + I2C_BSC_S, STATUS_BIT_CLKT | STATUS_BIT_ERR | STATUS_BIT_DONE); + write32(baseAddress + I2C_BSC_DLEN, count); + write32(baseAddress + I2C_BSC_C, CONTROL_BIT_I2CEN | CONTROL_BIT_ST | CONTROL_BIT_READ); + + while (!(read32(baseAddress + I2C_BSC_S) & STATUS_BIT_DONE)) + { + while (read32(baseAddress + I2C_BSC_S) & STATUS_BIT_RXD) + { + *data++ = (unsigned char)read32(baseAddress + I2C_BSC_FIFO); + count--; + } + } + + while (count > 0 && (read32(baseAddress + I2C_BSC_S) & STATUS_BIT_RXD)) + { + *data++ = (unsigned char)read32(baseAddress + I2C_BSC_FIFO); + count--; + } + + unsigned status = read32(baseAddress + I2C_BSC_S); + if (status & STATUS_BIT_ERR) + { + write32(baseAddress + I2C_BSC_S, STATUS_BIT_ERR); + } + else if ((status & STATUS_BIT_CLKT) == 0 && count == 0) + { + success = 1; + } + + write32(baseAddress + I2C_BSC_S, STATUS_BIT_DONE); + } + else + { + success = 1; + } + } + return success; +} + +int RPI_I2CWrite(int BSCMaster, unsigned char slaveAddress, void* buffer, unsigned count) +{ + int success = 0; + if (slaveAddress < 0x80) + { + if (count > 0) + { + unsigned baseAddress = GetBaseAddress(BSCMaster); + unsigned char* data = (unsigned char*)buffer; + + write32(baseAddress + I2C_BSC_A, slaveAddress); + write32(baseAddress + I2C_BSC_C, CONTROL_BIT_CLEAR1); + write32(baseAddress + I2C_BSC_S, STATUS_BIT_CLKT | STATUS_BIT_ERR | STATUS_BIT_DONE); + write32(baseAddress + I2C_BSC_DLEN, count); + + for (unsigned i = 0; count > 0 && i < FIFO_SIZE; i++) + { + write32(baseAddress + I2C_BSC_FIFO, *data++); + count--; + } + + write32(baseAddress + I2C_BSC_C, CONTROL_BIT_I2CEN | CONTROL_BIT_ST); + + while (!(read32(baseAddress + I2C_BSC_S) & STATUS_BIT_DONE)) + { + while (count > 0 && (read32(baseAddress + I2C_BSC_S) & STATUS_BIT_TXD)) + { + write32(baseAddress + I2C_BSC_FIFO, *data++); + count--; + } + } + + unsigned status = read32(baseAddress + I2C_BSC_S); + if (status & STATUS_BIT_ERR) + { + write32(baseAddress + I2C_BSC_S, STATUS_BIT_ERR); + } + else if ((status & STATUS_BIT_CLKT) == 0 && count == 0) + { + success = 1; + } + + write32(baseAddress + I2C_BSC_S, STATUS_BIT_DONE); + } + else + { + success = 1; + } + } + //DEBUG_LOG("I2C Write %d %d\r\n", count, success); + return success; +} diff --git a/src/rpi-i2c.h b/src/rpi-i2c.h new file mode 100644 index 0000000..ac7a81a --- /dev/null +++ b/src/rpi-i2c.h @@ -0,0 +1,11 @@ +#ifndef RPI_AUX_H +#define RPI_AUX_H + +#include "rpi-base.h" + +extern void RPI_I2CInit(int BSCMaster, int fast); +extern void RPI_I2CSetClock(int BSCMaster, int clock_freq); +extern int RPI_I2CRead(int BSCMaster, unsigned char slaveAddress, void* buffer, unsigned count); +extern int RPI_I2CWrite(int BSCMaster, unsigned char slaveAddress, void* buffer, unsigned count); + +#endif