From 721ab56e3a54c2ce27cc02b99b57327fef6f01f1 Mon Sep 17 00:00:00 2001 From: frntc <48584231+frntc@users.noreply.github.com> Date: Mon, 28 Feb 2022 22:16:38 +0100 Subject: [PATCH] Add files via upload better SID-emulation, bug fixes, HDMI output --- Source/Firmware/264screen.cpp | 2263 ++++++++++++------------ Source/Firmware/kernel_menu264.cpp | 1476 ++++++++-------- Source/Firmware/kernel_menu264.h | 292 ++-- Source/Firmware/kernel_sid264.cpp | 2618 ++++++++++++++-------------- Source/Firmware/kernel_sid264.h | 398 ++--- Source/Firmware/sound.cpp | 518 +++--- Source/Firmware/sound.h | 137 +- Source/Firmware/tft_sid_vis.h | 519 +++--- 8 files changed, 4181 insertions(+), 4040 deletions(-) diff --git a/Source/Firmware/264screen.cpp b/Source/Firmware/264screen.cpp index 5bda39b5..811857da 100644 --- a/Source/Firmware/264screen.cpp +++ b/Source/Firmware/264screen.cpp @@ -1,1107 +1,1156 @@ -/* - _________.__ .___ __ .__ __ ________ ________ _____ - / _____/|__| __| _/____ | | _|__| ____ | | __ \_____ \/ _____/ / | | - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / ____/ __ \ / | |_ - / \| / /_/ \ ___/| <| \ \___| < / \ |__\ \/ ^ / -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \_______ \_____ /\____ | - \/ \/ \/ \/ \/ \/ \/ \/ |__| - - 264screen.cpp - - RasPiC64 - A framework for interfacing the C64 (and C16/+4) and a Raspberry Pi 3B/3B+ - - menu/screen code - Copyright (c) 2019, 2020 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ - -// this file contains unelegant code for generating the menu screens -// and handling key presses forwarded from the C64 - -#include "linux/kernel.h" -#include "c64screen.h" -#include "lowlevel_arm64.h" -#include "dirscan.h" -#include "264config.h" -#include "crt.h" -#include "kernel_menu264.h" - -#define KEY_F1 133 -#define KEY_F4 138 -#define KEY_F2 137 -#define KEY_F5 135 -#define KEY_F3 134 -#define KEY_F6 139 -#define KEY_HELP 140 -#define KEY_F7 136 - -const int VK_LEFT = 157; -const int VK_RIGHT = 29; -const int VK_UP = 145; -const int VK_DOWN = 17; -const int VK_HOME = 19; -const int VK_S = 83; - -const int VK_ESC = 95; -const int VK_DEL = 20; -const int VK_RETURN = 13; -const int VK_SHIFT_RETURN = 141; -const int VK_MOUNT = 205; // SHIFT-M -const int VK_MOUNT_START = 77; // M - -extern CLogger *logger; - -// todo -extern u32 prgSizeLaunch; -extern unsigned char prgDataLaunch[ 65536 ]; - -static const char DRIVE[] = "SD:"; -static const char SETTINGS_FILE[] = "SD:C16/special.cfg"; - -u8 c64screen[ 40 * 25 + 1024 * 4 ]; -u8 c64color[ 40 * 25 + 1024 * 4 ]; - -char *errorMsg = NULL; - -char errorMessages[5][41] = { -// 1234567890123456789012345678901234567890 - " NO ERROR ", - " ERROR: UNKNOWN/UNSUPPORTED .CRT TYPE ", - " ERROR: NO .CRT-FILE ", - " ERROR READING FILE ", - " SETTINGS SAVED " -}; - - -#define MENU_MAIN 0x00 -#define MENU_BROWSER 0x01 -#define MENU_ERROR 0x02 -#define MENU_CONFIG 0x03 -u32 menuScreen = 0, - previousMenuScreen = 0; -u32 updateMenu = 1; -u32 typeInName = 0; -u32 typeCurPos = 0; - -int cursorPos = 0; -int scrollPos = 0; -int lastLine; -int lastRolled = -1; -int lastScrolled = -1; -int lastSubIndex = -1; -int subGeoRAM = 0; -int subSID = 0; -int subHasKernal = -1; -int subHasLaunch = -1; - -void clearC64() -{ - memset( c64screen, ' ', 40 * 25 ); - memset( c64color, 0, 40 * 25 ); -} - -u8 PETSCII2ScreenCode( u8 c ) -{ - if ( c < 32 ) return c + 128; - if ( c < 64 ) return c; - if ( c < 96 ) return c - 64; - if ( c < 128 ) return c - 32; - if ( c < 160 ) return c + 64; - if ( c < 192 ) return c - 64; - return c - 128; -} - -void printC64( u32 x, u32 y, const char *t, u8 color, u8 flag, u32 convert, u32 maxL ) -{ - u32 l = min( strlen( t ), maxL ); - - for ( u32 i = 0; i < l; i++ ) - { - u32 c = t[ i ], c2 = c; - - // screen code conversion - if ( convert == 1 ) - { - c2 = PETSCII2ScreenCode( c ); - } else - { - if ( convert == 2 && c >= 'a' && c <= 'z' ) - c = c + 'A' - 'a'; - if ( convert == 3 && c >= 'A' && c <= 'Z' ) - c = c + 'a' - 'A'; - if ( ( c >= 'a' ) && ( c <= 'z' ) ) - c2 = c + 1 - 'a'; - } - - c64screen[ x + y * 40 + i ] = c2 | flag; - c64color[ x + y * 40 + i ] = color; - } -} - -u32 extraMsg = 0; - -int scanFileTree( u32 cursorPos, u32 scrollPos ) -{ - extraMsg = 0; - - u32 lines = 0; - s32 idx = scrollPos; - while ( lines < DISPLAY_LINES && idx < nDirEntries ) - { - if ( idx >= nDirEntries ) - break; - - if ( (u32)idx == cursorPos && dir[ idx ].f & DIR_D64_FILE ) - extraMsg = 1; - - if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) - { - if ( !(dir[ idx ].f & DIR_UNROLLED) ) - idx = dir[ idx ].next; else - idx ++; - } else - idx ++; - - lines ++; - } - return idx; -} - - -int printFileTree( s32 cursorPos, s32 scrollPos ) -{ - s32 lines = 0; - s32 idx = scrollPos; - s32 lastVisible = 0; - while ( lines < DISPLAY_LINES && idx < nDirEntries ) - { - if ( idx >= nDirEntries ) - break; - - u32 convert = 3; - u8 color = skinValues.SKIN_TEXT_BROWSER; - - if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) - color = skinValues.SKIN_TEXT_BROWSER_DIRECTORY; - - if ( dir[ idx ].f & DIR_FILE_IN_D64 ) - convert = 1; - - if ( idx == cursorPos ) - color = skinValues.SKIN_TEXT_BROWSER_CURRENT; - - char temp[1024], t2[ 1024 ] = {0}; - memset( t2, 0, 1024 ); - int leading = 0; - if( dir[ idx ].level > 0 ) - { - for ( u32 j = 0; j < dir[ idx ].level - 1; j++ ) - { - strcat( t2, " " ); - leading ++; - } - if ( convert != 3 ) - t2[ dir[ idx ].level - 1 ] = 93+32; else - t2[ dir[ idx ].level - 1 ] = 93; - leading ++; - } - - if ( (dir[ idx ].parent != 0xffffffff && dir[ dir[ idx ].parent ].f & DIR_D64_FILE && dir[ idx ].parent == (u32)(idx - 1) ) ) - { - if ( dir[ idx ].size > 0 ) - sprintf( temp, "%s%s ", t2, dir[ idx ].name ); else - sprintf( temp, "%s%s", t2, dir[ idx ].name ); - - if ( strlen( temp ) > 34 ) temp[ 35 ] = 0; - - if ( idx == cursorPos ) - { - printC64( 2, lines + 3, temp, color, 0x80, convert ); - } else - { - printC64( 2, lines + 3, t2, color, 0x00, convert ); - printC64( 2 + leading, lines + 3, (char*)dir[ idx ].name, color, 0x80, convert ); - } - } else - { - if ( dir[ idx ].size > 0 ) - sprintf( temp, "%s%s ", t2, dir[ idx ].name ); else - sprintf( temp, "%s%s", t2, dir[ idx ].name ); - if ( strlen( temp ) > 34 ) temp[ 35 ] = 0; - - printC64( 2, lines + 3, temp, color, (idx == cursorPos) ? 0x80 : 0, convert ); - - if ( dir[ idx ].size > 0 ) - { - if ( convert == 1 ) - { - // file in D64 - sprintf( temp, " %3dK", dir[ idx ].size / 1024 ); - } else - { - if ( dir[ idx ].size / 1024 > 999 ) - sprintf( temp, " %1.1fm", (float)dir[ idx ].size / (1024 * 1024) ); else - sprintf( temp, " %3dk", dir[ idx ].size / 1024 ); - } - - printC64( 32, lines + 3, temp, color, (idx == cursorPos) ? 0x80 : 0, convert ); - } - } - lastVisible = idx; - - if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) - { - if ( !(dir[ idx ].f & DIR_UNROLLED) ) - idx = dir[ idx ].next; else - idx ++; - } else - idx ++; - - lines ++; - } - - return lastVisible; -} - - -void printBrowserScreen() -{ - clearC64(); - - //printC64(0,0, "0123456789012345678901234567890123456789", 15, 0 ); - printC64( 0, 1, " .- sidekick264 browser -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); - printC64( 0, 23, " F1/F2 Page Up/Down, HELP Back to Menu ", skinValues.SKIN_BROWSER_TEXT_HEADER, 0, 3 ); - printC64( 2, 23, "F1", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); - printC64( 5, 23, "F2", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); - printC64( 22, 23, "HELP", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); - - if ( subGeoRAM ) - { - printC64( 0, 24, " choose PRG w/ NeoRAM to start ", skinValues.SKIN_BROWSER_TEXT_FOOTER, 0, 3 ); - printC64( 13, 24, "PRG w/ NeoRAM", skinValues.SKIN_BROWSER_TEXT_FOOTER_HIGHLIGHTED, 0, 3 ); - } else - if ( subSID ) - { - printC64( 0, 24, " choose PRG w/ SID+FM to start ", skinValues.SKIN_BROWSER_TEXT_FOOTER, 0, 3 ); - printC64( 13, 24, "PRG w/ SID+FM", skinValues.SKIN_BROWSER_TEXT_FOOTER_HIGHLIGHTED, 0, 3 ); - } - - lastLine = printFileTree( cursorPos, scrollPos ); - - // scroll bar - int t = scrollPos * DISPLAY_LINES / nDirEntries; - int b = lastLine * DISPLAY_LINES / nDirEntries; - - // bar on the right - for ( int i = 0; i < DISPLAY_LINES; i++ ) - { - char c = 94; - if ( t <= i && i <= b ) - c = 96 + 128 - 64; - c64screen[ 38 + ( i + 3 ) * 40 ] = c; - c64color[ 38 + ( i + 3 ) * 40 ] = 1; - } - - c64screen[ 1000 ] = ((0x6000 >> 8) & 0xFC); - c64screen[ 1001 ] = skinValues.SKIN_BROWSER_BACKGROUND_COLOR; - c64screen[ 1002 ] = skinValues.SKIN_BROWSER_BORDER_COLOR; - - /*startInjectCode(); - // charset address - injectPOKE( 0xff13, ((0x6000 >> 8) & 0xFC) ); - injectPOKE( 0xff15, skinValues.SKIN_BROWSER_BACKGROUND_COLOR ); - injectPOKE( 0xff19, skinValues.SKIN_BROWSER_BORDER_COLOR );*/ -} - -void resetMenuState( int keep = 0 ) -{ - if ( !( keep & 1 ) ) subGeoRAM = 0; - if ( !( keep & 2 ) ) subSID = 0; - subHasKernal = -1; - subHasLaunch = -1; -} - -int getMainMenuSelection( int key, char **FILE, char **FILE2, int *addIdx ) -{ - *FILE = NULL; - - if ( key >= 'a' && key <= 'z' ) - key = key + 'A' - 'a'; - - - if ( key == KEY_F7 ) { resetMenuState(0); return 1;/* Exit */ } else - if ( key == KEY_HELP ) { resetMenuState(3); return 2;/* Browser */ } else - if ( key == KEY_F1 ) { resetMenuState(1); return 3;/* GEORAM */ } else - if ( key == KEY_F2 ) { resetMenuState(2); return 4;/* SID */ } else - { - if ( key >= 'A' && key < 'A' + menuItems[ 1 ] ) // PRG - { - int i = key - 'A'; - *FILE = menuFile[ 1 ][ i ]; - //logger->Write( "RaspiMenu", LogNotice, "getselection: %s -> %s\n", menuText[ 1 ][ i ], menuFile[ 1 ][ i ] ); - return 6; - } else - if ( key >= '1' && key < '1' + menuItems[ 2 ] ) // CRT - { - resetMenuState(); - int i = key - '1'; - *FILE = menuFile[ 2 ][ i ]; - *FILE2 = menuFile2[ 2 ][ i ]; - //logger->Write( "RaspiMenu", LogNotice, "%s -> %s + %s\n", menuText[ 2 ][ i ], menuFile[ 2 ][ i ], menuFile2[ 2 ][ i ] ); - return 5; - } - } - - return 0; -} - -extern void deactivateCart(); - -#define MAX_SETTINGS 16 -u32 curSettingsLine = 0; -s32 rangeSettings[ MAX_SETTINGS ] = { 4, 10, 3, 2, 16, 15, 4, 2, 16, 15, 2, 2, 16, 15, 16, 16 }; -s32 settings[ MAX_SETTINGS ] = { 0, 0, 0, 0, 15, 0, 0, 0, 15, 14, 0, 0, 15, 7, 15, 15 }; -u8 geoRAM_SlotNames[ 10 ][ 21 ]; - -void writeSettingsFile() -{ - char cfg[ 16384 ]; - u32 cfgBytes = 0; - memset( cfg, 0, 16384 ); - cfgBytes = sizeof( s32 ) * MAX_SETTINGS; - memcpy( cfg, settings, cfgBytes ); - memcpy( &cfg[ cfgBytes ], geoRAM_SlotNames, sizeof( u8 ) * 10 * 21 ); - cfgBytes += sizeof( u8 ) * 10 * 21; - - writeFile( logger, DRIVE, SETTINGS_FILE, (u8*)cfg, cfgBytes ); -} - - -void readSettingsFile() -{ - char cfg[ 16384 ]; - u32 cfgBytes; - memset( cfg, 0, 16384 ); - - memset( settings, 0, sizeof( s32 ) * MAX_SETTINGS ); - memset( geoRAM_SlotNames, 32, sizeof( u8 ) * 10 * 21 ); - - if ( !readFile( logger, DRIVE, SETTINGS_FILE, (u8*)cfg, &cfgBytes ) ) - writeSettingsFile(); - - memcpy( settings, cfg, sizeof( s32 ) * MAX_SETTINGS ); - memcpy( geoRAM_SlotNames, &cfg[ sizeof( s32 ) * MAX_SETTINGS ], sizeof( u8 ) * 10 * 21 ); -} - -void applySIDSettings() -{ - // how elegant... - extern void setSIDConfiguration( u32 mode, u32 sid1, u32 sid2, u32 sid2addr, u32 rr, u32 addr, u32 exp, s32 v1, s32 p1, s32 v2, s32 p2, s32 v3, s32 p3, s32 digiblasterVol, s32 sidfreq, s32 tedVol ); - setSIDConfiguration( 0, settings[2], settings[6], settings[7]-1, settings[3], settings[7], settings[11], - settings[4], settings[5], settings[8], settings[9], settings[12], settings[13], settings[14], settings[10], settings[15] ); -} - - -// ugly, hard-coded handling of UI -void handleC64( int k, u32 *launchKernel, char *FILENAME, char *filenameKernal ) -{ - char *filename, *filename2; - - if ( menuScreen == MENU_MAIN ) - { - if ( k == VK_SHIFT_RETURN ) - { - *launchKernel = 255; // reboot - return; - } - - if ( k == KEY_HELP ) - { - menuScreen = MENU_BROWSER; - handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); - return; - } - if ( k == KEY_F3 ) - { - menuScreen = MENU_CONFIG; - handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); - return; - } - - int temp; - int r = getMainMenuSelection( k, &filename, &filename2, &temp ); - - if ( subSID == 1 ) - { - if ( k == 27 ) - { - resetMenuState(); - subHasLaunch = -1; - subSID = 0; - return; - } - // start - if ( ( r == 4 ) || ( k == 13 ) ) - { - FILENAME[ 0 ] = 0; - *launchKernel = 8; - return; - } - } - - if ( subGeoRAM == 1 ) - { - if ( k == 27 ) - { - resetMenuState(); - subHasKernal = -1; - subHasLaunch = -1; - subGeoRAM = 0; - return; - } - // - if ( r == 7 ) // kernal selected - { - strcpy( filenameKernal, filename ); - return; - } - // start - if ( ( ( r == 3 ) || ( k == 13 ) ) ) - { - *launchKernel = 3; - return; - } - } - - switch ( r ) - { - case 1: // exit to basic - deactivateCart(); - return; - case 2: // Browser - break; - case 3: // GeoRAM - subGeoRAM = 1; - subSID = 0; - return; - case 4: // SID+FM - subSID = 1; - subGeoRAM = 0; - return; - case 5: // .CRT file - *launchKernel = 5; - errorMsg = NULL; - strncpy( &FILENAME[0], filename, 2047 ); - if ( strcmp( filename2, "none" ) == 0 ) - FILENAME[2048] = 0; else - strncpy( &FILENAME[2048], filename2, 2047 ); - return; - case 6: // .PRG file - strcpy( FILENAME, filename ); - *launchKernel = 4; - return; - } - } else - if ( menuScreen == MENU_BROWSER ) - { - // browser screen - if ( k == KEY_HELP ) - { - menuScreen = MENU_MAIN; - handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); - return; - } - - int rep = 1; - if ( k == KEY_F2 ) { k = 17; rep = DISPLAY_LINES - 1; } - if ( k == KEY_F1 ) { k = 145; rep = DISPLAY_LINES - 1; } - - lastLine = scanFileTree( cursorPos, scrollPos ); - - for ( int i = 0; i < rep; i++ ) - { - // left - if ( k == VK_LEFT || - ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && k == 13 && (dir[ cursorPos ].f & DIR_UNROLLED ) ) ) - { - typeInName = 0; - if ( dir[ cursorPos ].parent != 0xffffffff ) - { - lastSubIndex = cursorPos; - lastRolled = dir[ cursorPos ].parent; - // fix - if (! ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && - ( dir[ cursorPos ].f & DIR_UNROLLED ) ) ) - cursorPos = dir[ cursorPos ].parent; - // fix 2 - lastScrolled = scrollPos; - } - if ( !( dir[ cursorPos ].f & DIR_UNROLLED ) ) - k = VK_UP; - if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) - dir[ cursorPos ].f &= ~DIR_UNROLLED; - k = 0; - } else - // right - if ( k == VK_RIGHT || - ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && k == 13 && !(dir[ cursorPos ].f & DIR_UNROLLED ) ) ) - { - typeInName = 0; - if ( (dir[ cursorPos ].f & DIR_DIRECTORY) && !(dir[ cursorPos ].f & DIR_SCANNED) ) - { - // build path - char path[ 8192 ] = {0}; - u32 n = 0, c = cursorPos; - u32 nodes[ 256 ]; - - nodes[ n ++ ] = c; - - while ( dir[ c ].parent != 0xffffffff ) - { - c = nodes[ n ++ ] = dir[ c ].parent; - } - - sprintf( path, "SD:" ); - - for ( u32 i = n - 1; i >= 1; i -- ) - { - if ( i != n - 1 ) - strcat( path, "//" ); - strcat( path, (char*)dir[ nodes[i] ].name ); - } - strcat( path, "//" ); - - extern void insertDirectoryContents( int node, char *basePath, int takeAll ); - insertDirectoryContents( nodes[ 0 ], path, dir[ cursorPos ].f & DIR_LISTALL ); - } - - if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) - dir[ cursorPos ].f |= DIR_UNROLLED; - - if ( cursorPos == lastRolled ) - { - scrollPos = lastScrolled; - cursorPos = lastSubIndex; - } else - if ( cursorPos < nDirEntries - 1 ) - { - cursorPos ++; - lastLine = scanFileTree( cursorPos, scrollPos ); - } - lastRolled = -1; - - k = 0; - } else - // down - if ( k == VK_DOWN ) - { - typeInName = 0; - int oldPos = cursorPos; - if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) - { - if ( dir[ cursorPos ].f & DIR_UNROLLED ) - cursorPos ++; else - cursorPos = dir[ cursorPos ].next; - } else - cursorPos ++; - if ( cursorPos >= nDirEntries ) - cursorPos = oldPos; - } else - // up - if ( k == VK_UP ) - { - typeInName = 0; - cursorPos --; - if ( cursorPos < 0 ) cursorPos = 0; - - // current item has parent(s) -> check if they are unrolled - int c = cursorPos; - while ( c >= 0 && dir[ c ].parent != 0xffffffff ) - { - c = dir[ c ].parent; - if ( c >= 0 && c < nDirEntries && ( dir[ c ].f & DIR_DIRECTORY || dir[ c ].f & DIR_D64_FILE ) && !(dir[ c ].f & DIR_UNROLLED) ) - cursorPos = c; - } - } - - //if ( cursorPos < 0 ) cursorPos = 0; - //if ( cursorPos >= nDirEntries ) cursorPos = nDirEntries - 1; - - // scrollPos decrease - if ( cursorPos < scrollPos ) - { - scrollPos = cursorPos; - - if ( dir[ scrollPos ].parent != 0xffffffff && !(dir[ dir[ scrollPos ].parent ].f & DIR_UNROLLED) ) - scrollPos = dir[ scrollPos ].parent; - } - - // scrollPos increase - if ( cursorPos >= lastLine ) - { - if ( dir[ scrollPos ].f & DIR_DIRECTORY || dir[ scrollPos ].f & DIR_D64_FILE ) - { - if ( dir[ scrollPos ].f & DIR_UNROLLED ) - scrollPos ++; else - scrollPos = dir[ scrollPos ].next; - } else - scrollPos ++; - - lastLine = scanFileTree( cursorPos, scrollPos ); - } - - if ( scrollPos < 0 ) scrollPos = 0; - if ( scrollPos >= nDirEntries ) scrollPos = nDirEntries - 1; - if ( cursorPos < 0 ) cursorPos = 0; - if ( cursorPos >= nDirEntries ) cursorPos = nDirEntries - 1; - } - - if ( k == 13 ) - { - // build path - char path[ 8192 ] = {0}; - char d64file[ 32 ] = {0}; - s32 n = 0, c = cursorPos; - u32 fileIndex = 0xffffffff; - u32 nodes[ 256 ]; - - nodes[ n ++ ] = c; - - s32 curC = c; - - if ( (dir[ c ].f & DIR_FILE_IN_D64 && ((dir[ c ].f>>SHIFT_TYPE)&7) == 2) || dir[ c ].f & DIR_PRG_FILE || dir[ c ].f & DIR_CRT_FILE ) - { - while ( dir[ c ].parent != 0xffffffff ) - { - c = nodes[ n ++ ] = dir[ c ].parent; - } - - int stopPath = 0; - if ( dir[ cursorPos ].f & DIR_FILE_IN_D64 ) - { - strcpy( d64file, (char*)&dir[ cursorPos ].name[128] ); - fileIndex = dir[ cursorPos ].f & ((1<= stopPath; i -- ) - { - if ( i != n-1 ) - strcat( path, "\\" ); - strcat( path, (char*)dir[ nodes[i] ].name ); - } - - - if ( dir[ curC ].f & DIR_PRG_FILE ) - { - strcpy( FILENAME, path ); - *launchKernel = 4; - return; - } - - if ( dir[ curC ].f & DIR_FILE_IN_D64 ) - { - extern u8 d64buf[ 1024 * 1024 ]; - extern int readD64File( CLogger *logger, const char *DRIVE, const char *FILENAME, u8 *data, u32 *size ); - - u32 imgsize = 0; - - // mount file system - FATFS m_FileSystem; - if ( f_mount( &m_FileSystem, DRIVE, 1 ) != FR_OK ) - logger->Write( "RaspiMenu", LogPanic, "Cannot mount drive: %s", DRIVE ); - - if ( !readD64File( logger, "", path, d64buf, &imgsize ) ) - return; - - // unmount file system - if ( f_mount( 0, DRIVE, 0 ) != FR_OK ) - logger->Write( "RaspiMenu", LogPanic, "Cannot unmount drive: %s", DRIVE ); - - if ( d64ParseExtract( d64buf, imgsize, D64_GET_FILE + fileIndex, prgDataLaunch, (s32*)&prgSizeLaunch ) == 0 ) - { - strcpy( FILENAME, path ); - logger->Write( "RaspiMenu", LogNotice, "loaded: %d bytes", prgSizeLaunch ); - *launchKernel = 40; - } - return; - } - } - } - } else - if ( menuScreen == MENU_CONFIG ) - { - if ( k == KEY_HELP ) - { - menuScreen = MENU_BROWSER; - handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); - return; - } - if ( k == KEY_F3 ) - { - menuScreen = MENU_MAIN; - handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); - return; - } - - if ( ( ( k == 'n' || k == 'N' ) || ( k == 13 && curSettingsLine == 1 ) )&& typeInName == 0 ) - { - typeInName = 1; - // currently selected georam slot is 'settings[ 1 ]' - typeCurPos = 0; - while ( typeCurPos < 19 && geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] != 0 ) typeCurPos ++; - } else - if ( typeInName == 1 ) - { - int key = k; - if ( key >= 'a' && key <= 'z' ) - key = key + 'A' - 'a'; - switch ( k ) - { - case 13: - typeInName = 0; - break; - case 157: - if ( typeCurPos > 0 ) - typeCurPos --; - break; - case 20: - if ( typeCurPos > 0 ) - typeCurPos --; - geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] = 32; - break; - case 29: - if ( typeCurPos < 18 ) - typeCurPos ++; - break; - default: - // normal character - geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] = k; - if ( typeCurPos < 17 ) - typeCurPos ++; - break; - } - } else - // left - if ( k == 157 ) - { - settings[ curSettingsLine ] --; - //+= rangeSettings[ curSettingsLine ] - 1; - if ( settings[ curSettingsLine ] < 0 ) - settings[ curSettingsLine ] = rangeSettings[ curSettingsLine ] - 1; else - settings[ curSettingsLine ] = max( 0, settings[ curSettingsLine ] ); - - if ( rangeSettings[ curSettingsLine ] < 15 ) - { - settings[ curSettingsLine ] %= rangeSettings[ curSettingsLine ]; - } else - settings[ curSettingsLine ] = max( 0, settings[ curSettingsLine ] ); - - } else - // right - if ( k == 29 ) - { - settings[ curSettingsLine ] ++; - if ( rangeSettings[ curSettingsLine ] < 15 ) - settings[ curSettingsLine ] %= rangeSettings[ curSettingsLine ]; else - settings[ curSettingsLine ] = min( settings[ curSettingsLine ], rangeSettings[ curSettingsLine ] - 1 ); - } else - // down - if ( k == 17 ) - { - curSettingsLine ++; - curSettingsLine %= MAX_SETTINGS; - } else - // up - if ( k == 145 ) - { - if ( curSettingsLine == 0 ) - curSettingsLine = MAX_SETTINGS - 1; else - curSettingsLine --; - } - if( (k == 's' || k == 'S') && typeInName == 0 ) - { - writeSettingsFile(); - errorMsg = errorMessages[ 4 ]; - previousMenuScreen = menuScreen; - menuScreen = MENU_ERROR; - } - - applySIDSettings(); - } else - { - menuScreen = previousMenuScreen; - } - -} - -extern int subGeoRAM, subSID, subHasKernal; - -void printSidekickLogo() -{ - if ( skinFontLoaded ) - { - u32 a = 91; - - for ( u32 j = 0; j < 4; j++ ) - for ( u32 i = 0; i < 7; i++ ) - { - c64screen[ i + 33 + j * 40 ] = (a++); - c64color[ i + 33 + j * 40 ] = j < 2 ? skinValues.SKIN_MENU_TEXT_ITEM : skinValues.SKIN_MENU_TEXT_KEY; - } - } -} - - -void printMainMenu() -{ - clearC64(); - // "012345678901234567890123456789012345XXXX" - printC64( 0, 1, " .- Sidekick264 - Frenetic -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); - - extern u8 c64screen[ 40 * 25 + 1024 * 4 ]; - - if ( subGeoRAM ) - { - printC64( 0, 23, " choose PRG (also via HELP) ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); - printC64( 0, 24, " ESC back, RETURN/F1 launch ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); - printC64( 28, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 7, 24, "ESC", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 17, 24, "RETURN", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 24, 24, "F1", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - } else - if ( subSID ) - { - printC64( 0, 23, " choose PRG (also via HELP) ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); - printC64( 0, 24, " ESC back, RETURN/F2 launch ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); - printC64( 28, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 7, 24, "ESC", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 17, 24, "RETURN", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 24, 24, "F2", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - } else - { - printC64( 0, 23, " HELP Browser, F7 Exit to Basic", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); - printC64( 3, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - printC64( 17, 23, "F7", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); - } - - if ( machine264 & 1 ) - printC64( 36, 23, "64KB", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); else - printC64( 36, 23, "16KB", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); - if ( machine264 & 2 ) - printC64( 36, 24, "NTSC", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); else - printC64( 37, 24, "PAL", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); - - // menu headers + titels - for ( int i = 0; i < CATEGORY_NAMES; i++ ) - if ( menuX[ i ] != -1 ) - printC64( menuX[ i ], menuY[ i ], categoryNamesPrint[ i ], skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); - - for ( int i = 1; i < CATEGORY_NAMES; i++ ) - if ( menuX[ i ] != -1 ) - for ( int j = 0; j < menuItems[ i ]; j++ ) - { - char key[ 2 ] = { 0, 0 }; - switch ( i ) - { - case 1: key[ 0 ] = 'A' + j; break; - case 2: key[ 0 ] = '1' + j; break; - //case 3: key[ 0 ] = 'A' + j + menuItems[ 2 ]; break; - //case 4: key[ 0 ] = '1' + j + menuItems[ 1 ]; break; - }; - int flag = 0; - if ( i == 4 && j == subHasKernal ) - flag = 0x80; - - printC64( menuItemPos[ i ][ j ][ 0 ], menuItemPos[ i ][ j ][ 1 ], key, skinValues.SKIN_MENU_TEXT_KEY, flag ); - printC64( menuItemPos[ i ][ j ][ 0 ] + 2, menuItemPos[ i ][ j ][ 1 ], menuText[ i ][ j ], skinValues.SKIN_MENU_TEXT_ITEM, flag ); - } - - // special menu - printC64( menuX[ 0 ], menuY[ 0 ]+1, "F1", skinValues.SKIN_MENU_TEXT_KEY, subGeoRAM ? 0x80 : 0 ); - printC64( menuX[ 0 ], menuY[ 0 ]+2, "F2", skinValues.SKIN_MENU_TEXT_KEY, subSID ? 0x80 : 0 ); - printC64( menuX[ 0 ]+3, menuY[ 0 ]+1, "NeoRAM264", skinValues.SKIN_MENU_TEXT_ITEM, subGeoRAM ? 0x80 : 0 ); - printC64( menuX[ 0 ]+3, menuY[ 0 ]+2, "SID Emulation", skinValues.SKIN_MENU_TEXT_ITEM, subSID ? 0x80 : 0 ); - - printC64( menuX[ 0 ], menuY[ 0 ]+3, "F3", skinValues.SKIN_MENU_TEXT_KEY, 0 ); - printC64( menuX[ 0 ]+3, menuY[ 0 ]+3, "Settings", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); - - printSidekickLogo(); - - c64screen[ 1000 ] = ((0x6800 >> 8) & 0xFC); - c64screen[ 1001 ] = skinValues.SKIN_MENU_BACKGROUND_COLOR; - c64screen[ 1002 ] = skinValues.SKIN_MENU_BORDER_COLOR; -/* startInjectCode(); - // charset address - injectPOKE( 0xff13, ((0x6800 >> 8) & 0xFC) ); - injectPOKE( 0xff15, skinValues.SKIN_MENU_BACKGROUND_COLOR ); - injectPOKE( 0xff19, skinValues.SKIN_MENU_BORDER_COLOR );*/ -} - - -void settingsGetGEORAMInfo( char *filename, u32 *size ) -{ - *size = 512 * ( 1 << settings[ 0 ] ); - sprintf( filename, "SD:GEORAM/slot%02d.ram", settings[ 1 ] ); -} - - - -void printSettingsScreen() -{ - clearC64(); - // "012345678901234567890123456789012345XXXX" - printC64( 0, 1, " .- Sidekick264 - Frenetic -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); - printC64( 0, 23, " F3 Back to Menu, S Save Settings ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); - printC64( 4, 23, "F3", skinValues.SKIN_MENU_TEXT_HEADER, 128, 0 ); - printC64( 21, 23, "S", skinValues.SKIN_MENU_TEXT_HEADER, 128, 0 ); - - u32 x = 1, x2 = 8,y1 = 1, y2 = 1; - u32 l = curSettingsLine; - - // special menu - printC64( x+1, y1+3, "NeoRAM", skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); - - printC64( x+1, y1+4, "Memory", skinValues.SKIN_MENU_TEXT_ITEM, (l==0)?0x80:0 ); - - char memStr[ 4 ][ 8 ] = { "512 KB", "1 MB", "2 MB", "4 MB" }; - printC64( x2+10, y1+4, memStr[ settings[ 0 ] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==0)?0x80:0 ); - - printC64( x+1, y1+5, "File on SD", skinValues.SKIN_MENU_TEXT_ITEM, (l==1)?0x80:0 ); - char t[ 64 ]; - sprintf( t, "SD:GEORAM/slot%02d.ram", settings[ 1 ] ); - printC64( x2+10, y1+5, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==1)?0x80:0 ); - - printC64( x2+10, y1+6, (const char*)"\" \"", skinValues.SKIN_MENU_TEXT_ITEM, (typeInName==1)?0x80:0 ); - printC64( x2+11, y1+6, (const char*)geoRAM_SlotNames[ settings[ 1 ] ], skinValues.SKIN_MENU_TEXT_ITEM, (typeInName==1)?0x80:0 ); - if ( typeInName ) - c64color[ x2+11+typeCurPos + (y1+6)*40 ] = skinValues.SKIN_MENU_TEXT_CATEGORY; - - y2 -=1; - printC64( x+1, y2+9, "Sound Emulation (w/ reSID, fmopl)", skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); - - u32 textCol2 = skinValues.SKIN_MENU_TEXT_ITEM - 32; - y2 --; - - printC64( x+1, y2+11, "SID #1 ($FD40)", skinValues.SKIN_MENU_TEXT_ITEM, (l==2)?0x80:0 ); - char sidStrS[ 3 ][ 20 ] = { "6581", "8580", "8580 w/ Digiboost" }; - printC64( x2+10, y2+11, sidStrS[ settings[2] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==2)?0x80:0 ); - - printC64( x+1, y2+12, "Register Read", skinValues.SKIN_MENU_TEXT_ITEM, (l==3)?0x80:0 ); - char sidStrO[ 3 ][ 8 ] = { "off", "on" }; - printC64( x2+10, y2+12, sidStrO[ settings[3] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==3)?0x80:0 ); - - printC64( x+1, y2+13, "Volume", skinValues.SKIN_MENU_TEXT_ITEM, (l==4)?0x80:0 ); - printC64( x+1+6, y2+13, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); - printC64( x+1+7, y2+13, "Panning", skinValues.SKIN_MENU_TEXT_ITEM, (l==5)?0x80:0 ); - sprintf( t, "%2d", settings[ 4 ] ); - printC64( x2+10, y2+13, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==4)?0x80:0 ); - printC64( x2+13, y2+13, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); - sprintf( t, "%2d", settings[ 5 ] - 7 ); - printC64( x2+15, y2+13, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==5)?0x80:0 ); - - printC64( x+1, y2+14, "SID #2", textCol2, (l==6)?0x80:0 ); - char sidStrS2[ 4 ][ 20 ] = { "6581", "8580", "8580 w/ Digiboost", "none" }; - printC64( x2+10, y2+14, sidStrS2[ settings[6] ], textCol2, (l==6)?0x80:0 ); - - printC64( x+1, y2+15, "Address", textCol2, (l==7)?0x80:0 ); - char sidStrA[ 2 ][ 8 ] = { "$FD40", "$FE80" }; - printC64( x2+10, y2+15, sidStrA[ settings[7] ], textCol2, (l==7)?0x80:0 ); - - printC64( x+1, y2+16, "Volume", textCol2, (l==8)?0x80:0 ); - printC64( x+1+6, y2+16, "/", textCol2, 0 ); - printC64( x+1+7, y2+16, "Panning", textCol2, (l==9)?0x80:0 ); - sprintf( t, "%2d", settings[ 8 ] ); - printC64( x2+10, y2+16, t, textCol2, (l==8)?0x80:0 ); - printC64( x2+13, y2+16, "/", textCol2, 0 ); - sprintf( t, "%2d", settings[ 9 ] - 7 ); - printC64( x2+15, y2+16, t, textCol2, (l==9)?0x80:0 ); - - printC64( x+1, y2+17, "SID Frequency", skinValues.SKIN_MENU_TEXT_ITEM, (l==10)?0x80:0 ); - char sidStrFreq[ 2 ][ 12 ] = { "886 KHz", "985 KHz" }; - printC64( x2+10, y2+17, sidStrFreq[ settings[10] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==10)?0x80:0 ); - - y2+=2; - printC64( x+1, y2+17, "SFX Sound Exp.", skinValues.SKIN_MENU_TEXT_ITEM, (l==11)?0x80:0 ); - char sidStrO2[ 3 ][ 12 ] = { "off", "on ($FDE2)" }; - printC64( x2+10, y2+17, sidStrO2[ settings[11] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==11)?0x80:0 ); - - printC64( x+1, y2+18, "Volume", skinValues.SKIN_MENU_TEXT_ITEM, (l==12)?0x80:0 ); - printC64( x+1+6, y2+18, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); - printC64( x+1+7, y2+18, "Panning", skinValues.SKIN_MENU_TEXT_ITEM, (l==13)?0x80:0 ); - sprintf( t, "%2d", settings[ 12 ] ); - printC64( x2+10, y2+18, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==12)?0x80:0 ); - printC64( x2+13, y2+18, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); - sprintf( t, "%2d", settings[ 13 ] - 7 ); - printC64( x2+15, y2+18, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==13)?0x80:0 ); - - y2--; - printC64( x+1, y2+20, "Digiblaster Vol", textCol2, (l==14)?0x80:0 ); - sprintf( t, "%2d", settings[ 14 ] ); - printC64( x2+10, y2+20, t, textCol2, (l==14)?0x80:0 ); - printC64( x2+13, y2+20, "($FDE5)", textCol2, (l==14)?0x80:0 ); - - - printC64( x+1, y2+21, "EmulaTED Volume", skinValues.SKIN_MENU_TEXT_ITEM, (l==15)?0x80:0 ); - sprintf( t, "%02d", settings[ 15 ] ); - printC64( x2+10, y2+21, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==15)?0x80:0 ); - - printSidekickLogo(); - - c64screen[ 1000 ] = ((0x6800 >> 8) & 0xFC); - c64screen[ 1001 ] = skinValues.SKIN_MENU_BACKGROUND_COLOR; - c64screen[ 1002 ] = skinValues.SKIN_MENU_BORDER_COLOR; - -/* startInjectCode(); - // charset address - injectPOKE( 0xff13, ((0x6800 >> 8) & 0xFC) ); - injectPOKE( 0xff15, skinValues.SKIN_MENU_BACKGROUND_COLOR ); - injectPOKE( 0xff19, skinValues.SKIN_MENU_BORDER_COLOR );*/ -} - - -void renderC64() -{ - if ( menuScreen == MENU_MAIN ) - { - printMainMenu(); - } else - if ( menuScreen == MENU_BROWSER ) - { - printBrowserScreen(); - } else - if ( menuScreen == MENU_CONFIG ) - { - printSettingsScreen(); - } else - //if ( menuScreen == MENU_ERROR ) - { - if ( errorMsg != NULL ) - { - int convert = 0; - if ( previousMenuScreen == MENU_BROWSER ) - convert = 3; - - printC64( 0, 10, "שששששששששששששששששששששששששששששששששששששששש", skinValues.SKIN_ERROR_BAR, 0, 1 ); - printC64( 0, 11, " ", skinValues.SKIN_ERROR_TEXT, 0 ); - printC64( 0, 12, errorMsg, skinValues.SKIN_ERROR_TEXT, 0, convert ); - printC64( 0, 13, " ", skinValues.SKIN_ERROR_TEXT, 0 ); - printC64( 0, 14, "רררררררררררררררררררררררררררררררררררררררר", skinValues.SKIN_ERROR_BAR, 0, 1 ); - } - } -} - +/* + _________.__ .___ __ .__ __ ________ ________ _____ + / _____/|__| __| _/____ | | _|__| ____ | | __ \_____ \/ _____/ / | | + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / ____/ __ \ / | |_ + / \| / /_/ \ ___/| <| \ \___| < / \ |__\ \/ ^ / +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \_______ \_____ /\____ | + \/ \/ \/ \/ \/ \/ \/ \/ |__| + + 264screen.cpp + + RasPiC64 - A framework for interfacing the C64 (and C16/+4) and a Raspberry Pi 3B/3B+ + - menu/screen code + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ + +// this file contains unelegant code for generating the menu screens +// and handling key presses forwarded from the C64 + +#include "linux/kernel.h" +#include "c64screen.h" +#include "lowlevel_arm64.h" +#include "dirscan.h" +#include "264config.h" +#include "crt.h" +#include "kernel_menu264.h" + +#define KEY_F1 133 +#define KEY_F4 138 +#define KEY_F2 137 +#define KEY_F5 135 +#define KEY_F3 134 +#define KEY_F6 139 +#define KEY_HELP 140 +#define KEY_F7 136 + +const int VK_LEFT = 157; +const int VK_RIGHT = 29; +const int VK_UP = 145; +const int VK_DOWN = 17; +const int VK_HOME = 19; +const int VK_S = 83; + +const int VK_ESC = 95; +const int VK_DEL = 20; +const int VK_RETURN = 13; +const int VK_SHIFT_RETURN = 141; +const int VK_MOUNT = 205; // SHIFT-M +const int VK_MOUNT_START = 77; // M + +extern CLogger *logger; + +// todo +extern u32 prgSizeLaunch; +extern unsigned char prgDataLaunch[ 65536 ]; + +static const char DRIVE[] = "SD:"; +static const char SETTINGS_FILE[] = "SD:C16/special.cfg"; + +u8 c64screen[ 40 * 25 + 1024 * 4 ]; +u8 c64color[ 40 * 25 + 1024 * 4 ]; + +char *errorMsg = NULL; + +char errorMessages[5][41] = { +// 1234567890123456789012345678901234567890 + " NO ERROR ", + " ERROR: UNKNOWN/UNSUPPORTED .CRT TYPE ", + " ERROR: NO .CRT-FILE ", + " ERROR READING FILE ", + " SETTINGS SAVED " +}; + + +#define MENU_MAIN 0x00 +#define MENU_BROWSER 0x01 +#define MENU_ERROR 0x02 +#define MENU_CONFIG 0x03 +u32 menuScreen = 0, + previousMenuScreen = 0; +u32 updateMenu = 1; +u32 typeInName = 0; +u32 typeCurPos = 0; + +int cursorPos = 0; +int scrollPos = 0; +int lastLine; +int lastRolled = -1; +int lastScrolled = -1; +int lastSubIndex = -1; +int subGeoRAM = 0; +int subSID = 0; +int subHasKernal = -1; +int subHasLaunch = -1; + +void clearC64() +{ + memset( c64screen, ' ', 40 * 25 ); + memset( c64color, 0, 40 * 25 ); +} + +u8 PETSCII2ScreenCode( u8 c ) +{ + if ( c < 32 ) return c + 128; + if ( c < 64 ) return c; + if ( c < 96 ) return c - 64; + if ( c < 128 ) return c - 32; + if ( c < 160 ) return c + 64; + if ( c < 192 ) return c - 64; + return c - 128; +} + +void printC64( u32 x, u32 y, const char *t, u8 color, u8 flag, u32 convert, u32 maxL ) +{ + u32 l = min( strlen( t ), maxL ); + + for ( u32 i = 0; i < l; i++ ) + { + u32 c = t[ i ], c2 = c; + + // screen code conversion + if ( convert == 1 ) + { + c2 = PETSCII2ScreenCode( c ); + } else + { + if ( convert == 2 && c >= 'a' && c <= 'z' ) + c = c + 'A' - 'a'; + if ( convert == 3 && c >= 'A' && c <= 'Z' ) + c = c + 'a' - 'A'; + if ( ( c >= 'a' ) && ( c <= 'z' ) ) + c2 = c + 1 - 'a'; + } + + c64screen[ x + y * 40 + i ] = c2 | flag; + c64color[ x + y * 40 + i ] = color; + } +} + +u32 extraMsg = 0; + +int scanFileTree( u32 cursorPos, u32 scrollPos ) +{ + extraMsg = 0; + + u32 lines = 0; + s32 idx = scrollPos; + while ( lines < DISPLAY_LINES && idx < nDirEntries ) + { + if ( idx >= nDirEntries ) + break; + + if ( (u32)idx == cursorPos && dir[ idx ].f & DIR_D64_FILE ) + extraMsg = 1; + + if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) + { + if ( !(dir[ idx ].f & DIR_UNROLLED) ) + idx = dir[ idx ].next; else + idx ++; + } else + idx ++; + + lines ++; + } + return idx; +} + + +int printFileTree( s32 cursorPos, s32 scrollPos ) +{ + s32 lines = 0; + s32 idx = scrollPos; + s32 lastVisible = 0; + while ( lines < DISPLAY_LINES && idx < nDirEntries ) + { + if ( idx >= nDirEntries ) + break; + + u32 convert = 3; + u8 color = skinValues.SKIN_TEXT_BROWSER; + + if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) + color = skinValues.SKIN_TEXT_BROWSER_DIRECTORY; + + if ( dir[ idx ].f & DIR_FILE_IN_D64 ) + convert = 1; + + if ( idx == cursorPos ) + color = skinValues.SKIN_TEXT_BROWSER_CURRENT; + + char temp[1024], t2[ 1024 ] = {0}; + memset( t2, 0, 1024 ); + int leading = 0; + if( dir[ idx ].level > 0 ) + { + for ( u32 j = 0; j < dir[ idx ].level - 1; j++ ) + { + strcat( t2, " " ); + leading ++; + } + if ( convert != 3 ) + t2[ dir[ idx ].level - 1 ] = 93+32; else + t2[ dir[ idx ].level - 1 ] = 93; + leading ++; + } + + if ( (dir[ idx ].parent != 0xffffffff && dir[ dir[ idx ].parent ].f & DIR_D64_FILE && dir[ idx ].parent == (u32)(idx - 1) ) ) + { + if ( dir[ idx ].size > 0 ) + sprintf( temp, "%s%s ", t2, dir[ idx ].name ); else + sprintf( temp, "%s%s", t2, dir[ idx ].name ); + + if ( strlen( temp ) > 34 ) temp[ 35 ] = 0; + + if ( idx == cursorPos ) + { + printC64( 2, lines + 3, temp, color, 0x80, convert ); + } else + { + printC64( 2, lines + 3, t2, color, 0x00, convert ); + printC64( 2 + leading, lines + 3, (char*)dir[ idx ].name, color, 0x80, convert ); + } + } else + { + if ( dir[ idx ].size > 0 ) + sprintf( temp, "%s%s ", t2, dir[ idx ].name ); else + sprintf( temp, "%s%s", t2, dir[ idx ].name ); + if ( strlen( temp ) > 34 ) temp[ 35 ] = 0; + + printC64( 2, lines + 3, temp, color, (idx == cursorPos) ? 0x80 : 0, convert ); + + if ( dir[ idx ].size > 0 ) + { + if ( convert == 1 ) + { + // file in D64 + sprintf( temp, " %3dK", dir[ idx ].size / 1024 ); + } else + { + if ( dir[ idx ].size / 1024 > 999 ) + sprintf( temp, " %1.1fm", (float)dir[ idx ].size / (1024 * 1024) ); else + sprintf( temp, " %3dk", dir[ idx ].size / 1024 ); + } + + printC64( 32, lines + 3, temp, color, (idx == cursorPos) ? 0x80 : 0, convert ); + } + } + lastVisible = idx; + + if ( dir[ idx ].f & DIR_DIRECTORY || dir[ idx ].f & DIR_D64_FILE ) + { + if ( !(dir[ idx ].f & DIR_UNROLLED) ) + idx = dir[ idx ].next; else + idx ++; + } else + idx ++; + + lines ++; + } + + return lastVisible; +} + + +void printBrowserScreen() +{ + clearC64(); + + //printC64(0,0, "0123456789012345678901234567890123456789", 15, 0 ); + printC64( 0, 1, " .- sidekick264 browser -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); + printC64( 0, 23, " F1/F2 Page Up/Down, HELP Back to Menu ", skinValues.SKIN_BROWSER_TEXT_HEADER, 0, 3 ); + printC64( 2, 23, "F1", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); + printC64( 5, 23, "F2", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); + printC64( 22, 23, "HELP", skinValues.SKIN_BROWSER_TEXT_HEADER, 128, 3 ); + + if ( subGeoRAM ) + { + printC64( 0, 24, " choose PRG w/ NeoRAM to start ", skinValues.SKIN_BROWSER_TEXT_FOOTER, 0, 3 ); + printC64( 13, 24, "PRG w/ NeoRAM", skinValues.SKIN_BROWSER_TEXT_FOOTER_HIGHLIGHTED, 0, 3 ); + } else + if ( subSID ) + { + printC64( 0, 24, " choose PRG w/ SID+FM to start ", skinValues.SKIN_BROWSER_TEXT_FOOTER, 0, 3 ); + printC64( 13, 24, "PRG w/ SID+FM", skinValues.SKIN_BROWSER_TEXT_FOOTER_HIGHLIGHTED, 0, 3 ); + } + + lastLine = printFileTree( cursorPos, scrollPos ); + + // scroll bar + int t = scrollPos * DISPLAY_LINES / nDirEntries; + int b = lastLine * DISPLAY_LINES / nDirEntries; + + // bar on the right + for ( int i = 0; i < DISPLAY_LINES; i++ ) + { + char c = 94; + if ( t <= i && i <= b ) + c = 96 + 128 - 64; + c64screen[ 38 + ( i + 3 ) * 40 ] = c; + c64color[ 38 + ( i + 3 ) * 40 ] = 1; + } + + c64screen[ 1000 ] = ((0x6000 >> 8) & 0xFC); + c64screen[ 1001 ] = skinValues.SKIN_BROWSER_BACKGROUND_COLOR; + c64screen[ 1002 ] = skinValues.SKIN_BROWSER_BORDER_COLOR; + + /*startInjectCode(); + // charset address + injectPOKE( 0xff13, ((0x6000 >> 8) & 0xFC) ); + injectPOKE( 0xff15, skinValues.SKIN_BROWSER_BACKGROUND_COLOR ); + injectPOKE( 0xff19, skinValues.SKIN_BROWSER_BORDER_COLOR );*/ +} + +void resetMenuState( int keep = 0 ) +{ + if ( !( keep & 1 ) ) subGeoRAM = 0; + if ( !( keep & 2 ) ) subSID = 0; + subHasKernal = -1; + subHasLaunch = -1; +} + +int getMainMenuSelection( int key, char **FILE, char **FILE2, int *addIdx ) +{ + *FILE = NULL; + + if ( key >= 'a' && key <= 'z' ) + key = key + 'A' - 'a'; + + + if ( key == KEY_F7 ) { resetMenuState(0); return 1;/* Exit */ } else + if ( key == KEY_HELP ) { resetMenuState(3); return 2;/* Browser */ } else + if ( key == KEY_F1 ) { resetMenuState(1); return 3;/* GEORAM */ } else + if ( key == KEY_F2 ) { resetMenuState(2); return 4;/* SID */ } else + { + if ( key >= 'A' && key < 'A' + menuItems[ 1 ] ) // PRG + { + int i = key - 'A'; + *FILE = menuFile[ 1 ][ i ]; + //logger->Write( "RaspiMenu", LogNotice, "getselection: %s -> %s\n", menuText[ 1 ][ i ], menuFile[ 1 ][ i ] ); + return 6; + } else + if ( key >= '1' && key < '1' + menuItems[ 2 ] ) // CRT + { + resetMenuState(); + int i = key - '1'; + *FILE = menuFile[ 2 ][ i ]; + *FILE2 = menuFile2[ 2 ][ i ]; + //logger->Write( "RaspiMenu", LogNotice, "%s -> %s + %s\n", menuText[ 2 ][ i ], menuFile[ 2 ][ i ], menuFile2[ 2 ][ i ] ); + return 5; + } + } + + return 0; +} + +extern void deactivateCart(); + +#define MAX_SETTINGS 18 +u32 curSettingsLine = 0; +s32 rangeSettings[ MAX_SETTINGS ] = { 4, 10, 3, 2, 2, 16, 15, 4, 2, 16, 15, 2, 2, 16, 15, 16, 16, 2 }; +s32 settings[ MAX_SETTINGS ] = { 0, 0, 0, 0, 0, 15, 0, 0, 0, 15, 14, 0, 0, 15, 7, 15, 15, 0 }; +u8 geoRAM_SlotNames[ 10 ][ 21 ]; + +// 0 +// 1 +// 2 SID #1 type +// 3 SID #1 addr +// 4 register read +// 5 vol +// 6 panning +// 7 SID #2 type +// 8 SID #2 addr +// 9 vol +// 10 panning +// 11 clock +// 12 SFX +// 13 vol +// 14 pan +// 15 Digiblaster vol +// 16 TED vol +// 17 output + +void writeSettingsFile() +{ + char cfg[ 16384 ]; + u32 cfgBytes = 0; + memset( cfg, 0, 16384 ); + cfgBytes = sizeof( s32 ) * MAX_SETTINGS; + memcpy( cfg, settings, cfgBytes ); + memcpy( &cfg[ cfgBytes ], geoRAM_SlotNames, sizeof( u8 ) * 10 * 21 ); + cfgBytes += sizeof( u8 ) * 10 * 21; + + writeFile( logger, DRIVE, SETTINGS_FILE, (u8*)cfg, cfgBytes ); +} + + +void readSettingsFile() +{ + char cfg[ 16384 ]; + u32 cfgBytes; + memset( cfg, 0, 16384 ); + + memset( settings, 0, sizeof( s32 ) * MAX_SETTINGS ); + memset( geoRAM_SlotNames, 32, sizeof( u8 ) * 10 * 21 ); + + if ( !readFile( logger, DRIVE, SETTINGS_FILE, (u8*)cfg, &cfgBytes ) ) + writeSettingsFile(); + + memcpy( settings, cfg, sizeof( s32 ) * MAX_SETTINGS ); + memcpy( geoRAM_SlotNames, &cfg[ sizeof( s32 ) * MAX_SETTINGS ], sizeof( u8 ) * 10 * 21 ); +} + +void applySIDSettings() +{ + // how elegant... + extern void setSIDConfiguration( u32 mode, u32 sid1, u32 sid1addr, u32 sid2, u32 sid2addr, u32 rr, u32 addr, u32 exp, s32 v1, s32 p1, s32 v2, s32 p2, s32 v3, s32 p3, s32 digiblasterVol, s32 sidfreq, s32 tedVol, u8 output ); + u8 useHDMI = settings[ 17 ]; + extern u8 hdmiSoundAvailable; + if ( !hdmiSoundAvailable ) + useHDMI = 0; + setSIDConfiguration( 0, settings[2], settings[ 3 ], settings[7], settings[8]-1, settings[4], settings[8], settings[12], + settings[5], settings[6], settings[9], settings[10], settings[13], settings[14], settings[15], settings[11], settings[16], useHDMI ); +} + + +// ugly, hard-coded handling of UI +void handleC64( int k, u32 *launchKernel, char *FILENAME, char *filenameKernal ) +{ + char *filename, *filename2; + + if ( menuScreen == MENU_MAIN ) + { + /*if ( k == VK_SHIFT_RETURN ) + { + *launchKernel = 255; // reboot + return; + }*/ + + if ( k == KEY_HELP ) + { + menuScreen = MENU_BROWSER; + handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); + return; + } + if ( k == KEY_F3 ) + { + menuScreen = MENU_CONFIG; + handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); + return; + } + + int temp; + int r = getMainMenuSelection( k, &filename, &filename2, &temp ); + + if ( subSID == 1 ) + { + if ( k == 27 ) + { + resetMenuState(); + subHasLaunch = -1; + subSID = 0; + return; + } + // start + if ( ( r == 4 ) || ( k == 13 ) ) + { + FILENAME[ 0 ] = 0; + *launchKernel = 8; + return; + } + } + + if ( subGeoRAM == 1 ) + { + if ( k == 27 ) + { + resetMenuState(); + subHasKernal = -1; + subHasLaunch = -1; + subGeoRAM = 0; + return; + } + // + if ( r == 7 ) // kernal selected + { + strcpy( filenameKernal, filename ); + return; + } + // start + if ( ( ( r == 3 ) || ( k == 13 ) ) ) + { + *launchKernel = 3; + return; + } + } + + switch ( r ) + { + case 1: // exit to basic + deactivateCart(); + return; + case 2: // Browser + break; + case 3: // GeoRAM + subGeoRAM = 1; + subSID = 0; + return; + case 4: // SID+FM + subSID = 1; + subGeoRAM = 0; + return; + case 5: // .CRT file + *launchKernel = 5; + errorMsg = NULL; + strncpy( &FILENAME[0], filename, 2047 ); + if ( strcmp( filename2, "none" ) == 0 ) + FILENAME[2048] = 0; else + strncpy( &FILENAME[2048], filename2, 2047 ); + return; + case 6: // .PRG file + strcpy( FILENAME, filename ); + *launchKernel = 4; + return; + } + } else + if ( menuScreen == MENU_BROWSER ) + { + // browser screen + if ( k == KEY_HELP ) + { + menuScreen = MENU_MAIN; + handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); + return; + } + + int rep = 1; + if ( k == KEY_F2 ) { k = 17; rep = DISPLAY_LINES - 1; } + if ( k == KEY_F1 ) { k = 145; rep = DISPLAY_LINES - 1; } + + lastLine = scanFileTree( cursorPos, scrollPos ); + + for ( int i = 0; i < rep; i++ ) + { + // left + if ( k == VK_LEFT || + ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && k == 13 && (dir[ cursorPos ].f & DIR_UNROLLED ) ) ) + { + typeInName = 0; + if ( dir[ cursorPos ].parent != 0xffffffff ) + { + lastSubIndex = cursorPos; + lastRolled = dir[ cursorPos ].parent; + // fix + if (! ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && + ( dir[ cursorPos ].f & DIR_UNROLLED ) ) ) + cursorPos = dir[ cursorPos ].parent; + // fix 2 + lastScrolled = scrollPos; + } + if ( !( dir[ cursorPos ].f & DIR_UNROLLED ) ) + k = VK_UP; + if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) + dir[ cursorPos ].f &= ~DIR_UNROLLED; + k = 0; + } else + // right + if ( k == VK_RIGHT || + ( ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) && k == 13 && !(dir[ cursorPos ].f & DIR_UNROLLED ) ) ) + { + typeInName = 0; + if ( (dir[ cursorPos ].f & DIR_DIRECTORY) && !(dir[ cursorPos ].f & DIR_SCANNED) ) + { + // build path + char path[ 8192 ] = {0}; + u32 n = 0, c = cursorPos; + u32 nodes[ 256 ]; + + nodes[ n ++ ] = c; + + while ( dir[ c ].parent != 0xffffffff ) + { + c = nodes[ n ++ ] = dir[ c ].parent; + } + + sprintf( path, "SD:" ); + + for ( u32 i = n - 1; i >= 1; i -- ) + { + if ( i != n - 1 ) + strcat( path, "//" ); + strcat( path, (char*)dir[ nodes[i] ].name ); + } + strcat( path, "//" ); + + extern void insertDirectoryContents( int node, char *basePath, int takeAll ); + insertDirectoryContents( nodes[ 0 ], path, dir[ cursorPos ].f & DIR_LISTALL ); + } + + if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) + dir[ cursorPos ].f |= DIR_UNROLLED; + + if ( cursorPos == lastRolled ) + { + scrollPos = lastScrolled; + cursorPos = lastSubIndex; + } else + if ( cursorPos < nDirEntries - 1 ) + { + cursorPos ++; + lastLine = scanFileTree( cursorPos, scrollPos ); + } + lastRolled = -1; + + k = 0; + } else + // down + if ( k == VK_DOWN ) + { + typeInName = 0; + int oldPos = cursorPos; + if ( dir[ cursorPos ].f & DIR_DIRECTORY || dir[ cursorPos ].f & DIR_D64_FILE ) + { + if ( dir[ cursorPos ].f & DIR_UNROLLED ) + cursorPos ++; else + cursorPos = dir[ cursorPos ].next; + } else + cursorPos ++; + if ( cursorPos >= nDirEntries ) + cursorPos = oldPos; + } else + // up + if ( k == VK_UP ) + { + typeInName = 0; + cursorPos --; + if ( cursorPos < 0 ) cursorPos = 0; + + // current item has parent(s) -> check if they are unrolled + int c = cursorPos; + while ( c >= 0 && dir[ c ].parent != 0xffffffff ) + { + c = dir[ c ].parent; + if ( c >= 0 && c < nDirEntries && ( dir[ c ].f & DIR_DIRECTORY || dir[ c ].f & DIR_D64_FILE ) && !(dir[ c ].f & DIR_UNROLLED) ) + cursorPos = c; + } + } + + //if ( cursorPos < 0 ) cursorPos = 0; + //if ( cursorPos >= nDirEntries ) cursorPos = nDirEntries - 1; + + // scrollPos decrease + if ( cursorPos < scrollPos ) + { + scrollPos = cursorPos; + + if ( dir[ scrollPos ].parent != 0xffffffff && !(dir[ dir[ scrollPos ].parent ].f & DIR_UNROLLED) ) + scrollPos = dir[ scrollPos ].parent; + } + + // scrollPos increase + if ( cursorPos >= lastLine ) + { + if ( dir[ scrollPos ].f & DIR_DIRECTORY || dir[ scrollPos ].f & DIR_D64_FILE ) + { + if ( dir[ scrollPos ].f & DIR_UNROLLED ) + scrollPos ++; else + scrollPos = dir[ scrollPos ].next; + } else + scrollPos ++; + + lastLine = scanFileTree( cursorPos, scrollPos ); + } + + if ( scrollPos < 0 ) scrollPos = 0; + if ( scrollPos >= nDirEntries ) scrollPos = nDirEntries - 1; + if ( cursorPos < 0 ) cursorPos = 0; + if ( cursorPos >= nDirEntries ) cursorPos = nDirEntries - 1; + } + + if ( k == 13 ) + { + // build path + char path[ 8192 ] = {0}; + char d64file[ 32 ] = {0}; + s32 n = 0, c = cursorPos; + u32 fileIndex = 0xffffffff; + u32 nodes[ 256 ]; + + nodes[ n ++ ] = c; + + s32 curC = c; + + if ( (dir[ c ].f & DIR_FILE_IN_D64 && ((dir[ c ].f>>SHIFT_TYPE)&7) == 2) || dir[ c ].f & DIR_PRG_FILE || dir[ c ].f & DIR_CRT_FILE ) + { + while ( dir[ c ].parent != 0xffffffff ) + { + c = nodes[ n ++ ] = dir[ c ].parent; + } + + int stopPath = 0; + if ( dir[ cursorPos ].f & DIR_FILE_IN_D64 ) + { + strcpy( d64file, (char*)&dir[ cursorPos ].name[128] ); + fileIndex = dir[ cursorPos ].f & ((1<= stopPath; i -- ) + { + if ( i != n-1 ) + strcat( path, "\\" ); + strcat( path, (char*)dir[ nodes[i] ].name ); + } + + + if ( dir[ curC ].f & DIR_PRG_FILE ) + { + strcpy( FILENAME, path ); + *launchKernel = 4; + return; + } + + if ( dir[ curC ].f & DIR_FILE_IN_D64 ) + { + extern u8 d64buf[ 1024 * 1024 ]; + extern int readD64File( CLogger *logger, const char *DRIVE, const char *FILENAME, u8 *data, u32 *size ); + + u32 imgsize = 0; + + // mount file system + FATFS m_FileSystem; + if ( f_mount( &m_FileSystem, DRIVE, 1 ) != FR_OK ) + logger->Write( "RaspiMenu", LogPanic, "Cannot mount drive: %s", DRIVE ); + + if ( !readD64File( logger, "", path, d64buf, &imgsize ) ) + return; + + // unmount file system + if ( f_mount( 0, DRIVE, 0 ) != FR_OK ) + logger->Write( "RaspiMenu", LogPanic, "Cannot unmount drive: %s", DRIVE ); + + if ( d64ParseExtract( d64buf, imgsize, D64_GET_FILE + fileIndex, prgDataLaunch, (s32*)&prgSizeLaunch ) == 0 ) + { + strcpy( FILENAME, path ); + logger->Write( "RaspiMenu", LogNotice, "loaded: %d bytes", prgSizeLaunch ); + *launchKernel = 40; + } + return; + } + } + } + } else + if ( menuScreen == MENU_CONFIG ) + { + if ( k == KEY_HELP ) + { + menuScreen = MENU_BROWSER; + handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); + return; + } + if ( k == KEY_F3 ) + { + menuScreen = MENU_MAIN; + handleC64( 0xffffffff, launchKernel, FILENAME, filenameKernal ); + return; + } + + if ( ( ( k == 'n' || k == 'N' ) || ( k == 13 && curSettingsLine == 1 ) )&& typeInName == 0 ) + { + typeInName = 1; + // currently selected georam slot is 'settings[ 1 ]' + typeCurPos = 0; + while ( typeCurPos < 19 && geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] != 0 ) typeCurPos ++; + } else + if ( typeInName == 1 ) + { + int key = k; + if ( key >= 'a' && key <= 'z' ) + key = key + 'A' - 'a'; + switch ( k ) + { + case 13: + typeInName = 0; + break; + case 157: + if ( typeCurPos > 0 ) + typeCurPos --; + break; + case 20: + if ( typeCurPos > 0 ) + typeCurPos --; + geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] = 32; + break; + case 29: + if ( typeCurPos < 18 ) + typeCurPos ++; + break; + default: + // normal character + geoRAM_SlotNames[ settings[ 1 ] ][ typeCurPos ] = k; + if ( typeCurPos < 17 ) + typeCurPos ++; + break; + } + } else + // left + if ( k == 157 ) + { + if ( rangeSettings[ curSettingsLine ] == 17 ) // a volume variable + { + settings[ curSettingsLine ] = max( 0, settings[ curSettingsLine ] - 1 ); + } else + { + settings[ curSettingsLine ] --; + if ( rangeSettings[ curSettingsLine ] < 15 ) + { + if ( settings[ curSettingsLine ] < 0 ) + settings[ curSettingsLine ] = rangeSettings[ curSettingsLine ] - 1; + } else + settings[ curSettingsLine ] = max( 0, settings[ curSettingsLine ] ); + + } + if ( curSettingsLine == 17 ) // PWM or HDMI -> force to PWM if HDMI not available + { + extern u8 hdmiSoundAvailable; + if ( !hdmiSoundAvailable ) + settings[ 17 ] = 0; + } + } else + // right + if ( k == 29 ) + { + settings[ curSettingsLine ] ++; + if ( rangeSettings[ curSettingsLine ] < 15 ) + settings[ curSettingsLine ] %= rangeSettings[ curSettingsLine ]; else + settings[ curSettingsLine ] = min( settings[ curSettingsLine ], rangeSettings[ curSettingsLine ] - 1 ); + + if ( curSettingsLine == 17 ) // PWM or HDMI -> force to PWM if HDMI not available + { + extern u8 hdmiSoundAvailable; + if ( !hdmiSoundAvailable ) + settings[ 17 ] = 0; + } + } else + // down + if ( k == 17 ) + { + curSettingsLine ++; + curSettingsLine %= MAX_SETTINGS; + } else + // up + if ( k == 145 ) + { + if ( curSettingsLine == 0 ) + curSettingsLine = MAX_SETTINGS - 1; else + curSettingsLine --; + } + if( (k == 's' || k == 'S') && typeInName == 0 ) + { + writeSettingsFile(); + errorMsg = errorMessages[ 4 ]; + previousMenuScreen = menuScreen; + menuScreen = MENU_ERROR; + } + + applySIDSettings(); + } else + { + menuScreen = previousMenuScreen; + } + +} + +extern int subGeoRAM, subSID, subHasKernal; + +void printSidekickLogo() +{ + if ( skinFontLoaded ) + { + u32 a = 91; + + for ( u32 j = 0; j < 4; j++ ) + for ( u32 i = 0; i < 7; i++ ) + { + c64screen[ i + 33 + j * 40 ] = (a++); + c64color[ i + 33 + j * 40 ] = j < 2 ? skinValues.SKIN_MENU_TEXT_ITEM : skinValues.SKIN_MENU_TEXT_KEY; + } + } +} + + +void printMainMenu() +{ + clearC64(); + // "012345678901234567890123456789012345XXXX" + printC64( 0, 1, " .- Sidekick264 - Frenetic -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); + + extern u8 c64screen[ 40 * 25 + 1024 * 4 ]; + + if ( subGeoRAM ) + { + printC64( 0, 23, " choose PRG (also via HELP) ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); + printC64( 0, 24, " ESC back, RETURN/F1 launch ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); + printC64( 28, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 7, 24, "ESC", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 17, 24, "RETURN", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 24, 24, "F1", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + } else + if ( subSID ) + { + printC64( 0, 23, " choose PRG (also via HELP) ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); + printC64( 0, 24, " ESC back, RETURN/F2 launch ", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); + printC64( 28, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 7, 24, "ESC", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 17, 24, "RETURN", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 24, 24, "F2", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + } else + { + printC64( 0, 23, " HELP Browser, F7 Exit to Basic", skinValues.SKIN_MENU_TEXT_FOOTER, 0 ); + printC64( 3, 23, "HELP", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + printC64( 17, 23, "F7", skinValues.SKIN_MENU_TEXT_FOOTER, 128, 0 ); + } + + if ( machine264 & 1 ) + printC64( 36, 23, "64KB", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); else + printC64( 36, 23, "16KB", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); + if ( machine264 & 2 ) + printC64( 36, 24, "NTSC", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); else + printC64( 37, 24, "PAL", skinValues.SKIN_MENU_TEXT_SYSINFO, 0 ); + + // menu headers + titels + for ( int i = 0; i < CATEGORY_NAMES; i++ ) + if ( menuX[ i ] != -1 ) + printC64( menuX[ i ], menuY[ i ], categoryNamesPrint[ i ], skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); + + for ( int i = 1; i < CATEGORY_NAMES; i++ ) + if ( menuX[ i ] != -1 ) + for ( int j = 0; j < menuItems[ i ]; j++ ) + { + char key[ 2 ] = { 0, 0 }; + switch ( i ) + { + case 1: key[ 0 ] = 'A' + j; break; + case 2: key[ 0 ] = '1' + j; break; + //case 3: key[ 0 ] = 'A' + j + menuItems[ 2 ]; break; + //case 4: key[ 0 ] = '1' + j + menuItems[ 1 ]; break; + }; + int flag = 0; + if ( i == 4 && j == subHasKernal ) + flag = 0x80; + + printC64( menuItemPos[ i ][ j ][ 0 ], menuItemPos[ i ][ j ][ 1 ], key, skinValues.SKIN_MENU_TEXT_KEY, flag ); + printC64( menuItemPos[ i ][ j ][ 0 ] + 2, menuItemPos[ i ][ j ][ 1 ], menuText[ i ][ j ], skinValues.SKIN_MENU_TEXT_ITEM, flag ); + } + + // special menu + printC64( menuX[ 0 ], menuY[ 0 ]+1, "F1", skinValues.SKIN_MENU_TEXT_KEY, subGeoRAM ? 0x80 : 0 ); + printC64( menuX[ 0 ], menuY[ 0 ]+2, "F2", skinValues.SKIN_MENU_TEXT_KEY, subSID ? 0x80 : 0 ); + printC64( menuX[ 0 ]+3, menuY[ 0 ]+1, "NeoRAM264", skinValues.SKIN_MENU_TEXT_ITEM, subGeoRAM ? 0x80 : 0 ); + printC64( menuX[ 0 ]+3, menuY[ 0 ]+2, "SID Emulation", skinValues.SKIN_MENU_TEXT_ITEM, subSID ? 0x80 : 0 ); + + printC64( menuX[ 0 ], menuY[ 0 ]+3, "F3", skinValues.SKIN_MENU_TEXT_KEY, 0 ); + printC64( menuX[ 0 ]+3, menuY[ 0 ]+3, "Settings", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); + + printSidekickLogo(); + + c64screen[ 1000 ] = ((0x6800 >> 8) & 0xFC); + c64screen[ 1001 ] = skinValues.SKIN_MENU_BACKGROUND_COLOR; + c64screen[ 1002 ] = skinValues.SKIN_MENU_BORDER_COLOR; +/* startInjectCode(); + // charset address + injectPOKE( 0xff13, ((0x6800 >> 8) & 0xFC) ); + injectPOKE( 0xff15, skinValues.SKIN_MENU_BACKGROUND_COLOR ); + injectPOKE( 0xff19, skinValues.SKIN_MENU_BORDER_COLOR );*/ +} + + +void settingsGetGEORAMInfo( char *filename, u32 *size ) +{ + *size = 512 * ( 1 << settings[ 0 ] ); + sprintf( filename, "SD:GEORAM/slot%02d.ram", settings[ 1 ] ); +} + + + +void printSettingsScreen() +{ + clearC64(); + // "012345678901234567890123456789012345XXXX" + printC64( 0, 1, " .- Sidekick264 - Frenetic -. ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); + printC64( 0, 24, " F3 Back to Menu, S Save Settings ", skinValues.SKIN_MENU_TEXT_HEADER, 0 ); + printC64( 4, 24, "F3", skinValues.SKIN_MENU_TEXT_HEADER, 128, 0 ); + printC64( 21, 24, "S", skinValues.SKIN_MENU_TEXT_HEADER, 128, 0 ); + + u32 x = 1, x2 = 8,y1 = 1, y2 = 1; + u32 l = curSettingsLine; + + // special menu + printC64( x+1, y1+3, "NeoRAM", skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); + + printC64( x+1, y1+4, "Memory", skinValues.SKIN_MENU_TEXT_ITEM, (l==0)?0x80:0 ); + + char memStr[ 4 ][ 8 ] = { "512 KB", "1 MB", "2 MB", "4 MB" }; + printC64( x2+10, y1+4, memStr[ settings[ 0 ] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==0)?0x80:0 ); + + printC64( x+1, y1+5, "File on SD", skinValues.SKIN_MENU_TEXT_ITEM, (l==1)?0x80:0 ); + char t[ 64 ]; + sprintf( t, "SD:GEORAM/slot%02d.ram", settings[ 1 ] ); + printC64( x2+10, y1+5, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==1)?0x80:0 ); + + printC64( x2+10, y1+6, (const char*)"\" \"", skinValues.SKIN_MENU_TEXT_ITEM, (typeInName==1)?0x80:0 ); + printC64( x2+11, y1+6, (const char*)geoRAM_SlotNames[ settings[ 1 ] ], skinValues.SKIN_MENU_TEXT_ITEM, (typeInName==1)?0x80:0 ); + if ( typeInName ) + c64color[ x2+11+typeCurPos + (y1+6)*40 ] = skinValues.SKIN_MENU_TEXT_CATEGORY; + + y2 -=1; + printC64( x+1, y2+9, "Sound Emulation (w/ reSID, fmopl)", skinValues.SKIN_MENU_TEXT_CATEGORY, 0 ); + + u32 textCol2 = skinValues.SKIN_MENU_TEXT_ITEM - 32; + y2 --; + + printC64( x+1, y2+11, "SID #1", skinValues.SKIN_MENU_TEXT_ITEM, (l==2)?0x80:0 ); + char sidStrS[ 3 ][ 20 ] = { "6581", "8580", "8580 w/ Digiboost" }; + printC64( x2+10, y2+11, sidStrS[ settings[2] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==2)?0x80:0 ); + + char sidStrA1[ 2 ][ 8 ] = { "$FD40", "$D400" }; + printC64( x+1, y2+12, "Address", skinValues.SKIN_MENU_TEXT_ITEM, (l==3)?0x80:0 ); + printC64( x2+10, y2+12, sidStrA1[ settings[3] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==3)?0x80:0 ); + + y2 ++; + printC64( x+1, y2+12, "Register Read", skinValues.SKIN_MENU_TEXT_ITEM, (l==4)?0x80:0 ); + char sidStrO[ 3 ][ 8 ] = { "off", "on" }; + printC64( x2+10, y2+12, sidStrO[ settings[4] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==4)?0x80:0 ); + + printC64( x+1, y2+13, "Volume", skinValues.SKIN_MENU_TEXT_ITEM, (l==5)?0x80:0 ); + printC64( x+1+6, y2+13, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); + printC64( x+1+7, y2+13, "Panning", skinValues.SKIN_MENU_TEXT_ITEM, (l==6)?0x80:0 ); + sprintf( t, "%2d", settings[ 5 ] ); + printC64( x2+10, y2+13, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==5)?0x80:0 ); + printC64( x2+13, y2+13, "/", skinValues.SKIN_MENU_TEXT_ITEM, 0 ); + sprintf( t, "%2d", settings[ 6 ] - 7 ); + printC64( x2+15, y2+13, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==6)?0x80:0 ); + + printC64( x+1, y2+14, "SID #2", textCol2, (l==7)?0x80:0 ); + char sidStrS2[ 4 ][ 20 ] = { "6581", "8580", "8580 w/ Digiboost", "none" }; + printC64( x2+10, y2+14, sidStrS2[ settings[7] ], textCol2, (l==7)?0x80:0 ); + + printC64( x+1, y2+15, "Address", textCol2, (l==8)?0x80:0 ); + char sidStrA[ 2 ][ 8 ] = { "$FD40", "$FE80" }; + printC64( x2+10, y2+15, sidStrA[ settings[8] ], textCol2, (l==8)?0x80:0 ); + + printC64( x+1, y2+16, "Volume", textCol2, (l==9)?0x80:0 ); + printC64( x+1+6, y2+16, "/", textCol2, 0 ); + printC64( x+1+7, y2+16, "Panning", textCol2, (l==10)?0x80:0 ); + sprintf( t, "%2d", settings[ 9 ] ); + printC64( x2+10, y2+16, t, textCol2, (l==9)?0x80:0 ); + printC64( x2+13, y2+16, "/", textCol2, 0 ); + sprintf( t, "%2d", settings[ 10 ] - 7 ); + printC64( x2+15, y2+16, t, textCol2, (l==10)?0x80:0 ); + + printC64( x+1, y2+17, "SID Frequency", skinValues.SKIN_MENU_TEXT_ITEM, (l==11)?0x80:0 ); +// char sidStrFreq[ 2 ][ 12 ] = { "886 KHz", "985 KHz" }; + char sidStrFreq[ 2 ][ 13 ] = { "TED clock", "VIC-II clock" }; + printC64( x2+10, y2+17, sidStrFreq[ settings[11] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==11)?0x80:0 ); + + y2+=1; + printC64( x+1, y2+17, "SFX Sound Exp.", textCol2, (l==12)?0x80:0 ); + char sidStrO2[ 3 ][ 12 ] = { "off", "on ($FDE2)" }; + printC64( x2+10, y2+17, sidStrO2[ settings[12] ], textCol2, (l==12)?0x80:0 ); + + printC64( x+1, y2+18, "Volume", textCol2, (l==13)?0x80:0 ); + printC64( x+1+6, y2+18, "/", textCol2, 0 ); + printC64( x+1+7, y2+18, "Panning", textCol2, (l==14)?0x80:0 ); + sprintf( t, "%2d", settings[ 13 ] ); + printC64( x2+10, y2+18, t, textCol2, (l==13)?0x80:0 ); + printC64( x2+13, y2+18, "/", textCol2, 0 ); + sprintf( t, "%2d", settings[ 14 ] - 7 ); + printC64( x2+15, y2+18, t, textCol2, (l==14)?0x80:0 ); + + y2--; + printC64( x+1, y2+20, "Digiblaster Vol", skinValues.SKIN_MENU_TEXT_ITEM, (l==15)?0x80:0 ); + sprintf( t, "%2d", settings[ 15 ] ); + printC64( x2+10, y2+20, t, skinValues.SKIN_MENU_TEXT_ITEM, (l==15)?0x80:0 ); + printC64( x2+13, y2+20, "($FDE5)", skinValues.SKIN_MENU_TEXT_ITEM, (l==15)?0x80:0 ); + + + printC64( x+1, y2+21, "EmulaTED Volume", textCol2, (l==16)?0x80:0 ); + sprintf( t, "%2d", settings[ 16 ] ); + printC64( x2+10, y2+21, t, textCol2, (l==16)?0x80:0 ); + + y2 --; + printC64( x+1, y2+23, "Output", skinValues.SKIN_MENU_TEXT_ITEM, (l==17)?0x80:0 ); + char sidStrOutput[ 2 ][ 17 ] = { "PWM (audio jack)", "HDMI" }; + printC64( x2+10, y2+23, sidStrOutput[ settings[17] ], skinValues.SKIN_MENU_TEXT_ITEM, (l==17)?0x80:0 ); + + printSidekickLogo(); + + c64screen[ 1000 ] = ((0x6800 >> 8) & 0xFC); + c64screen[ 1001 ] = skinValues.SKIN_MENU_BACKGROUND_COLOR; + c64screen[ 1002 ] = skinValues.SKIN_MENU_BORDER_COLOR; + +/* startInjectCode(); + // charset address + injectPOKE( 0xff13, ((0x6800 >> 8) & 0xFC) ); + injectPOKE( 0xff15, skinValues.SKIN_MENU_BACKGROUND_COLOR ); + injectPOKE( 0xff19, skinValues.SKIN_MENU_BORDER_COLOR );*/ +} + + +void renderC64() +{ + if ( menuScreen == MENU_MAIN ) + { + printMainMenu(); + } else + if ( menuScreen == MENU_BROWSER ) + { + printBrowserScreen(); + } else + if ( menuScreen == MENU_CONFIG ) + { + printSettingsScreen(); + } else + //if ( menuScreen == MENU_ERROR ) + { + if ( errorMsg != NULL ) + { + int convert = 0; + if ( previousMenuScreen == MENU_BROWSER ) + convert = 3; + + printC64( 0, 10, "\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9", skinValues.SKIN_ERROR_BAR, 0, 1 ); + printC64( 0, 11, " ", skinValues.SKIN_ERROR_TEXT, 0 ); + printC64( 0, 12, errorMsg, skinValues.SKIN_ERROR_TEXT, 0, convert ); + printC64( 0, 13, " ", skinValues.SKIN_ERROR_TEXT, 0 ); + printC64( 0, 14, "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8", skinValues.SKIN_ERROR_BAR, 0, 1 ); + } + } +} + diff --git a/Source/Firmware/kernel_menu264.cpp b/Source/Firmware/kernel_menu264.cpp index bd252188..57e845ce 100644 --- a/Source/Firmware/kernel_menu264.cpp +++ b/Source/Firmware/kernel_menu264.cpp @@ -1,724 +1,752 @@ -/* - _________.__ .___ __ .__ __ _____ - / _____/|__| __| _/____ | | _|__| ____ | | __ / \ ____ ____ __ __ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / \ / \_/ __ \ / \| | \ - / \| / /_/ \ ___/| <| \ \___| < / Y \ ___/| | \ | / -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \____|__ /\___ >___| /____/ - \/ \/ \/ \/ \/ \/ \/ \/ \/ - - kernel_menu264.cpp - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - Sidekick Menu: ugly glue code to expose some functionality in one menu with browser - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include "kernel_menu264.h" -#include "dirscan.h" -#include "config.h" -#include "264screen.h" -#include "charlogo.h" - -// we will read these files -static const char DRIVE[] = "SD:"; -static const char FILENAME_PRG[] = "SD:C16/rpimenu16.prg"; // .PRG to start -static const char FILENAME_CBM80[] = "SD:C16/launch16.cbm80"; // launch code (CBM80 8k cart) -static const char FILENAME_CONFIG[] = "SD:C16/sidekick264.cfg"; -static const char FILENAME_NEORAM[] = "SD:C16/launchNeoRAM.prg"; - -static const char FILENAME_SPLASH_HDMI[] = "SD:SPLASH/Sidekick-Logo.raw"; -static const char FILENAME_SPLASH_RGB[] = "SD:SPLASH/sk264_main.tga"; -static const char FILENAME_TFT_FONT[] = "SD:SPLASH/PXLfont88665b-RF2.3-C64sys.bin"; - -char FILENAME_LOGO_RGBA[128] = "SD:SPLASH/sk264_logo_blend.tga"; - -static u32 disableCart = 0; -static u32 resetCounter = 0; -static u32 transferStarted = 0; -static u32 currentOfs = 0; - -// the menu .PRG and program to launch -static u32 prgSize AAA; -static unsigned char prgData[ 65536 ] AAA; - -u32 prgSizeLaunch AAA; -unsigned char prgDataLaunch[ 65536 ] AAA; - -u32 prgCurSize = 0; -unsigned char *prgCurLaunch; - -// CBM80 to launch the menu -static unsigned char cart_pool[ 32768 + 128 ] AAA; -static unsigned char *cartL1 AA; -static unsigned char cart_c16[ 32768 ] AAA; -static unsigned char cartCBM80[ 16384 + 128 ] AA; - -// custom charset -static unsigned char charset[ 4096 ] AAA; - -u32 releaseDMA = 0; -u32 doneWithHandling = 0; -u32 pullIRQ = 0; -u32 c64CycleCount = 0; -u32 nBytesRead = 0; -u32 first = 1; - -u32 cartMode = 0; // 0 == launcher, 1 == c1lo cartridge from SD-card, 2 == C1lo+hi cartridge - -// this FILENAME may contain 2 strings at ofs 0 and 2048 -#define MAX_FILENAME_LENGTH 2048 -char FILENAME[ MAX_FILENAME_LENGTH * 2 ]; - -static u32 launchKernel = 0; -u32 lastChar = 0; - -static u8 *curTransfer = &c64screen[ 0 ]; - -char filenameKernal[ 2048 ]; - -CLogger *logger; -CScreenDevice *screen; - -static u32 LED_DEACTIVATE_CART1_HIGH; -static u32 LED_DEACTIVATE_CART1_LOW; -static u32 LED_DEACTIVATE_CART2_HIGH; -static u32 LED_DEACTIVATE_CART2_LOW; - -static u32 LED_ACTIVATE_CART1_HIGH; -static u32 LED_ACTIVATE_CART1_LOW; -static u32 LED_ACTIVATE_CART2_HIGH; -static u32 LED_ACTIVATE_CART2_LOW; - -static u32 LED_INIT1_HIGH; -static u32 LED_INIT1_LOW; -static u32 LED_INIT2_HIGH; -static u32 LED_INIT2_LOW; -static u32 LED_INIT3_HIGH; -static u32 LED_INIT3_LOW; -static u32 LED_INIT4_HIGH; -static u32 LED_INIT4_LOW; -static u32 LED_INIT5_HIGH; -static u32 LED_INIT5_LOW; -static u32 LED_INITERR_HIGH; -static u32 LED_INITERR_LOW; - -static void initScreenAndLEDCodes() -{ - if ( screenType == 0 ) // OLED with SCL and SDA (i.e. 2 Pins) -> 4 LEDs - { - LED_DEACTIVATE_CART1_HIGH = LATCH_LED_ALL; - LED_DEACTIVATE_CART1_LOW = 0; - LED_DEACTIVATE_CART2_HIGH = 0; - LED_DEACTIVATE_CART2_LOW = LATCH_LED_ALL; - - LED_ACTIVATE_CART1_HIGH = LATCH_LED_ALL; - LED_ACTIVATE_CART1_LOW = 0; - LED_ACTIVATE_CART2_HIGH = 0; - LED_ACTIVATE_CART2_LOW = LATCH_LED_ALL; - - LED_INIT1_HIGH = LATCH_LED0; - LED_INIT1_LOW = LATCH_LED1to3; - LED_INIT2_HIGH = LATCH_LED1; - LED_INIT2_LOW = 0; - LED_INIT3_HIGH = LATCH_LED2; - LED_INIT3_LOW = 0; - LED_INIT4_HIGH = LATCH_LED3; - LED_INIT4_LOW = 0; - LED_INIT5_HIGH = 0; - LED_INIT5_LOW = LATCH_LED_ALL; - LED_INITERR_HIGH = LATCH_LED_ALL; - LED_INITERR_LOW = 0; - } else - if ( screenType == 1 ) // RGB TFT with SCL, SDA, DC, RES -> 2 LEDs - { - LED_DEACTIVATE_CART1_HIGH = (LATCH_LED0|LATCH_LED1); - LED_DEACTIVATE_CART1_LOW = 0; - LED_DEACTIVATE_CART2_HIGH = 0; - LED_DEACTIVATE_CART2_LOW = (LATCH_LED0|LATCH_LED1); - - LED_ACTIVATE_CART1_HIGH = (LATCH_LED0|LATCH_LED1); - LED_ACTIVATE_CART1_LOW = 0; - LED_ACTIVATE_CART2_HIGH = 0; - LED_ACTIVATE_CART2_LOW = (LATCH_LED0|LATCH_LED1); - - LED_INIT1_HIGH = LATCH_LED0; - LED_INIT1_LOW = LATCH_LED1; - LED_INIT2_HIGH = LATCH_LED1; - LED_INIT2_LOW = LATCH_LED0; - LED_INIT3_HIGH = LATCH_LED0; - LED_INIT3_LOW = LATCH_LED1; - LED_INIT4_HIGH = LATCH_LED1; - LED_INIT4_LOW = LATCH_LED0; - LED_INIT5_HIGH = 0; - LED_INIT5_LOW = (LATCH_LED0|LATCH_LED1); - LED_INITERR_HIGH = (LATCH_LED0|LATCH_LED1); - LED_INITERR_LOW = 0; - } -} - - -u32 doActivateCart = 0; - -void deactivateCart() -{ - doActivateCart = 0; - disableCart = 1; - //latchSetClearImm( LED_DEACTIVATE_CART1_HIGH, LED_DEACTIVATE_CART1_LOW | LATCH_RESET | LATCH_ENABLE_KERNAL ); - latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); - SET_GPIO( bGAME | bEXROM | bNMI | bDMA ); - - if ( screenType == 0 ) - { - for ( int j = 255; j > 63; j -- ) - { - oledSetContrast( j ); - flushI2CBuffer( true ); - DELAY( 1 << 17 ); - } - } else - if ( screenType == 1 ) - { - flushI2CBuffer( true ); - tftBlendRGBA( 0, 0, 0, 128, tftFrameBuffer, 8 ); - tftSendFramebuffer16BitImm( tftFrameBuffer ); - //flush4BitBuffer( true ); - //DELAY( 1 << 17 ); - } - - //latchSetClearImm( LATCH_RESET | LED_DEACTIVATE_CART2_HIGH, LED_DEACTIVATE_CART2_LOW | LATCH_ENABLE_KERNAL ); - latchSetClearImm( LATCH_RESET, LATCH_ENABLE_KERNAL ); - -} - -void activateCart() -{ - disableCart = 0; - transferStarted = 0; - doActivateCart = 0; - - if ( cartMode > 0 ) - { - cartMode = 0; - memset( &cartL1[8192], 0, 32768-8192 ); - memcpy( cartL1, cartCBM80, 8192 ); - } - - latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); -// DELAY( 1 << 20 ); - - if ( screenType == 0 ) - { - oledSetContrast( 255 ); - flushI2CBuffer(); - } else - if ( screenType == 1 ) - { - tftCopyBackground2Framebuffer(); - tftSendFramebuffer16BitImm( tftFrameBuffer ); - flush4BitBuffer( true ); -// DELAY( 1 << 17 ); - } - - latchSetClearImm( LATCH_RESET, LATCH_ENABLE_KERNAL ); -} - -#ifdef COMPILE_MENU_WITH_SOUND -CTimer *pTimer; -CScheduler *pScheduler; -CInterruptSystem *pInterrupt; -CVCHIQDevice *pVCHIQ; -#endif - -boolean CKernelMenu::Initialize( void ) -{ - boolean bOK = TRUE; - - m_CPUThrottle.SetSpeed( CPUSpeedMaximum ); - -#ifdef USE_HDMI_VIDEO - if ( bOK ) bOK = m_Screen.Initialize(); - - if ( bOK ) - { - CDevice *pTarget = m_DeviceNameService.GetDevice( m_Options.GetLogDevice(), FALSE ); - if ( pTarget == 0 ) - pTarget = &m_Screen; - - screen = &m_Screen; - - bOK = m_Logger.Initialize( pTarget ); - logger = &m_Logger; - } -#endif - - if ( bOK ) bOK = m_Interrupt.Initialize(); - if ( bOK ) bOK = m_Timer.Initialize(); - m_EMMC.Initialize(); - -#ifdef COMPILE_MENU_WITH_SOUND - pTimer = &m_Timer; - pScheduler = &m_Scheduler; - pInterrupt = &m_Interrupt; - - if ( bOK ) bOK = m_VCHIQ.Initialize(); - pVCHIQ = &m_VCHIQ; -#endif - - // initialize ARM cycle counters (for accurate timing) - initCycleCounter(); - - // initialize GPIOs - gpioInit(); - - // initialize latch and software I2C buffer - initLatch(); - - initScreenAndLEDCodes(); - latchSetClearImm( LED_INIT1_HIGH, LED_INIT1_LOW ); - - u32 size = 0; - -#if 1 - u8 tempHDMI[ 640 * 480 * 3 ]; - readFile( logger, (char*)DRIVE, (char*)FILENAME_SPLASH_HDMI, tempHDMI, &size ); - u32 xOfs = ( screen->GetWidth() - 640 ) / 2; - u32 yOfs = ( screen->GetHeight() - 480 ) / 2; - u8 *p = tempHDMI; - for ( u32 j = 0; j < screen->GetHeight(); j++ ) - for ( u32 i = 0; i < screen->GetWidth(); i++ ) - { - #define _CONV_RGB(red, green, blue) (((red>>3) & 0x1F) << 11 \ - | ((green>>3) & 0x1F) << 6 \ - | ((blue>>3) & 0x1F)) - - int a = i - xOfs; if ( a < 0 ) a = 0; if ( a > 639 ) a = 639; - int b = j - yOfs; if ( b < 0 ) b = 0; if ( b > 479 ) b = 479; - p = &tempHDMI[ ( a + b * 640 ) * 3 ]; - screen->SetPixel( a, b, _CONV_RGB( p[ 0 ], p[ 1 ], p[ 2 ] ) ); - } -#endif - - // read launch code - readFile( logger, (char*)DRIVE, (char*)FILENAME_CBM80, cartCBM80, &size ); - - cartL1 = (unsigned char *)( ((u64)&cart_pool+64) & ~63 ); - - cartMode = 0; - memcpy( cartL1, cartCBM80, 8192 ); - - latchSetClearImm( LED_INIT2_HIGH, LED_INIT2_LOW ); - - // read .PRG - readFile( logger, (char*)DRIVE, (char*)FILENAME_PRG, prgData, &prgSize ); - - // first byte of prgData will be used for transmitting ceil(prgSize/256) - for ( u32 i = prgSize; i > 0; i-- ) - prgData[ i ] = prgData[ i - 1 ]; - prgData[0] = ( ( prgSize + 255 ) >> 8 ); - - - latchSetClearImm( LED_INIT3_HIGH, LED_INIT3_LOW ); - - extern void scanDirectories264( char *DRIVE ); - scanDirectories264( (char *)DRIVE ); - - latchSetClearImm( LED_INIT4_HIGH, LED_INIT4_LOW ); - - // - // guess automatic timings from current RPi clock rate - // - u32 rpiClockMHz = (u32)(m_CPUThrottle.GetClockRate () / 1000000); - if ( rpiClockMHz < 1500 ) - setDefaultTimings264( AUTO_TIMING_RPI0_264 ); else - setDefaultTimings264( AUTO_TIMING_RPI3_264 ); - - if ( !readConfig( logger, (char*)DRIVE, (char*)FILENAME_CONFIG ) ) - { - latchSetClearImm( LED_INITERR_HIGH, LED_INITERR_LOW ); - logger->Write( "RaspiMenu", LogPanic, "error reading .cfg" ); - } - - u32 t; - if ( skinFontFilename[0] != 0 && readFile( logger, (char*)DRIVE, (char*)skinFontFilename, charset, &t ) ) - { - skinFontLoaded = 1; - memcpy( 1024 + charset+8*(91), skcharlogo_raw, 224 ); - memcpy( 2048 + charset+8*(91), skcharlogo_raw, 224 ); - memcpy( charset + 94 * 8, charset + 2048 + 233 * 8, 8 ); - } - - readSettingsFile(); - applySIDSettings(); - renderC64(); - disableCart = 0; - - latchSetClearImm( LED_INIT5_HIGH, LED_INIT5_LOW ); - - return bOK; -} - - -// cache warmup - -volatile u8 forceRead; - -__attribute__( ( always_inline ) ) inline void warmCache( void *fiqh, bool screen=true ) -{ - if ( screen ) - { - CACHE_PRELOAD_DATA_CACHE( c64screen, 1024, CACHE_PRELOADL2STRM ); - CACHE_PRELOAD_DATA_CACHE( c64color, 1024, CACHE_PRELOADL2STRM ); - } - CACHE_PRELOAD_DATA_CACHE( prgData, 65536, CACHE_PRELOADL2STRM ); - CACHE_PRELOAD_DATA_CACHE( cartL1, 32768, CACHE_PRELOADL2KEEP ); - - CACHE_PRELOAD_INSTRUCTION_CACHE( (void*)fiqh, 2048*2 ); - - FORCE_READ_LINEARa( cartL1, 32768, 32768 * 10 ) - FORCE_READ_LINEARa( prgData, prgSize, 65536 * 4 ) - FORCE_READ_LINEARa( (void*)fiqh, 2048*2, 65536 ); -} - -void CKernelMenu::Run( void ) -{ - nBytesRead = 0; - c64CycleCount = 0; - lastChar = 0; - resetCounter = 0; - transferStarted = 0; - currentOfs = 0; - launchKernel = 0; - updateMenu = 0; - subGeoRAM = 0; - //subSID = 0; - subHasLaunch = -1; - FILENAME[0] = 0; - first = 1; - prgCurSize = prgSize; - prgCurLaunch = &prgData[0]; - - if ( screenType == 0 ) - { - // no idea why, but I have one SSD1306 that requires this to be called twice after power up - splashScreen( raspi_264_splash ); - splashScreen( raspi_264_splash ); - } else - if ( screenType == 1 ) - { - tftLoadCharset( DRIVE, FILENAME_TFT_FONT ); - tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB, 8 ); - tftCopyBackground2Framebuffer(); - tftInitImm( screenRotation ); - tftSendFramebuffer16BitImm( tftFrameBuffer ); - } - - if ( !disableCart ) - { - latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); - - // setup FIQ - DisableIRQs(); - m_InputPin.ConnectInterrupt( m_InputPin.FIQHandler, this ); - m_InputPin.EnableInterrupt( GPIOInterruptOnRisingEdge ); - - launchKernel = 0; - - warmCache( (void*)this->FIQHandler ); - warmCache( (void*)this->FIQHandler ); - warmCache( (void*)this->FIQHandler ); - - // start c64 - DELAY(1<<10); - latchSetClearImm( LATCH_RESET, 0 ); - } - - // wait forever - while ( true ) - { - asm volatile ("wfi"); - - #if 1 - if ( doActivateCart ) - activateCart(); - - if ( launchKernel ) - { - m_InputPin.DisableInterrupt(); - m_InputPin.DisconnectInterrupt(); - EnableIRQs(); - return; - } - - if ( updateMenu == 1 ) - { - handleC64( lastChar, &launchKernel, FILENAME, filenameKernal ); - renderC64(); - - /*char tt[64]; - static int lastPrintChar = 0; - if ( lastChar != 0 ) - lastPrintChar = lastChar; - sprintf( tt, "key=%d", lastPrintChar ); - printC64( 0, 0, tt, 1, 0 );*/ - - //c64screen[0]=lastChar&255; - lastChar = 0xfffffff; - doneWithHandling = 1; - updateMenu = 0; - - - if ( launchKernel == 5 ) - // either: launch CRT, no need to call an external RPi-kernel - // or: NeoRAM - Autostart - { - if ( FILENAME[ 2048 ] != 0 && strcmp( &FILENAME[ 2048 ], "neoram" ) == 0 ) - { - launchKernel = 6; - // trigger IRQ on C16/+4 which tells the menu code that the new screen is ready - DELAY( 1 << 17 ); - CLR_GPIO( bNMI ); - pullIRQ = 64; - m_InputPin.DisableInterrupt(); - m_InputPin.DisconnectInterrupt(); - EnableIRQs(); - return; - } else - { - u32 size; - //logger->Write( "menu", LogNotice, "reading %s", (char*)&FILENAME[0] ); - readFile( logger, (char*)DRIVE, (char*)&FILENAME[0], &cart_c16[0], &size ); - cartMode = 1; - if ( FILENAME[ 2048 ] != 0 ) - { - //logger->Write( "menu", LogNotice, "reading %s", (char*)&FILENAME[2048] ); - readFile( logger, (char*)DRIVE, (char*)&FILENAME[2048], &cart_c16[0x4000], &size ); - cartMode = 2; - } - memcpy( cartL1, cart_c16, 32768 ); - CLR_GPIO( bCTRL257 ); - latchSetClearImm( LATCH_LED0to1, LATCH_RESET | LATCH_ENABLE_KERNAL ); - DELAY( 1 << 17 ); - SET_GPIO( bNMI ); - latchSetClearImm( LATCH_RESET, LATCH_LED0to1 | LATCH_ENABLE_KERNAL ); - } - } else - { - CACHE_PRELOAD_DATA_CACHE( c64screen, 1024, CACHE_PRELOADL2STRM ); - CACHE_PRELOAD_DATA_CACHE( c64color, 1024, CACHE_PRELOADL2STRM ); - // trigger IRQ on C16/+4 which tells the menu code that the new screen is ready - DELAY( 1 << 17 ); - CLR_GPIO( bNMI ); - pullIRQ = 64; - } - } - #endif - } - - // and we'll never reach this... - m_InputPin.DisableInterrupt(); -} - - -void CKernelMenu::FIQHandler (void *pParam) {} - -void CGPIOPinFIQ2::FIQHandler( void *pParam ) -{ - register u32 D; - - START_AND_READ_EXPPORT264 - - if ( disableCart ) - goto get_out; - - if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 == 0xfde5 ) - { - PUT_DATA_ON_BUS_AND_CLEAR257( *( curTransfer++ ) ) - CACHE_PRELOADL2STRM( curTransfer ); - goto get_out; - } - - if ( BUS_AVAILABLE264 && C1LO_ACCESS && CPU_READS_FROM_BUS ) - { - PUT_DATA_ON_BUS_AND_CLEAR257( cartL1[ GET_ADDRESS264 & 0x3fff ] ) - goto get_out; - } - - if ( BUS_AVAILABLE264 && C1HI_ACCESS && CPU_READS_FROM_BUS && cartMode == 2 ) - { - PUT_DATA_ON_BUS_AND_CLEAR257( cartL1[ 0x4000 + (GET_ADDRESS264 & 0x3fff) ] ) - goto get_out; - } - - if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 >= 0xfd90 && GET_ADDRESS264 <= 0xfd97 ) - { - PUT_DATA_ON_BUS_AND_CLEAR257( GET_ADDRESS264 - 0xfd90 + ( (cartMode==0)?0:1 ) ) - CACHE_PRELOADL2STRM( curTransfer ); - goto get_out; - } - - if ( CPU_WRITES_TO_BUS ) - { - READ_D0to7_FROM_BUS( D ) - - if ( GET_ADDRESS264 == 0xfd91 ) - { - // keypress - lastChar = D; - updateMenu = 1; - doneWithHandling = 0; - goto get_out; - } else - if ( GET_ADDRESS264 == 0xfde5 ) - { - if ( D == 0 ) - curTransfer = &c64screen[ 0 ]; else - if ( D == 1 ) - curTransfer = &c64color[ 0 ]; else - if ( D == 2 ) - curTransfer = &charset[ 0 ]; else - if ( D >= 250 ) - machine264 = D - 252; else - curTransfer = &prgCurLaunch[0]; - - // TODO disable Cart when D=123? - - CACHE_PRELOADL2STRM( curTransfer ); - goto get_out; - } - } - - -get_out: - register CGPIOPinFIQ2 *pThis = (CGPIOPinFIQ2 *)pParam; - PeripheralEntry(); - write32( ARM_GPIO_GPEDS0 + pThis->m_nRegOffset, pThis->m_nRegMask ); - PeripheralExit(); - - if ( pullIRQ ) - { - if ( --pullIRQ == 0 ) - SET_GPIO( bNMI ); - } - - if ( BUTTON_PRESSED && ( disableCart || cartMode > 0 ) ) - { - doActivateCart = 1; - } - - UPDATE_COUNTERS_MIN( c64CycleCount, resetCounter ) - - if ( resetCounter > 3 ) - { - resetCounter = 0; - c64CycleCount = 0; - } - - write32( ARM_GPIO_GPCLR0, bCTRL257 ); - - RESET_CPU_CYCLE_COUNTER -} - - -void mainMenu() -{ - CKernelMenu kernel; - if ( kernel.Initialize() ) - kernel.Run(); - setLatchFIQ( LATCH_LED0 ); - prepareOutputLatch(); - outputLatch(); -} - -int main( void ) -{ - CKernelMenu kernel; - kernel.Initialize(); - - extern void KernelLaunchRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); - extern void KernelSIDRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); - extern void KernelRLRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME_KERNAL, const char *FILENAME, const char *FILENAME_RAM, u32 sizeRAM, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); - - while ( true ) - { - //latchSetClearImm( LATCH_LED1, 0 ); - - kernel.Run(); - - //latchSetClearImm( LATCH_LED0, LATCH_RESET | LATCH_ENABLE_KERNAL ); - SET_GPIO( bNMI | bDMA ); - - BEGIN_CYCLE_COUNTER - WAIT_UP_TO_CYCLE( 5000*1000 ) - - //logger->Write( "menu", LogNotice, "launch kernel %d", launchKernel ); - - char geoRAMFile[ 2048 ]; - u32 geoRAMSize; - - /* for debugging purposes only*/ - /*if ( launchKernel == 255 ) - { - reboot (); - } else*/ - switch ( launchKernel ) - { - case 3: - settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); - KernelRLRun( kernel.m_InputPin, &kernel, NULL, NULL, geoRAMFile, geoRAMSize ); - break; - case 4: - if ( subSID ) { - KernelSIDRun( kernel.m_InputPin, &kernel, FILENAME, false ); - break; - } - if ( subGeoRAM ) { - settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); - KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME, geoRAMFile, geoRAMSize, false ); - break; - } else { - KernelLaunchRun( kernel.m_InputPin, &kernel, FILENAME, false ); - } - doActivateCart = 1; - disableCart = 0; - break; - case 6: - KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME_NEORAM, FILENAME, 4096, false ); - break; - case 40: - if ( subSID ) { - KernelSIDRun( kernel.m_InputPin, &kernel, FILENAME, true, prgDataLaunch, prgSizeLaunch ); - break; - } - if ( subGeoRAM ) { - settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); - KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME, geoRAMFile, geoRAMSize, true, prgDataLaunch, prgSizeLaunch ); - break; - } else { - KernelLaunchRun( kernel.m_InputPin, &kernel, FILENAME, true, prgDataLaunch, prgSizeLaunch ); - } - break; - case 8: - KernelSIDRun( kernel.m_InputPin, &kernel, NULL ); - break; - default: - break; - } - } - - return EXIT_HALT; -} +/* + _________.__ .___ __ .__ __ _____ + / _____/|__| __| _/____ | | _|__| ____ | | __ / \ ____ ____ __ __ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / \ / \_/ __ \ / \| | \ + / \| / /_/ \ ___/| <| \ \___| < / Y \ ___/| | \ | / +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \____|__ /\___ >___| /____/ + \/ \/ \/ \/ \/ \/ \/ \/ \/ + + kernel_menu264.cpp + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - Sidekick Menu: ugly glue code to expose some functionality in one menu with browser + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include "kernel_menu264.h" +#include "dirscan.h" +#include "config.h" +#include "264screen.h" +#include "charlogo.h" + +// we will read these files +static const char DRIVE[] = "SD:"; +static const char FILENAME_PRG[] = "SD:C16/rpimenu16.prg"; // .PRG to start +static const char FILENAME_CBM80[] = "SD:C16/launch16.cbm80"; // launch code (CBM80 8k cart) +static const char FILENAME_CONFIG[] = "SD:C16/sidekick264.cfg"; +static const char FILENAME_NEORAM[] = "SD:C16/launchNeoRAM.prg"; + +static const char FILENAME_SPLASH_HDMI[] = "SD:SPLASH/Sidekick-Logo.raw"; +static const char FILENAME_SPLASH_RGB[] = "SD:SPLASH/sk264_main.tga"; +static const char FILENAME_TFT_FONT[] = "SD:SPLASH/PXLfont88665b-RF2.3-C64sys.bin"; + +char FILENAME_LOGO_RGBA[128] = "SD:SPLASH/sk264_logo_blend.tga"; + +static u32 disableCart = 0; +static u32 resetCounter = 0; +static u32 transferStarted = 0; +static u32 currentOfs = 0; + +// the menu .PRG and program to launch +static u32 prgSize AAA; +static unsigned char prgData[ 65536 ] AAA; + +u32 prgSizeLaunch AAA; +unsigned char prgDataLaunch[ 65536 ] AAA; + +u32 prgCurSize = 0; +unsigned char *prgCurLaunch; + +// CBM80 to launch the menu +static unsigned char cart_pool[ 32768 + 128 ] AAA; +static unsigned char *cartL1 AA; +static unsigned char cart_c16[ 32768 ] AAA; +static unsigned char cartCBM80[ 16384 + 128 ] AA; + +// custom charset +static unsigned char charset[ 4096 ] AAA; + +u32 releaseDMA = 0; +u32 doneWithHandling = 0; +u32 pullIRQ = 0; +u32 c64CycleCount = 0; +u32 nBytesRead = 0; +u32 first = 1; + +u32 cartMode = 0; // 0 == launcher, 1 == c1lo cartridge from SD-card, 2 == C1lo+hi cartridge + +// this FILENAME may contain 2 strings at ofs 0 and 2048 +#define MAX_FILENAME_LENGTH 2048 +char FILENAME[ MAX_FILENAME_LENGTH * 2 ]; + +static u32 launchKernel = 0; +u32 lastChar = 0; + +static u8 *curTransfer = &c64screen[ 0 ]; + +char filenameKernal[ 2048 ]; + +CLogger *logger; +CScreenDevice *screen; + +static u32 LED_DEACTIVATE_CART1_HIGH; +static u32 LED_DEACTIVATE_CART1_LOW; +static u32 LED_DEACTIVATE_CART2_HIGH; +static u32 LED_DEACTIVATE_CART2_LOW; + +static u32 LED_ACTIVATE_CART1_HIGH; +static u32 LED_ACTIVATE_CART1_LOW; +static u32 LED_ACTIVATE_CART2_HIGH; +static u32 LED_ACTIVATE_CART2_LOW; + +static u32 LED_INIT1_HIGH; +static u32 LED_INIT1_LOW; +static u32 LED_INIT2_HIGH; +static u32 LED_INIT2_LOW; +static u32 LED_INIT3_HIGH; +static u32 LED_INIT3_LOW; +static u32 LED_INIT4_HIGH; +static u32 LED_INIT4_LOW; +static u32 LED_INIT5_HIGH; +static u32 LED_INIT5_LOW; +static u32 LED_INITERR_HIGH; +static u32 LED_INITERR_LOW; + +static void initScreenAndLEDCodes() +{ + if ( screenType == 0 ) // OLED with SCL and SDA (i.e. 2 Pins) -> 4 LEDs + { + LED_DEACTIVATE_CART1_HIGH = LATCH_LED_ALL; + LED_DEACTIVATE_CART1_LOW = 0; + LED_DEACTIVATE_CART2_HIGH = 0; + LED_DEACTIVATE_CART2_LOW = LATCH_LED_ALL; + + LED_ACTIVATE_CART1_HIGH = LATCH_LED_ALL; + LED_ACTIVATE_CART1_LOW = 0; + LED_ACTIVATE_CART2_HIGH = 0; + LED_ACTIVATE_CART2_LOW = LATCH_LED_ALL; + + LED_INIT1_HIGH = LATCH_LED0; + LED_INIT1_LOW = LATCH_LED1to3; + LED_INIT2_HIGH = LATCH_LED1; + LED_INIT2_LOW = 0; + LED_INIT3_HIGH = LATCH_LED2; + LED_INIT3_LOW = 0; + LED_INIT4_HIGH = LATCH_LED3; + LED_INIT4_LOW = 0; + LED_INIT5_HIGH = 0; + LED_INIT5_LOW = LATCH_LED_ALL; + LED_INITERR_HIGH = LATCH_LED_ALL; + LED_INITERR_LOW = 0; + } else + if ( screenType == 1 ) // RGB TFT with SCL, SDA, DC, RES -> 2 LEDs + { + LED_DEACTIVATE_CART1_HIGH = (LATCH_LED0|LATCH_LED1); + LED_DEACTIVATE_CART1_LOW = 0; + LED_DEACTIVATE_CART2_HIGH = 0; + LED_DEACTIVATE_CART2_LOW = (LATCH_LED0|LATCH_LED1); + + LED_ACTIVATE_CART1_HIGH = (LATCH_LED0|LATCH_LED1); + LED_ACTIVATE_CART1_LOW = 0; + LED_ACTIVATE_CART2_HIGH = 0; + LED_ACTIVATE_CART2_LOW = (LATCH_LED0|LATCH_LED1); + + LED_INIT1_HIGH = LATCH_LED0; + LED_INIT1_LOW = LATCH_LED1; + LED_INIT2_HIGH = LATCH_LED1; + LED_INIT2_LOW = LATCH_LED0; + LED_INIT3_HIGH = LATCH_LED0; + LED_INIT3_LOW = LATCH_LED1; + LED_INIT4_HIGH = LATCH_LED1; + LED_INIT4_LOW = LATCH_LED0; + LED_INIT5_HIGH = 0; + LED_INIT5_LOW = (LATCH_LED0|LATCH_LED1); + LED_INITERR_HIGH = (LATCH_LED0|LATCH_LED1); + LED_INITERR_LOW = 0; + } +} + + +u32 doActivateCart = 0; + +void deactivateCart() +{ + doActivateCart = 0; + disableCart = 1; + //latchSetClearImm( LED_DEACTIVATE_CART1_HIGH, LED_DEACTIVATE_CART1_LOW | LATCH_RESET | LATCH_ENABLE_KERNAL ); + latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); + SET_GPIO( bGAME | bEXROM | bNMI | bDMA ); + + if ( screenType == 0 ) + { + for ( int j = 255; j > 63; j -- ) + { + oledSetContrast( j ); + flushI2CBuffer( true ); + DELAY( 1 << 17 ); + } + } else + if ( screenType == 1 ) + { + flushI2CBuffer( true ); + tftBlendRGBA( 0, 0, 0, 128, tftFrameBuffer, 8 ); + tftSendFramebuffer16BitImm( tftFrameBuffer ); + //flush4BitBuffer( true ); + //DELAY( 1 << 17 ); + } + + //latchSetClearImm( LATCH_RESET | LED_DEACTIVATE_CART2_HIGH, LED_DEACTIVATE_CART2_LOW | LATCH_ENABLE_KERNAL ); + latchSetClearImm( LATCH_RESET, LATCH_ENABLE_KERNAL ); + +} + +void activateCart() +{ + disableCart = 0; + transferStarted = 0; + doActivateCart = 0; + + if ( cartMode > 0 ) + { + cartMode = 0; + memset( &cartL1[8192], 0, 32768-8192 ); + memcpy( cartL1, cartCBM80, 8192 ); + } + + latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); +// DELAY( 1 << 20 ); + + if ( screenType == 0 ) + { + oledSetContrast( 255 ); + flushI2CBuffer(); + } else + if ( screenType == 1 ) + { + tftCopyBackground2Framebuffer(); + tftSendFramebuffer16BitImm( tftFrameBuffer ); + flush4BitBuffer( true ); +// DELAY( 1 << 17 ); + } + + latchSetClearImm( LATCH_RESET, LATCH_ENABLE_KERNAL ); +} + +#define HDMI_SOUND + +s32 lastHDMISoundSample = 0; +#ifdef HDMI_SOUND +u8 hdmiSoundAvailable = 0; +CHDMISoundBaseDevice *hdmiSoundDevice = NULL; +#endif + +#ifdef COMPILE_MENU_WITH_SOUND +CTimer *pTimer; +CScheduler *pScheduler; +CInterruptSystem *pInterrupt; +CVCHIQDevice *pVCHIQ; +#endif + +boolean CKernelMenu::Initialize( void ) +{ + boolean bOK = TRUE; + + m_CPUThrottle.SetSpeed( CPUSpeedMaximum ); + +#ifdef USE_HDMI_VIDEO + if ( bOK ) bOK = m_Screen.Initialize(); + + if ( bOK ) + { + CDevice *pTarget = m_DeviceNameService.GetDevice( m_Options.GetLogDevice(), FALSE ); + if ( pTarget == 0 ) + pTarget = &m_Screen; + + screen = &m_Screen; + + bOK = m_Logger.Initialize( pTarget ); + logger = &m_Logger; + } +#endif + + if ( bOK ) bOK = m_Interrupt.Initialize(); + if ( bOK ) bOK = m_Timer.Initialize(); + m_EMMC.Initialize(); + +#ifdef COMPILE_MENU_WITH_SOUND + pTimer = &m_Timer; + pScheduler = &m_Scheduler; + pInterrupt = &m_Interrupt; + + if ( bOK ) bOK = m_VCHIQ.Initialize(); + pVCHIQ = &m_VCHIQ; +#endif + + // initialize ARM cycle counters (for accurate timing) + initCycleCounter(); + + // initialize GPIOs + gpioInit(); + + // initialize latch and software I2C buffer + initLatch(); + + initScreenAndLEDCodes(); + latchSetClearImm( LED_INIT1_HIGH, LED_INIT1_LOW ); + + u32 size = 0; + +#if 1 + u8 tempHDMI[ 640 * 480 * 3 ]; + readFile( logger, (char*)DRIVE, (char*)FILENAME_SPLASH_HDMI, tempHDMI, &size ); + u32 xOfs = ( screen->GetWidth() - 640 ) / 2; + u32 yOfs = ( screen->GetHeight() - 480 ) / 2; + u8 *p = tempHDMI; + for ( u32 j = 0; j < screen->GetHeight(); j++ ) + for ( u32 i = 0; i < screen->GetWidth(); i++ ) + { + #define _CONV_RGB(red, green, blue) (((red>>3) & 0x1F) << 11 \ + | ((green>>3) & 0x1F) << 6 \ + | ((blue>>3) & 0x1F)) + + int a = i - xOfs; if ( a < 0 ) a = 0; if ( a > 639 ) a = 639; + int b = j - yOfs; if ( b < 0 ) b = 0; if ( b > 479 ) b = 479; + p = &tempHDMI[ ( a + b * 640 ) * 3 ]; + screen->SetPixel( a, b, _CONV_RGB( p[ 0 ], p[ 1 ], p[ 2 ] ) ); + } +#endif + + // read launch code + readFile( logger, (char*)DRIVE, (char*)FILENAME_CBM80, cartCBM80, &size ); + + cartL1 = (unsigned char *)( ((u64)&cart_pool+64) & ~63 ); + + cartMode = 0; + memcpy( cartL1, cartCBM80, 8192 ); + + latchSetClearImm( LED_INIT2_HIGH, LED_INIT2_LOW ); + + // read .PRG + readFile( logger, (char*)DRIVE, (char*)FILENAME_PRG, prgData, &prgSize ); + + // first byte of prgData will be used for transmitting ceil(prgSize/256) + for ( u32 i = prgSize; i > 0; i-- ) + prgData[ i ] = prgData[ i - 1 ]; + prgData[0] = ( ( prgSize + 255 ) >> 8 ); + + + latchSetClearImm( LED_INIT3_HIGH, LED_INIT3_LOW ); + + extern void scanDirectories264( char *DRIVE ); + scanDirectories264( (char *)DRIVE ); + + latchSetClearImm( LED_INIT4_HIGH, LED_INIT4_LOW ); + + // + // guess automatic timings from current RPi clock rate + // + u32 rpiClockMHz = (u32)(m_CPUThrottle.GetClockRate () / 1000000); + if ( rpiClockMHz < 1500 ) + setDefaultTimings264( AUTO_TIMING_RPI0_264 ); else + setDefaultTimings264( AUTO_TIMING_RPI3_264 ); + + if ( !readConfig( logger, (char*)DRIVE, (char*)FILENAME_CONFIG ) ) + { + latchSetClearImm( LED_INITERR_HIGH, LED_INITERR_LOW ); + logger->Write( "RaspiMenu", LogPanic, "error reading .cfg" ); + } + + u32 t; + if ( skinFontFilename[0] != 0 && readFile( logger, (char*)DRIVE, (char*)skinFontFilename, charset, &t ) ) + { + skinFontLoaded = 1; + memcpy( 1024 + charset+8*(91), skcharlogo_raw, 224 ); + memcpy( 2048 + charset+8*(91), skcharlogo_raw, 224 ); + memcpy( charset + 94 * 8, charset + 2048 + 233 * 8, 8 ); + } + + readSettingsFile(); + +#ifdef HDMI_SOUND + hdmiSoundAvailable = 1; + hdmiSoundDevice = new CHDMISoundBaseDevice( 48000 ); + if ( hdmiSoundDevice ) + { + hdmiSoundDevice->SetWriteFormat( SoundFormatSigned24, 2 ); + if (!hdmiSoundDevice->Start ()) + { + //m_Logger.Write ("SK264", LogPanic, "Cannot start sound device"); + hdmiSoundAvailable = 0; + } else + hdmiSoundDevice->Cancel(); + } else + hdmiSoundAvailable = 0; +#endif + + applySIDSettings(); + renderC64(); + disableCart = 0; + + latchSetClearImm( LED_INIT5_HIGH, LED_INIT5_LOW ); + + return bOK; +} + + +// cache warmup + +volatile u8 forceRead; + +__attribute__( ( always_inline ) ) inline void warmCache( void *fiqh, bool screen=true ) +{ + if ( screen ) + { + CACHE_PRELOAD_DATA_CACHE( c64screen, 1024, CACHE_PRELOADL2STRM ); + CACHE_PRELOAD_DATA_CACHE( c64color, 1024, CACHE_PRELOADL2STRM ); + } + CACHE_PRELOAD_DATA_CACHE( prgData, 65536, CACHE_PRELOADL2STRM ); + CACHE_PRELOAD_DATA_CACHE( cartL1, 32768, CACHE_PRELOADL2KEEP ); + + CACHE_PRELOAD_INSTRUCTION_CACHE( (void*)fiqh, 2048*2 ); + + FORCE_READ_LINEARa( cartL1, 32768, 32768 * 10 ) + FORCE_READ_LINEARa( prgData, prgSize, 65536 * 4 ) + FORCE_READ_LINEARa( (void*)fiqh, 2048*2, 65536 ); +} + +void CKernelMenu::Run( void ) +{ + nBytesRead = 0; + c64CycleCount = 0; + lastChar = 0; + resetCounter = 0; + transferStarted = 0; + currentOfs = 0; + launchKernel = 0; + updateMenu = 0; + subGeoRAM = 0; + //subSID = 0; + subHasLaunch = -1; + FILENAME[0] = 0; + first = 1; + prgCurSize = prgSize; + prgCurLaunch = &prgData[0]; + + if ( screenType == 0 ) + { + // no idea why, but I have one SSD1306 that requires this to be called twice after power up + splashScreen( raspi_264_splash ); + splashScreen( raspi_264_splash ); + } else + if ( screenType == 1 ) + { + tftLoadCharset( DRIVE, FILENAME_TFT_FONT ); + tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB, 8 ); + tftCopyBackground2Framebuffer(); + tftInitImm( screenRotation ); + tftSendFramebuffer16BitImm( tftFrameBuffer ); + } + + if ( hdmiSoundDevice ) + hdmiSoundDevice->Start(); + + if ( !disableCart ) + { + latchSetClearImm( 0, LATCH_RESET | LATCH_ENABLE_KERNAL ); + + // setup FIQ + DisableIRQs(); + m_InputPin.ConnectInterrupt( m_InputPin.FIQHandler, this ); + m_InputPin.EnableInterrupt( GPIOInterruptOnRisingEdge ); + + launchKernel = 0; + + warmCache( (void*)this->FIQHandler ); + warmCache( (void*)this->FIQHandler ); + warmCache( (void*)this->FIQHandler ); + + // start c64 + DELAY(1<<10); + latchSetClearImm( LATCH_RESET, 0 ); + } + + // wait forever + while ( true ) + { + asm volatile ("wfi"); + + #if 1 + if ( doActivateCart ) + activateCart(); + + if ( launchKernel ) + { + m_InputPin.DisableInterrupt(); + m_InputPin.DisconnectInterrupt(); + EnableIRQs(); + return; + } + + if ( updateMenu == 1 ) + { + handleC64( lastChar, &launchKernel, FILENAME, filenameKernal ); + renderC64(); + + /*char tt[64]; + static int lastPrintChar = 0; + if ( lastChar != 0 ) + lastPrintChar = lastChar; + sprintf( tt, "key=%d", lastPrintChar ); + printC64( 0, 0, tt, 1, 0 );*/ + + //c64screen[0]=lastChar&255; + lastChar = 0xfffffff; + doneWithHandling = 1; + updateMenu = 0; + + + if ( launchKernel == 5 ) + // either: launch CRT, no need to call an external RPi-kernel + // or: NeoRAM - Autostart + { + if ( FILENAME[ 2048 ] != 0 && strcmp( &FILENAME[ 2048 ], "neoram" ) == 0 ) + { + launchKernel = 6; + // trigger IRQ on C16/+4 which tells the menu code that the new screen is ready + DELAY( 1 << 17 ); + CLR_GPIO( bNMI ); + pullIRQ = 64; + m_InputPin.DisableInterrupt(); + m_InputPin.DisconnectInterrupt(); + EnableIRQs(); + return; + } else + { + u32 size; + //logger->Write( "menu", LogNotice, "reading %s", (char*)&FILENAME[0] ); + readFile( logger, (char*)DRIVE, (char*)&FILENAME[0], &cart_c16[0], &size ); + cartMode = 1; + if ( FILENAME[ 2048 ] != 0 ) + { + //logger->Write( "menu", LogNotice, "reading %s", (char*)&FILENAME[2048] ); + readFile( logger, (char*)DRIVE, (char*)&FILENAME[2048], &cart_c16[0x4000], &size ); + cartMode = 2; + } + memcpy( cartL1, cart_c16, 32768 ); + CLR_GPIO( bCTRL257 ); + latchSetClearImm( LATCH_LED0to1, LATCH_RESET | LATCH_ENABLE_KERNAL ); + DELAY( 1 << 17 ); + SET_GPIO( bNMI ); + latchSetClearImm( LATCH_RESET, LATCH_LED0to1 | LATCH_ENABLE_KERNAL ); + } + } else + { + CACHE_PRELOAD_DATA_CACHE( c64screen, 1024, CACHE_PRELOADL2STRM ); + CACHE_PRELOAD_DATA_CACHE( c64color, 1024, CACHE_PRELOADL2STRM ); + // trigger IRQ on C16/+4 which tells the menu code that the new screen is ready + DELAY( 1 << 17 ); + CLR_GPIO( bNMI ); + pullIRQ = 64; + } + } + #endif + } + + // and we'll never reach this... + m_InputPin.DisableInterrupt(); +} + + +void CKernelMenu::FIQHandler (void *pParam) {} + +void CGPIOPinFIQ2::FIQHandler( void *pParam ) +{ + register u32 D; + + START_AND_READ_EXPPORT264 + + if ( disableCart ) + goto get_out; + + if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 == 0xfde5 ) + { + PUT_DATA_ON_BUS_AND_CLEAR257( *( curTransfer++ ) ) + CACHE_PRELOADL2STRM( curTransfer ); + goto get_out; + } + + if ( BUS_AVAILABLE264 && C1LO_ACCESS && CPU_READS_FROM_BUS ) + { + PUT_DATA_ON_BUS_AND_CLEAR257( cartL1[ GET_ADDRESS264 & 0x3fff ] ) + goto get_out; + } + + if ( BUS_AVAILABLE264 && C1HI_ACCESS && CPU_READS_FROM_BUS && cartMode == 2 ) + { + PUT_DATA_ON_BUS_AND_CLEAR257( cartL1[ 0x4000 + (GET_ADDRESS264 & 0x3fff) ] ) + goto get_out; + } + + if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 >= 0xfd90 && GET_ADDRESS264 <= 0xfd97 ) + { + PUT_DATA_ON_BUS_AND_CLEAR257( GET_ADDRESS264 - 0xfd90 + ( (cartMode==0)?0:1 ) ) + CACHE_PRELOADL2STRM( curTransfer ); + goto get_out; + } + + if ( CPU_WRITES_TO_BUS ) + { + READ_D0to7_FROM_BUS( D ) + + if ( GET_ADDRESS264 == 0xfd91 ) + { + // keypress + lastChar = D; + updateMenu = 1; + doneWithHandling = 0; + goto get_out; + } else + if ( GET_ADDRESS264 == 0xfde5 ) + { + if ( D == 0 ) + curTransfer = &c64screen[ 0 ]; else + if ( D == 1 ) + curTransfer = &c64color[ 0 ]; else + if ( D == 2 ) + curTransfer = &charset[ 0 ]; else + if ( D >= 250 ) + machine264 = D - 252; else + curTransfer = &prgCurLaunch[0]; + + // TODO disable Cart when D=123? + + CACHE_PRELOADL2STRM( curTransfer ); + goto get_out; + } + } + + +get_out: + register CGPIOPinFIQ2 *pThis = (CGPIOPinFIQ2 *)pParam; + PeripheralEntry(); + write32( ARM_GPIO_GPEDS0 + pThis->m_nRegOffset, pThis->m_nRegMask ); + PeripheralExit(); + + if ( pullIRQ ) + { + if ( --pullIRQ == 0 ) + SET_GPIO( bNMI ); + } + + if ( BUTTON_PRESSED && ( disableCart || cartMode > 0 ) ) + { + doActivateCart = 1; + } + + UPDATE_COUNTERS_MIN( c64CycleCount, resetCounter ) + + if ( resetCounter > 3 ) + { + resetCounter = 0; + c64CycleCount = 0; + } + + write32( ARM_GPIO_GPCLR0, bCTRL257 ); + + RESET_CPU_CYCLE_COUNTER +} + + +void mainMenu() +{ + CKernelMenu kernel; + if ( kernel.Initialize() ) + kernel.Run(); + setLatchFIQ( LATCH_LED0 ); + prepareOutputLatch(); + outputLatch(); +} + +int main( void ) +{ + CKernelMenu kernel; + kernel.Initialize(); + + extern void KernelLaunchRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); + extern void KernelSIDRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); + extern void KernelRLRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME_KERNAL, const char *FILENAME, const char *FILENAME_RAM, u32 sizeRAM, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ); + + while ( true ) + { + //latchSetClearImm( LATCH_LED1, 0 ); + + kernel.Run(); + + //latchSetClearImm( LATCH_LED0, LATCH_RESET | LATCH_ENABLE_KERNAL ); + SET_GPIO( bNMI | bDMA ); + + BEGIN_CYCLE_COUNTER + WAIT_UP_TO_CYCLE( 5000*1000 ) + + //logger->Write( "menu", LogNotice, "launch kernel %d", launchKernel ); + + char geoRAMFile[ 2048 ]; + u32 geoRAMSize; + + /* for debugging purposes only*/ + /*if ( launchKernel == 255 ) + { + reboot (); + } else*/ + switch ( launchKernel ) + { + case 3: + settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); + KernelRLRun( kernel.m_InputPin, &kernel, NULL, NULL, geoRAMFile, geoRAMSize ); + break; + case 4: + if ( subSID ) { + KernelSIDRun( kernel.m_InputPin, &kernel, FILENAME, false ); + break; + } + if ( subGeoRAM ) { + settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); + KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME, geoRAMFile, geoRAMSize, false ); + break; + } else { + KernelLaunchRun( kernel.m_InputPin, &kernel, FILENAME, false ); + } + doActivateCart = 1; + disableCart = 0; + break; + case 6: + KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME_NEORAM, FILENAME, 4096, false ); + break; + case 40: + if ( subSID ) { + KernelSIDRun( kernel.m_InputPin, &kernel, FILENAME, true, prgDataLaunch, prgSizeLaunch ); + break; + } + if ( subGeoRAM ) { + settingsGetGEORAMInfo( geoRAMFile, &geoRAMSize ); + KernelRLRun( kernel.m_InputPin, &kernel, NULL, FILENAME, geoRAMFile, geoRAMSize, true, prgDataLaunch, prgSizeLaunch ); + break; + } else { + KernelLaunchRun( kernel.m_InputPin, &kernel, FILENAME, true, prgDataLaunch, prgSizeLaunch ); + } + break; + case 8: + KernelSIDRun( kernel.m_InputPin, &kernel, NULL ); + break; + default: + break; + } + } + + return EXIT_HALT; +} diff --git a/Source/Firmware/kernel_menu264.h b/Source/Firmware/kernel_menu264.h index 44f3fc41..7b70d9f9 100644 --- a/Source/Firmware/kernel_menu264.h +++ b/Source/Firmware/kernel_menu264.h @@ -1,145 +1,147 @@ -/* - _________.__ .___ __ .__ __ _____ - / _____/|__| __| _/____ | | _|__| ____ | | __ / \ ____ ____ __ __ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / \ / \_/ __ \ / \| | \ - / \| / /_/ \ ___/| <| \ \___| < / Y \ ___/| | \ | / -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \____|__ /\___ >___| /____/ - \/ \/ \/ \/ \/ \/ \/ \/ \/ - - kernel_menu264.h - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - Sidekick Menu: ugly glue code to expose some functionality in one menu with browser - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ -#ifndef _kernel_h -#define _kernel_h - -// use the OLED connected to the latch -#define USE_OLED - -// 0 = SSD1306 OLED 128x64 -// 1 = ST7789 RGB TFT 240x240 -extern int screenType; - -//#if defined(USE_OLED) && !defined(USE_LATCH_OUTPUT) -#define USE_LATCH_OUTPUT -//#endif - -#define USE_HDMI_VIDEO - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#ifdef USE_VCHIQ_SOUND -#include -#include -#endif - -#include "lowlevel_arm64.h" -#include "gpio_defs.h" -#include "latch.h" -#include "helpers.h" -#include "helpers264.h" -#include "crt.h" -#include "mygpiopinfiq.h" - -#include "oled.h" -#include "splash_sidekick264.h" -#include "tft_st7789.h" - -extern CLogger *logger; - -void startInjectCode(); -void injectPOKE( u32 a, u8 d ); - -class CKernelMenu -{ -public: - CKernelMenu( void ) - : m_CPUThrottle( CPUSpeedMaximum ), - #ifdef USE_HDMI_VIDEO - m_Screen( m_Options.GetWidth(), m_Options.GetHeight() ), - #endif - m_Timer( &m_Interrupt ), - m_Logger( m_Options.GetLogLevel(), &m_Timer ), -#ifdef COMPILE_MENU_WITH_SOUND - #ifdef USE_VCHIQ_SOUND - m_VCHIQ( &m_Memory, &m_Interrupt ), - #endif -#endif - m_InputPin( PHI2, GPIOModeInput, &m_Interrupt ), - m_EMMC( &m_Interrupt, &m_Timer, 0 ) - { - } - - ~CKernelMenu( void ) - { - } - - boolean Initialize( void ); - - void Run( void ); - -private: - static void FIQHandler( void *pParam ); - -public: - // do not change this order - CMemorySystem m_Memory; - CKernelOptions m_Options; - CDeviceNameService m_DeviceNameService; - CCPUThrottle m_CPUThrottle; -#ifdef USE_HDMI_VIDEO - CScreenDevice m_Screen; -#endif - CInterruptSystem m_Interrupt; - CTimer m_Timer; - CLogger m_Logger; - CScheduler m_Scheduler; -#ifdef COMPILE_MENU_WITH_SOUND - #ifdef USE_VCHIQ_SOUND - CVCHIQDevice m_VCHIQ; - #endif - CSoundBaseDevice *m_pSound; -#endif - CGPIOPinFIQ2 m_InputPin; - CEMMCDevice m_EMMC; -}; - -#endif +/* + _________.__ .___ __ .__ __ _____ + / _____/|__| __| _/____ | | _|__| ____ | | __ / \ ____ ____ __ __ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / / \ / \_/ __ \ / \| | \ + / \| / /_/ \ ___/| <| \ \___| < / Y \ ___/| | \ | / +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ \____|__ /\___ >___| /____/ + \/ \/ \/ \/ \/ \/ \/ \/ \/ + + kernel_menu264.h + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - Sidekick Menu: ugly glue code to expose some functionality in one menu with browser + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ +#ifndef _kernel_h +#define _kernel_h + +// use the OLED connected to the latch +#define USE_OLED + +// 0 = SSD1306 OLED 128x64 +// 1 = ST7789 RGB TFT 240x240 +extern int screenType; + +//#if defined(USE_OLED) && !defined(USE_LATCH_OUTPUT) +#define USE_LATCH_OUTPUT +//#endif + +#define USE_HDMI_VIDEO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef USE_VCHIQ_SOUND +#include +#include +#endif +#include +#include + +#include "lowlevel_arm64.h" +#include "gpio_defs.h" +#include "latch.h" +#include "helpers.h" +#include "helpers264.h" +#include "crt.h" +#include "mygpiopinfiq.h" + +#include "oled.h" +#include "splash_sidekick264.h" +#include "tft_st7789.h" + +extern CLogger *logger; + +void startInjectCode(); +void injectPOKE( u32 a, u8 d ); + +class CKernelMenu +{ +public: + CKernelMenu( void ) + : m_CPUThrottle( CPUSpeedMaximum ), + #ifdef USE_HDMI_VIDEO + m_Screen( m_Options.GetWidth(), m_Options.GetHeight() ), + #endif + m_Timer( &m_Interrupt ), + m_Logger( m_Options.GetLogLevel(), &m_Timer ), +#ifdef COMPILE_MENU_WITH_SOUND + #ifdef USE_VCHIQ_SOUND + m_VCHIQ( &m_Memory, &m_Interrupt ), + #endif +#endif + m_InputPin( PHI2, GPIOModeInput, &m_Interrupt ), + m_EMMC( &m_Interrupt, &m_Timer, 0 ) + { + } + + ~CKernelMenu( void ) + { + } + + boolean Initialize( void ); + + void Run( void ); + +private: + static void FIQHandler( void *pParam ); + +public: + // do not change this order + CMemorySystem m_Memory; + CKernelOptions m_Options; + CDeviceNameService m_DeviceNameService; + CCPUThrottle m_CPUThrottle; +#ifdef USE_HDMI_VIDEO + CScreenDevice m_Screen; +#endif + CInterruptSystem m_Interrupt; + CTimer m_Timer; + CLogger m_Logger; + CScheduler m_Scheduler; +#ifdef COMPILE_MENU_WITH_SOUND + #ifdef USE_VCHIQ_SOUND + CVCHIQDevice m_VCHIQ; + #endif + CSoundBaseDevice *m_pSound; +#endif + CGPIOPinFIQ2 m_InputPin; + CEMMCDevice m_EMMC; +}; + +#endif diff --git a/Source/Firmware/kernel_sid264.cpp b/Source/Firmware/kernel_sid264.cpp index d3859c94..a7ac7b65 100644 --- a/Source/Firmware/kernel_sid264.cpp +++ b/Source/Firmware/kernel_sid264.cpp @@ -1,1283 +1,1337 @@ -/* - _________.__ .___ __ .__ __ _________.___________ - / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ - / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / - \/ \/ \/ \/ \/ \/ \/ \/ - - kernel_sid264.cpp - - RasPiC64 - A framework for interfacing the C64 (and C16/+4) and a Raspberry Pi 3B/3B+ - - Sidekick SID: a SID and SFX Sound Expander Emulation for the C16/+4 - (using reSID by Dag Lem and FMOPL by Jarek Burczynski, Tatsuyuki Satoh, Marco van den Heuvel, and Acho A. Tang) - Copyright (c) 2019-2021 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ -#include -#include "kernel_sid264.h" -#ifdef COMPILE_MENU -#include "kernel_menu.h" -#include "launch264.h" - -//u32 launchPrg; -#endif - -#undef USE_VCHIQ_SOUND - -static const char DRIVE[] = "SD:"; -static const char FILENAME_SPLASH_RGB[] = "SD:SPLASH/sk64_sid_bg2.tga"; -static const char FILENAME_SPLASH_RGB2[] = "SD:SPLASH/sk64_sid_bg.tga"; -static const char FILENAME_LED_RGB[] = "SD:SPLASH/sk64_sid_led.tga"; - -// _________.___________ ____ ________ ______ ____________ -//_______ ____ / _____/| \______ \ / _ \ ___.__. _____ \_____ \ / __ \/_ \_____ \ -//\_ __ \_/ __ \ \_____ \ | || | \ > _ < | |/ ____/ -// | | \/\ ___/ / \| || ` \ / <_\ \/ \___ | Y Y \/ \/ -- \| / \ -// |__| \___ >_______ /|___/_______ / \_____\ \ / ____|__|_| /______ /\______ /|___\_______ \ -// \/ \/ \/ \/ \/ \/ \/ \/ \/ -#include "resid/sid.h" -using namespace reSID; - -//u32 CLOCKFREQ = 1773447;//886723; -u32 CLOCKFREQ = 2*985248; - -//MUX zum TESTEN auf IO1 -//IO1 kann man spהter einsparen, in dem man verODERt mit ROMH (C1low und C1high) und dann die Adresse ausdekodiert - -// SID types and digi boost (only for MOS8580) -unsigned int SID_MODEL[2] = { 8580, 8580 }; -unsigned int SID_DigiBoost[2] = { 0, 0 }; - -// do not change this value -#define NUM_SIDS 2 -SID *sid[ NUM_SIDS ]; - -#ifdef EMULATE_OPL2 -FM_OPL *pOPL; -u32 fmOutRegister; -#endif - -u32 fillSoundBuffer = 0xffffffff; - -// a ring buffer storing SID-register writes (filled in FIQ handler) -// TODO should be much smaller -#define RING_SIZE (1024*128) -u32 ringBufGPIO[ RING_SIZE ]; -unsigned long long ringTime[ RING_SIZE ]; -u32 ringWrite; - -// prepared GPIO output when SID-registers are read -u32 outRegisters[ 32 ]; - -static int busValue = 0; -static int busValueTTL = 0; -u32 fmFakeOutput = 0; -u32 fmAutoDetectStep = 0; -u32 sidFakeOutput = 0; -u32 sidAutoDetectStep = 0; -u32 sidAutoDetectStep_2 = 0; -uint8_t sidAutoDetectRegs[ 32 ]; -uint8_t sidAutoDetectRegs_2[ 32 ]; - -// counts the #cycles when the C64-reset line is pulled down (to detect a reset) -u32 resetCounter, - cyclesSinceReset, - resetPressed, resetReleased; - -s32 outputDigiblaster = 0, digiblasterVolume = 256, tedVolume = 0; - -// hack -static u32 h_nRegOffset; -static u32 h_nRegMask; - -// this is the actual configuration of the emulation -u32 cfgRegisterRead = 0; // don't use this when you have a SID(-replacement) in the C64 -u32 cfgEmulateOPL2 = 1; -u32 cfgSID2_Disabled = 0; -u32 cfgSID2_PlaySameAsSID1 = 0; -u32 cfgMixStereo = 1; -u32 cfgSID2_Addr = 0; -s32 cfgVolSID1_Left, cfgVolSID1_Right; -s32 cfgVolSID2_Left, cfgVolSID2_Right; -s32 cfgVolOPL_Left, cfgVolOPL_Right; - -void setSIDConfiguration( u32 mode, u32 sid1, u32 sid2, u32 sid2addr, u32 rr, u32 addr, u32 exp, s32 v1, s32 p1, s32 v2, s32 p2, s32 v3, s32 p3, s32 digiblasterVol, s32 sidfreq, s32 tedVol ) -{ - SID_MODEL[ 0 ] = ( sid1 == 0 ) ? 6581 : 8580; - SID_DigiBoost[ 0 ] = ( sid1 == 2 ) ? 1 : 0; - SID_MODEL[ 1 ] = ( sid2 == 0 ) ? 6581 : 8580; - SID_DigiBoost[ 1 ] = ( sid2 == 2 ) ? 1 : 0; - - // panning = 0 .. 14, vol = 0 .. 15 - // volumes -> 0 .. 255 - cfgVolSID1_Left = v1 * ( 14 - p1 ) * 255 / 210; - cfgVolSID1_Right = v1 * ( p1 ) * 255 / 210; - cfgVolSID2_Left = v2 * ( 14 - p2 ) * 255 / 210; - cfgVolSID2_Right = v2 * ( p2 ) * 255 / 210; - cfgVolOPL_Left = v3 * ( 14 - p3 ) * 255 / 210; - cfgVolOPL_Right = v3 * ( p3 ) * 255 / 210; - -/* int maxVolFactor = max( cfgVolSID1_Left, max( cfgVolSID1_Right, max( cfgVolSID2_Left, max( cfgVolSID2_Right, max( cfgVolOPL_Left, cfgVolOPL_Right ) ) ) ) ); - - cfgVolSID1_Left = cfgVolSID1_Left * 256 / maxVolFactor; - cfgVolSID1_Right = cfgVolSID1_Right * 256 / maxVolFactor; - cfgVolSID2_Left = cfgVolSID2_Left * 256 / maxVolFactor; - cfgVolSID2_Right = cfgVolSID2_Right * 256 / maxVolFactor; - cfgVolOPL_Left = cfgVolOPL_Left * 256 / maxVolFactor; - cfgVolOPL_Right = cfgVolOPL_Right * 256 / maxVolFactor;*/ - - if ( sid2 == 3 ) - { - cfgSID2_Disabled = 1; cfgVolSID2_Left = cfgVolSID2_Right = 0; - } else - cfgSID2_Disabled = 0; - - if ( addr == 0 ) cfgSID2_PlaySameAsSID1 = 1; else cfgSID2_PlaySameAsSID1 = 0; - if ( mode == 0 ) cfgMixStereo = 1; else cfgMixStereo = 0; - - if ( addr == 0 ) - cfgSID2_Addr = 0xfd40; else - cfgSID2_Addr = 0xfe80; - cfgRegisterRead = rr; - if ( exp ) - cfgEmulateOPL2 = 0xfde2; else - cfgEmulateOPL2 = 0; - - if ( !exp ) - { - cfgVolOPL_Left = cfgVolOPL_Right = 0; - } - digiblasterVolume = 255 * digiblasterVol / 15; - if ( sidfreq == 0 ) - CLOCKFREQ = 1773447; else - CLOCKFREQ = 2*985248; - - tedVolume = 255 * tedVol / 15; -} - - -//___ ___ __ ___ ___ __ -// | |__ | \ |__ |\/| | | | /\ | | / \ |\ | -// | |___ |__/ |___ | | \__/ |___ /~~\ | | \__/ | \| -// taken directly from Yape, please see license in the header file -#include "TEDsound.h" - - -// __ __ __ ___ ___ -// /__` | | \ /\ |\ | | \ |__ |\/| | |\ | | | -// .__/ | |__/ /~~\ | \| |__/ | | | | | \| | | -// -void initSID() -{ - resetCounter = 0; - - for ( int i = 0; i < NUM_SIDS; i++ ) - { - sid[ i ] = new SID; - - for ( int j = 0; j < 24; j++ ) - sid[ i ]->write( j, 0 ); - - if ( SID_MODEL[ i ] == 6581 ) - { - sid[ i ]->set_chip_model( MOS6581 ); - } else - { - sid[ i ]->set_chip_model( MOS8580 ); - if ( SID_DigiBoost[ i ] == 0 ) - { - sid[ i ]->set_voice_mask( 0x07 ); - sid[ i ]->input( 0 ); - } else - { - sid[ i ]->set_voice_mask( 0x0f ); - sid[ i ]->input( -32768 ); - } - } - } - -#ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 ) - { - pOPL = ym3812_init( 3579545, SAMPLERATE ); - ym3812_reset_chip( pOPL ); - fmFakeOutput = 0; - fmAutoDetectStep = 0; - } -#endif - sidFakeOutput = - sidAutoDetectStep = - sidAutoDetectStep_2 = 0; - - for ( int i = 0; i < 20; i++ ) - { - sidAutoDetectRegs[ i ] = 0; - sidAutoDetectRegs_2[ i ] = 0; - } - - outputDigiblaster = 0; - - // ring buffer init - ringWrite = 0; - for ( int i = 0; i < RING_SIZE; i++ ) - ringTime[ i ] = 0; - - - tedSoundInit( SAMPLERATE ); -} - -void quitSID() -{ - for ( int i = 0; i < NUM_SIDS; i++ ) - delete sid[ i ]; -} - -unsigned long long cycleCountC64; - -// to do: integrate HDMI audio out -#define SAMPLERATE 44100 -u32 SAMPLERATE_ADJUSTED = SAMPLERATE; -u32 trackSampleProgress; - -#ifdef COMPILE_MENU -extern CLogger *logger; -extern CTimer *pTimer; -extern CScheduler *pScheduler; -extern CInterruptSystem *pInterrupt; -extern CVCHIQDevice *pVCHIQ; -extern CScreenDevice *screen; -CSoundBaseDevice *m_pSound; -#else - -CLogger *logger; -CTimer *pTimer; -CScheduler *pScheduler; -CInterruptSystem *pInterrupt; -CVCHIQDevice *pVCHIQ; -CScreenDevice *screen; - - -boolean CKernel::Initialize( void ) -{ - boolean bOK = TRUE; - -#ifdef USE_HDMI_VIDEO - if ( bOK ) bOK = m_Screen.Initialize(); - - if ( bOK ) - { - CDevice *pTarget = m_DeviceNameService.GetDevice( m_Options.GetLogDevice(), FALSE ); - if ( pTarget == 0 ) - pTarget = &m_Screen; - - bOK = m_Logger.Initialize( pTarget ); - logger = &m_Logger; - } -#endif - - if ( bOK ) bOK = m_Interrupt.Initialize(); - if ( bOK ) bOK = m_Timer.Initialize(); - -#ifdef USE_VCHIQ_SOUND - if ( bOK ) bOK = m_VCHIQ.Initialize(); - pVCHIQ = &m_VCHIQ; -#endif - - pTimer = &m_Timer; - pScheduler = &m_Scheduler; - pInterrupt = &m_Interrupt; - screen = &m_Screen; - - return bOK; -} -#endif - -static u32 renderDone = 0; - -static u32 visMode = 0; -static u32 visModeGotoNext = 0; - -static float px = 120.0f; -static float dx = 0.0f; -static u32 startRow = 1, endRow = 1; -static float vuValueAvg = 0.01f; -static u32 visUpdate = 1; -static int scopeX = 0; -static u8 scopeValues[ 240 ] = {0}; -static u32 nPrevLEDs[3] = {0, 0, 0}; -static float ledValueAvg[3] = { 0.01f, 0.01f, 0.01f }; - -static void initVisualization() -{ - visMode = 0; - visModeGotoNext = 0; - px = 120.0f; - dx = 0.0f; - startRow = 1; endRow = 1; - vuValueAvg = 0.01f; - visUpdate = 1; - scopeX = 0; - memset( scopeValues, 0, 256 ); - nPrevLEDs[0] = nPrevLEDs[1] = nPrevLEDs[2] = 0; - ledValueAvg[0] = ledValueAvg[1] = ledValueAvg[2] = 0.01f; -} - -static unsigned char tftBackground2[ 240 * 240 * 2 ]; -static unsigned char tftLEDs[ 240 * 240 * 2 ]; - -static u32 vu_Mode = 0; -static u32 vuMeter[4] = { 0, 0, 0, 0 }; -static u32 vu_nLEDs = 0; - -static u32 allUsedLEDs = 0; - -#ifdef COMPILE_MENU -void KernelSIDFIQHandler( void *pParam ); - -void KernelSIDRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ) -#else -void CKernel::Run( void ) -#endif -{ - // initialize latch and software I2C buffer - #ifndef COMPILE_MENU - // initialize ARM cycle counters (for accurate timing) - initCycleCounter(); - - // initialize GPIOs - gpioInit(); - SET_BANK2_OUTPUT - - // initialize latch and software I2C buffer - initLatch(); - - latchSetClearImm( 0, LATCH_RESET | allUsedLEDs | LATCH_ENABLE_KERNAL ); - SETCLR_GPIO( bNMI | bDMA, 0 ); - #endif - - if ( screenType == 0 ) - allUsedLEDs = LATCH_LED_ALL; - if ( screenType == 1 ) - allUsedLEDs = LATCH_LED0to1; - - if ( screenType == 0 ) - { - splashScreen( raspi_sid_splash ); - } else - if ( screenType == 1 ) - { - tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB, 8 ); - - int w, h; - extern char FILENAME_LOGO_RGBA[128]; - extern unsigned char tempTGA[ 256 * 256 * 4 ]; - - if ( tftLoadTGA( DRIVE, FILENAME_LOGO_RGBA, tempTGA, &w, &h, true ) ) - { - tftBlendRGBA( tempTGA, tftBackground, 0 ); - } - - tftCopyBackground2Framebuffer(); - - u32 c1 = rgb24to16( 166, 250, 128 ); - u32 c2 = rgb24to16( 98, 216, 204 ); - u32 c3 = rgb24to16( 233, 114, 36 ); - char b1[64], b2[64], b3[64], b4[64]; - int charWidth = 16; - sprintf( b1, "%s", SID_MODEL[ 0 ] == 6581 ? "6581":"8580" ); - - if ( cfgSID2_Disabled ) - b2[ 0 ] = 0; else - { - if ( cfgSID2_PlaySameAsSID1 ) - sprintf( b2, "+%s", SID_MODEL[ 1 ] == 6581 ? "6581":"8580"); else - sprintf( b2, "+%s", SID_MODEL[ 1 ] == 6581 ? "6581":"8580"); - } - - if ( cfgEmulateOPL2 ) - sprintf( b3, "+fm" ); else - b3[ 0 ] = 0; - - if ( digiblasterVolume ) - sprintf( b4, "+digi" ); else - b4[ 0 ] = 0; - - strcat( b3, b4 ); - - if ( tedVolume ) - sprintf( b4, "+ted" ); else - b4[ 0 ] = 0; - - strcat( b3, b4 ); - - int l = strlen( b1 ) + strlen( b2 ) + strlen( b3 ); - charWidth = min( 16, 240 / l ); - //if ( l * 16 >= 240 ) - //charWidth = 10; - int sx = max( 0, ( 240 - charWidth * l ) / 2 - 2 ); - tftPrint( b1, sx, 224, c1, charWidth - 16 ); sx += strlen( b1 ) * charWidth; - tftPrint( b2, sx, 224, c2, charWidth - 16 ); sx += strlen( b2 ) * charWidth; - tftPrint( b3, sx, 224, c3, charWidth - 16 ); - - tftInit(); - tftSendFramebuffer16BitImm( tftFrameBuffer ); - tftConvertFrameBuffer12Bit(); - - tftClearDirty(); - extern void tftPrepareDirtyUpdates(); - tftPrepareDirtyUpdates(); - tftUse12BitColor(); - - memcpy( tftBackground2, tftBackground, 240 * 240 * 2 ); - tftLoadBackgroundTGA( DRIVE, FILENAME_LED_RGB, 8 ); - memcpy( tftLEDs, tftBackground, 240 * 240 * 2 ); - - tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB2, 8 ); - - initVisualization(); - - } - - // logger->Write( "", LogNotice, "initialize SIDs..." ); - initSID(); - - #ifdef COMPILE_MENU - if ( FILENAME == NULL && !hasData ) - { - launchPrg_l264 = 0; - disableCart_l264 = 1; - } else - { - //logger->Write( "", LogNotice, "launch '%s'", FILENAME ); - launchPrg_l264 = 1; - if ( launchGetProgram( FILENAME, hasData, prgDataExt, prgSizeExt ) ) - launchInitLoader( false ); else - launchPrg_l264 = 0; - } - #endif - - // first byte of prgData will be used for transmitting ceil(prgSize/256) - for ( u32 i = prgSize_l264; i > 0; i-- ) - prgData_l264[ i ] = prgData_l264[ i - 1 ]; - prgData_l264[0] = ( ( prgSize_l264 + 255 ) >> 8 ); - -#ifndef COMPILE_MENU - obsolete, do not use this part - - latchSetClearImm( 0, LATCH_RESET | allUsedLEDs | LATCH_ENABLE_KERNAL ); - - cycleCountC64 = 0; - while ( cycleCountC64 < 10 ) - { - pScheduler->MsSleep( 100 ); - } - - // - // measure clock rate of the C64 (more accurate syncing with emulation, esp. for HDMI output) - // - cycleCountC64 = 0; - unsigned long long startTime = pTimer->GetClockTicks(); - unsigned long long curTime; - - do { - curTime = pTimer->GetClockTicks(); - } while ( curTime - startTime < 1000000 ); - - unsigned long long clockFreq = cycleCountC64 * 1000000 / ( curTime - startTime ); - CLOCKFREQ = clockFreq; - logger->Write( "", LogNotice, "Measured clock frequency: %u Hz", (u32)CLOCKFREQ ); -#endif - - for ( int i = 0; i < NUM_SIDS; i++ ) - sid[ i ]->set_sampling_parameters( CLOCKFREQ, SAMPLE_INTERPOLATE, SAMPLERATE ); - - // - // initialize sound output (either PWM which is output in the FIQ handler, or via HDMI) - // - initSoundOutput( &m_pSound, pVCHIQ, 1, 0 ); - - for ( int i = 0; i < NUM_SIDS; i++ ) - for ( int j = 0; j < 24; j++ ) - sid[ i ]->write( j, 0 ); - - #ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 ) - { - fmFakeOutput = 0; - ym3812_reset_chip( pOPL ); - } - #endif - -// logger->Write( "", LogNotice, "start emulating..." ); - cycleCountC64 = 0; - unsigned long long nCyclesEmulated = 0; - unsigned long long samplesElapsed = 0; - // how far did we consume the commands in the ring buffer? - unsigned int ringRead = 0; - - #ifdef COMPILE_MENU - // let's be very convincing about the caches ;-) - SyncDataAndInstructionCache(); - for ( u32 i = 0; i < 4; i++ ) - { - launchPrepareAndWarmCache264(); - - // FIQ handler - CACHE_PRELOAD_INSTRUCTION_CACHE( (void*)&FIQ_HANDLER, 6*1024 ); - FORCE_READ_LINEAR32a( (void*)&FIQ_HANDLER, 6*1024, 32768 ); - } - - // - // setup FIQ - // - #ifdef COMPILE_MENU - m_InputPin.ConnectInterrupt( KernelSIDFIQHandler, kernelMenu ); - #else - m_InputPin.ConnectInterrupt( this->FIQHandler, this ); - #endif - h_nRegOffset = m_InputPin.nRegOffset(); - h_nRegMask = m_InputPin.nRegMask(); - - - if ( !launchPrg_l264 ) - SETCLR_GPIO( bNMI | bDMA, 0 ); - - DELAY(10<<10); - m_InputPin.EnableInterrupt( GPIOInterruptOnRisingEdge ); - asm volatile ("wfi"); - latchSetClearImm( LATCH_RESET, allUsedLEDs | LATCH_ENABLE_KERNAL ); -/* DELAY(1<<26); - latchSetClearImm( 0, LATCH_RESET ); - DELAY(10<<10); - latchSetClearImm( LATCH_RESET, 0 );*/ - - if ( launchPrg_l264 ) - { - //launchPrepareAndWarmCache(); - //launchPrepareAndWarmCache(); - //launchPrepareAndWarmCache(); - - while ( !disableCart_l264 ) - { - #ifdef COMPILE_MENU - TEST_FOR_JUMP_TO_MAINMENU( cycleCountC64, resetCounter ) - #endif - asm volatile ("wfi"); - } - } - #endif - - resetCounter = 0; - cycleCountC64 = 0; - nCyclesEmulated = 0; - samplesElapsed = 0; - ringRead = 0; - for ( int i = 0; i < NUM_SIDS; i++ ) - for ( int j = 0; j < 24; j++ ) - sid[ i ]->write( j, 0 ); - - #ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 ) - { - fmFakeOutput = 0; - ym3812_reset_chip( pOPL ); - } - #endif - - //logger->Write( "", LogNotice, "start emulating..." ); - - // new main loop mainloop - while ( true ) - { - #ifdef COMPILE_MENU - TEST_FOR_JUMP_TO_MAINMENU( cycleCountC64, resetCounter ) - #endif - - if ( resetCounter > 3 && resetReleased ) - { - resetCounter = 0; - - for ( int i = 0; i < NUM_SIDS; i++ ) - for ( int j = 0; j < 24; j++ ) - sid[ i ]->write( j, 0 ); - - #ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 ) - { - fmFakeOutput = 0; - fmAutoDetectStep = 0; - ym3812_reset_chip( pOPL ); - } - #endif - - sidFakeOutput = - sidAutoDetectStep = - sidAutoDetectStep_2 = 0; - - for ( int i = 0; i < 20; i++ ) - { - sidAutoDetectRegs[ i ] = 0; - sidAutoDetectRegs_2[ i ] = 0; - } - } - - #ifdef USE_OLED - if ( renderDone == 2 ) - { - if ( !sendFramebufferDone() ) - sendFramebufferNext( 1 ); - - if ( sendFramebufferDone() ) - renderDone = 3; - } - if ( bufferEmptyI2C() && renderDone == 1 ) - { - sendFramebufferStart(); - renderDone = 2; - } - - #endif - - #ifndef EMULATION_IN_FIQ - - #ifndef USE_PWM_DIRECT - static u32 nSamplesInThisRun = 0; - #endif - - unsigned long long cycleCount = cycleCountC64; - while ( cycleCount > nCyclesEmulated ) - { - #ifndef USE_PWM_DIRECT - static int start = 0; - if ( nSamplesInThisRun > 2205 / 8 ) - { - if ( !start ) - { - m_pSound->Start(); - start = 1; - } else - { - //pScheduler->MsSleep( 1 ); - pScheduler->Yield(); - } - nSamplesInThisRun = 0; - } - nSamplesInThisRun++; - #endif - - unsigned long long samplesElapsedBefore = samplesElapsed; - - do { // do SID emulation until time passed to create an additional sample (i.e. there may be several cycles until a sample value is created) - #ifdef USE_PWM_DIRECT - u32 cyclesToEmulate = 16; - #else - u32 cyclesToEmulate = 2; - #endif - sid[ 0 ]->clock( cyclesToEmulate ); - #ifndef SID2_DISABLED - if ( !cfgSID2_Disabled ) - sid[ 1 ]->clock( cyclesToEmulate ); - #endif - - outRegisters[ 27 ] = sid[ 0 ]->read( 27 ); - outRegisters[ 28 ] = sid[ 0 ]->read( 28 ); - - nCyclesEmulated += cyclesToEmulate; - - // apply register updates (we do one-cycle emulation steps, but in case we need to catch up...) - unsigned int readUpTo = ringWrite; - - if ( ringRead != readUpTo && nCyclesEmulated >= ringTime[ ringRead ] ) - { - unsigned char A, D; - decodeGPIO( ringBufGPIO[ ringRead ], &A, &D ); - - u32 tedCommand = ( ringBufGPIO[ ringRead ] >> A6 ) & 1; - - if ( tedCommand ) - { - writeSoundReg( A, D ); - } else - #ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 && (ringBufGPIO[ ringRead ] & bIO2) ) - { - if ( ( ( A & ( 1 << 4 ) ) == 0 ) ) - ym3812_write( pOPL, 0, D ); else - ym3812_write( pOPL, 1, D ); - } else - #endif - //#if !defined(SID2_DISABLED) && !defined(SID2_PLAY_SAME_AS_SID1) - // TODO: generic masks - if ( !cfgSID2_Disabled && !cfgSID2_PlaySameAsSID1 && (ringBufGPIO[ ringRead ] & SID2_MASK) ) - { - sid[ 1 ]->write( A & 31, D ); - } else - //#endif - { - sid[ 0 ]->write( A & 31, D ); - outRegisters[ A & 31 ] = D; - //#if !defined(SID2_DISABLED) && defined(SID2_PLAY_SAME_AS_SID1) - if ( !cfgSID2_Disabled && cfgSID2_PlaySameAsSID1 ) - sid[ 1 ]->write( A & 31, D ); - //#endif - } - - ringRead++; - ringRead &= ( RING_SIZE - 1 ); - } - - samplesElapsed = ( ( unsigned long long )nCyclesEmulated * ( unsigned long long )SAMPLERATE ) / ( unsigned long long )CLOCKFREQ; - - } while ( samplesElapsed == samplesElapsedBefore ); - - s16 val1 = sid[ 0 ]->output(); - s16 val2 = 0; - s32 valOPL = 0; - - #ifndef SID2_DISABLED - if ( !cfgSID2_Disabled ) - val2 = sid[ 1 ]->output(); - #endif - - #ifdef EMULATE_OPL2 - if ( cfgEmulateOPL2 ) - { - ym3812_update_one( pOPL, &valOPL, 1 ); - // TODO asynchronous read back is an issue, needs to be fixed - fmOutRegister = encodeGPIO( ym3812_read( pOPL, 0 ) ); - } - #endif - - // - // mixer - // - s32 left, right; - - short tedV = 0; - - if ( tedVolume > 0 ) - tedV = TEDcalcNextSample(); - - // yes, it's 1 byte shifted in the buffer, need to fix - right = ( val1 * cfgVolSID1_Left + val2 * cfgVolSID2_Left + valOPL * cfgVolOPL_Left + 2 * outputDigiblaster * digiblasterVolume + tedV * tedVolume ) >> 8; - left = ( val1 * cfgVolSID1_Right + val2 * cfgVolSID2_Right + valOPL * cfgVolOPL_Right + 2 * outputDigiblaster * digiblasterVolume + tedV * tedVolume ) >> 8; - - right = max( -32767, min( 32767, right ) ); - left = max( -32767, min( 32767, left ) ); - - #ifdef USE_PWM_DIRECT - putSample( left, right ); - #else - putSample( left ); - putSample( right ); - #endif - - // vu meter - static u32 vu_nValues = 0; - static float vu_Sum[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; - - //if ( vu_Mode != 2 ) - { - float t = (left+right) / (float)32768.0f * 0.5f; - vu_Sum[ 0 ] += t * t * 1.0f; - - vu_Sum[ 1 ] += val1 * val1 / (float)32768.0f / (float)32768.0f; - vu_Sum[ 2 ] += val2 * val2 / (float)32768.0f / (float)32768.0f; - vu_Sum[ 3 ] += valOPL * valOPL / (float)32768.0f / (float)32768.0f; - - if ( ++ vu_nValues == 256*2 ) - { - for ( u32 i = 0; i < 4; i++ ) - { - float vu_Volume = max( 0.0f, 2.0f * (log10( 0.1f + sqrt( (float)vu_Sum[ i ] / (float)vu_nValues ) ) + 1.0f) ); - u32 v = vu_Volume * 1024.0f; - if ( i == 0 ) - { - // moving average - float v = min( 1.0f, (float)vuMeter[ 0 ] / 1024.0f ); - static float led4Avg = 0.0f; - led4Avg = led4Avg * 0.8f + v * ( 1.0f - 0.8f ); - - vu_nLEDs = max( 0, min( 4, (led4Avg * 8.0f) ) ); - if ( vu_nLEDs > 4 ) vu_nLEDs = 4; - } - vuMeter[ i ] = v; - vu_Sum[ i ] = 0; - } - - vu_nValues = 0; - } - } - - // ugly code which renders 3 oscilloscopes (SID1, SID2, FM) to HDMI and 1 for the OLED - if ( screenType == 0 ) - { - #include "oscilloscope_hack.h" - } else - if ( screenType == 1 ) - { - const float scaleVis = 1.0f; - const u32 nLevelMeters = 3; - #include "tft_sid_vis.h" - } - } - #endif - } - - m_InputPin.DisableInterrupt(); -} - - -#ifdef USE_PWM_DIRECT -static unsigned long long samplesElapsedBeforeFIQ = 0; -#endif - - -#ifdef COMPILE_MENU -void KernelSIDFIQHandler( void *pParam ) -#else -void CKernel::FIQHandler (void *pParam) -#endif -{ - unsigned long long samplesElapsedFIQ; - static s32 latchDelayOut = 10; - u32 swizzle = 0; - - register u32 D; - - #ifdef COMPILE_MENU - if ( launchPrg_l264 && !disableCart_l264 ) - { - LAUNCH_FIQ( cycleCountC64, resetCounter, h_nRegOffset, h_nRegMask ) - } - #endif - - START_AND_READ_EXPPORT264 - - if ( CPU_WRITES_TO_BUS ) - { - READ_D0to7_FROM_BUS( D ) - } else - - // this is an ugly hack to signal the menu code that it can reset (only necessary on a +4) - if ( /*BUS_AVAILABLE264 && CPU_READS_FROM_BUS && */GET_ADDRESS264 >= 0xfd90 && GET_ADDRESS264 <= 0xfd97 ) - { - WRITE_D0to7_TO_BUS( GET_ADDRESS264 - 0xfd90 + 1 ) - PeripheralEntry(); - write32( ARM_GPIO_GPEDS0 + h_nRegOffset, h_nRegMask ); - PeripheralExit(); - write32( ARM_GPIO_GPCLR0, bCTRL257 ); - FINISH_BUS_HANDLING - return; - } - - PeripheralEntry(); - write32( ARM_GPIO_GPEDS0 + h_nRegOffset, h_nRegMask ); - PeripheralExit(); - write32( ARM_GPIO_GPCLR0, bCTRL257 ); - - // __ ___ __ __ __ - // |__) |__ /\ | \ /__` | | \ - // | \ |___ /~~\ |__/ .__/ | |__/ - // - if ( cfgRegisterRead && BUS_AVAILABLE264 && GET_ADDRESS264 >= 0xfd40 && GET_ADDRESS264 <= 0xfd5f && CPU_READS_FROM_BUS ) - { - u32 A = ( g2 >> A0 ) & 31; - u32 D; - - A &= 31; - if ( A == 0x1b ) - { - // fake autodetection - if ( sidAutoDetectStep == 1 ) - { - sidAutoDetectStep = 0; - if ( SID_MODEL[ 0 ] == 8580 ) - D = 2; else - D = 3; - } else - { - static u32 seed = 123456789; - seed = (1103515245 * seed + 12345) & 2147483647; - if ( SID_MODEL[ 0 ] == 8580 ) - D = seed & 255; else - D = seed & 127; - } - } else - { - if ( A >= 0x19 && A <= 0x1a ) - D = 0xff; else -// if ( A >= 0x1b && A <= 0x1c ) -// D = outRegisters[ A ]; else - D = busValue; - } - - WRITE_D0to7_TO_BUS( D ) - //FINISH_BUS_HANDLING - //return; - goto get_out; - } - - if ( CPU_RESET ) { - resetReleased = 0; resetPressed = 1; resetCounter ++; - } else { - if ( resetPressed ) resetReleased = 1; - resetPressed = 0; - } - - // this is a weird attempt of correctly mapping the clock cycles - { - if ( armCycleCounter > 500 * 15/10 ) - cycleCountC64 ++; - } - - RESET_CPU_CYCLE_COUNTER - armCycleCounter = 0; - - cycleCountC64 ++; - - #ifdef COMPILE_MENU - // preload cache - if ( !( launchPrg_l264 && !disableCart_l264 ) ) - { - CACHE_PRELOADL1STRMW( &ringWrite ); - //CACHE_PRELOADL1STRMW( &ringBufGPIO[ ringWrite ] ); - //CACHE_PRELOADL1STRMW( &ringTime[ ringWrite ] ); - CACHE_PRELOADL1STRM( &sampleBuffer[ smpLast ] ); - CACHE_PRELOADL1STRM( &outRegisters[ 0 ] ); - CACHE_PRELOADL1STRM( &outRegisters[ 16 ] ); - } - #endif - - if ( busValueTTL < 0 ) - { - busValue = 0; - } else - busValueTTL --; - - - // __ ___ __ ___ - // |__) |__ /\ | \ |__ |\/| - // | \ |___ /~~\ |__/ | | | - // - if ( cfgEmulateOPL2 ) - { - #ifdef EMULATE_OPL2 - if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 == 0xfde4 ) - { - // - // this is not a real read of the YM3812 status register! - // only a fake that let's the detection routine be satisfied - // - u32 D = fmFakeOutput; - fmFakeOutput = 0xc0 - fmFakeOutput; - - WRITE_D0to7_TO_BUS( D ) - - FINISH_BUS_HANDLING - return; - } - #ifdef EMULATE_OPL2_THIS_PART_IS_DISABLED - else - // reading of the external Sound Expander Keyboard => we don't have it, return 0xFF - if ( ( CPU_READS_FROM_BUS && IO2_ACCESS ) && ( GET_IO12_ADDRESS >= 0x08 ) && ( GET_IO12_ADDRESS <= 0x0F ) ) - { - WRITE_D0to7_TO_BUS( 0xFF ) - - FINISH_BUS_HANDLING - return; - } else - #endif - // __ ___ ___ ___ - // | | |__) | | |__ |__ |\/| - // |/\| | \ | | |___ | | | - // - if ( BUS_AVAILABLE264 && CPU_WRITES_TO_BUS && - ( GET_ADDRESS264 == cfgEmulateOPL2 || GET_ADDRESS264 == (cfgEmulateOPL2+1) || GET_ADDRESS264 == (cfgEmulateOPL2+0x10) ) ) - { - register u32 A;// = ( GET_ADDRESS264 - cfgEmulateOPL2 + 0x40 ); - if ( GET_ADDRESS264 == cfgEmulateOPL2 ) A = 0x40; else A = 0x50; - register u32 remapAddr = (A&31) << A0; - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - if ( A == 0x40 && D == 0x04 && fmAutoDetectStep != 2 ) - fmAutoDetectStep = 1; - if ( A > 0x40 && D == 0x60 && fmAutoDetectStep == 1 ) - fmAutoDetectStep = 2; - if ( A == 0x40 && D == 0x04 && fmAutoDetectStep == 2 ) - fmAutoDetectStep = 3; - if ( A > 0x40 && D == 0x80 && fmAutoDetectStep == 3 ) - { - fmAutoDetectStep = 4; - fmFakeOutput = 0; - } - - ringBufGPIO[ ringWrite ] = ( remapAddr ) | ( D << D0 ) | bIO2; - ringTime[ ringWrite ] = cycleCountC64; - ringWrite ++; - ringWrite &= ( RING_SIZE - 1 ); - #pragma GCC diagnostic pop - - //FINISH_BUS_HANDLING - //return; - goto get_out; - } - #endif // EMULATE_OPL2 - } - // __ ___ ___ __ __ - // | | |__) | | |__ /__` | | \ - // |/\| | \ | | |___ .__/ | |__/ - // - if ( BUS_AVAILABLE264 && ( GET_ADDRESS264 >= 0xfd40 && GET_ADDRESS264 <= 0xfd58 ) && CPU_WRITES_TO_BUS ) - { - register u32 A = GET_ADDRESS264 - 0xfd40; - register u32 remapAddr = (A&31) << A0; - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - - if ( A < 32 && sidAutoDetectStep == 0 && - sidAutoDetectRegs[ 0x12 ] == 0xff && - sidAutoDetectRegs[ 0x0e ] == 0xff && - sidAutoDetectRegs[ 0x0f ] == 0xff && - (A&31) == 0x12 && D == 0x20 ) - { - sidAutoDetectStep = 1; - } - sidAutoDetectRegs[ A & 31 ] = D; - - ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ) & ~bIO2; - ringTime[ ringWrite ] = cycleCountC64; - ringWrite ++; - ringWrite &= ( RING_SIZE - 1 ); - - busValue = D; - if ( SID_MODEL[ 0 ] == 8580 ) - busValueTTL = 0xa2000; else // 8580 - busValueTTL = 0x1d00; // 6581 - - #pragma GCC diagnostic pop - - // optionally we could directly set the SID-output registers (instead of where the emulation runs) - //u32 A = ( g2 >> A0 ) & 31; - //outRegisters[ A ] = g1 & D_FLAG; - - goto get_out; - } - - if ( BUS_AVAILABLE264 && cfgSID2_Addr == 0xfe80 && GET_ADDRESS264 >= 0xfe80 && GET_ADDRESS264 <= 0xfe98 && CPU_WRITES_TO_BUS ) - { - register u32 A = GET_ADDRESS264 - 0xfe80; - register u32 remapAddr = (A&31) << A0; - remapAddr |= SID2_MASK; - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ) & ~bIO2; - #pragma GCC diagnostic pop - ringTime[ ringWrite ] = cycleCountC64; - ringWrite ++; - ringWrite &= ( RING_SIZE - 1 ); - goto get_out; - } - - if ( BUS_AVAILABLE264 && ( GET_ADDRESS264 == 0xfd5e ) && CPU_WRITES_TO_BUS ) - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - outputDigiblaster = ((s32)D - 128) * 4; - #pragma GCC diagnostic pop - goto get_out; - } - - if ( tedVolume > 0 && BUS_AVAILABLE264 && ( GET_ADDRESS264 >= 0xff0e && GET_ADDRESS264 <= 0xff12 ) && CPU_WRITES_TO_BUS ) - { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - - register u32 A = GET_ADDRESS264 - 0xff0e; - register u32 remapAddr = (A&31) << A0; - remapAddr |= 1 << A6; - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ); - #pragma GCC diagnostic pop - ringTime[ ringWrite ] = cycleCountC64; - ringWrite ++; - ringWrite &= ( RING_SIZE - 1 ); - - #pragma GCC diagnostic pop - goto get_out; - } - - - - - // ___ ___ __ ___ __ - // |__ |\/| | | | /\ | | / \ |\ | | |\ | |__ | / \ - // |___ | | \__/ |___ /~~\ | | \__/ | \| | | \| | | \__X - // OPTIONAL and omitted for this release - // - #ifdef EMULATION_IN_FIQ - run_emulation: - #include "fragment_emulation_in_fiq.h" - #endif - - // __ __ ___ __ ___ - // |__) | | |\/| / \ | | | |__) | | | - // | |/\| | | \__/ \__/ | | \__/ | - // OPTIONAL - // - #ifdef USE_PWM_DIRECT - samplesElapsedFIQ = ( ( unsigned long long )cycleCountC64 * ( unsigned long long )SAMPLERATE ) / ( unsigned long long )CLOCKFREQ; - - if ( samplesElapsedFIQ != samplesElapsedBeforeFIQ ) - { - write32( ARM_GPIO_GPCLR0, bCTRL257 ); - samplesElapsedBeforeFIQ = samplesElapsedFIQ; - - u32 s = getSample(); - u16 s1 = s & 65535; - u16 s2 = s >> 16; - - s32 d1 = (s32)( ( *(s16*)&s1 + 32768 ) * PWMRange ) >> 17; - s32 d2 = (s32)( ( *(s16*)&s2 + 32768 ) * PWMRange ) >> 17; - write32( ARM_PWM_DAT1, d1 ); - write32( ARM_PWM_DAT2, d2 ); - goto get_out; - } - #endif - - static u32 omitLatch = 0; - - static u32 lastButtonPressed = 0; - - if ( lastButtonPressed > 0 ) - lastButtonPressed --; - - static u32 buttonIsPressed = 0; - - if ( BUTTON_PRESSED && lastButtonPressed == 0 ) - buttonIsPressed ++; else - buttonIsPressed = 0; - - if ( buttonIsPressed > 50 ) - { - visModeGotoNext = 1; - vu_Mode = ( vu_Mode + 1 ) & 3; - lastButtonPressed = 100000; - } - - if ( omitLatch ) - goto get_out; - // ___ __ - // | /\ | / ` |__| - // |___ /~~\ | \__, | | - // - #ifdef USE_LATCH_OUTPUT - if ( screenType == 0 ) - { - if ( --latchDelayOut == 1 && renderDone == 3 ) - { - prefetchI2C(); - } - if ( latchDelayOut <= 0 && renderDone == 3 ) - { - latchDelayOut = 2; - prepareOutputLatch(); - if ( bufferEmptyI2C() ) renderDone = 0; - OUTPUT_LATCH_AND_FINISH_BUS_HANDLING - return; - } - } - #endif - -#if 1 - if ( screenType == 0 ) - { - static u32 fCount = 0; - fCount ++; - fCount &= 255; - - if ( vu_Mode == 0 ) - { - setLatchFIQ( LATCH_ON[ vu_nLEDs ] ); - clrLatchFIQ( LATCH_OFF[ vu_nLEDs ] ); - } else - if ( vu_Mode == 1 ) - { - if ( swizzle < vuMeter[ 0 ] ) - setLatchFIQ( allUsedLEDs ); else - clrLatchFIQ( allUsedLEDs ); - } else - if ( vu_Mode == 2 ) - { - swizzle = - ( (fCount & 128) >> 7 ) | - ( (fCount & 64) >> 5 ) | - ( (fCount & 32) >> 3 ) | - ( (fCount & 16) >> 1 ) | - ( (fCount & 8) << 1 ) | - ( (fCount & 4) << 3 ) | - ( (fCount & 2) << 5 ) | - ( (fCount & 1) << 7 ); - - u32 led = - ( ( swizzle < vuMeter[ 1 ] ) ? LATCH_LED1 : 0 ) | - ( ( swizzle < vuMeter[ 2 ] ) ? LATCH_LED2 : 0 ) | - ( ( swizzle < vuMeter[ 3 ] ) ? LATCH_LED3 : 0 ); - - setLatchFIQ( led ); - clrLatchFIQ( ( (~led) & allUsedLEDs ) | LATCH_LED0 ); - } else - clrLatchFIQ( allUsedLEDs ); - } else - { - - //prepareOutputLatch4Bit(); - //outputLatch(); - prepareOutputLatch4Bit(); - outputLatch(); - //OUTPUT_LATCH_AND_FINISH_BUS_HANDLING - } -#endif - -get_out: - - if ( resetCounter > 3 ) - { - disableCart_l264 = transferStarted_l264 = 0; - } - - //OUTPUT_LATCH_AND_FINISH_BUS_HANDLING - write32( ARM_GPIO_GPCLR0, bCTRL257 ); -} - -#ifndef COMPILE_MENU -int main( void ) -{ - CKernel kernel; - if ( kernel.Initialize() ) - kernel.Run(); - - halt(); - return EXIT_HALT; -} +/* + _________.__ .___ __ .__ __ _________.___________ + / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ + / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / + \/ \/ \/ \/ \/ \/ \/ \/ + + kernel_sid264.cpp + + RasPiC64 - A framework for interfacing the C64 (and C16/+4) and a Raspberry Pi 3B/3B+ + - Sidekick SID: a SID and SFX Sound Expander Emulation for the C16/+4 + (using reSID by Dag Lem and FMOPL by Jarek Burczynski, Tatsuyuki Satoh, Marco van den Heuvel, and Acho A. Tang) + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ +#include +#include "kernel_sid264.h" +#ifdef COMPILE_MENU +#include "kernel_menu.h" +#include "launch264.h" + +//u32 launchPrg; +#endif + +#define HDMI_SOUND + + +#undef USE_VCHIQ_SOUND + +static const char DRIVE[] = "SD:"; +static const char FILENAME_SPLASH_RGB[] = "SD:SPLASH/sk64_sid_bg2.tga"; +static const char FILENAME_SPLASH_RGB2[] = "SD:SPLASH/sk64_sid_bg.tga"; +static const char FILENAME_LED_RGB[] = "SD:SPLASH/sk64_sid_led.tga"; + +// _________.___________ ____ ________ ______ ____________ +//_______ ____ / _____/| \______ \ / _ \ ___.__. _____ \_____ \ / __ \/_ \_____ \ +//\_ __ \_/ __ \ \_____ \ | || | \ > _ < | |/ ____/ +// | | \/\ ___/ / \| || ` \ / <_\ \/ \___ | Y Y \/ \/ -- \| / \ +// |__| \___ >_______ /|___/_______ / \_____\ \ / ____|__|_| /______ /\______ /|___\_______ \ +// \/ \/ \/ \/ \/ \/ \/ \/ \/ +#include "resid/sid.h" +using namespace reSID; + +u32 CLOCKFREQ_P4 = 1773447;//886723; +u32 CLOCKFREQ = 2*985248; +u32 CLOCKFREQ_ADJ = 985248; + +//MUX zum TESTEN auf IO1 +//IO1 kann man spן¿½ter einsparen, in dem man verODERt mit ROMH (C1low und C1high) und dann die Adresse ausdekodiert + +// SID types and digi boost (only for MOS8580) +unsigned int SID_MODEL[2] = { 8580, 8580 }; +unsigned int SID_DigiBoost[2] = { 0, 0 }; + +// do not change this value +#define NUM_SIDS 2 +SID *sid[ NUM_SIDS ]; + +#ifdef EMULATE_OPL2 +FM_OPL *pOPL; +u32 fmOutRegister; +#endif + +u32 fillSoundBuffer = 0xffffffff; + +// a ring buffer storing SID-register writes (filled in FIQ handler) +// TODO should be much smaller +#define RING_SIZE (1024*128) +u32 ringBufGPIO[ RING_SIZE ]; +unsigned long long ringTime[ RING_SIZE ]; +u32 ringWrite; + +// prepared GPIO output when SID-registers are read +u32 outRegisters[ 32 ]; + +static int busValue = 0; +static int busValueTTL = 0; +u32 fmFakeOutput = 0; +u32 fmAutoDetectStep = 0; +u32 sidFakeOutput = 0; +u32 sidAutoDetectStep = 0; +u32 sidAutoDetectStep_2 = 0; +uint8_t sidAutoDetectRegs[ 32 ]; +uint8_t sidAutoDetectRegs_2[ 32 ]; + +// counts the #cycles when the C64-reset line is pulled down (to detect a reset) +u32 resetCounter, + cyclesSinceReset, + resetPressed, resetReleased; + +s32 outputDigiblaster = 0, digiblasterVolume = 256, tedVolume = 0; + +// hack +static u32 h_nRegOffset; +static u32 h_nRegMask; + +// this is the actual configuration of the emulation +u32 cfgRegisterRead = 0; // don't use this when you have a SID(-replacement) in the C64 +u32 cfgEmulateOPL2 = 1; +u32 cfgSID2_Disabled = 0; +u32 cfgSID2_PlaySameAsSID1 = 0; +u32 cfgMixStereo = 1; +u32 cfgSID1_Addr = 0; +u32 cfgSID2_Addr = 0; +s32 cfgVolSID1_Left, cfgVolSID1_Right; +s32 cfgVolSID2_Left, cfgVolSID2_Right; +s32 cfgVolOPL_Left, cfgVolOPL_Right; + +u8 outputHDMI = 0; + +void setSIDConfiguration( u32 mode, u32 sid1, u32 sid1addr, u32 sid2, u32 sid2addr, u32 rr, u32 addr, u32 exp, s32 v1, s32 p1, s32 v2, s32 p2, s32 v3, s32 p3, s32 digiblasterVol, s32 sidfreq, s32 tedVol, u8 output ) +{ + SID_MODEL[ 0 ] = ( sid1 == 0 ) ? 6581 : 8580; + SID_DigiBoost[ 0 ] = ( sid1 == 2 ) ? 1 : 0; + SID_MODEL[ 1 ] = ( sid2 == 0 ) ? 6581 : 8580; + SID_DigiBoost[ 1 ] = ( sid2 == 2 ) ? 1 : 0; + + // panning = 0 .. 14, vol = 0 .. 15 + // volumes -> 0 .. 255 + cfgVolSID1_Left = v1 * ( 14 - p1 ) * 255 / 210; + cfgVolSID1_Right = v1 * ( p1 ) * 255 / 210; + cfgVolSID2_Left = v2 * ( 14 - p2 ) * 255 / 210; + cfgVolSID2_Right = v2 * ( p2 ) * 255 / 210; + cfgVolOPL_Left = v3 * ( 14 - p3 ) * 255 / 210; + cfgVolOPL_Right = v3 * ( p3 ) * 255 / 210; + + if ( sid1addr ) + cfgSID1_Addr = 0xd400; else + cfgSID1_Addr = 0xfd40; + + if ( sid2 == 3 ) + { + cfgSID2_Disabled = 1; cfgVolSID2_Left = cfgVolSID2_Right = 0; + } else + cfgSID2_Disabled = 0; + + if ( addr == 0 ) cfgSID2_PlaySameAsSID1 = 1; else cfgSID2_PlaySameAsSID1 = 0; + if ( mode == 0 ) cfgMixStereo = 1; else cfgMixStereo = 0; + + if ( addr == 0 ) + cfgSID2_Addr = 0xfd40; else + cfgSID2_Addr = 0xfe80; + cfgRegisterRead = rr; + if ( exp ) + cfgEmulateOPL2 = 0xfde2; else + cfgEmulateOPL2 = 0; + + if ( !exp ) + { + cfgVolOPL_Left = cfgVolOPL_Right = 0; + } + digiblasterVolume = 255 * digiblasterVol / 15; + if ( sidfreq == 0 ) + { + CLOCKFREQ = 1773447; + CLOCKFREQ_ADJ = CLOCKFREQ / 2; + } else + { + CLOCKFREQ = 2*985248; + CLOCKFREQ_ADJ = CLOCKFREQ / 2; + } + +// CLOCKFREQ = 1773447; +// CLOCKFREQ_ADJ = CLOCKFREQ/2; + + //CLOCKFREQ = 2*985248; + //CLOCKFREQ_ADJ = CLOCKFREQ / 2; + + tedVolume = 255 * tedVol / 15; + + outputHDMI = output; +} + + +//___ ___ __ ___ ___ __ +// | |__ | \ |__ |\/| | | | /\ | | / \ |\ | +// | |___ |__/ |___ | | \__/ |___ /~~\ | | \__/ | \| +// taken directly from Yape, please see license in the header file +#include "TEDsound.h" + + +// __ __ __ ___ ___ +// /__` | | \ /\ |\ | | \ |__ |\/| | |\ | | | +// .__/ | |__/ /~~\ | \| |__/ | | | | | \| | | +// +void initSID() +{ + resetCounter = 0; + + for ( int i = 0; i < NUM_SIDS; i++ ) + { + sid[ i ] = new SID; + + for ( int j = 0; j < 24; j++ ) + sid[ i ]->write( j, 0 ); + + if ( SID_MODEL[ i ] == 6581 ) + { + sid[ i ]->set_chip_model( MOS6581 ); + } else + { + sid[ i ]->set_chip_model( MOS8580 ); + if ( SID_DigiBoost[ i ] == 0 ) + { + sid[ i ]->set_voice_mask( 0x07 ); + sid[ i ]->input( 0 ); + } else + { + sid[ i ]->set_voice_mask( 0x0f ); + sid[ i ]->input( -32768 ); + } + } + } + +#ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 ) + { + pOPL = ym3812_init( 3579545, SAMPLERATE ); + ym3812_reset_chip( pOPL ); + fmFakeOutput = 0; + fmAutoDetectStep = 0; + } +#endif + sidFakeOutput = + sidAutoDetectStep = + sidAutoDetectStep_2 = 0; + + for ( int i = 0; i < 20; i++ ) + { + sidAutoDetectRegs[ i ] = 0; + sidAutoDetectRegs_2[ i ] = 0; + } + + outputDigiblaster = 0; + + // ring buffer init + ringWrite = 0; + for ( int i = 0; i < RING_SIZE; i++ ) + ringTime[ i ] = 0; + + + tedSoundInit( SAMPLERATE ); +} + +void quitSID() +{ + for ( int i = 0; i < NUM_SIDS; i++ ) + delete sid[ i ]; +} + +unsigned long long cycleCountC64; +unsigned long long nCyclesEmulated = 0; +u8 flushBuffer = 0; +unsigned long long samplesElapsed = 0; + +unsigned long long adjustedCycleCount( const unsigned long long cycleCountC64 ) +{ + return ( cycleCountC64 * (unsigned long long)CLOCKFREQ_ADJ ) / (unsigned long long)1773447; +} + +u32 SAMPLERATE_ADJUSTED = SAMPLERATE; +u32 trackSampleProgress; + +#ifdef COMPILE_MENU +extern CLogger *logger; +extern CTimer *pTimer; +extern CScheduler *pScheduler; +extern CInterruptSystem *pInterrupt; +extern CVCHIQDevice *pVCHIQ; +extern CScreenDevice *screen; +CSoundBaseDevice *m_pSound; +#else + +CLogger *logger; +CTimer *pTimer; +CScheduler *pScheduler; +CInterruptSystem *pInterrupt; +CVCHIQDevice *pVCHIQ; +CScreenDevice *screen; + + +boolean CKernel::Initialize( void ) +{ + boolean bOK = TRUE; + +#ifdef USE_HDMI_VIDEO + if ( bOK ) bOK = m_Screen.Initialize(); + + if ( bOK ) + { + CDevice *pTarget = m_DeviceNameService.GetDevice( m_Options.GetLogDevice(), FALSE ); + if ( pTarget == 0 ) + pTarget = &m_Screen; + + bOK = m_Logger.Initialize( pTarget ); + logger = &m_Logger; + } +#endif + + if ( bOK ) bOK = m_Interrupt.Initialize(); + if ( bOK ) bOK = m_Timer.Initialize(); + +#ifdef USE_VCHIQ_SOUND + if ( bOK ) bOK = m_VCHIQ.Initialize(); + pVCHIQ = &m_VCHIQ; +#endif + + pTimer = &m_Timer; + pScheduler = &m_Scheduler; + pInterrupt = &m_Interrupt; + screen = &m_Screen; + + return bOK; +} +#endif + +static u32 renderDone = 0; + +static u32 visMode = 0; +static u32 visModeGotoNext = 0; + +static float px = 120.0f; +static float dx = 0.0f; +static s32 startRow = 1, endRow = 1; +static float vuValueAvg = 0.01f; +static u32 visUpdate = 1; +static int scopeX = 0; +static u8 scopeValues[ 240 ] = {0}; +static u32 nPrevLEDs[3] = {0, 0, 0}; +static float ledValueAvg[3] = { 0.01f, 0.01f, 0.01f }; + +static void initVisualization() +{ + visMode = 0; + visModeGotoNext = 0; + px = 120.0f; + dx = 0.0f; + startRow = 1; endRow = 1; + vuValueAvg = 0.01f; + visUpdate = 1; + scopeX = 0; + memset( scopeValues, 0, 256 ); + nPrevLEDs[0] = nPrevLEDs[1] = nPrevLEDs[2] = 0; + ledValueAvg[0] = ledValueAvg[1] = ledValueAvg[2] = 0.01f; +} + +static unsigned char tftBackground2[ 240 * 240 * 2 ]; +static unsigned char tftLEDs[ 240 * 240 * 2 ]; + +static u32 vu_Mode = 0; +static u32 vuMeter[4] = { 0, 0, 0, 0 }; +static u32 vu_nLEDs = 0; + +static u32 allUsedLEDs = 0; + +#ifdef COMPILE_MENU +void KernelSIDFIQHandler( void *pParam ); + +void KernelSIDRun( CGPIOPinFIQ2 m_InputPin, CKernelMenu *kernelMenu, const char *FILENAME, bool hasData = false, u8 *prgDataExt = NULL, u32 prgSizeExt = 0 ) +#else +void CKernel::Run( void ) +#endif +{ + // initialize latch and software I2C buffer + #ifndef COMPILE_MENU + // initialize ARM cycle counters (for accurate timing) + initCycleCounter(); + + // initialize GPIOs + gpioInit(); + SET_BANK2_OUTPUT + + // initialize latch and software I2C buffer + initLatch(); + + latchSetClearImm( 0, LATCH_RESET | allUsedLEDs | LATCH_ENABLE_KERNAL ); + SETCLR_GPIO( bNMI | bDMA, 0 ); + #endif + + if ( screenType == 0 ) + allUsedLEDs = LATCH_LED_ALL; + if ( screenType == 1 ) + allUsedLEDs = LATCH_LED0to1; + + if ( screenType == 0 ) + { + splashScreen( raspi_sid_splash ); + } else + if ( screenType == 1 ) + { + tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB, 8 ); + + int w, h; + extern char FILENAME_LOGO_RGBA[128]; + extern unsigned char tempTGA[ 256 * 256 * 4 ]; + + if ( tftLoadTGA( DRIVE, FILENAME_LOGO_RGBA, tempTGA, &w, &h, true ) ) + { + tftBlendRGBA( tempTGA, tftBackground, 0 ); + } + + tftCopyBackground2Framebuffer(); + + u32 c1 = rgb24to16( 166, 250, 128 ); + u32 c2 = rgb24to16( 98, 216, 204 ); + u32 c3 = rgb24to16( 233, 114, 36 ); + char b1[64], b2[64], b3[64], b4[64]; + int charWidth = 16; + sprintf( b1, "%s", SID_MODEL[ 0 ] == 6581 ? "6581":"8580" ); + + if ( cfgSID2_Disabled ) + b2[ 0 ] = 0; else + { + if ( cfgSID2_PlaySameAsSID1 ) + sprintf( b2, "+%s", SID_MODEL[ 1 ] == 6581 ? "6581":"8580"); else + sprintf( b2, "+%s", SID_MODEL[ 1 ] == 6581 ? "6581":"8580"); + } + + if ( cfgEmulateOPL2 ) + sprintf( b3, "+fm" ); else + b3[ 0 ] = 0; + + if ( digiblasterVolume ) + sprintf( b4, "+digi" ); else + b4[ 0 ] = 0; + + strcat( b3, b4 ); + + if ( tedVolume ) + sprintf( b4, "+ted" ); else + b4[ 0 ] = 0; + + strcat( b3, b4 ); + + int l = strlen( b1 ) + strlen( b2 ) + strlen( b3 ); + charWidth = min( 16, 240 / l ); + //if ( l * 16 >= 240 ) + //charWidth = 10; + int sx = max( 0, ( 240 - charWidth * l ) / 2 - 2 ); + tftPrint( b1, sx, 224, c1, charWidth - 16 ); sx += strlen( b1 ) * charWidth; + tftPrint( b2, sx, 224, c2, charWidth - 16 ); sx += strlen( b2 ) * charWidth; + tftPrint( b3, sx, 224, c3, charWidth - 16 ); + + tftInit(); + tftSendFramebuffer16BitImm( tftFrameBuffer ); + tftConvertFrameBuffer12Bit(); + + tftClearDirty(); + extern void tftPrepareDirtyUpdates(); + tftPrepareDirtyUpdates(); + tftUse12BitColor(); + + memcpy( tftBackground2, tftBackground, 240 * 240 * 2 ); + tftLoadBackgroundTGA( DRIVE, FILENAME_LED_RGB, 8 ); + memcpy( tftLEDs, tftBackground, 240 * 240 * 2 ); + + tftLoadBackgroundTGA( DRIVE, FILENAME_SPLASH_RGB2, 8 ); + + initVisualization(); + + } + + // logger->Write( "", LogNotice, "initialize SIDs..." ); + initSID(); + + #ifdef COMPILE_MENU + if ( FILENAME == NULL && !hasData ) + { + launchPrg_l264 = 0; + disableCart_l264 = 1; + } else + { + //logger->Write( "", LogNotice, "launch '%s'", FILENAME ); + launchPrg_l264 = 1; + if ( launchGetProgram( FILENAME, hasData, prgDataExt, prgSizeExt ) ) + launchInitLoader( false ); else + launchPrg_l264 = 0; + } + #endif + + // first byte of prgData will be used for transmitting ceil(prgSize/256) + for ( u32 i = prgSize_l264; i > 0; i-- ) + prgData_l264[ i ] = prgData_l264[ i - 1 ]; + prgData_l264[0] = ( ( prgSize_l264 + 255 ) >> 8 ); + + for ( int i = 0; i < NUM_SIDS; i++ ) + { + int SID_passband = 90; + int SID_gain = 97; + int SID_filterbias = 1000; + + sid[ i ]->adjust_filter_bias( SID_filterbias / 1000.0f ); + sid[ i ]->set_sampling_parameters( CLOCKFREQ, SAMPLE_FAST, SAMPLERATE, SAMPLERATE * SID_passband / 200.0f, SID_gain / 100.0f ); + + //sid[ i ]->set_sampling_parameters( CLOCKFREQ, SAMPLE_INTERPOLATE, SAMPLERATE ); + } + + // + // initialize sound output (either PWM which is output in the FIQ handler, or via HDMI) + // + initSoundOutput( &m_pSound, pVCHIQ, 1, 0 ); + + for ( int i = 0; i < NUM_SIDS; i++ ) + for ( int j = 0; j < 24; j++ ) + sid[ i ]->write( j, 0 ); + + #ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 ) + { + fmFakeOutput = 0; + ym3812_reset_chip( pOPL ); + } + #endif + +// logger->Write( "", LogNotice, "start emulating..." ); + // how far did we consume the commands in the ring buffer? + unsigned int ringRead = 0; + + #ifdef COMPILE_MENU + // let's be very convincing about the caches ;-) + SyncDataAndInstructionCache(); + for ( u32 i = 0; i < 4; i++ ) + { + launchPrepareAndWarmCache264(); + + // FIQ handler + CACHE_PRELOAD_INSTRUCTION_CACHE( (void*)&FIQ_HANDLER, 6*1024 ); + FORCE_READ_LINEAR32a( (void*)&FIQ_HANDLER, 6*1024, 32768 ); + } + + cycleCountC64 = 0; + nCyclesEmulated = 0; + samplesElapsed = 0; + // + // setup FIQ + // + #ifdef COMPILE_MENU + m_InputPin.ConnectInterrupt( KernelSIDFIQHandler, kernelMenu ); + #else + m_InputPin.ConnectInterrupt( this->FIQHandler, this ); + #endif + h_nRegOffset = m_InputPin.nRegOffset(); + h_nRegMask = m_InputPin.nRegMask(); + + + if ( !launchPrg_l264 ) + SETCLR_GPIO( bNMI | bDMA, 0 ); + + DELAY(10<<10); + m_InputPin.EnableInterrupt( GPIOInterruptOnRisingEdge ); + asm volatile ("wfi"); + +#ifdef HDMI_SOUND + extern CHDMISoundBaseDevice *hdmiSoundDevice; + if ( outputHDMI ) + { + flushBuffer = 0; + } +#endif + + latchSetClearImm( LATCH_RESET, allUsedLEDs | LATCH_ENABLE_KERNAL ); + + if ( launchPrg_l264 ) + { + while ( !disableCart_l264 ) + { + #ifdef COMPILE_MENU + TEST_FOR_JUMP_TO_MAINMENU( cycleCountC64, resetCounter ) + #endif + asm volatile ("wfi"); + } + } + #endif + + resetCounter = 0; + cycleCountC64 = 0; + nCyclesEmulated = 0; + samplesElapsed = 0; + ringRead = 0; + for ( int i = 0; i < NUM_SIDS; i++ ) + for ( int j = 0; j < 24; j++ ) + sid[ i ]->write( j, 0 ); + + #ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 ) + { + fmFakeOutput = 0; + ym3812_reset_chip( pOPL ); + } + #endif + + // new main loop mainloop + while ( true ) + { + #ifdef COMPILE_MENU + //TEST_FOR_JUMP_TO_MAINMENU( cycleCountC64, resetCounter ) + if ( cycleCountC64 > 2000000 && resetCounter > 500000 ) { + hdmiSoundDevice->Cancel(); + EnableIRQs(); + m_InputPin.DisableInterrupt(); + m_InputPin.DisconnectInterrupt(); + return; + } + #endif + + if ( resetCounter > 3 && resetReleased ) + { + resetCounter = 0; + + for ( int i = 0; i < NUM_SIDS; i++ ) + for ( int j = 0; j < 24; j++ ) + sid[ i ]->write( j, 0 ); + + #ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 ) + { + fmFakeOutput = 0; + fmAutoDetectStep = 0; + ym3812_reset_chip( pOPL ); + } + #endif + + sidFakeOutput = + sidAutoDetectStep = + sidAutoDetectStep_2 = 0; + + for ( int i = 0; i < 20; i++ ) + { + sidAutoDetectRegs[ i ] = 0; + sidAutoDetectRegs_2[ i ] = 0; + } + } + + #ifdef USE_OLED + if ( renderDone == 2 ) + { + if ( !sendFramebufferDone() ) + sendFramebufferNext( 1 ); + + if ( sendFramebufferDone() ) + renderDone = 3; + } + if ( bufferEmptyI2C() && renderDone == 1 ) + { + sendFramebufferStart(); + renderDone = 2; + } + + #endif + + #ifndef EMULATION_IN_FIQ + + #ifndef USE_PWM_DIRECT + static u32 nSamplesInThisRun = 0; + #endif + + unsigned long long cycleCount = adjustedCycleCount( cycleCountC64 ); + while ( cycleCount > nCyclesEmulated ) + { + unsigned long long samplesElapsedBefore = samplesElapsed; + + long long cycleNextSampleReady = ( ( unsigned long long )(samplesElapsedBefore+1) * ( unsigned long long )CLOCKFREQ_ADJ ) / ( unsigned long long )SAMPLERATE; + u32 cyclesToNextSample = cycleNextSampleReady - nCyclesEmulated; + + do { // do SID emulation until time passed to create an additional sample (i.e. there may be several cycles until a sample value is created) + u32 cyclesToEmulate = min( 32, cycleCount - nCyclesEmulated ); + if ( cyclesToEmulate > cyclesToNextSample ) + cyclesToEmulate = cyclesToNextSample; + + if ( ringRead != ringWrite ) + { + int cyclesToNextWrite = (signed long long)ringTime[ ringRead ] - (signed long long)nCyclesEmulated; + + if ( (int)cyclesToEmulate > cyclesToNextWrite && cyclesToNextWrite > 0 ) + cyclesToEmulate = cyclesToNextWrite; + } + if ( cyclesToEmulate <= 0 ) + cyclesToEmulate = 1; + + sid[ 0 ]->clock( cyclesToEmulate ); + #ifndef SID2_DISABLED + if ( !cfgSID2_Disabled ) + sid[ 1 ]->clock( cyclesToEmulate ); + #endif + + outRegisters[ 27 ] = sid[ 0 ]->read( 27 ); + outRegisters[ 28 ] = sid[ 0 ]->read( 28 ); + + nCyclesEmulated += cyclesToEmulate; + cyclesToNextSample -= cyclesToEmulate; + + // apply register updates (we do one-cycle emulation steps, but in case we need to catch up...) + unsigned int readUpTo = ringWrite; + + if ( ringRead != readUpTo && nCyclesEmulated >= ringTime[ ringRead ] ) + { + { + unsigned char A, D; + decodeGPIO( ringBufGPIO[ ringRead ], &A, &D ); + + u32 tedCommand = ( ringBufGPIO[ ringRead ] >> A6 ) & 1; + + if ( tedCommand ) + { + writeSoundReg( A, D ); + } else + #ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 && (ringBufGPIO[ ringRead ] & bIO2) ) + { + if ( ( ( A & ( 1 << 4 ) ) == 0 ) ) + ym3812_write( pOPL, 0, D ); else + ym3812_write( pOPL, 1, D ); + } else + #endif + //#if !defined(SID2_DISABLED) && !defined(SID2_PLAY_SAME_AS_SID1) + // TODO: generic masks + if ( !cfgSID2_Disabled && !cfgSID2_PlaySameAsSID1 && (ringBufGPIO[ ringRead ] & SID2_MASK) ) + { + sid[ 1 ]->write( A & 31, D ); + } else + //#endif + { + sid[ 0 ]->write( A & 31, D ); + outRegisters[ A & 31 ] = D; + //#if !defined(SID2_DISABLED) && defined(SID2_PLAY_SAME_AS_SID1) + if ( !cfgSID2_Disabled && cfgSID2_PlaySameAsSID1 ) + sid[ 1 ]->write( A & 31, D ); + //#endif + } + } + ringRead++; + ringRead &= ( RING_SIZE - 1 ); + } + + samplesElapsed = ( ( unsigned long long )nCyclesEmulated * ( unsigned long long )SAMPLERATE ) / ( unsigned long long )CLOCKFREQ_ADJ; + + } while ( samplesElapsed == samplesElapsedBefore ); + + s16 val1 = sid[ 0 ]->output(); + s16 val2 = 0; + s32 valOPL = 0; + + #ifndef SID2_DISABLED + if ( !cfgSID2_Disabled ) + val2 = sid[ 1 ]->output(); + #endif + + #ifdef EMULATE_OPL2 + if ( cfgEmulateOPL2 ) + { + ym3812_update_one( pOPL, &valOPL, 1 ); + // TODO asynchronous read back is an issue, needs to be fixed + fmOutRegister = encodeGPIO( ym3812_read( pOPL, 0 ) ); + } + #endif + + // + // mixer + // + s32 left, right; + + short tedV = 0; + + if ( tedVolume > 0 ) + tedV = TEDcalcNextSample(); + + // yes, it's 1 byte shifted in the buffer, need to fix + right = ( val1 * cfgVolSID1_Left + val2 * cfgVolSID2_Left + valOPL * cfgVolOPL_Left + 2 * outputDigiblaster * digiblasterVolume + tedV * tedVolume ) >> 8; + left = ( val1 * cfgVolSID1_Right + val2 * cfgVolSID2_Right + valOPL * cfgVolOPL_Right + 2 * outputDigiblaster * digiblasterVolume + tedV * tedVolume ) >> 8; + + right = max( -32767, min( 32767, right ) ); + left = max( -32767, min( 32767, left ) ); + + #ifdef USE_PWM_DIRECT + putSample( left, right ); + #else + putSample( left ); + putSample( right ); + #endif + + // vu meter + static u32 vu_nValues = 0; + static float vu_Sum[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + //if ( vu_Mode != 2 ) + { + float t = (left+right) / (float)32768.0f * 0.5f; + vu_Sum[ 0 ] += t * t * 1.0f; + + vu_Sum[ 1 ] += val1 * val1 / (float)32768.0f / (float)32768.0f; + vu_Sum[ 2 ] += val2 * val2 / (float)32768.0f / (float)32768.0f; + vu_Sum[ 3 ] += valOPL * valOPL / (float)32768.0f / (float)32768.0f; + + if ( ++ vu_nValues == 256*2 ) + { + for ( u32 i = 0; i < 4; i++ ) + { + float vu_Volume = max( 0.0f, 2.0f * (log10( 0.1f + sqrt( (float)vu_Sum[ i ] / (float)vu_nValues ) ) + 1.0f) ); + u32 v = vu_Volume * 1024.0f; + if ( i == 0 ) + { + // moving average + float v = min( 1.0f, (float)vuMeter[ 0 ] / 1024.0f ); + static float led4Avg = 0.0f; + led4Avg = led4Avg * 0.8f + v * ( 1.0f - 0.8f ); + + vu_nLEDs = max( 0, min( 4, (led4Avg * 8.0f) ) ); + if ( vu_nLEDs > 4 ) vu_nLEDs = 4; + } + vuMeter[ i ] = v; + vu_Sum[ i ] = 0; + } + + vu_nValues = 0; + } + } + + // ugly code which renders 3 oscilloscopes (SID1, SID2, FM) to HDMI and 1 for the OLED + if ( screenType == 0 ) + { + #include "oscilloscope_hack.h" + } else + if ( screenType == 1 ) + { + const float scaleVis = 1.0f; + const u32 nLevelMeters = 3; + #include "tft_sid_vis.h" + } + } + #endif + } + + m_InputPin.DisableInterrupt(); +} + + +#ifdef USE_PWM_DIRECT +static unsigned long long samplesElapsedBeforeFIQ = 0; +#endif + + +#ifdef COMPILE_MENU +void KernelSIDFIQHandler( void *pParam ) +#else +void CKernel::FIQHandler (void *pParam) +#endif +{ + unsigned long long samplesElapsedFIQ; + static s32 latchDelayOut = 10; + u32 swizzle = 0; + + register u32 D; + + #ifdef COMPILE_MENU + if ( launchPrg_l264 && !disableCart_l264 ) + { + LAUNCH_FIQ( cycleCountC64, resetCounter, h_nRegOffset, h_nRegMask ) + } + #endif + + START_AND_READ_EXPPORT264 + + if ( CPU_WRITES_TO_BUS ) + { + READ_D0to7_FROM_BUS( D ) + } else + + // this is an ugly hack to signal the menu code that it can reset (only necessary on a +4) + if ( /*BUS_AVAILABLE264 && CPU_READS_FROM_BUS && */GET_ADDRESS264 >= 0xfd90 && GET_ADDRESS264 <= 0xfd97 ) + { + WRITE_D0to7_TO_BUS( GET_ADDRESS264 - 0xfd90 + 1 ) + PeripheralEntry(); + write32( ARM_GPIO_GPEDS0 + h_nRegOffset, h_nRegMask ); + PeripheralExit(); + write32( ARM_GPIO_GPCLR0, bCTRL257 ); + FINISH_BUS_HANDLING + return; + } + + PeripheralEntry(); + write32( ARM_GPIO_GPEDS0 + h_nRegOffset, h_nRegMask ); + PeripheralExit(); + write32( ARM_GPIO_GPCLR0, bCTRL257 ); + + // __ ___ __ __ __ + // |__) |__ /\ | \ /__` | | \ + // | \ |___ /~~\ |__/ .__/ | |__/ + // + if ( cfgRegisterRead && BUS_AVAILABLE264 && GET_ADDRESS264 >= 0xfd40 && GET_ADDRESS264 <= 0xfd5f && CPU_READS_FROM_BUS ) + { + u32 A = ( g2 >> A0 ) & 31; + u32 D; + + A &= 31; + if ( A == 0x1b ) + { + // fake autodetection + if ( sidAutoDetectStep == 1 ) + { + sidAutoDetectStep = 0; + if ( SID_MODEL[ 0 ] == 8580 ) + D = 2; else + D = 3; + } else + { + static u32 seed = 123456789; + seed = (1103515245 * seed + 12345) & 2147483647; + if ( SID_MODEL[ 0 ] == 8580 ) + D = seed & 255; else + D = seed & 127; + } + } else + { + if ( A >= 0x19 && A <= 0x1a ) + D = 0xff; else +// if ( A >= 0x1b && A <= 0x1c ) +// D = outRegisters[ A ]; else + D = busValue; + } + + WRITE_D0to7_TO_BUS( D ) + //FINISH_BUS_HANDLING + //return; + goto get_out; + } + + if ( CPU_RESET ) { + resetReleased = 0; resetPressed = 1; resetCounter ++; + } else { + if ( resetPressed ) resetReleased = 1; + resetPressed = 0; + } + + // this is a weird attempt of correctly mapping the clock cycles + { + if ( armCycleCounter > 500 * 15/10 ) + cycleCountC64 ++; + } + + RESET_CPU_CYCLE_COUNTER + armCycleCounter = 0; + + cycleCountC64 ++; + + #ifdef COMPILE_MENU + // preload cache + if ( !( launchPrg_l264 && !disableCart_l264 ) ) + { + CACHE_PRELOADL1STRMW( &ringWrite ); + //CACHE_PRELOADL1STRMW( &ringBufGPIO[ ringWrite ] ); + //CACHE_PRELOADL1STRMW( &ringTime[ ringWrite ] ); + CACHE_PRELOADL1STRM( &sampleBuffer[ smpLast ] ); + CACHE_PRELOADL1STRM( &outRegisters[ 0 ] ); + CACHE_PRELOADL1STRM( &outRegisters[ 16 ] ); + } + #endif + + if ( busValueTTL < 0 ) + { + busValue = 0; + } else + busValueTTL --; + + + // __ ___ __ ___ + // |__) |__ /\ | \ |__ |\/| + // | \ |___ /~~\ |__/ | | | + // + if ( cfgEmulateOPL2 ) + { + #ifdef EMULATE_OPL2 + if ( BUS_AVAILABLE264 && CPU_READS_FROM_BUS && GET_ADDRESS264 == 0xfde4 ) + { + // + // this is not a real read of the YM3812 status register! + // only a fake that let's the detection routine be satisfied + // + u32 D = fmFakeOutput; + fmFakeOutput = 0xc0 - fmFakeOutput; + + WRITE_D0to7_TO_BUS( D ) + + FINISH_BUS_HANDLING + return; + } + #ifdef EMULATE_OPL2_THIS_PART_IS_DISABLED + else + // reading of the external Sound Expander Keyboard => we don't have it, return 0xFF + if ( ( CPU_READS_FROM_BUS && IO2_ACCESS ) && ( GET_IO12_ADDRESS >= 0x08 ) && ( GET_IO12_ADDRESS <= 0x0F ) ) + { + WRITE_D0to7_TO_BUS( 0xFF ) + + FINISH_BUS_HANDLING + return; + } else + #endif + // __ ___ ___ ___ + // | | |__) | | |__ |__ |\/| + // |/\| | \ | | |___ | | | + // + if ( BUS_AVAILABLE264 && CPU_WRITES_TO_BUS && + ( GET_ADDRESS264 == cfgEmulateOPL2 || GET_ADDRESS264 == (cfgEmulateOPL2+1) || GET_ADDRESS264 == (cfgEmulateOPL2+0x10) ) ) + { + register u32 A;// = ( GET_ADDRESS264 - cfgEmulateOPL2 + 0x40 ); + if ( GET_ADDRESS264 == cfgEmulateOPL2 ) A = 0x40; else A = 0x50; + register u32 remapAddr = (A&31) << A0; + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + if ( A == 0x40 && D == 0x04 && fmAutoDetectStep != 2 ) + fmAutoDetectStep = 1; + if ( A > 0x40 && D == 0x60 && fmAutoDetectStep == 1 ) + fmAutoDetectStep = 2; + if ( A == 0x40 && D == 0x04 && fmAutoDetectStep == 2 ) + fmAutoDetectStep = 3; + if ( A > 0x40 && D == 0x80 && fmAutoDetectStep == 3 ) + { + fmAutoDetectStep = 4; + fmFakeOutput = 0; + } + + ringBufGPIO[ ringWrite ] = ( remapAddr ) | ( D << D0 ) | bIO2; + ringTime[ ringWrite ] = adjustedCycleCount( cycleCountC64 ); + ringWrite ++; + ringWrite &= ( RING_SIZE - 1 ); + #pragma GCC diagnostic pop + + //FINISH_BUS_HANDLING + //return; + goto get_out; + } + #endif // EMULATE_OPL2 + } + // __ ___ ___ __ __ + // | | |__) | | |__ /__` | | \ + // |/\| | \ | | |___ .__/ | |__/ + // +// if ( BUS_AVAILABLE264 && ( GET_ADDRESS264 >= 0xfd40 && GET_ADDRESS264 <= 0xfd58 ) && CPU_WRITES_TO_BUS ) + if ( BUS_AVAILABLE264 && ( GET_ADDRESS264 >= cfgSID1_Addr && GET_ADDRESS264 <= cfgSID1_Addr + 0x18 ) && CPU_WRITES_TO_BUS ) + { + register u32 A = GET_ADDRESS264 - 0xd400; + register u32 remapAddr = (A&31) << A0; + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + + if ( A < 32 && sidAutoDetectStep == 0 && + sidAutoDetectRegs[ 0x12 ] == 0xff && + sidAutoDetectRegs[ 0x0e ] == 0xff && + sidAutoDetectRegs[ 0x0f ] == 0xff && + (A&31) == 0x12 && D == 0x20 ) + { + sidAutoDetectStep = 1; + } + sidAutoDetectRegs[ A & 31 ] = D; + + ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ) & ~bIO2; + ringTime[ ringWrite ] = adjustedCycleCount( cycleCountC64 ); + ringWrite ++; + ringWrite &= ( RING_SIZE - 1 ); + + busValue = D; + if ( SID_MODEL[ 0 ] == 8580 ) + busValueTTL = 0xa2000; else // 8580 + busValueTTL = 0x1d00; // 6581 + + #pragma GCC diagnostic pop + + // optionally we could directly set the SID-output registers (instead of where the emulation runs) + //u32 A = ( g2 >> A0 ) & 31; + //outRegisters[ A ] = g1 & D_FLAG; + + goto get_out; + } + + if ( BUS_AVAILABLE264 && cfgSID2_Addr == 0xfe80 && GET_ADDRESS264 >= 0xfe80 && GET_ADDRESS264 <= 0xfe98 && CPU_WRITES_TO_BUS ) + { + register u32 A = GET_ADDRESS264 - 0xfe80; + register u32 remapAddr = (A&31) << A0; + remapAddr |= SID2_MASK; + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ) & ~bIO2; + #pragma GCC diagnostic pop + ringTime[ ringWrite ] = adjustedCycleCount( cycleCountC64 ); + ringWrite ++; + ringWrite &= ( RING_SIZE - 1 ); + goto get_out; + } + + if ( BUS_AVAILABLE264 && ( GET_ADDRESS264 == 0xfd5e ) && CPU_WRITES_TO_BUS ) + { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + outputDigiblaster = ((s32)D - 128) * 4; + #pragma GCC diagnostic pop + goto get_out; + } + + if ( tedVolume > 0 && BUS_AVAILABLE264 && ( GET_ADDRESS264 >= 0xff0e && GET_ADDRESS264 <= 0xff12 ) && CPU_WRITES_TO_BUS ) + { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + + register u32 A = GET_ADDRESS264 - 0xff0e; + register u32 remapAddr = (A&31) << A0; + remapAddr |= 1 << A6; + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + ringBufGPIO[ ringWrite ] = ( remapAddr | ( D << D0 ) ); + #pragma GCC diagnostic pop + ringTime[ ringWrite ] = adjustedCycleCount( cycleCountC64 ); + ringWrite ++; + ringWrite &= ( RING_SIZE - 1 ); + + #pragma GCC diagnostic pop + goto get_out; + } + + + + + // ___ ___ __ ___ __ + // |__ |\/| | | | /\ | | / \ |\ | | |\ | |__ | / \ + // |___ | | \__/ |___ /~~\ | | \__/ | \| | | \| | | \__X + // OPTIONAL and omitted for this release + // + #ifdef EMULATION_IN_FIQ + run_emulation: + #include "fragment_emulation_in_fiq.h" + #endif + + // __ __ ___ __ ___ + // |__) | | |\/| / \ | | | |__) | | | + // | |/\| | | \__/ \__/ | | \__/ | + // OPTIONAL + // + #ifdef USE_PWM_DIRECT + samplesElapsedFIQ = ( ( unsigned long long )adjustedCycleCount( cycleCountC64 ) * ( unsigned long long )SAMPLERATE ) / ( unsigned long long )CLOCKFREQ_ADJ; + + if ( samplesElapsedFIQ != samplesElapsedBeforeFIQ ) + { + //write32( ARM_GPIO_GPCLR0, bCTRL257 ); + samplesElapsedBeforeFIQ = samplesElapsedFIQ; + +#ifdef HDMI_SOUND + if ( outputHDMI ) +#else + if ( false ) +#endif + { + static int first = 1; + + extern CHDMISoundBaseDevice *hdmiSoundDevice; + + if ( first && nCyclesEmulated ) + { + hdmiSoundDevice->Start(); + first = 0; + } + + if ( !first ) + { + u32 s = getSample(); + u16 s1 = s & 65535; + u16 s2 = s >> 16; + + if ( flushBuffer || nCyclesEmulated == 0 ) s1 = s2 = 0; + + if ( hdmiSoundDevice->IsWritable() ) + { + hdmiSoundDevice->WriteSample( (s32)(*(s16*)&s1) << 8 ); + hdmiSoundDevice->WriteSample( (s32)(*(s16*)&s2) << 8 ); + //hdmiSoundDevice->WriteSample( 0 ); + } + /*extern CHDMISoundBaseDevice *hdmiSoundDevice; + if ( hdmiSoundDevice->IsWritable() && curChannel == 0 ) + { + hdmiSoundDevice->WriteSample( (s32)(*(s16*)&s1) << 8 ); + curChannel = 1; + } + if ( hdmiSoundDevice->IsWritable() && curChannel == 1 ) + { + hdmiSoundDevice->WriteSample( (s32)(*(s16*)&s2) << 8 ); + curChannel = 0; + }*/ + + } + } else + { + u32 s = getSample(); + u16 s1 = s & 65535; + u16 s2 = s >> 16; + + s32 d1 = (s32)( ( *(s16*)&s1 + 32768 ) * PWMRange ) >> 17; + s32 d2 = (s32)( ( *(s16*)&s2 + 32768 ) * PWMRange ) >> 17; + write32( ARM_PWM_DAT1, d1 ); + write32( ARM_PWM_DAT2, d2 ); + } + goto get_out; + } + #endif + + static u32 omitLatch = 0; + + static u32 lastButtonPressed = 0; + + if ( lastButtonPressed > 0 ) + lastButtonPressed --; + + static u32 buttonIsPressed = 0; + + if ( BUTTON_PRESSED && lastButtonPressed == 0 ) + buttonIsPressed ++; else + buttonIsPressed = 0; + + if ( buttonIsPressed > 50 ) + { + visModeGotoNext = 1; + vu_Mode = ( vu_Mode + 1 ) & 3; + lastButtonPressed = 100000; + } + + if ( omitLatch ) + goto get_out; + // ___ __ + // | /\ | / ` |__| + // |___ /~~\ | \__, | | + // +#if 1 + #ifdef USE_LATCH_OUTPUT + if ( screenType == 0 ) + { + if ( --latchDelayOut == 1 && renderDone == 3 ) + { + prefetchI2C(); + } + if ( latchDelayOut <= 0 && renderDone == 3 ) + { + latchDelayOut = 2; + prepareOutputLatch(); + if ( bufferEmptyI2C() ) renderDone = 0; + OUTPUT_LATCH_AND_FINISH_BUS_HANDLING + return; + } + } + #endif +#endif +#if 1 + if ( screenType == 0 ) + { + static u32 fCount = 0; + fCount ++; + fCount &= 255; + + if ( vu_Mode == 0 ) + { + setLatchFIQ( LATCH_ON[ vu_nLEDs ] ); + clrLatchFIQ( LATCH_OFF[ vu_nLEDs ] ); + } else + if ( vu_Mode == 1 ) + { + if ( swizzle < vuMeter[ 0 ] ) + setLatchFIQ( allUsedLEDs ); else + clrLatchFIQ( allUsedLEDs ); + } else + if ( vu_Mode == 2 ) + { + swizzle = + ( (fCount & 128) >> 7 ) | + ( (fCount & 64) >> 5 ) | + ( (fCount & 32) >> 3 ) | + ( (fCount & 16) >> 1 ) | + ( (fCount & 8) << 1 ) | + ( (fCount & 4) << 3 ) | + ( (fCount & 2) << 5 ) | + ( (fCount & 1) << 7 ); + + u32 led = + ( ( swizzle < vuMeter[ 1 ] ) ? LATCH_LED1 : 0 ) | + ( ( swizzle < vuMeter[ 2 ] ) ? LATCH_LED2 : 0 ) | + ( ( swizzle < vuMeter[ 3 ] ) ? LATCH_LED3 : 0 ); + + setLatchFIQ( led ); + clrLatchFIQ( ( (~led) & allUsedLEDs ) | LATCH_LED0 ); + } else + clrLatchFIQ( allUsedLEDs ); + } else + { + + //prepareOutputLatch4Bit(); + //outputLatch(); + prepareOutputLatch4Bit(); + outputLatch(); + //OUTPUT_LATCH_AND_FINISH_BUS_HANDLING + } +#endif + +get_out: + + if ( resetCounter > 3 ) + { + disableCart_l264 = transferStarted_l264 = 0; + } + + //OUTPUT_LATCH_AND_FINISH_BUS_HANDLING + //write32( ARM_GPIO_GPCLR0, bCTRL257 ); +} + +#ifndef COMPILE_MENU +int main( void ) +{ + CKernel kernel; + if ( kernel.Initialize() ) + kernel.Run(); + + halt(); + return EXIT_HALT; +} #endif \ No newline at end of file diff --git a/Source/Firmware/kernel_sid264.h b/Source/Firmware/kernel_sid264.h index 2bc96ad2..cffb608b 100644 --- a/Source/Firmware/kernel_sid264.h +++ b/Source/Firmware/kernel_sid264.h @@ -1,199 +1,199 @@ -/* - _________.__ .___ __ .__ __ _________.___________ - / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ - / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / - \/ \/ \/ \/ \/ \/ \/ \/ - - kernel_sid264.h - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - Sidekick SID: a SID and SFX Sound Expander Emulation for the C16/+4 - (using reSID by Dag Lem and FMOPL by Jarek Burczynski, Tatsuyuki Satoh, Marco van den Heuvel, and Acho A. Tang) - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ -#ifndef _kernel_sid_h -#define _kernel_sid_h - -// use the OLED connected to the latch -#define USE_OLED - -// -// choose whether to output sound via the headphone jack (PWM), otherwise HDMI audio will be used (higher delay) -// -#define USE_PWM_DIRECT - -// -// sample rate -// -#define SAMPLERATE 44100 - -// 6581 or 8580 -extern unsigned int SID_MODEL[2]; -extern unsigned int SID_DigiBoost[2]; - -// -// options for the 2nd SID -// -//#define SID2_DISABLED -//#define SID2_PLAY_SAME_AS_SID1 - -// $D420 (others not yet supported) -#define SID2_MASK (1< -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_VCHIQ_SOUND -#include -#include -#endif - -#include "lowlevel_arm64.h" -#include "gpio_defs.h" -#include "latch.h" -#include "sound.h" -#include "helpers.h" -#include "helpers264.h" -#include "mygpiopinfiq.h" - -#ifdef USE_OLED -#include "oled.h" -#include "splash_sid.h" -#endif - -#ifdef EMULATE_OPL2 -#include "fmopl.h" -#endif - -#ifdef COMPILE_MENU -#include "kernel_menu264.h" -void KernelSIDFIQHandler( void *pParam ); -#define FIQ_HANDLER KernelSIDFIQHandler -#define FIQ_PARENT kernelMenu -#else -extern CLogger *logger; -#define FIQ_HANDLER (m_InputPin.FIQHandler) -#define FIQ_PARENT this -#endif - - -#ifndef min -#define min( a, b ) ( ((a)<(b))?(a):(b) ) -#endif -#ifndef max -#define max( a, b ) ( ((a)>(b))?(a):(b) ) -#endif - -class CKernel -{ -public: - CKernel( void ) - : m_CPUThrottle( CPUSpeedMaximum ), - #ifdef USE_HDMI_VIDEO - m_Screen( m_Options.GetWidth(), m_Options.GetHeight() ), - #endif - m_Timer( &m_Interrupt ), - m_Logger( m_Options.GetLogLevel(), &m_Timer ), - #ifdef USE_VCHIQ_SOUND - m_VCHIQ( &m_Memory, &m_Interrupt ), - #endif - m_pSound( 0 ), - m_InputPin( PHI2, GPIOModeInput, &m_Interrupt ) - { - } - - ~CKernel( void ) - { - } - - boolean Initialize( void ); - - void Run( void ); - -private: - static void FIQHandler( void *pParam ); - - // do not change this order - CMemorySystem m_Memory; - CKernelOptions m_Options; - CDeviceNameService m_DeviceNameService; - CCPUThrottle m_CPUThrottle; -#ifdef USE_HDMI_VIDEO - CScreenDevice m_Screen; -#endif - CInterruptSystem m_Interrupt; - CTimer m_Timer; - CLogger m_Logger; - CScheduler m_Scheduler; -#ifdef USE_VCHIQ_SOUND - CVCHIQDevice m_VCHIQ; -#endif - CSoundBaseDevice *m_pSound; - CGPIOPinFIQ2 m_InputPin; -}; - -extern void setSIDConfiguration( u32 mode, u32 sid1, u32 sid2, u32 rr, u32 addr, u32 exp ); - - -#endif +/* + _________.__ .___ __ .__ __ _________.___________ + / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ + / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / + \/ \/ \/ \/ \/ \/ \/ \/ + + kernel_sid264.h + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - Sidekick SID: a SID and SFX Sound Expander Emulation for the C16/+4 + (using reSID by Dag Lem and FMOPL by Jarek Burczynski, Tatsuyuki Satoh, Marco van den Heuvel, and Acho A. Tang) + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ +#ifndef _kernel_sid_h +#define _kernel_sid_h + +// use the OLED connected to the latch +#define USE_OLED + +// +// choose whether to output sound via the headphone jack (PWM), otherwise HDMI audio will be used (higher delay) +// +#define USE_PWM_DIRECT + +// +// sample rate +// +#define SAMPLERATE 48000 + +// 6581 or 8580 +extern unsigned int SID_MODEL[2]; +extern unsigned int SID_DigiBoost[2]; + +// +// options for the 2nd SID +// +//#define SID2_DISABLED +//#define SID2_PLAY_SAME_AS_SID1 + +// $D420 (others not yet supported) +#define SID2_MASK (1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_VCHIQ_SOUND +#include +#include +#endif + +#include "lowlevel_arm64.h" +#include "gpio_defs.h" +#include "latch.h" +#include "sound.h" +#include "helpers.h" +#include "helpers264.h" +#include "mygpiopinfiq.h" + +#ifdef USE_OLED +#include "oled.h" +#include "splash_sid.h" +#endif + +#ifdef EMULATE_OPL2 +#include "fmopl.h" +#endif + +#ifdef COMPILE_MENU +#include "kernel_menu264.h" +void KernelSIDFIQHandler( void *pParam ); +#define FIQ_HANDLER KernelSIDFIQHandler +#define FIQ_PARENT kernelMenu +#else +extern CLogger *logger; +#define FIQ_HANDLER (m_InputPin.FIQHandler) +#define FIQ_PARENT this +#endif + + +#ifndef min +#define min( a, b ) ( ((a)<(b))?(a):(b) ) +#endif +#ifndef max +#define max( a, b ) ( ((a)>(b))?(a):(b) ) +#endif + +class CKernel +{ +public: + CKernel( void ) + : m_CPUThrottle( CPUSpeedMaximum ), + #ifdef USE_HDMI_VIDEO + m_Screen( m_Options.GetWidth(), m_Options.GetHeight() ), + #endif + m_Timer( &m_Interrupt ), + m_Logger( m_Options.GetLogLevel(), &m_Timer ), + #ifdef USE_VCHIQ_SOUND + m_VCHIQ( &m_Memory, &m_Interrupt ), + #endif + m_pSound( 0 ), + m_InputPin( PHI2, GPIOModeInput, &m_Interrupt ) + { + } + + ~CKernel( void ) + { + } + + boolean Initialize( void ); + + void Run( void ); + +private: + static void FIQHandler( void *pParam ); + + // do not change this order + CMemorySystem m_Memory; + CKernelOptions m_Options; + CDeviceNameService m_DeviceNameService; + CCPUThrottle m_CPUThrottle; +#ifdef USE_HDMI_VIDEO + CScreenDevice m_Screen; +#endif + CInterruptSystem m_Interrupt; + CTimer m_Timer; + CLogger m_Logger; + CScheduler m_Scheduler; +#ifdef USE_VCHIQ_SOUND + CVCHIQDevice m_VCHIQ; +#endif + CSoundBaseDevice *m_pSound; + CGPIOPinFIQ2 m_InputPin; +}; + +extern void setSIDConfiguration( u32 mode, u32 sid1, u32 sid2, u32 rr, u32 addr, u32 exp ); + + +#endif diff --git a/Source/Firmware/sound.cpp b/Source/Firmware/sound.cpp index 3339fa08..750d1fc9 100644 --- a/Source/Firmware/sound.cpp +++ b/Source/Firmware/sound.cpp @@ -1,258 +1,260 @@ -/* - _________.__ .___ __ .__ __ _________ .___ - / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/ ____ __ __ ____ __| _/ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ / _ \| | \/ \ / __ | - / \| / /_/ \ ___/| <| \ \___| < / ( <_> ) | / | \/ /_/ | -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /\____/|____/|___| /\____ | - \/ \/ \/ \/ \/ \/ \/ \/ \/ - - sound.cpp - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - sound output code - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ -#include "kernel_sid.h" - -#define WRITE_CHANNELS 2 // 1: Mono, 2: Stereo -#define CHUNK_SIZE 2000 // number of samples, written to sound device at once - -#define FORMAT SoundFormatSigned16 -#define TYPE s16 -#define TYPE_SIZE sizeof (s16) -#define FACTOR ((1 << 15)-1) -#define NULL_LEVEL 0 - -// forward declarations -void initPWMOutput(); -void cbSound( void *d ); -void clearSoundBuffer(); - -#ifdef USE_VCHIQ_SOUND -short PCMBuffer[ PCMBufferSize ]; -#endif - -u32 nSamplesPrecompute; -static int FirstBufferUpdate = 1; -u32 soundDebugCode; - -void initSoundOutput( CSoundBaseDevice **m_pSound, CVCHIQDevice *m_VCHIQ, u32 outputPWM, u32 outputHDMI ) -{ - clearSoundBuffer(); - -#ifdef USE_PWM_DIRECT - if ( outputPWM ) - initPWMOutput(); -#endif -#ifdef USE_VCHIQ_SOUND -// if ( m_pSound == NULL || m_VCHIQ == NULL || outputHDMI == 0 ) - if ( outputHDMI == 0 ) - return; - - //( *m_pSound ) = new CPWMSoundBaseDevice( &m_Interrupt, SAMPLERATE, CHUNK_SIZE ); - if ( (*m_pSound) == NULL ) - { - ( *m_pSound ) = new CVCHIQSoundBaseDevice( m_VCHIQ, SAMPLERATE, CHUNK_SIZE, VCHIQSoundDestinationHDMI ); - ( *m_pSound )->AllocateQueue( QUEUE_SIZE_MSECS ); - ( *m_pSound )->SetWriteFormat( FORMAT, WRITE_CHANNELS ); - ( *m_pSound )->RegisterNeedDataCallback( cbSound, (void*)( *m_pSound ) ); - } - - extern u32 SAMPLERATE_ADJUSTED; - SAMPLERATE_ADJUSTED = SAMPLERATE; - FirstBufferUpdate = 1; - - clearSoundBuffer(); - PCMCountLast = PCMCountCur = 0; - nSamplesPrecompute = QUEUE_SIZE_MSECS * SAMPLERATE / 1000; - soundDebugCode = 0; - -/* for ( u32 i = 0; i < QUEUE_SIZE_MSECS * SAMPLERATE / 1000 * 3 / 2; i++ ) - { - putSample( 0 ); - putSample( 0 ); - }*/ - -#endif -} - -// __________ __ __ _____ _________ .___ -// \______ \/ \ / \/ \ / _____/ ____ __ __ ____ __| _/ -// | ___/\ \/\/ / \ / \ \_____ \ / _ \| | \/ \ / __ | -// | | \ / Y \ / ( <_> ) | / | \/ /_/ | -// |____| \__/\ /\____|__ / /_______ /\____/|____/|___| /\____ | -// \/ \/ \/ \/ \/ - -// for PWM Output -// -u32 sampleBuffer[ 128 ]; -u32 smpLast, smpCur; - -#ifdef USE_PWM_DIRECT - -#define CLOCK_FREQ 500000000 -#define CLOCK_DIVIDER 2 - -// PWM control register -#define ARM_PWM_CTL_PWEN1 (1 << 0) -#define ARM_PWM_CTL_MODE1 (1 << 1) -#define ARM_PWM_CTL_RPTL1 (1 << 2) -#define ARM_PWM_CTL_SBIT1 (1 << 3) -#define ARM_PWM_CTL_POLA1 (1 << 4) -#define ARM_PWM_CTL_USEF1 (1 << 5) -#define ARM_PWM_CTL_CLRF1 (1 << 6) -#define ARM_PWM_CTL_MSEN1 (1 << 7) -#define ARM_PWM_CTL_PWEN2 (1 << 8) -#define ARM_PWM_CTL_MODE2 (1 << 9) -#define ARM_PWM_CTL_RPTL2 (1 << 10) -#define ARM_PWM_CTL_SBIT2 (1 << 11) -#define ARM_PWM_CTL_POLA2 (1 << 12) -#define ARM_PWM_CTL_USEF2 (1 << 13) -#define ARM_PWM_CTL_MSEN2 (1 << 14) - -// PWM status register -#define ARM_PWM_STA_FULL1 (1 << 0) -#define ARM_PWM_STA_EMPT1 (1 << 1) -#define ARM_PWM_STA_WERR1 (1 << 2) -#define ARM_PWM_STA_RERR1 (1 << 3) -#define ARM_PWM_STA_GAPO1 (1 << 4) -#define ARM_PWM_STA_GAPO2 (1 << 5) -#define ARM_PWM_STA_GAPO3 (1 << 6) -#define ARM_PWM_STA_GAPO4 (1 << 7) -#define ARM_PWM_STA_BERR (1 << 8) -#define ARM_PWM_STA_STA1 (1 << 9) -#define ARM_PWM_STA_STA2 (1 << 10) -#define ARM_PWM_STA_STA3 (1 << 11) -#define ARM_PWM_STA_STA4 (1 << 12) - -u32 PWMRange; - -void initPWMOutput() -{ - __attribute__((unused)) CGPIOPin *m_Audio1 = new CGPIOPin( GPIOPinAudioLeft, GPIOModeAlternateFunction0 ); - __attribute__((unused)) CGPIOPin *m_Audio2 = new CGPIOPin( GPIOPinAudioRight, GPIOModeAlternateFunction0 ); - CGPIOClock *m_Clock = new CGPIOClock( GPIOClockPWM, GPIOClockSourcePLLD ); - - u32 nSampleRate = SAMPLERATE; - PWMRange = ( CLOCK_FREQ / CLOCK_DIVIDER + nSampleRate / 2 ) / nSampleRate; - - PeripheralEntry(); - - m_Clock->Start( CLOCK_DIVIDER ); - CTimer::SimpleusDelay( 2000 ); - - write32( ARM_PWM_RNG1, PWMRange ); - write32( ARM_PWM_RNG2, PWMRange ); - - u32 nControl = ARM_PWM_CTL_PWEN1 | ARM_PWM_CTL_PWEN2; - write32( ARM_PWM_CTL, nControl ); - - CTimer::SimpleusDelay( 2000 ); - - PeripheralExit(); -} -#endif - -// ___ ___________ _____ .___ _________ .___ -// / | \______ \ / \ | | / _____/ ____ __ __ ____ __| _/ -// / ~ \ | \ / \ / \| | \_____ \ / _ \| | \/ \ / __ | -// \ Y / ` \/ Y \ | / ( <_> ) | / | \/ /_/ | -// \___|_ /_______ /\____|__ /___| /_______ /\____/|____/|___| /\____ | -// \/ \/ \/ \/ \/ \/ -#ifdef USE_VCHIQ_SOUND -u32 PCMCountLast, PCMCountCur; - -u32 samplesInBuffer() -{ - u32 nFrames; - if ( PCMCountLast <= PCMCountCur ) - nFrames = PCMCountCur - PCMCountLast; else - nFrames = PCMCountCur + PCMBufferSize - PCMCountLast; - return nFrames; -} - -u32 samplesInBufferFree() -{ - return PCMBufferSize - 16 - samplesInBuffer(); -} - -bool pcmBufferFull() -{ - if ( ( (PCMCountCur+1) == (PCMCountLast) ) || - ( PCMCountCur == (PCMBufferSize-1) && PCMCountLast == 0 ) ) - return true; - - return false; -} -#endif - -// -// callback called when more samples are needed by the HDMI sound playback -// this code is incredibly ugly and inefficient (more memory copies than necessary) -// -#ifdef USE_VCHIQ_SOUND - -// -// don't look too close... -// this is more than ugly, but the version without this copying has a bug when reaching the end of the buffer -// presumably this happens when writing part 1 of two does not transfer all bytes and then we're loosing a frame -// once this is fixed, call 2x m_pSound->Write instead of copying the data to temp -// -void cbSound( void *d ) -{ - extern u32 fillSoundBuffer; - if ( fillSoundBuffer != 0xffffffff ) - { - fillSoundBuffer = 1; - return; - } - - CSoundBaseDevice *m_pSound = (CSoundBaseDevice*)d; - extern u32 trackSampleProgress; - - s32 nFramesComputed = samplesInBuffer(); - s32 nFramesMax = m_pSound->GetQueueSizeFrames() - m_pSound->GetQueueFramesAvail(); - trackSampleProgress = 1024 * 1024 + ( nFramesComputed - nFramesMax ); - - s32 nWriteFrames = min( nFramesComputed, nFramesMax ); - - if ( nWriteFrames * 2 + PCMCountLast > PCMBufferSize ) - { - m_pSound->Write( &PCMBuffer[ PCMCountLast ], (PCMBufferSize - PCMCountLast) * TYPE_SIZE ); - m_pSound->Write( &PCMBuffer[ 0 ], (2 * nWriteFrames - (PCMBufferSize - PCMCountLast)) * TYPE_SIZE ); - } else - { - m_pSound->Write( &PCMBuffer[ PCMCountLast ], 2 * nWriteFrames * TYPE_SIZE ); - } - PCMCountLast += nWriteFrames * 2; - PCMCountLast %= PCMBufferSize; - return; -} - -#endif - -void clearSoundBuffer() -{ -#ifdef USE_PWM_DIRECT - memset( sampleBuffer, 0, sizeof( u32 ) * 128 ); -#endif -#ifdef USE_VCHIQ_SOUND - memset( PCMBuffer, 0, sizeof( short ) * PCMBufferSize ); -#endif -} - +/* + _________.__ .___ __ .__ __ _________ .___ + / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/ ____ __ __ ____ __| _/ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ / _ \| | \/ \ / __ | + / \| / /_/ \ ___/| <| \ \___| < / ( <_> ) | / | \/ /_/ | +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /\____/|____/|___| /\____ | + \/ \/ \/ \/ \/ \/ \/ \/ \/ + + sound.cpp + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - sound output code + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ +#include "kernel_sid.h" + +#define WRITE_CHANNELS 2 // 1: Mono, 2: Stereo +#define CHUNK_SIZE 2000 // number of samples, written to sound device at once + +#define FORMAT SoundFormatSigned16 +#define TYPE s16 +#define TYPE_SIZE sizeof (s16) +#define FACTOR ((1 << 15)-1) +#define NULL_LEVEL 0 + +// forward declarations +void initPWMOutput(); +void cbSound( void *d ); +void clearSoundBuffer(); + +#ifdef USE_VCHIQ_SOUND +short PCMBuffer[ PCMBufferSize ]; +#endif + +u32 nSamplesPrecompute; +static int FirstBufferUpdate = 1; +u32 soundDebugCode; + +void initSoundOutput( CSoundBaseDevice **m_pSound, CVCHIQDevice *m_VCHIQ, u32 outputPWM, u32 outputHDMI ) +{ + clearSoundBuffer(); + +#ifdef USE_PWM_DIRECT + if ( outputPWM ) + initPWMOutput(); +#endif +#ifdef USE_VCHIQ_SOUND +// if ( m_pSound == NULL || m_VCHIQ == NULL || outputHDMI == 0 ) + if ( outputHDMI == 0 ) + return; + + //( *m_pSound ) = new CPWMSoundBaseDevice( &m_Interrupt, SAMPLERATE, CHUNK_SIZE ); + if ( (*m_pSound) == NULL ) + { + ( *m_pSound ) = new CVCHIQSoundBaseDevice( m_VCHIQ, SAMPLERATE, CHUNK_SIZE, VCHIQSoundDestinationHDMI ); + ( *m_pSound )->AllocateQueue( QUEUE_SIZE_MSECS ); + ( *m_pSound )->SetWriteFormat( FORMAT, WRITE_CHANNELS ); + ( *m_pSound )->RegisterNeedDataCallback( cbSound, (void*)( *m_pSound ) ); + } + + extern u32 SAMPLERATE_ADJUSTED; + SAMPLERATE_ADJUSTED = SAMPLERATE; + FirstBufferUpdate = 1; + + clearSoundBuffer(); + PCMCountLast = PCMCountCur = 0; + nSamplesPrecompute = QUEUE_SIZE_MSECS * SAMPLERATE / 1000; + soundDebugCode = 0; + +/* for ( u32 i = 0; i < QUEUE_SIZE_MSECS * SAMPLERATE / 1000 * 3 / 2; i++ ) + { + putSample( 0 ); + putSample( 0 ); + }*/ + +#endif +} + +// __________ __ __ _____ _________ .___ +// \______ \/ \ / \/ \ / _____/ ____ __ __ ____ __| _/ +// | ___/\ \/\/ / \ / \ \_____ \ / _ \| | \/ \ / __ | +// | | \ / Y \ / ( <_> ) | / | \/ /_/ | +// |____| \__/\ /\____|__ / /_______ /\____/|____/|___| /\____ | +// \/ \/ \/ \/ \/ + +// for PWM Output +// +u32 sampleBuffer[ 128 ]; +u32 smpLast, smpCur; + +#ifdef USE_PWM_DIRECT + +#define CLOCK_FREQ 500000000 +#define CLOCK_DIVIDER 2 + +// PWM control register +#define ARM_PWM_CTL_PWEN1 (1 << 0) +#define ARM_PWM_CTL_MODE1 (1 << 1) +#define ARM_PWM_CTL_RPTL1 (1 << 2) +#define ARM_PWM_CTL_SBIT1 (1 << 3) +#define ARM_PWM_CTL_POLA1 (1 << 4) +#define ARM_PWM_CTL_USEF1 (1 << 5) +#define ARM_PWM_CTL_CLRF1 (1 << 6) +#define ARM_PWM_CTL_MSEN1 (1 << 7) +#define ARM_PWM_CTL_PWEN2 (1 << 8) +#define ARM_PWM_CTL_MODE2 (1 << 9) +#define ARM_PWM_CTL_RPTL2 (1 << 10) +#define ARM_PWM_CTL_SBIT2 (1 << 11) +#define ARM_PWM_CTL_POLA2 (1 << 12) +#define ARM_PWM_CTL_USEF2 (1 << 13) +#define ARM_PWM_CTL_MSEN2 (1 << 14) + +// PWM status register +#define ARM_PWM_STA_FULL1 (1 << 0) +#define ARM_PWM_STA_EMPT1 (1 << 1) +#define ARM_PWM_STA_WERR1 (1 << 2) +#define ARM_PWM_STA_RERR1 (1 << 3) +#define ARM_PWM_STA_GAPO1 (1 << 4) +#define ARM_PWM_STA_GAPO2 (1 << 5) +#define ARM_PWM_STA_GAPO3 (1 << 6) +#define ARM_PWM_STA_GAPO4 (1 << 7) +#define ARM_PWM_STA_BERR (1 << 8) +#define ARM_PWM_STA_STA1 (1 << 9) +#define ARM_PWM_STA_STA2 (1 << 10) +#define ARM_PWM_STA_STA3 (1 << 11) +#define ARM_PWM_STA_STA4 (1 << 12) + +u32 PWMRange; + +void initPWMOutput() +{ + __attribute__((unused)) CGPIOPin *m_Audio1 = new CGPIOPin( GPIOPinAudioLeft, GPIOModeAlternateFunction0 ); + __attribute__((unused)) CGPIOPin *m_Audio2 = new CGPIOPin( GPIOPinAudioRight, GPIOModeAlternateFunction0 ); + CGPIOClock *m_Clock = new CGPIOClock( GPIOClockPWM, GPIOClockSourcePLLD ); + + u32 nSampleRate = SAMPLERATE; + PWMRange = ( CLOCK_FREQ / CLOCK_DIVIDER + nSampleRate / 2 ) / nSampleRate; + + PeripheralEntry(); + + m_Clock->Start( CLOCK_DIVIDER ); + CTimer::SimpleusDelay( 2000 ); + + write32( ARM_PWM_RNG1, PWMRange ); + write32( ARM_PWM_RNG2, PWMRange ); + + u32 nControl = ARM_PWM_CTL_PWEN1 | ARM_PWM_CTL_PWEN2; + write32( ARM_PWM_CTL, nControl ); + + CTimer::SimpleusDelay( 2000 ); + + PeripheralExit(); + + smpLast = smpCur = 0; +} +#endif + +// ___ ___________ _____ .___ _________ .___ +// / | \______ \ / \ | | / _____/ ____ __ __ ____ __| _/ +// / ~ \ | \ / \ / \| | \_____ \ / _ \| | \/ \ / __ | +// \ Y / ` \/ Y \ | / ( <_> ) | / | \/ /_/ | +// \___|_ /_______ /\____|__ /___| /_______ /\____/|____/|___| /\____ | +// \/ \/ \/ \/ \/ \/ +#ifdef USE_VCHIQ_SOUND +u32 PCMCountLast, PCMCountCur; + +u32 samplesInBuffer() +{ + u32 nFrames; + if ( PCMCountLast <= PCMCountCur ) + nFrames = PCMCountCur - PCMCountLast; else + nFrames = PCMCountCur + PCMBufferSize - PCMCountLast; + return nFrames; +} + +u32 samplesInBufferFree() +{ + return PCMBufferSize - 16 - samplesInBuffer(); +} + +bool pcmBufferFull() +{ + if ( ( (PCMCountCur+1) == (PCMCountLast) ) || + ( PCMCountCur == (PCMBufferSize-1) && PCMCountLast == 0 ) ) + return true; + + return false; +} +#endif + +// +// callback called when more samples are needed by the HDMI sound playback +// this code is incredibly ugly and inefficient (more memory copies than necessary) +// +#ifdef USE_VCHIQ_SOUND + +// +// don't look too close... +// this is more than ugly, but the version without this copying has a bug when reaching the end of the buffer +// presumably this happens when writing part 1 of two does not transfer all bytes and then we're loosing a frame +// once this is fixed, call 2x m_pSound->Write instead of copying the data to temp +// +void cbSound( void *d ) +{ + extern u32 fillSoundBuffer; + if ( fillSoundBuffer != 0xffffffff ) + { + fillSoundBuffer = 1; + return; + } + + CSoundBaseDevice *m_pSound = (CSoundBaseDevice*)d; + extern u32 trackSampleProgress; + + s32 nFramesComputed = samplesInBuffer(); + s32 nFramesMax = m_pSound->GetQueueSizeFrames() - m_pSound->GetQueueFramesAvail(); + trackSampleProgress = 1024 * 1024 + ( nFramesComputed - nFramesMax ); + + s32 nWriteFrames = min( nFramesComputed, nFramesMax ); + + if ( nWriteFrames * 2 + PCMCountLast > PCMBufferSize ) + { + m_pSound->Write( &PCMBuffer[ PCMCountLast ], (PCMBufferSize - PCMCountLast) * TYPE_SIZE ); + m_pSound->Write( &PCMBuffer[ 0 ], (2 * nWriteFrames - (PCMBufferSize - PCMCountLast)) * TYPE_SIZE ); + } else + { + m_pSound->Write( &PCMBuffer[ PCMCountLast ], 2 * nWriteFrames * TYPE_SIZE ); + } + PCMCountLast += nWriteFrames * 2; + PCMCountLast %= PCMBufferSize; + return; +} + +#endif + +void clearSoundBuffer() +{ +#ifdef USE_PWM_DIRECT + memset( sampleBuffer, 0, sizeof( u32 ) * 128 ); +#endif +#ifdef USE_VCHIQ_SOUND + memset( PCMBuffer, 0, sizeof( short ) * PCMBufferSize ); +#endif +} + diff --git a/Source/Firmware/sound.h b/Source/Firmware/sound.h index 2c2d104c..7d90640d 100644 --- a/Source/Firmware/sound.h +++ b/Source/Firmware/sound.h @@ -1,69 +1,70 @@ -/* - _________.__ .___ __ .__ __ _________ .___ - / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/ ____ __ __ ____ __| _/ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ / _ \| | \/ \ / __ | - / \| / /_/ \ ___/| <| \ \___| < / ( <_> ) | / | \/ /_/ | -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /\____/|____/|___| /\____ | - \/ \/ \/ \/ \/ \/ \/ \/ \/ - - sound.h - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - sound output code - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ -#ifndef _sound_h_ -#define _sound_h_ - -#define PCMBufferSize (48000/4) -#define QUEUE_SIZE_MSECS 50 // size of the sound queue in milliseconds duration -extern u32 nSamplesPrecompute; - -extern u32 PWMRange; - -extern void initSoundOutput( CSoundBaseDevice **m_pSound = NULL, CVCHIQDevice *m_VCHIQ = NULL, u32 outputPWM = 0, u32 outputHDMI = 0 ); -extern void clearSoundBuffer(); - -extern u32 sampleBuffer[ 128 ]; -extern u32 smpLast, smpCur; - -static __attribute__( ( always_inline ) ) inline void putSample( s16 a, s16 b ) -{ - u16 *a_ = (u16*)&a, *b_ = (u16*)&b; - sampleBuffer[ smpCur ++ ] = (u32)*a_ + ( ((u32)*b_) << 16 ); - smpCur &= 127; -} - -static __attribute__( ( always_inline ) ) inline s32 getSample() -{ - u32 ret = sampleBuffer[ smpLast ++ ]; - smpLast &= 127; - return ret; -} - -extern short PCMBuffer[ PCMBufferSize ]; -extern u32 PCMCountLast, PCMCountCur; - -static __attribute__( ( always_inline ) ) inline void putSample( short s ) -{ - PCMBuffer[ PCMCountCur ++ ] = s; - PCMCountCur %= PCMBufferSize; -} - - +/* + _________.__ .___ __ .__ __ _________ .___ + / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/ ____ __ __ ____ __| _/ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ / _ \| | \/ \ / __ | + / \| / /_/ \ ___/| <| \ \___| < / ( <_> ) | / | \/ /_/ | +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /\____/|____/|___| /\____ | + \/ \/ \/ \/ \/ \/ \/ \/ \/ + + sound.h + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - sound output code + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ +#ifndef _sound_h_ +#define _sound_h_ + +#define PCMBufferSize (48000/4) +#define QUEUE_SIZE_MSECS 50 // size of the sound queue in milliseconds duration +extern u32 nSamplesPrecompute; + +extern u32 PWMRange; + +extern void initSoundOutput( CSoundBaseDevice **m_pSound = NULL, CVCHIQDevice *m_VCHIQ = NULL, u32 outputPWM = 0, u32 outputHDMI = 0 ); +extern void clearSoundBuffer(); + +extern u32 sampleBuffer[ 128 ]; +extern u32 smpLast, smpCur; + +static __attribute__( ( always_inline ) ) inline void putSample( s16 a, s16 b ) +{ + u16 *a_ = (u16*)&a, *b_ = (u16*)&b; + sampleBuffer[ smpCur ++ ] = (u32)*a_ + ( ((u32)*b_) << 16 ); + smpCur &= 127; +} + +static __attribute__( ( always_inline ) ) inline s32 getSample() +{ + if ( smpLast == smpCur ) return sampleBuffer[ smpLast ]; + u32 ret = sampleBuffer[ smpLast ++ ]; + smpLast &= 127; + return ret; +} + +extern short PCMBuffer[ PCMBufferSize ]; +extern u32 PCMCountLast, PCMCountCur; + +static __attribute__( ( always_inline ) ) inline void putSample( short s ) +{ + PCMBuffer[ PCMCountCur ++ ] = s; + PCMCountCur %= PCMBufferSize; +} + + #endif \ No newline at end of file diff --git a/Source/Firmware/tft_sid_vis.h b/Source/Firmware/tft_sid_vis.h index 50f2418c..d90f256d 100644 --- a/Source/Firmware/tft_sid_vis.h +++ b/Source/Firmware/tft_sid_vis.h @@ -1,258 +1,263 @@ -/* - _________.__ .___ __ .__ __ _________.___________ - / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ - \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ - / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ -/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / - \/ \/ \/ \/ \/ \/ \/ \/ - - tft_sid_vis.h - - Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ - - this is some visuals code for the SID/FM/etc emulation together with ST7789 displays - Copyright (c) 2019-2022 Carsten Dachsbacher - - Logo created with http://patorjk.com/software/taag/ - - This program 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. - - This program 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 this program. If not, see . -*/ - { - if ( visUpdate && visMode == 0 && bufferIsFreeI2C() > 1024 ) // oscilloscope - { - if ( scopeX == 0 ) - tftClearDirty(); - - s32 y = 160 + min( 40, max( -40, ( left + right ) / 1 / 192 ) ); - y = max( 10, min( 229, y ) ); - - if ( y != scopeValues[scopeX] ) - { - extern unsigned char tftFrameBuffer12Bit[ 240 * 240 * 3 / 2 ]; - setDoubleWPixel12( scopeValues[scopeX], scopeX*2, *(u32*)&tftFrameBuffer12Bit[ scopeX * 2 * 3 / 2 + scopeValues[scopeX] * 240 * 3 / 2 ] ); - setDoubleWPixel12( y, scopeX*2, 0xffffffff ); - - scopeValues[scopeX] = y; - } - - scopeX++; - if ( scopeX >= 118 ) - { - //visUpdate = 0; - scopeX = 0; - } - - if ( visModeGotoNext ) - { - visUpdate = 1; - visModeGotoNext = 0; - visMode ++; - } - } else - if ( visMode == 1 ) // transition oscilloscope -> vu meter - { - if ( visUpdate ) - { - tftClearDirty(); - for ( u32 j = 104; j < 224; j++ ) - for ( u32 i = 0; i < 240; i++ ) - setPixelDirty( j, i, *(u16*)&tftBackground[ (i + j * 240) * 2 ] ); - } - - visUpdate = 0; - - if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) - { - visUpdate = 1; - visModeGotoNext = 0; - visMode ++; - } - - if ( bufferEmptyI2C() ) - tftUpdateNextDirtyRegions(); - } else - if ( visMode == 2 ) // vu meter - { - if ( visUpdate ) - { - visUpdate = 0; - tftClearDirty(); - - // remove old needle - for ( u32 i = startRow; i < endRow; i++ ) - { - s32 x = (s32)( px + i * dx ); - setPixelDirty( 220 - i, x, *(u16*)&tftBackground[ (x + (220-i) * 240) * 2 ] ); - } - - // moving average - float vuValue = min( 1.98f, 1.98f * (float)vuMeter[ 0 ] / 1024.0f * 1.5f * scaleVis ); - - const float movAvg = 0.8f; - vuValueAvg = vuValueAvg * movAvg + vuValue * ( 1.0f - movAvg ); - dx = -1.0f + vuValueAvg + 0.01f; - - startRow = 40.0f * cosf( atanf(dx) ); - endRow = 100.0f * cosf( atanf(dx) ); - - for ( u32 i = startRow; i < endRow; i++ ) - { - s32 x = (s32)( px + i * dx ); - setPixelDirty( 220 - i, x, 0 ); - } - } else - { - if ( bufferEmptyI2C() ) - { - if ( tftUpdateNextDirtyRegions() == 0 ) - { - visUpdate = 1; - - if ( visModeGotoNext ) - { - visModeGotoNext = 0; - visMode ++; - } - } - } - } - } else - if ( visMode == 3 ) // transition vu meter -> level meter - { - if ( visUpdate ) - { - tftClearDirty(); - for ( u32 j = 104; j < 114; j++ ) - for ( u32 i = 0; i < 240; i++ ) - setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); - for ( u32 j = 216; j < 224; j++ ) - for ( u32 i = 0; i < 240; i++ ) - setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); - for ( u32 j = 114; j < 216; j++ ) - for ( u32 i = 0; i < 240; i++ ) - setPixelDirty( j, i, 0 ); - - for ( u32 l = 0; l < nLevelMeters; l++ ) - { - const u32 xpos = 16; - u32 ypos = 134 + l * 20 + (3-nLevelMeters)*10; - - for ( u32 j = 0; j < 16; j++ ) - for ( u32 i = 0; i < 9 * 23; i++ ) - setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + (j+16) * 240) * 2 ] ); - } - } - - visUpdate = 0; - - if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) - { - visUpdate = 1; - visModeGotoNext = 0; - visMode ++; - - px = 120.0f; - dx = 0.0f; - vuValueAvg = 0.01f; - - startRow = endRow = 1; - } - - if ( bufferEmptyI2C() ) - tftUpdateNextDirtyRegions(); - } else - if ( visMode == 4 ) // level meter - { - if ( visUpdate ) - { - visUpdate = 0; - tftClearDirty(); - - const float movAvg = 0.8f; - - - for ( u32 l = 0; l < nLevelMeters; l++ ) - { - const u32 xpos = 16; - u32 ypos = 134 + l * 20 + (3-nLevelMeters)*10; - - // moving average - float v = min( 1.0f, (float)vuMeter[ 1+l ] / 1024.0f ); - ledValueAvg[ l ] = ledValueAvg[ l ] * movAvg + v * ( 1.0f - movAvg ); - - u32 nLEDs = max( 0, min( 9, (ledValueAvg[ l ] * 9.5f * scaleVis) ) ); - - if ( nLEDs > nPrevLEDs[ l ] ) // render more bright LEDs - { - for ( u32 j = 0; j < 16; j++ ) - for ( u32 i = nPrevLEDs[ l ] * 23; i < nLEDs * 23; i++ ) - setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + j * 240) * 2 ] ); - } else - if ( nLEDs < nPrevLEDs[ l ] && nPrevLEDs[ l ] > 0 ) // render some dark LEDs - { - for ( u32 j = 0; j < 16; j++ ) - for ( u32 i = nLEDs * 23; i < nPrevLEDs[ l ] * 23; i++ ) - setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + (j+16) * 240) * 2 ] ); - } - - nPrevLEDs[ l ] = nLEDs; - } - } else - { - if ( bufferEmptyI2C() ) - { - if ( tftUpdateNextDirtyRegions() == 0 ) - { - visUpdate = 1; - - if ( visModeGotoNext ) - { - visModeGotoNext = 0; - visMode ++; - } - } - } - } - } else - if ( visMode == 5 ) // transition vu meter -> level meter - { - if ( visUpdate ) - { - tftClearDirty(); - for ( u32 j = 104; j < 224; j++ ) - for ( u32 i = 0; i < 240; i++ ) - setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); - } - - visUpdate = 0; - - if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) - { - visUpdate = 1; - visModeGotoNext = 0; - visMode = 0; - - px = 120.0f; - dx = 0.0f; - vuValueAvg = 0.01f; - nPrevLEDs[ 0 ] = nPrevLEDs[ 1 ] = nPrevLEDs[ 2 ] = 0; - - startRow = endRow = 1; - } - - if ( bufferEmptyI2C() ) - tftUpdateNextDirtyRegions(); - } - } - +/* + _________.__ .___ __ .__ __ _________.___________ + / _____/|__| __| _/____ | | _|__| ____ | | __ / _____/| \______ \ + \_____ \ | |/ __ |/ __ \| |/ / |/ ___\| |/ / \_____ \ | || | \ + / \| / /_/ \ ___/| <| \ \___| < / \| || ` \ +/_______ /|__\____ |\___ >__|_ \__|\___ >__|_ \ /_______ /|___/_______ / + \/ \/ \/ \/ \/ \/ \/ \/ + + tft_sid_vis.h + + Sidekick64 - A framework for interfacing 8-Bit Commodore computers (C64/C128,C16/Plus4,VC20) and a Raspberry Pi Zero 2 or 3A+/3B+ + - this is some visuals code for the SID/FM/etc emulation together with ST7789 displays + Copyright (c) 2019-2022 Carsten Dachsbacher + + Logo created with http://patorjk.com/software/taag/ + + This program 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. + + This program 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 this program. If not, see . +*/ + { + if ( visUpdate && visMode == 0 && bufferIsFreeI2C() > 1024 ) // oscilloscope + { + if ( scopeX == 0 ) + tftClearDirty(); + + s32 y = 160 + min( 40, max( -40, ( left + right ) / 1 / 192 ) ); + y = max( 10, min( 229, y ) ); + + if ( y != scopeValues[scopeX] ) + { + extern unsigned char tftFrameBuffer12Bit[ 240 * 240 * 3 / 2 ]; + setDoubleWPixel12( scopeValues[scopeX], scopeX*2, *(u32*)&tftFrameBuffer12Bit[ scopeX * 2 * 3 / 2 + scopeValues[scopeX] * 240 * 3 / 2 ] ); + setDoubleWPixel12( y, scopeX*2, 0xffffffff ); + + scopeValues[scopeX] = y; + } + + scopeX++; + if ( scopeX >= 118 ) + { + //visUpdate = 0; + scopeX = 0; + } + + if ( visModeGotoNext ) + { + visUpdate = 1; + visModeGotoNext = 0; + visMode ++; + } + } else + if ( visMode == 1 ) // transition oscilloscope -> vu meter + { + if ( visUpdate ) + { + tftClearDirty(); + for ( u32 j = 104; j < 224; j++ ) + for ( u32 i = 0; i < 240; i++ ) + setPixelDirty( j, i, *(u16*)&tftBackground[ (i + j * 240) * 2 ] ); + } + + visUpdate = 0; + + if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) + { + visUpdate = 1; + visModeGotoNext = 0; + visMode ++; + } + + if ( bufferEmptyI2C() ) + tftUpdateNextDirtyRegions(); + } else + if ( visMode == 2 ) // vu meter + { + if ( visUpdate ) + { + visUpdate = 0; + tftClearDirty(); + + // remove old needle + for ( s32 i = startRow; i < endRow; i++ ) + { + s32 x = (s32)( px + i * dx ); + if ( x < 0 ) x = 0; + if ( x > 239 ) x = 239; + setPixelDirty( 220 - i, x, *(u16*)&tftBackground[ (x + (220-i) * 240) * 2 ] ); + } + + // moving average + float vuValue = min( 1.98f, 1.98f * (float)vuMeter[ 0 ] / 1024.0f * 1.5f * scaleVis ); + + const float movAvg = 0.8f; + vuValueAvg = vuValueAvg * movAvg + vuValue * ( 1.0f - movAvg ); + dx = -1.0f + vuValueAvg + 0.01f; + + float a = atanf( dx ); + startRow = 40.0f * cosf( a ); + endRow = 100.0f * cosf( a ); + + for ( s32 i = startRow; i < endRow; i++ ) + { + s32 x = (s32)( px + i * dx ); + if ( x < 0 ) x = 0; + if ( x > 239 ) x = 239; + setPixelDirty( 220 - i, x, 0 ); + } + } else + { + if ( bufferEmptyI2C() ) + { + if ( tftUpdateNextDirtyRegions() == 0 ) + { + visUpdate = 1; + + if ( visModeGotoNext ) + { + visModeGotoNext = 0; + visMode ++; + } + } + } + } + } else + if ( visMode == 3 ) // transition vu meter -> level meter + { + if ( visUpdate ) + { + tftClearDirty(); + for ( u32 j = 104; j < 114; j++ ) + for ( u32 i = 0; i < 240; i++ ) + setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); + for ( u32 j = 216; j < 224; j++ ) + for ( u32 i = 0; i < 240; i++ ) + setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); + for ( u32 j = 114; j < 216; j++ ) + for ( u32 i = 0; i < 240; i++ ) + setPixelDirty( j, i, 0 ); + + for ( u32 l = 0; l < nLevelMeters; l++ ) + { + const u32 xpos = 16; + u32 ypos = 134 + l * 20 + (3-nLevelMeters)*10; + + for ( u32 j = 0; j < 16; j++ ) + for ( u32 i = 0; i < 9 * 23; i++ ) + setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + (j+16) * 240) * 2 ] ); + } + } + + visUpdate = 0; + + if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) + { + visUpdate = 1; + visModeGotoNext = 0; + visMode ++; + + px = 120.0f; + dx = 0.0f; + vuValueAvg = 0.01f; + + startRow = endRow = 1; + } + + if ( bufferEmptyI2C() ) + tftUpdateNextDirtyRegions(); + } else + if ( visMode == 4 ) // level meter + { + if ( visUpdate ) + { + visUpdate = 0; + tftClearDirty(); + + const float movAvg = 0.8f; + + + for ( u32 l = 0; l < nLevelMeters; l++ ) + { + const u32 xpos = 16; + u32 ypos = 134 + l * 20 + (3-nLevelMeters)*10; + + // moving average + float v = min( 1.0f, (float)vuMeter[ 1+l ] / 1024.0f ); + ledValueAvg[ l ] = ledValueAvg[ l ] * movAvg + v * ( 1.0f - movAvg ); + + u32 nLEDs = max( 0, min( 9, (ledValueAvg[ l ] * 9.5f * scaleVis) ) ); + + if ( nLEDs > nPrevLEDs[ l ] ) // render more bright LEDs + { + for ( u32 j = 0; j < 16; j++ ) + for ( u32 i = nPrevLEDs[ l ] * 23; i < nLEDs * 23; i++ ) + setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + j * 240) * 2 ] ); + } else + if ( nLEDs < nPrevLEDs[ l ] && nPrevLEDs[ l ] > 0 ) // render some dark LEDs + { + for ( u32 j = 0; j < 16; j++ ) + for ( u32 i = nLEDs * 23; i < nPrevLEDs[ l ] * 23; i++ ) + setPixelDirty( ypos + j, xpos + i, *(u16*)&tftLEDs[ (i + (j+16) * 240) * 2 ] ); + } + + nPrevLEDs[ l ] = nLEDs; + } + } else + { + if ( bufferEmptyI2C() ) + { + if ( tftUpdateNextDirtyRegions() == 0 ) + { + visUpdate = 1; + + if ( visModeGotoNext ) + { + visModeGotoNext = 0; + visMode ++; + } + } + } + } + } else + if ( visMode == 5 ) // transition vu meter -> level meter + { + if ( visUpdate ) + { + tftClearDirty(); + for ( u32 j = 104; j < 224; j++ ) + for ( u32 i = 0; i < 240; i++ ) + setPixelDirty( j, i, *(u16*)&tftBackground2[ (i + j * 240) * 2 ] ); + } + + visUpdate = 0; + + if ( bufferEmptyI2C() && !tftIsDirtyRegion() ) + { + visUpdate = 1; + visModeGotoNext = 0; + visMode = 0; + + px = 120.0f; + dx = 0.0f; + vuValueAvg = 0.01f; + nPrevLEDs[ 0 ] = nPrevLEDs[ 1 ] = nPrevLEDs[ 2 ] = 0; + + startRow = endRow = 1; + } + + if ( bufferEmptyI2C() ) + tftUpdateNextDirtyRegions(); + } + } + vu_nLEDs = 0xffff; \ No newline at end of file