diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java index afad4c94..f117caad 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java @@ -208,7 +208,7 @@ public class Apple2Preferences { switch (key) { case "HIRES_COLOR": // long - menuEnum = Apple2VideoSettingsMenu.SETTINGS.COLOR_CONFIGURE; + menuEnum = Apple2VideoSettingsMenu.SETTINGS.COLOR_MODE_CONFIGURE; break; case "LANDSCAPE_MODE": // bool menuEnum = Apple2VideoSettingsMenu.SETTINGS.LANDSCAPE_MODE; diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2VideoSettingsMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2VideoSettingsMenu.java index 2ab904ef..3a211104 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2VideoSettingsMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2VideoSettingsMenu.java @@ -56,10 +56,18 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu { return true; } - public enum HiresColor { - BW, - COLOR, - INTERPOLATED + public enum ColorMode { + COLOR_MODE_MONO, + COLOR_MODE_COLOR, + COLOR_MODE_INTERP, + COLOR_MODE_COLOR_MONITOR, + COLOR_MODE_MONO_TV, + COLOR_MODE_COLOR_TV, + } + + public enum MonoMode { + MONO_MODE_BW, + MONO_MODE_GREEN, } // must match interface_colorscheme_t @@ -159,7 +167,7 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu { } } }, - COLOR_CONFIGURE { + COLOR_MODE_CONFIGURE { @Override public final String getTitle(Apple2Activity activity) { return activity.getResources().getString(R.string.color_configure); @@ -182,7 +190,103 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu { @Override public Object getPrefDefault() { - return HiresColor.INTERPOLATED.ordinal(); + return ColorMode.COLOR_MODE_COLOR_TV.ordinal(); + } + + @Override + public View getView(Apple2Activity activity, View convertView) { + convertView = _basicView(activity, this, convertView); + _addPopupIcon(activity, this, convertView); + return convertView; + } + + @Override + public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) { + final Apple2AbstractMenu.IMenuEnum self = this; + _alertDialogHandleSelection(activity, R.string.video_configure, new String[]{ + settingsMenu.mActivity.getResources().getString(R.string.color_mono), + settingsMenu.mActivity.getResources().getString(R.string.color_color), + settingsMenu.mActivity.getResources().getString(R.string.color_interpolated), + settingsMenu.mActivity.getResources().getString(R.string.color_monitor), + settingsMenu.mActivity.getResources().getString(R.string.color_tv_mono), + settingsMenu.mActivity.getResources().getString(R.string.color_tv), + }, new IPreferenceLoadSave() { + @Override + public int intValue() { + return (int) Apple2Preferences.getJSONPref(self); + } + + @Override + public void saveInt(int value) { + Apple2Preferences.setJSONPref(self, ColorMode.values()[value].ordinal()); + } + }); + } + }, + SHOW_HALF_SCANLINES { + @Override + public final String getTitle(Apple2Activity activity) { + return activity.getResources().getString(R.string.show_half_scanlines); + } + + @Override + public final String getSummary(Apple2Activity activity) { + return ""; + } + + @Override + public String getPrefDomain() { + return Apple2Preferences.PREF_DOMAIN_VIDEO; + } + + @Override + public String getPrefKey() { + return "showHalfScanlines"; + } + + @Override + public Object getPrefDefault() { + return true; + } + + @Override + public View getView(final Apple2Activity activity, View convertView) { + final IMenuEnum self = this; + convertView = _basicView(activity, this, convertView); + CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this)); + cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + Apple2Preferences.setJSONPref(self, isChecked); + } + }); + return convertView; + } + }, + MONO_MODE_CONFIGURE { + @Override + public final String getTitle(Apple2Activity activity) { + return activity.getResources().getString(R.string.mono_configure); + } + + @Override + public final String getSummary(Apple2Activity activity) { + return activity.getResources().getString(R.string.mono_configure_summary); + } + + @Override + public String getPrefDomain() { + return Apple2Preferences.PREF_DOMAIN_VIDEO; + } + + @Override + public String getPrefKey() { + return "monoMode"; + } + + @Override + public Object getPrefDefault() { + return MonoMode.MONO_MODE_BW.ordinal(); } @Override @@ -197,8 +301,7 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu { final Apple2AbstractMenu.IMenuEnum self = this; _alertDialogHandleSelection(activity, R.string.video_configure, new String[]{ settingsMenu.mActivity.getResources().getString(R.string.color_bw), - settingsMenu.mActivity.getResources().getString(R.string.color_color), - settingsMenu.mActivity.getResources().getString(R.string.color_interpolated), + settingsMenu.mActivity.getResources().getString(R.string.color_green), }, new IPreferenceLoadSave() { @Override public int intValue() { @@ -207,7 +310,7 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu { @Override public void saveInt(int value) { - Apple2Preferences.setJSONPref(self, HiresColor.values()[value].ordinal()); + Apple2Preferences.setJSONPref(self, MonoMode.values()[value].ordinal()); } }); } diff --git a/Android/app/src/main/res/values-de/strings.xml b/Android/app/src/main/res/values-de/strings.xml index 030efe9f..ef25d230 100644 --- a/Android/app/src/main/res/values-de/strings.xml +++ b/Android/app/src/main/res/values-de/strings.xml @@ -162,8 +162,8 @@ Aktiviere Softmenü Knöpfe in den oberen Ecken des Bildschirms Video-Konfiguration… Farbeinstellungen - Configure color - Color mode + Display mode + Video display emulation mode Show joystick/keypad heading Shows current axis direction and magnitude Landscape @@ -188,5 +188,13 @@ Green on black Blue on black White on black + Color Monitor + Monochrome TV + Color TV + Show half scanlines + Monocolor mode + Configure monochrome color + Green screen + Monochrome diff --git a/Android/app/src/main/res/values-es/strings.xml b/Android/app/src/main/res/values-es/strings.xml index d93a7527..9df04c3d 100644 --- a/Android/app/src/main/res/values-es/strings.xml +++ b/Android/app/src/main/res/values-es/strings.xml @@ -160,8 +160,8 @@ Los botones del menú en la parte superior de la pantalla Configurar el video… Ajustes de color - Configure color - Color mode + Display mode + Video display emulation mode Show joystick/keypad heading Shows current axis direction and magnitude Landscape @@ -188,5 +188,13 @@ Green on black Blue on black White on black + Color Monitor + Monochrome TV + Color TV + Show half scanlines + Monocolor mode + Configure monochrome color + Green screen + Monochrome diff --git a/Android/app/src/main/res/values-fr/strings.xml b/Android/app/src/main/res/values-fr/strings.xml index 3cef03ab..8f0163b7 100644 --- a/Android/app/src/main/res/values-fr/strings.xml +++ b/Android/app/src/main/res/values-fr/strings.xml @@ -160,8 +160,8 @@ Activation soft des bouton du menu dans les coins en haut de l\'écran Configuration de la vidéo… Configuration des couleurs - Configure color - Color mode + Display mode + Video display emulation mode Show joystick/keypad heading Shows current axis direction and magnitude Landscape @@ -188,5 +188,13 @@ Green on black Blue on black White on black + Color Monitor + Monochrome TV + Color TV + Show half scanlines + Monocolor mode + Configure monochrome color + Green screen + Monochrome diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml index 8469d8df..611449d0 100644 --- a/Android/app/src/main/res/values/strings.xml +++ b/Android/app/src/main/res/values/strings.xml @@ -25,8 +25,8 @@ Audio latency Audio latency in secs Cancel - Configure color - Color mode + Display mode + Video display emulation mode Black/white Color Interpolated color @@ -188,5 +188,13 @@ Green on black Blue on black White on black + Color Monitor + Monochrome TV + Color TV + Show half scanlines + Monocolor mode + Configure monochrome color + Green screen + Monochrome diff --git a/Android/jni/sources.mk b/Android/jni/sources.mk index 85827782..a1cf6f18 100644 --- a/Android/jni/sources.mk +++ b/Android/jni/sources.mk @@ -21,6 +21,7 @@ APPLE2_VIDEO_SRC = \ $(APPLE2_SRC_PATH)/video/gltouchkbd.c \ $(APPLE2_SRC_PATH)/video/gltouchmenu.c \ $(APPLE2_SRC_PATH)/video/glvideo.c \ + $(APPLE2_SRC_PATH)/video/ntsc.c \ $(APPLE2_SRC_PATH)/video/video.c \ $(APPLE2_SRC_PATH)/video_util/matrixUtil.c \ $(APPLE2_SRC_PATH)/video_util/modelUtil.c \ diff --git a/Apple2Mac/Apple2Mac.xcodeproj/project.pbxproj b/Apple2Mac/Apple2Mac.xcodeproj/project.pbxproj index f0cc258c..327d29f6 100644 --- a/Apple2Mac/Apple2Mac.xcodeproj/project.pbxproj +++ b/Apple2Mac/Apple2Mac.xcodeproj/project.pbxproj @@ -129,6 +129,20 @@ 4A4B676D1DB47682005028A6 /* Apple2iOSTestUI-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4A4B676B1DB47682005028A6 /* Apple2iOSTestUI-Info.plist */; }; 4A4B676E1DB488B9005028A6 /* testui.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A405C1D85CF1B00B460B0 /* testui.c */; }; 4A4B676F1DB488BE005028A6 /* testtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 935C55751C136F0D0013166D /* testtrace.c */; }; + 4A589F782157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F792157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7A2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7B2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7C2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7D2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7E2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F7F2157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F802157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F812157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F822157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F832157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F842157E72B0026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; + 4A589F85215831560026A73A /* ntsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A589F722157E72B0026A73A /* ntsc.c */; }; 4A609CB31D725D4C0066AF38 /* external-disks in Resources */ = {isa = PBXBuildFile; fileRef = 4A609CB21D725D4C0066AF38 /* external-disks */; }; 4A6746721D848B51006520C2 /* external-disks in Resources */ = {isa = PBXBuildFile; fileRef = 4A609CB21D725D4C0066AF38 /* external-disks */; }; 4A69C1921A33DB90001579EF /* DDHidLib.framework in Copy Files (1 item) */ = {isa = PBXBuildFile; fileRef = 77C2796F1A1047AF000FE33F /* DDHidLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -943,11 +957,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4A0DEA9B216135BD000A2615 /* ntsc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ntsc.h; sourceTree = ""; }; 4A2636F819FDEDB700DBFB00 /* Apple2Mac.help */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Apple2Mac.help; sourceTree = ""; }; 4A4B671E1DB4723F005028A6 /* Apple2iOSTestTrace.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Apple2iOSTestTrace.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4A4B67681DB47560005028A6 /* Apple2iOSTestUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Apple2iOSTestUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4A4B676A1DB47682005028A6 /* Apple2iOSTestTrace-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Apple2iOSTestTrace-Info.plist"; sourceTree = ""; }; 4A4B676B1DB47682005028A6 /* Apple2iOSTestUI-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Apple2iOSTestUI-Info.plist"; sourceTree = ""; }; + 4A589F722157E72B0026A73A /* ntsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ntsc.c; sourceTree = ""; }; 4A609CB21D725D4C0066AF38 /* external-disks */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "external-disks"; path = "../external-disks"; sourceTree = ""; }; 4A7EDC911AE092680072E98A /* glhudmodel.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glhudmodel.c; sourceTree = ""; }; 4A7EDC921AE092680072E98A /* glnode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glnode.c; sourceTree = ""; }; @@ -1542,6 +1558,8 @@ 773B3D8A19568A570085CE5F /* video */ = { isa = PBXGroup; children = ( + 4A0DEA9B216135BD000A2615 /* ntsc.h */, + 4A589F722157E72B0026A73A /* ntsc.c */, 4AEDCEF5209E9D3F0090B44F /* glinput.h */, 4AEDCEF6209E9D3F0090B44F /* glutinput.c */, 4AEDCEF4209E9D3F0090B44F /* ncvideo.c */, @@ -2777,6 +2795,7 @@ 4A4B66E01DB4723F005028A6 /* A2IXPopupChoreographer.m in Sources */, 4A4B676F1DB488BE005028A6 /* testtrace.c in Sources */, 4A4B66E11DB4723F005028A6 /* gltouchjoy_kpad.c in Sources */, + 4A589F822157E72B0026A73A /* ntsc.c in Sources */, 4A4B66E21DB4723F005028A6 /* gltouchjoy.c in Sources */, 4A4B66E31DB4723F005028A6 /* soundcore.c in Sources */, 4A4B66E41DB4723F005028A6 /* soundcore-openal.c in Sources */, @@ -2836,6 +2855,7 @@ 4A4B672A1DB47560005028A6 /* A2IXPopupChoreographer.m in Sources */, 4A4B672B1DB47560005028A6 /* gltouchjoy_kpad.c in Sources */, 4A4B672C1DB47560005028A6 /* gltouchjoy.c in Sources */, + 4A589F832157E72B0026A73A /* ntsc.c in Sources */, 4A4B672D1DB47560005028A6 /* soundcore.c in Sources */, 4A4B672E1DB47560005028A6 /* soundcore-openal.c in Sources */, 4A4B672F1DB47560005028A6 /* opcodes.c in Sources */, @@ -2892,6 +2912,7 @@ 4A8A401D1D85CEEE00B460B0 /* prefs.c in Sources */, 4A8A401E1D85CEEE00B460B0 /* display.c in Sources */, 4A8F5D181F6A1FEC00AE37B5 /* video.c in Sources */, + 4A589F7C2157E72B0026A73A /* ntsc.c in Sources */, 4A8A401F1D85CEEE00B460B0 /* EmulatorGLView.m in Sources */, 4A8A40201D85CEEE00B460B0 /* disk.c in Sources */, 4A8A40211D85CEEE00B460B0 /* sha1.c in Sources */, @@ -2943,6 +2964,7 @@ 4ABDA9941D792E8C0086A35B /* prefs.c in Sources */, 4ABDA9951D792E8C0086A35B /* display.c in Sources */, 4A8F5D171F6A1FEB00AE37B5 /* video.c in Sources */, + 4A589F7B2157E72B0026A73A /* ntsc.c in Sources */, 4ABDA9961D792E8C0086A35B /* EmulatorGLView.m in Sources */, 4ABDA9971D792E8C0086A35B /* disk.c in Sources */, 4ABDA9981D792E8C0086A35B /* sha1.c in Sources */, @@ -2997,6 +3019,7 @@ 4ACD73271D20A83E00123DE6 /* A2IXPopupChoreographer.m in Sources */, 4ACD73281D20A83E00123DE6 /* gltouchjoy_kpad.c in Sources */, 4ACD73291D20A83E00123DE6 /* gltouchjoy.c in Sources */, + 4A589F7F2157E72B0026A73A /* ntsc.c in Sources */, 4ACD732A1D20A83E00123DE6 /* soundcore.c in Sources */, 4ACD732B1D20A83E00123DE6 /* soundcore-openal.c in Sources */, 4ACD732C1D20A83E00123DE6 /* opcodes.c in Sources */, @@ -3056,6 +3079,7 @@ 4ACD73751D20AB6A00123DE6 /* gltouchjoy_joy.c in Sources */, 4ACD73761D20AB6A00123DE6 /* A2IXPopupChoreographer.m in Sources */, 4ACD73771D20AB6A00123DE6 /* gltouchjoy_kpad.c in Sources */, + 4A589F842157E72B0026A73A /* ntsc.c in Sources */, 4ACD73781D20AB6A00123DE6 /* gltouchjoy.c in Sources */, 4ACD73791D20AB6A00123DE6 /* soundcore.c in Sources */, 4ACD737A1D20AB6A00123DE6 /* soundcore-openal.c in Sources */, @@ -3115,6 +3139,7 @@ 4ACD73C71D20B11D00123DE6 /* A2IXPopupChoreographer.m in Sources */, 4ACD73C81D20B11D00123DE6 /* gltouchjoy_kpad.c in Sources */, 4ACD73C91D20B11D00123DE6 /* gltouchjoy.c in Sources */, + 4A589F802157E72B0026A73A /* ntsc.c in Sources */, 4ACD73CA1D20B11D00123DE6 /* soundcore.c in Sources */, 4ACD73CB1D20B11D00123DE6 /* soundcore-openal.c in Sources */, 4ACD73CC1D20B11D00123DE6 /* opcodes.c in Sources */, @@ -3174,6 +3199,7 @@ 4ACD742D1D26210600123DE6 /* A2IXPopupChoreographer.m in Sources */, 4ACD742E1D26210600123DE6 /* gltouchjoy_kpad.c in Sources */, 4ACD742F1D26210600123DE6 /* gltouchjoy.c in Sources */, + 4A589F812157E72B0026A73A /* ntsc.c in Sources */, 4ACD74301D26210600123DE6 /* soundcore.c in Sources */, 4ACD74311D26210600123DE6 /* soundcore-openal.c in Sources */, 4ACD74321D26210600123DE6 /* opcodes.c in Sources */, @@ -3230,6 +3256,7 @@ 4AD4FE951A52464F00F958EC /* prefs.c in Sources */, 4AD4FE961A52464F00F958EC /* display.c in Sources */, 4A8F5D151F6A1FEA00AE37B5 /* video.c in Sources */, + 4A589F792157E72B0026A73A /* ntsc.c in Sources */, 4AA2D7E51D202A5B0054A5FF /* EmulatorGLView.m in Sources */, 4AD4FE971A52464F00F958EC /* disk.c in Sources */, 4AFC170C1AAE9C3200B215FA /* sha1.c in Sources */, @@ -3281,6 +3308,7 @@ 4ADC51FF19E8CA4500186B36 /* prefs.c in Sources */, 4ADC520019E8CA4500186B36 /* display.c in Sources */, 4A8F5D191F6A1FED00AE37B5 /* video.c in Sources */, + 4A589F7D2157E72B0026A73A /* ntsc.c in Sources */, 4AA2D7E61D202A5C0054A5FF /* EmulatorGLView.m in Sources */, 4ADC520119E8CA4500186B36 /* disk.c in Sources */, 4AFC170E1AAE9C3200B215FA /* sha1.c in Sources */, @@ -3351,6 +3379,7 @@ 773B3DAD19568A570085CE5F /* debugger.c in Sources */, 77E1C0B319D72700004344E0 /* vectorUtil.c in Sources */, 4A7EDC9E1AE092B80072E98A /* interface.c in Sources */, + 4A589F85215831560026A73A /* ntsc.c in Sources */, 773B3DAB19568A570085CE5F /* keys.c in Sources */, 773B3DBC19568A570085CE5F /* timing.c in Sources */, 773BC91A19F31E7B00996893 /* prefs.c in Sources */, @@ -3399,6 +3428,7 @@ 779DD830195BD9F900DF89E5 /* prefs.c in Sources */, 779DD831195BD9F900DF89E5 /* display.c in Sources */, 4A8F5D141F6A1FE900AE37B5 /* video.c in Sources */, + 4A589F782157E72B0026A73A /* ntsc.c in Sources */, 4AA2D7EC1D202BFA0054A5FF /* EmulatorGLView.m in Sources */, 779DD832195BD9F900DF89E5 /* disk.c in Sources */, 4AFC170B1AAE9C3200B215FA /* sha1.c in Sources */, @@ -3450,6 +3480,7 @@ 779F566519EB0B9100A6F107 /* prefs.c in Sources */, 779F566619EB0B9100A6F107 /* display.c in Sources */, 4A8F5D161F6A1FEB00AE37B5 /* video.c in Sources */, + 4A589F7A2157E72B0026A73A /* ntsc.c in Sources */, 4AA2D7E71D202A5D0054A5FF /* EmulatorGLView.m in Sources */, 779F566719EB0B9100A6F107 /* disk.c in Sources */, 4AFC170D1AAE9C3200B215FA /* sha1.c in Sources */, @@ -3510,6 +3541,7 @@ 935C55631C136E070013166D /* prefs.c in Sources */, 935C55731C136E6F0013166D /* json_parse.c in Sources */, 935C55641C136E070013166D /* rom-shim.c in Sources */, + 4A589F7E2157E72B0026A73A /* ntsc.c in Sources */, 935C55651C136E070013166D /* timing.c in Sources */, 4A8F5D271F6A202100AE37B5 /* memmngt.c in Sources */, 935C55661C136E070013166D /* glalert.c in Sources */, diff --git a/Apple2Mac/Apple2Mac/en.lproj/MainMenu.xib b/Apple2Mac/Apple2Mac/en.lproj/MainMenu.xib index 11dd437c..3cf70687 100644 --- a/Apple2Mac/Apple2Mac/en.lproj/MainMenu.xib +++ b/Apple2Mac/Apple2Mac/en.lproj/MainMenu.xib @@ -1,8 +1,9 @@ - - + + - + + @@ -373,9 +374,10 @@ CA + - + @@ -428,7 +430,7 @@ CA - + @@ -472,6 +474,9 @@ CA + +DQ + @@ -613,19 +618,13 @@ CA - + - - - - + - - - - + @@ -656,6 +652,7 @@ DQ + @@ -736,12 +733,9 @@ DQ - + - - - @@ -780,7 +774,7 @@ DQ - + @@ -809,12 +803,12 @@ DQ - + - + @@ -823,6 +817,15 @@ DQ + + + + + + + + + @@ -830,22 +833,8 @@ DQ - - - - - - - - - - - - - - - - + + @@ -853,6 +842,47 @@ DQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -861,12 +891,9 @@ DQ - + - - - @@ -1004,7 +1031,7 @@ DQ - + @@ -1026,18 +1053,20 @@ DQ + + - - - + + + - + - + diff --git a/Apple2Mac/Classes/OSX/EmulatorDiskController.m b/Apple2Mac/Classes/OSX/EmulatorDiskController.m index e4ad5c1b..4cdff604 100644 --- a/Apple2Mac/Classes/OSX/EmulatorDiskController.m +++ b/Apple2Mac/Classes/OSX/EmulatorDiskController.m @@ -33,9 +33,21 @@ @property (assign) IBOutlet NSButton *chooseDiskB; @property (assign) IBOutlet NSButton *startupLoadDiskA; @property (assign) IBOutlet NSButton *startupLoadDiskB; +@property (assign) IBOutlet NSButton *okButton; + +- (void)loadPrefsForDomain:(const char *)domain; @end +static EmulatorDiskController *diskInstance = nil; + +static void prefsChangeCallback(const char *domain) +{ + (void)domain; + assert(diskInstance); + [diskInstance loadPrefsForDomain:domain]; +} + @implementation EmulatorDiskController - (void)awakeFromNib @@ -43,7 +55,11 @@ #if CRASH_APP_ON_LOAD_BECAUSE_YAY_GJ_APPLE glGetError(); #endif - + assert(!diskInstance); + diskInstance = self; + + prefs_registerListener(PREF_DOMAIN_VM, prefsChangeCallback); + [self.diskInA setStringValue:NO_DISK_INSERTED]; [self.diskAProperties setStringValue:@""]; [self.diskInB setStringValue:NO_DISK_INSERTED]; @@ -52,12 +68,21 @@ [self.chooseDiskA setBezelStyle:NSRoundedBezelStyle]; [self.startupLoadDiskA setState:NSOffState]; [self.startupLoadDiskB setState:NSOffState]; - - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - +} + +- (void)loadPrefsForDomain:(const char *)domain +{ + assert(strcmp(domain, PREF_DOMAIN_VM) == 0); + (void)domain; + { - NSString *startupDiskA = [defaults stringForKey:kApple2PrefStartupDiskA]; - BOOL readOnlyA = [defaults boolForKey:kApple2PrefStartupDiskAProtected]; + NSString *startupDiskA = nil; + char *pathA = NULL; + startupDiskA = prefs_copyStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A, &pathA) ? [NSString stringWithUTF8String:pathA] : nil; + FREE(pathA); + + bool bVal = false; + BOOL readOnlyA = prefs_parseBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A_RO, &bVal) ? bVal : true; if (startupDiskA) { const char *path = [startupDiskA UTF8String]; @@ -78,8 +103,13 @@ } { - NSString *startupDiskB = [defaults stringForKey:kApple2PrefStartupDiskB]; - BOOL readOnlyB = [defaults boolForKey:kApple2PrefStartupDiskBProtected]; + NSString *startupDiskB = nil; + char *pathB = NULL; + startupDiskB = prefs_copyStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B, &pathB) ? [NSString stringWithUTF8String:pathB] : nil; + FREE(pathB); + + bool bVal = false; + BOOL readOnlyB = prefs_parseBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B_RO, &bVal) ? bVal : true; if (startupDiskB) { const char *path = [startupDiskB UTF8String]; @@ -102,34 +132,32 @@ - (void)_savePrefs { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - [defaults removeObjectForKey:kApple2PrefStartupDiskA]; - [defaults removeObjectForKey:kApple2PrefStartupDiskB]; - [defaults removeObjectForKey:kApple2PrefStartupDiskAProtected]; - [defaults removeObjectForKey:kApple2PrefStartupDiskBProtected]; - - if ([self.startupLoadDiskA state] == NSOnState) + if (([self.startupLoadDiskA state] == NSOnState) && (disk6.disk[0].fd >= 0)) { - if (disk6.disk[0].fd >= 0) - { - NSString *diskA = [NSString stringWithUTF8String:disk6.disk[0].file_name]; - [defaults setObject:diskA forKey:kApple2PrefStartupDiskA]; - NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskAProtection] cells] firstObject]; - [defaults setBool:([readOnlyChoice state] == NSOnState) forKey:kApple2PrefStartupDiskAProtected]; - } + prefs_setStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A, disk6.disk[0].file_name); + NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskAProtection] cells] firstObject]; + prefs_setBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A_RO, ([readOnlyChoice state] == NSOnState)); + } + else + { + prefs_setStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A, ""); + prefs_setBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A_RO, true); } - if ([self.startupLoadDiskB state] == NSOnState) + if (([self.startupLoadDiskB state] == NSOnState) && (disk6.disk[1].fd >= 0)) { - if (disk6.disk[1].fd >= 0) - { - NSString *diskB = [NSString stringWithUTF8String:disk6.disk[1].file_name]; - [defaults setObject:diskB forKey:kApple2PrefStartupDiskB]; - NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskBProtection] cells] firstObject]; - [defaults setBool:([readOnlyChoice state] == NSOnState) forKey:kApple2PrefStartupDiskBProtected]; - } + prefs_setStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B, disk6.disk[1].file_name); + NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskBProtection] cells] firstObject]; + prefs_setBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B_RO, ([readOnlyChoice state] == NSOnState) ); } + else + { + prefs_setStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B, ""); + prefs_setBoolValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B_RO, true); + } + + prefs_sync(PREF_DOMAIN_VM); + prefs_save(); } - (void)_protectionChangedForDrive:(int)drive @@ -185,12 +213,21 @@ } path = [NSString stringWithUTF8String:disk6.disk[drive].file_name]; NSString *imageName = [[path pathComponents] lastObject]; - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - + if (drive == 0) { [[self diskInA] setStringValue:imageName]; - if ([[defaults stringForKey:kApple2PrefStartupDiskA] isEqualToString:path]) + + bool isStartupDiskA = false; + { + char *pathA = NULL; + if (prefs_copyStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_A, &pathA)) { + isStartupDiskA = (strcmp(pathA, disk6.disk[drive].file_name) == 0); + } + FREE(pathA); + } + + if (isStartupDiskA) { [self.startupLoadDiskA setState:NSOnState]; //[self.diskAProtection setState:(readOnly ? NSOnState : NSOffState) atRow:0 column:0]; @@ -200,7 +237,17 @@ else { [[self diskInB] setStringValue:imageName]; - if ([[defaults stringForKey:kApple2PrefStartupDiskB] isEqualToString:path]) + + bool isStartupDiskB = false; + { + char *pathB = NULL; + if (prefs_copyStringValue(PREF_DOMAIN_VM, PREF_DISK_PATH_B, &pathB)) { + isStartupDiskB = (strcmp(pathB, disk6.disk[drive].file_name) == 0); + } + FREE(pathB); + } + + if (isStartupDiskB) { [self.startupLoadDiskB setState:NSOnState]; //[self.diskBProtection setState:(readOnly ? NSOnState : NSOffState) atRow:0 column:0]; @@ -304,6 +351,9 @@ return; } + [self.chooseDiskA setKeyEquivalent:@""]; + [self.okButton setKeyEquivalent:@"\r"]; + [(drive == 0) ? self.startupLoadDiskA : self.startupLoadDiskB setState:NSOffState]; [self _insertDisketteInDrive:drive path:path type:extension readOnly:readOnly]; } @@ -334,6 +384,9 @@ - (IBAction)disksOK:(id)sender { + [self.chooseDiskA setKeyEquivalent:@"\r"]; + [self.okButton setKeyEquivalent:@""]; + [[self disksWindow] close]; } diff --git a/Apple2Mac/Classes/OSX/EmulatorPrefsController.h b/Apple2Mac/Classes/OSX/EmulatorPrefsController.h index f9721221..17ba45ca 100644 --- a/Apple2Mac/Classes/OSX/EmulatorPrefsController.h +++ b/Apple2Mac/Classes/OSX/EmulatorPrefsController.h @@ -11,11 +11,6 @@ #import -#define kApple2PrefStartupDiskA @"kApple2PrefStartupDiskA" -#define kApple2PrefStartupDiskAProtected @"kApple2PrefStartupDiskAProtected" -#define kApple2PrefStartupDiskB @"kApple2PrefStartupDiskB" -#define kApple2PrefStartupDiskBProtected @"kApple2PrefStartupDiskBProtected" - @interface EmulatorPrefsController : NSWindowController @end diff --git a/Apple2Mac/Classes/OSX/EmulatorPrefsController.m b/Apple2Mac/Classes/OSX/EmulatorPrefsController.m index 73e65783..2aba31f8 100644 --- a/Apple2Mac/Classes/OSX/EmulatorPrefsController.m +++ b/Apple2Mac/Classes/OSX/EmulatorPrefsController.m @@ -15,18 +15,6 @@ #import "EmulatorWindowController.h" #import "common.h" -#define kApple2SavedPrefs @"kApple2SavedPrefs" -#define kApple2CPUSpeed @"kApple2CPUSpeed" -#define kApple2CPUSpeedIsMax @"kApple2CPUSpeedIsMax" -#define kApple2AltSpeed @"kApple2AltSpeed" -#define kApple2AltSpeedIsMax @"kApple2AltSpeedIsMax" -#define kApple2SoundcardConfig @"kApple2SoundcardConfig" -#define kApple2ColorConfig @"kApple2ColorConfig" -#define kApple2JoystickConfig @"kApple2JoystickConfig" -#define kApple2JoystickAutoRecenter @"kApple2JoystickAutoRecenter" -#define kApple2JoystickClipToRadius @"kApple2JoystickClipToRadius" -#define kApple2JoystickStep @"kApple2JoystickStep" - @interface EmulatorPrefsController () @property (assign) IBOutlet NSSlider *cpuSlider; @@ -39,6 +27,8 @@ @property (assign) IBOutlet NSMatrix *soundCardChoice; @property (assign) IBOutlet NSPopUpButton *colorChoice; +@property (assign) IBOutlet NSPopUpButton *monochromeColorChoice; +@property (assign) IBOutlet NSButton *scanlinesChoice; @property (assign) IBOutlet NSPopUpButton *joystickChoice; @property (assign) IBOutlet NSButton *joystickRecenter; @@ -53,34 +43,44 @@ @property (assign) IBOutlet NSTextField *button1Pressed; @property (assign) IBOutlet EmulatorJoystickCalibrationView *joystickCalibrationView; +- (void)loadPrefsForDomain:(const char *)domain; + @end +static EmulatorPrefsController *prefsInstance = nil; + +static void prefsChangeCallback(const char *domain) +{ + (void)domain; + assert(prefsInstance); + [prefsInstance loadPrefsForDomain:domain]; +} + @implementation EmulatorPrefsController - (void)awakeFromNib { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - BOOL firstTime = ![defaults boolForKey:kApple2SavedPrefs]; - if (firstTime) - { - [defaults setBool:YES forKey:kApple2SavedPrefs]; - [defaults setDouble:1.0 forKey:kApple2CPUSpeed]; - [defaults setDouble:CPU_SCALE_SLOWEST forKey:kApple2AltSpeed]; - [defaults setBool:NO forKey:kApple2CPUSpeedIsMax]; - [defaults setBool:NO forKey:kApple2AltSpeedIsMax]; - [defaults setInteger:COLOR_MODE_INTERP forKey:kApple2ColorConfig]; - [defaults setInteger:JOY_KPAD forKey:kApple2JoystickConfig]; - [defaults setBool:YES forKey:kApple2JoystickAutoRecenter]; - [defaults removeObjectForKey:kApple2PrefStartupDiskA]; - [defaults removeObjectForKey:kApple2PrefStartupDiskB]; - } - - cpu_scale_factor = [defaults doubleForKey:kApple2CPUSpeed]; + assert(!prefsInstance); + prefsInstance = self; + + prefs_registerListener(PREF_DOMAIN_AUDIO, prefsChangeCallback); + prefs_registerListener(PREF_DOMAIN_INTERFACE, prefsChangeCallback); + prefs_registerListener(PREF_DOMAIN_JOYSTICK, prefsChangeCallback); + prefs_registerListener(PREF_DOMAIN_KEYBOARD, prefsChangeCallback); + //prefs_registerListener(PREF_DOMAIN_TOUCHSCREEN, prefsChangeCallback); + prefs_registerListener(PREF_DOMAIN_VIDEO, prefsChangeCallback); + prefs_registerListener(PREF_DOMAIN_VM, prefsChangeCallback); + + [self _setupJoystickUI]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(drawJoystickCalibration:) name:(NSString *)kDrawTimerNotification object:nil]; +} + +- (void)loadPrefsForDomain:(const char *)domain +{ [self.cpuSlider setDoubleValue:cpu_scale_factor]; [self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_scale_factor*100]]; - if ([defaults boolForKey:kApple2CPUSpeedIsMax]) + if (cpu_scale_factor == CPU_SCALE_FASTEST) { - cpu_scale_factor = CPU_SCALE_FASTEST; [self.cpuMaxChoice setState:NSOnState]; [self.cpuSlider setEnabled:NO]; } @@ -90,12 +90,10 @@ [self.cpuSlider setEnabled:YES]; } - cpu_altscale_factor = [defaults doubleForKey:kApple2AltSpeed]; [self.altSlider setDoubleValue:cpu_altscale_factor]; [self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_altscale_factor*100]]; - if ([defaults boolForKey:kApple2AltSpeedIsMax]) + if (cpu_altscale_factor == CPU_SCALE_FASTEST) { - cpu_altscale_factor = CPU_SCALE_FASTEST; [self.altMaxChoice setState:NSOnState]; [self.altSlider setEnabled:NO]; } @@ -108,46 +106,21 @@ #warning TODO : actually implement sound card choices [self.soundCardChoice deselectAllCells]; [self.soundCardChoice selectCellAtRow:1 column:0]; - - NSInteger mode = [defaults integerForKey:kApple2ColorConfig]; - if (! ((mode >= COLOR_MODE_BW) && (mode < NUM_COLOROPTS)) ) - { - mode = COLOR_MODE_BW; - } - [self.colorChoice selectItemAtIndex:mode]; - prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, (color_mode_t)mode); - prefs_sync(PREF_DOMAIN_VIDEO); - mode = [defaults integerForKey:kApple2JoystickConfig]; - if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) ) - { - mode = JOY_PCJOY; - } - joy_mode = (joystick_mode_t)mode; - [self.joystickChoice selectItemAtIndex:mode]; + long lVal = 0; + NSInteger mode = prefs_parseLongValue(domain, PREF_COLOR_MODE, &lVal, /*base:*/10) ? getColorMode(lVal) : COLOR_MODE_DEFAULT; + [self.colorChoice selectItemAtIndex:mode]; + + [self.joystickChoice selectItemAtIndex:(NSInteger)joy_mode]; #ifdef KEYPAD_JOYSTICK - bool autoRecenter = [defaults integerForKey:kApple2JoystickAutoRecenter]; - [self.joystickRecenter setState:autoRecenter ? NSOnState : NSOffState]; - prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, autoRecenter); - - long joyStep = [defaults integerForKey:kApple2JoystickStep]; - if (!joyStep) - { - joyStep = 1; - } - [self.joystickStepLabel setIntegerValue:joyStep]; - [self.joystickStepper setIntegerValue:joyStep]; - prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, joyStep); - - prefs_sync(PREF_DOMAIN_JOYSTICK); + [self.joystickRecenter setState:joy_auto_recenter ? NSOnState : NSOffState]; + + [self.joystickStepLabel setIntegerValue:joy_step]; + [self.joystickStepper setIntegerValue:joy_step]; #endif - joy_clip_to_radius = [defaults boolForKey:kApple2JoystickClipToRadius]; [self.joystickClipToRadius setState:joy_clip_to_radius ? NSOnState : NSOffState]; - - [self _setupJoystickUI]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(drawJoystickCalibration:) name:(NSString *)kDrawTimerNotification object:nil]; } - (void)dealloc @@ -156,49 +129,23 @@ [super dealloc]; } -- (void)_savePrefs -{ - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - [defaults setBool:YES forKey:kApple2SavedPrefs]; - [defaults setDouble:cpu_scale_factor forKey:kApple2CPUSpeed]; - [defaults setDouble:cpu_altscale_factor forKey:kApple2AltSpeed]; - [defaults setBool:([self.cpuMaxChoice state] == NSOnState) forKey:kApple2CPUSpeedIsMax]; - [defaults setBool:([self.altMaxChoice state] == NSOnState) forKey:kApple2AltSpeedIsMax]; - - long lVal = 0; - color_mode_t mode = prefs_parseLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, &lVal, /*base:*/10) ? (color_mode_t)lVal : COLOR_MODE_INTERP; - [defaults setInteger:mode forKey:kApple2ColorConfig]; - [defaults setInteger:joy_mode forKey:kApple2JoystickConfig]; - - long joyStep = prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, &lVal, /*base:*/10) ? lVal : 1; - [defaults setInteger:joyStep forKey:kApple2JoystickStep]; - - bool bVal = false; - bool autoRecenter = prefs_parseBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, &bVal) ? bVal : true; - [defaults setBool:autoRecenter forKey:kApple2JoystickAutoRecenter]; - [defaults setBool:joy_clip_to_radius forKey:kApple2JoystickClipToRadius]; - - prefs_sync(PREF_DOMAIN_JOYSTICK); -} - - (IBAction)sliderDidMove:(id)sender { NSSlider *slider = (NSSlider *)sender; double value = [slider doubleValue]; if (slider == self.cpuSlider) { - cpu_scale_factor = value; + prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, value); [self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]]; } else { - cpu_altscale_factor = value; + prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, value); [self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]]; } - timing_initialize(); - - [self _savePrefs]; + prefs_sync(PREF_DOMAIN_VM); + prefs_save(); } - (IBAction)peggedChoiceChanged:(id)sender @@ -207,32 +154,43 @@ if (maxButton == self.cpuMaxChoice) { [self.cpuSlider setEnabled:([maxButton state] != NSOnState)]; - cpu_scale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.cpuSlider doubleValue]; + double value = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.cpuSlider doubleValue]; + prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, value); } else { [self.altSlider setEnabled:([maxButton state] != NSOnState)]; - cpu_altscale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.altSlider doubleValue]; + double value = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.altSlider doubleValue]; + prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, value); } - - timing_initialize(); - [self _savePrefs]; + prefs_sync(PREF_DOMAIN_VM); + prefs_save(); } - (IBAction)colorChoiceChanged:(id)sender { NSInteger mode = [self.colorChoice indexOfSelectedItem]; - if (! ((mode >= COLOR_MODE_BW) && (mode < NUM_COLOROPTS)) ) - { - mode = COLOR_MODE_BW; - } + mode = getColorMode(mode); prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, mode); prefs_sync(PREF_DOMAIN_VIDEO); - [self _savePrefs]; - -#warning HACK TODO FIXME need to refactor video resetting procedure - display_reset(); + prefs_save(); +} + +- (IBAction)monochromeColorChoiceChanged:(id)sender { + NSInteger mode = [self.monochromeColorChoice indexOfSelectedItem]; + mode = getMonoMode(mode); + prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_MONO_MODE, mode); + prefs_sync(PREF_DOMAIN_VIDEO); + prefs_save(); +} + +- (IBAction)scanlinesChoiceChanged:(id)sender +{ + NSControlStateValue state = [self.scanlinesChoice state]; + prefs_setBoolValue(PREF_DOMAIN_VIDEO, PREF_SHOW_HALF_SCANLINES, state == NSControlStateValueOn); + prefs_sync(PREF_DOMAIN_VIDEO); + prefs_save(); } - (IBAction)soundCardChoiceChanged:(id)sender @@ -257,34 +215,35 @@ - (IBAction)joystickChoiceChanged:(id)sender { NSInteger mode = [self.joystickChoice indexOfSelectedItem]; - if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) ) - { - mode = JOY_PCJOY; - } - joy_mode = (joystick_mode_t)mode; - [self _setupJoystickUI]; - [self _savePrefs]; + mode = getJoyMode(mode); + prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, mode); + prefs_sync(PREF_DOMAIN_JOYSTICK); + prefs_save(); } - (IBAction)autoRecenterChoiceChanged:(id)sender { bool autoRecenter = ([self.joystickRecenter state] == NSOnState); prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, autoRecenter); - [self _savePrefs]; + prefs_sync(PREF_DOMAIN_JOYSTICK); + prefs_save(); } - (IBAction)clipToRadiusChoiceChanged:(id)sender { - joy_clip_to_radius = ([self.joystickClipToRadius state] == NSOnState); - [self _savePrefs]; + bool clipToRadius = ([self.joystickClipToRadius state] == NSOnState); + prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_CLIP_TO_RADIUS, clipToRadius); + prefs_sync(PREF_DOMAIN_JOYSTICK); + prefs_save(); } - (IBAction)stepValueChanged:(id)sender { long joyStep = [self.joystickStepper intValue]; [self.joystickStepLabel setIntegerValue:joyStep]; - prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, joyStep); - [self _savePrefs]; + prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, joyStep); + prefs_sync(PREF_DOMAIN_JOYSTICK); + prefs_save(); } #pragma mark - diff --git a/src/display.c b/src/display.c index 03cae448..02961e25 100644 --- a/src/display.c +++ b/src/display.c @@ -9,47 +9,48 @@ * Copyright 1995 Stephen Lee * Copyright 1997, 1998 Aaron Culliney * Copyright 1998, 1999, 2000 Michael Deutschmann - * Copyright 2013-2015 Aaron Culliney + * Copyright 2013-2018 Aaron Culliney * */ #include "common.h" #include "video/video.h" +#include "video/ntsc.h" -#define SCANSTEP (SCANWIDTH-12) -#define SCANDSTEP (SCANWIDTH-6) +/* + * Color structure + */ +typedef struct A2Color_s { + uint8_t r; + uint8_t g; + uint8_t b; +} A2Color_s; -#define DYNAMIC_SZ 11 // 7 pixels (as bytes) + 2pre + 2post +typedef uint8_t (*glyph_getter_fn)(uint8_t idx, unsigned int row_off); +typedef void (*plot_fn)(color_mode_t mode, uint16_t bits14, unsigned int col, uint32_t *colors16, unsigned int fb_off); +typedef void (*flush_fn)(void); +typedef PIXEL_TYPE (*scanline_color_fn)(PIXEL_TYPE color); -A2Color_s colormap[256] = { { 0 } }; +static A2Color_s colormap[256] = { { 0 } }; -static uint8_t video__wider_font[0x8000] = { 0 }; -static uint8_t video__font[0x4000] = { 0 }; -static uint8_t video__int_font[5][0x4000] = { { 0 } }; // interface font +static uint8_t scan_last_bit = 0x0; -static color_mode_t color_mode = COLOR_MODE_COLOR; +static glyph_getter_fn glyph_getter[256>>5] = { NULL }; // /32 == 8 sections +static glyph_getter_fn flash_getter = NULL; -// Precalculated framebuffer offsets given VM addr -static unsigned int video__screen_addresses[8192] = { INT_MIN }; -static uint8_t video__columns[8192] = { 0 }; +static plot_fn plot[NUM_COLOROPTS] = { NULL }; +static flush_fn flush[6][NUM_COLOROPTS] = { { NULL } }; // 0-7, 8-15, 16-23, 24-31, 32-39, 40 -static uint8_t video__hires_even[0x800] = { 0 }; -static uint8_t video__hires_odd[0x800] = { 0 }; +static uint8_t glyph_map[256<<3] = { 0 }; // *8 rows +static uint8_t interface_font[5][0x4000] = { { 0 } }; // interface font -#define FB_SIZ (SCANWIDTH*SCANHEIGHT*sizeof(uint8_t)) - -#if INTERFACE_CLASSIC -static uint8_t fbInterface[FB_SIZ] = { 0 }; -#endif - -static uint8_t fbStaging[FB_SIZ] = { 0 }; -#define FB_BASE (&fbStaging[0]) - -static uint8_t video__odd_colors[2] = { COLOR_LIGHT_PURPLE, COLOR_LIGHT_BLUE }; -static uint8_t video__even_colors[2] = { COLOR_LIGHT_GREEN, COLOR_LIGHT_RED }; +static color_mode_t color_mode = COLOR_MODE_DEFAULT; +static mono_mode_t mono_mode = MONO_MODE_DEFAULT; +static scanline_color_fn scanline_color[2] = { NULL }; +static uint8_t half_scanlines = false; // video line offsets -static uint16_t video__line_offset[TEXT_ROWS + /*VBL:*/ 8 + /*extra:*/1] = { +uint16_t video_line_offset[TEXT_ROWS + /*VBL:*/ 8 + /*extra:*/1] = { 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8, 0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0, @@ -57,569 +58,500 @@ static uint16_t video__line_offset[TEXT_ROWS + /*VBL:*/ 8 + /*extra:*/1] = { 0x3F8 }; -static uint8_t video__dhires1[256] = { - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, - 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, -}; +// Precalculated framebuffer offsets given VM addr +static unsigned int screen_addresses[8192] = { INT_MIN }; -static uint8_t video__dhires2[256] = { - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, - 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9, - 0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, - 0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0xb,0xb,0xb,0xb,0xb,0xb,0xb,0xb, - 0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc, - 0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, - 0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe, - 0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, -}; +// Precalculated COLOR_MODE_COLOR and COLOR_MODE_INTERP colors +static PIXEL_TYPE general_interp[4096 << 2] = { 0 }; +static PIXEL_TYPE general_color [4096 << 2] = { 0 }; +static PIXEL_TYPE hires40_interp[4096 << 2] = { 0 }; +static PIXEL_TYPE hires40_color [4096 << 2] = { 0 }; +static PIXEL_TYPE *hires40_colors[NUM_COLOROPTS] = { 0 }; +static PIXEL_TYPE *general_colors[NUM_COLOROPTS] = { 0 }; -// forward decls of VM entry points +#define FB_SIZ (SCANWIDTH*SCANHEIGHT) -void video__write_2e_text0(uint16_t, uint8_t); -void video__write_2e_text0_mixed(uint16_t, uint8_t); -void video__write_2e_text1(uint16_t, uint8_t); -void video__write_2e_text1_mixed(uint16_t, uint8_t); -void video__write_2e_hgr0(uint16_t, uint8_t); -void video__write_2e_hgr0_mixed(uint16_t, uint8_t); -void video__write_2e_hgr1(uint16_t, uint8_t); -void video__write_2e_hgr1_mixed(uint16_t, uint8_t); +static PIXEL_TYPE fbFull[FB_SIZ + (SCANWIDTH<<1)] = { 0 }; // HACK NOTE: extra scanlines used for sampling // ---------------------------------------------------------------------------- // Initialization routines -static void _initialize_dhires_values(void) { - for (unsigned int i = 0; i < 0x80; i++) { - video__dhires1[i+0x80] = video__dhires1[i]; - video__dhires2[i+0x80] = video__dhires2[i]; - } -} - -static void _initialize_hires_values(void) { - // precalculate colors for all the 256*8 bit combinations - for (unsigned int value = 0x00; value <= 0xFF; value++) { - for (unsigned int e = value<<3, last_not_black=0, v=value, b=0; b < 7; b++, v >>= 1, e++) { - if (v & 1) { - if (last_not_black) { - video__hires_even[e] = COLOR_LIGHT_WHITE; - video__hires_odd[e] = COLOR_LIGHT_WHITE; - if (b > 0) - { - video__hires_even[e-1] = COLOR_LIGHT_WHITE; - video__hires_odd [e-1] = COLOR_LIGHT_WHITE; - } - } else { - if (b & 1) { - if (value & 0x80) { - video__hires_even[e] = COLOR_LIGHT_RED; - video__hires_odd [e] = COLOR_LIGHT_BLUE; - } else { - video__hires_even[e] = COLOR_LIGHT_GREEN; - video__hires_odd [e] = COLOR_LIGHT_PURPLE; - } - } else { - if (value & 0x80) { - video__hires_even[e] = COLOR_LIGHT_BLUE; - video__hires_odd [e] = COLOR_LIGHT_RED; - } else { - video__hires_even[e] = COLOR_LIGHT_PURPLE; - video__hires_odd [e] = COLOR_LIGHT_GREEN; - } - } - } - last_not_black = 1; - } else { - video__hires_even[e] = COLOR_BLACK; - video__hires_odd [e] = COLOR_BLACK; - last_not_black = 0; - } - } - } - - if (color_mode == COLOR_MODE_BW) { - for (unsigned int value = 0x00; value <= 0xFF; value++) { - for (unsigned int b = 0, e = value * 8; b < 7; b++, e++) { - if (video__hires_even[e] != COLOR_BLACK) { - video__hires_even[e] = COLOR_LIGHT_WHITE; - } - if (video__hires_odd[e] != COLOR_BLACK) { - video__hires_odd[e] = COLOR_LIGHT_WHITE; - } - } - } - } else if (color_mode == COLOR_MODE_INTERP) { - for (unsigned int value = 0x00; value <= 0xFF; value++) { - for (unsigned int b=1, e=value*8 + 1; b <= 5; b += 2, e += 2) { - if (video__hires_even[e] == COLOR_BLACK) { - if (video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] != COLOR_LIGHT_WHITE && - video__hires_even[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e-1]; - } - else if ( - video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] != COLOR_LIGHT_WHITE && - video__hires_even[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e-1]; - } - else if ( - video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] == COLOR_LIGHT_WHITE && - video__hires_even[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e+1]; - } - else if ( - video__hires_even[e-1] == COLOR_LIGHT_WHITE && - video__hires_even[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_even[e] = (value & 0x80) ? COLOR_LIGHT_BLUE : COLOR_LIGHT_PURPLE; - } - } - - if (video__hires_odd[e] == COLOR_BLACK) { - if (video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] != COLOR_LIGHT_WHITE && - video__hires_odd[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e-1]; - } - else if ( - video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] != COLOR_LIGHT_WHITE && - video__hires_odd[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e-1]; - } - else if ( - video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] == COLOR_LIGHT_WHITE && - video__hires_odd[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e+1]; - } - else if ( - video__hires_odd[e-1] == COLOR_LIGHT_WHITE && - video__hires_odd[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = (value & 0x80) ? COLOR_LIGHT_RED : COLOR_LIGHT_GREEN; - } - } - } - - for (unsigned int b = 0, e = value * 8; b <= 6; b += 2, e += 2) { - if (video__hires_even[ e ] == COLOR_BLACK) { - if (b > 0 && b < 6) { - if (video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] != COLOR_LIGHT_WHITE && - video__hires_even[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e-1]; - } - else if ( - video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] != COLOR_LIGHT_WHITE && - video__hires_even[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e-1]; - } - else if ( - video__hires_even[e-1] != COLOR_BLACK && - video__hires_even[e+1] != COLOR_BLACK && - video__hires_even[e-1] == COLOR_LIGHT_WHITE && - video__hires_even[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_even[e] = video__hires_even[e+1]; - } - else if ( - video__hires_even[e-1] == COLOR_LIGHT_WHITE && - video__hires_even[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_even[e] = (value & 0x80) ? COLOR_LIGHT_RED : COLOR_LIGHT_GREEN; - } - } - } - - if (video__hires_odd[e] == COLOR_BLACK) { - if (b > 0 && b < 6) { - if (video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] != COLOR_LIGHT_WHITE && - video__hires_odd[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e-1]; - } - else if ( - video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] != COLOR_LIGHT_WHITE && - video__hires_odd[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e-1]; - } - else if ( - video__hires_odd[e-1] != COLOR_BLACK && - video__hires_odd[e+1] != COLOR_BLACK && - video__hires_odd[e-1] == COLOR_LIGHT_WHITE && - video__hires_odd[e+1] != COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = video__hires_odd[e+1]; - } - else if ( - video__hires_odd[e-1] == COLOR_LIGHT_WHITE && - video__hires_odd[e+1] == COLOR_LIGHT_WHITE) - { - video__hires_odd[e] = (value & 0x80) ? COLOR_LIGHT_BLUE : COLOR_LIGHT_PURPLE; - } - } - } - } - } - } -} - -static void _initialize_row_col_tables(void) { - for (unsigned int y = 0; y < TEXT_ROWS; y++) { - for (unsigned int y2 = 0; y2 < FONT_GLYPH_Y; y2++) { - for (unsigned int x = 0; x < 40; x++) { - video__screen_addresses[video__line_offset[y] + (0x400*y2) + x] = ((y*FONT_HEIGHT_PIXELS + 2*y2) * SCANWIDTH) + (x*FONT_WIDTH_PIXELS) + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE; - video__columns [video__line_offset[y] + (0x400*y2) + x] = (uint8_t)x; - } - } - } - for (unsigned int i = 0; i < 8192; i++) { - assert(video__screen_addresses[i] != INT_MIN); - } -} - -#warning FIXME TODO : move _initialize_tables_video() to vm.c ... -static void _initialize_tables_video(void) { - // initialize text/lores & hires graphics routines - for (unsigned int y = 0; y < TEXT_ROWS; y++) { - for (unsigned int x = 0; x < TEXT_COLS; x++) { - unsigned int idx = video__line_offset[y] + x; - // text/lores pages - if (y < 20) { - cpu65_vmem_w[idx+0x400] = video__write_2e_text0; - cpu65_vmem_w[idx+0x800] = video__write_2e_text1; - } else { - cpu65_vmem_w[idx+0x400] = video__write_2e_text0_mixed; - cpu65_vmem_w[idx+0x800] = video__write_2e_text1_mixed; - } - - // hires/dhires pages - for (unsigned int i = 0; i < 8; i++) { - idx = video__line_offset[ y ] + (0x400*i) + x; - if (y < 20) { - cpu65_vmem_w[idx+0x2000] = video__write_2e_hgr0; - cpu65_vmem_w[idx+0x4000] = video__write_2e_hgr1; - } else { - cpu65_vmem_w[idx+0x2000] = video__write_2e_hgr0_mixed; - cpu65_vmem_w[idx+0x4000] = video__write_2e_hgr1_mixed; - } - } - } - } -} - -static void _initialize_color() { - unsigned char col2[ 3 ] = { 255,255,255 }; - - /* align the palette for hires graphics */ - for (unsigned int i = 0; i < 8; i++) { - for (unsigned int j = 0; j < 3; j++) { - unsigned int c = 0; - c = (i & 1) ? col2[ j ] : 0; - colormap[ j+i*3+32].red = c; - c = (i & 2) ? col2[ j ] : 0; - colormap[ j+i*3+32].green = c; - c = (i & 4) ? col2[ j ] : 0; - colormap[ j+i*3+32].blue = c; - } - } - - colormap[ COLOR_FLASHING_BLACK].red = 0; - colormap[ COLOR_FLASHING_BLACK].green = 0; - colormap[ COLOR_FLASHING_BLACK].blue = 0; - - colormap[ COLOR_LIGHT_WHITE].red = 255; - colormap[ COLOR_LIGHT_WHITE].green = 255; - colormap[ COLOR_LIGHT_WHITE].blue = 255; - - colormap[ COLOR_FLASHING_WHITE].red = 255; - colormap[ COLOR_FLASHING_WHITE].green = 255; - colormap[ COLOR_FLASHING_WHITE].blue = 255; - - colormap[IDX_BLACK ] = (A2Color_s) { .red = 0, .green = 0, .blue = 0 }; - colormap[IDX_MAGENTA ] = (A2Color_s) { .red = 195, .green = 0, .blue = 48 }; - colormap[IDX_DARKBLUE ] = (A2Color_s) { .red = 0, .green = 0, .blue = 130 }; - colormap[IDX_PURPLE ] = (A2Color_s) { .red = 166, .green = 52, .blue = 170 }; - colormap[IDX_DARKGREEN] = (A2Color_s) { .red = 0, .green = 146, .blue = 0 }; - colormap[IDX_DARKGREY ] = (A2Color_s) { .red = 105, .green = 105, .blue = 105 }; - colormap[IDX_MEDBLUE ] = (A2Color_s) { .red = 24, .green = 113, .blue = 255 }; - colormap[IDX_LIGHTBLUE] = (A2Color_s) { .red = 12, .green = 190, .blue = 235 }; - colormap[IDX_BROWN ] = (A2Color_s) { .red = 150, .green = 85, .blue = 40 }; - colormap[IDX_ORANGE ] = (A2Color_s) { .red = 255, .green = 24, .blue = 44 }; - colormap[IDX_LIGHTGREY] = (A2Color_s) { .red = 150, .green = 170, .blue = 170 }; - colormap[IDX_PINK ] = (A2Color_s) { .red = 255, .green = 158, .blue = 150 }; - colormap[IDX_GREEN ] = (A2Color_s) { .red = 0, .green = 255, .blue = 0 }; - colormap[IDX_YELLOW ] = (A2Color_s) { .red = 255, .green = 255, .blue = 0 }; - colormap[IDX_AQUA ] = (A2Color_s) { .red = 130, .green = 255, .blue = 130 }; - colormap[IDX_WHITE ] = (A2Color_s) { .red = 255, .green = 255, .blue = 255 }; - - /* mirror of lores colormap optimized for dhires code */ - colormap[0x00].red = 0; colormap[0x00].green = 0; - colormap[0x00].blue = 0; /* Black */ - colormap[0x08].red = 195; colormap[0x08].green = 0; - colormap[0x08].blue = 48; /* Magenta */ - colormap[0x01].red = 0; colormap[0x01].green = 0; - colormap[0x01].blue = 130; /* Dark Blue */ - colormap[0x09].red = 166; colormap[0x09].green = 52; - colormap[0x09].blue = 170; /* Purple */ - colormap[0x02].red = 0; colormap[0x02].green = 146; - colormap[0x02].blue = 0; /* Dark Green */ - colormap[0x0a].red = 105; colormap[0x0a].green = 105; - colormap[0x0a].blue = 105; /* Dark Grey*/ - colormap[0x03].red = 24; colormap[0x03].green = 113; - colormap[0x03].blue = 255; /* Medium Blue */ - colormap[0x0b].red = 12; colormap[0x0b].green = 190; - colormap[0x0b].blue = 235; /* Light Blue */ - colormap[0x04].red = 150; colormap[0x04].green = 85; - colormap[0x04].blue = 40; /* Brown */ - colormap[0x0c].red = 255; colormap[0x0c].green = 24; - colormap[0x0c].blue = 44; /* Orange */ - colormap[0x05].red = 150; colormap[0x05].green = 170; - colormap[0x05].blue = 170; /* Light Gray */ - colormap[0x0d].red = 255; colormap[0x0d].green = 158; - colormap[0x0d].blue = 150; /* Pink */ - colormap[0x06].red = 0; colormap[0x06].green = 255; - colormap[0x06].blue = 0; /* Green */ - colormap[0x0e].red = 255; colormap[0x0e].green = 255; - colormap[0x0e].blue = 0; /* Yellow */ - colormap[0x07].red = 130; colormap[0x07].green = 255; - colormap[0x07].blue = 130; /* Aqua */ - colormap[0x0f].red = 255; colormap[0x0f].green = 255; - colormap[0x0f].blue = 255; /* White */ - -#if USE_RGBA4444 - for (unsigned int i=0; i<256; i++) { - colormap[i].red = (colormap[i].red >>4); - colormap[i].green = (colormap[i].green >>4); - colormap[i].blue = (colormap[i].blue >>4); - } -#endif -} - -static void display_prefsChanged(const char *domain) { - long val = COLOR_MODE_INTERP; - prefs_parseLongValue(domain, PREF_COLOR_MODE, &val, /*base:*/10); - if (val < 0) { - val = COLOR_MODE_INTERP; - } - if (val >= NUM_COLOROPTS) { - val = COLOR_MODE_INTERP; - } - color_mode = (color_mode_t)val; - display_reset(); -} - -void display_reset(void) { - _initialize_hires_values(); - _initialize_tables_video(); -} - -void display_loadFont(const uint8_t first, const uint8_t quantity, const uint8_t *data, font_mode_t mode) { - uint8_t fg = 0; - uint8_t bg = 0; - switch (mode) { - case FONT_MODE_INVERSE: - fg = COLOR_BLACK; - bg = COLOR_LIGHT_WHITE; - break; - case FONT_MODE_FLASH: - fg = COLOR_FLASHING_WHITE; - bg = COLOR_FLASHING_BLACK; - break; - default: - fg = COLOR_LIGHT_WHITE; - bg = COLOR_BLACK; - break; - } - - unsigned int i = quantity * 8; - while (i--) { - unsigned int j = 8; - uint8_t x = data[i]; - while (j--) { - uint8_t y = (x & 128) ? fg : bg; - video__wider_font[(first << 7) + (i << 4) + (j << 1)] = video__wider_font[(first << 7) + (i << 4) + (j << 1) + 1] = - video__font[(first << 6) + (i << 3) + j] = y; - x <<= 1; - } - } -} - -static void _loadfont_int(int first, int quantity, const uint8_t *data) { +static void _load_interface_font(int first, int quantity, const uint8_t *data) { unsigned int i = quantity * 8; while (i--) { unsigned int j = 8; uint8_t x = data[i]; while (j--) { unsigned int y = (first << 6) + (i << 3) + j; + assert(y < 0x4000); if (x & 128) { - video__int_font[GREEN_ON_BLACK][y] = COLOR_LIGHT_GREEN; - video__int_font[GREEN_ON_BLUE][y] = COLOR_LIGHT_GREEN; - video__int_font[RED_ON_BLACK][y] = COLOR_LIGHT_RED; - video__int_font[BLUE_ON_BLACK][y] = COLOR_LIGHT_BLUE; - video__int_font[WHITE_ON_BLACK][y] = COLOR_LIGHT_WHITE; + interface_font[GREEN_ON_BLACK][y] = IDX_ICOLOR_G; + interface_font[GREEN_ON_BLUE][y] = IDX_ICOLOR_G; + interface_font[RED_ON_BLACK][y] = IDX_ICOLOR_R; + interface_font[BLUE_ON_BLACK][y] = IDX_ICOLOR_B; + interface_font[WHITE_ON_BLACK][y] = IDX_WHITE; } else { - video__int_font[GREEN_ON_BLACK][y] = COLOR_BLACK; - video__int_font[GREEN_ON_BLUE][y] = COLOR_MEDIUM_BLUE; - video__int_font[RED_ON_BLACK][y] = COLOR_BLACK; - video__int_font[BLUE_ON_BLACK][y] = COLOR_BLACK; - video__int_font[WHITE_ON_BLACK][y] = COLOR_BLACK; + interface_font[GREEN_ON_BLACK][y] = IDX_BLACK; + interface_font[GREEN_ON_BLUE][y] = IDX_ICOLOR_B; + interface_font[RED_ON_BLACK][y] = IDX_BLACK; + interface_font[BLUE_ON_BLACK][y] = IDX_BLACK; + interface_font[WHITE_ON_BLACK][y] = IDX_BLACK; } x <<= 1; } } } -static void _initialize_interface_fonts(void) { - _loadfont_int(0x00,0x40,ucase_glyphs); - _loadfont_int(0x40,0x20,ucase_glyphs); - _loadfont_int(0x60,0x20,lcase_glyphs); - _loadfont_int(0x80,0x40,ucase_glyphs); - _loadfont_int(0xC0,0x20,ucase_glyphs); - _loadfont_int(0xE0,0x20,lcase_glyphs); - _loadfont_int(MOUSETEXT_BEGIN,0x20,mousetext_glyphs); - _loadfont_int(ICONTEXT_BEGIN,0x20,interface_glyphs); +static void _initialize_colormap(void) { + +#if 0 // TRADITIONAL COLORS + colormap[IDX_BLACK ] = (A2Color_s) { .r = 0, .g = 0, .b = 0 }; + colormap[IDX_MAGENTA ] = (A2Color_s) { .r = 195, .g = 0, .b = 48 }; + colormap[IDX_DARKBLUE ] = (A2Color_s) { .r = 0, .g = 0, .b = 130 }; + colormap[IDX_PURPLE ] = (A2Color_s) { .r = 166, .g = 52, .b = 170 }; + colormap[IDX_DARKGREEN] = (A2Color_s) { .r = 0, .g = 146, .b = 0 }; + colormap[IDX_DARKGREY ] = (A2Color_s) { .r = 105, .g = 105, .b = 105 }; + colormap[IDX_MEDBLUE ] = (A2Color_s) { .r = 24, .g = 113, .b = 255 }; + colormap[IDX_LIGHTBLUE] = (A2Color_s) { .r = 12, .g = 190, .b = 235 }; + colormap[IDX_BROWN ] = (A2Color_s) { .r = 150, .g = 85, .b = 40 }; + colormap[IDX_ORANGE ] = (A2Color_s) { .r = 255, .g = 24, .b = 44 }; + colormap[IDX_LIGHTGREY] = (A2Color_s) { .r = 150, .g = 170, .b = 170 }; + colormap[IDX_PINK ] = (A2Color_s) { .r = 255, .g = 158, .b = 150 }; + colormap[IDX_GREEN ] = (A2Color_s) { .r = 0, .g = 255, .b = 0 }; + colormap[IDX_YELLOW ] = (A2Color_s) { .r = 255, .g = 255, .b = 0 }; + colormap[IDX_AQUA ] = (A2Color_s) { .r = 130, .g = 255, .b = 130 }; + colormap[IDX_WHITE ] = (A2Color_s) { .r = 255, .g = 255, .b = 255 }; +#else + colormap[IDX_BLACK ] = (A2Color_s) { .r = 0, .g = 0, .b = 0 }; + colormap[IDX_MAGENTA ] = (A2Color_s) { .r = 227, .g = 30, .b = 96 }; + colormap[IDX_DARKBLUE ] = (A2Color_s) { .r = 96, .g = 78, .b = 189 }; + colormap[IDX_PURPLE ] = (A2Color_s) { .r = 255, .g = 68, .b = 253 }; + colormap[IDX_DARKGREEN] = (A2Color_s) { .r = 0, .g = 163, .b = 96 }; + colormap[IDX_DARKGREY ] = (A2Color_s) { .r = 156, .g = 156, .b = 156 }; + colormap[IDX_MEDBLUE ] = (A2Color_s) { .r = 20, .g = 207, .b = 253 }; + colormap[IDX_LIGHTBLUE] = (A2Color_s) { .r = 208, .g = 195, .b = 255 }; + colormap[IDX_BROWN ] = (A2Color_s) { .r = 96, .g = 114, .b = 3 }; + colormap[IDX_ORANGE ] = (A2Color_s) { .r = 255, .g = 106, .b = 60 }; + colormap[IDX_LIGHTGREY] = (A2Color_s) { .r = 156, .g = 156, .b = 156 }; + colormap[IDX_PINK ] = (A2Color_s) { .r = 255, .g = 160, .b = 208 }; + colormap[IDX_GREEN ] = (A2Color_s) { .r = 20, .g = 245, .b = 60 }; + colormap[IDX_YELLOW ] = (A2Color_s) { .r = 208, .g = 221, .b = 141 }; + colormap[IDX_AQUA ] = (A2Color_s) { .r = 114, .g = 255, .b = 208 }; + colormap[IDX_WHITE ] = (A2Color_s) { .r = 255, .g = 255, .b = 255 }; +#endif + + for (unsigned int nyb=0x0; nyb<0x10; nyb++) { + unsigned int idx = nyb + IDX_LUMINANCE_HALF; + colormap[idx] = colormap[nyb]; + + colormap[idx].r >>= 1; + colormap[idx].g >>= 1; + colormap[idx].b >>= 1; + } + + colormap[IDX_ICOLOR_R ] = (A2Color_s) { .r = 255, .g = 0, .b = 0 }; + colormap[IDX_ICOLOR_G ] = (A2Color_s) { .r = 0, .g = 255, .b = 0 }; + colormap[IDX_ICOLOR_B ] = (A2Color_s) { .r = 0, .g = 0, .b = 255 }; + +#if USE_RGBA4444 + for (unsigned int i=0; i<256; i++) { + colormap[i].r = (colormap[i].r >>4); + colormap[i].g = (colormap[i].g >>4); + colormap[i].b = (colormap[i].b >>4); + } +#endif +} + +static void _initialize_color_values(PIXEL_TYPE *color_pixels, PIXEL_TYPE *interp_pixels, bool adjustHIRES40) { + + // NEXT xxxx PREV -- 12 bits + for (unsigned int nybPrev=0x00; nybPrev<0x10; nybPrev++) { + + uint8_t prevLumCount = 0; + { + for (int i=3; i>=0; i--) { + if (nybPrev & (1 << i)) { + ++prevLumCount; + } else { + break; + } + } + } + + for (unsigned int nybNext=0x00; nybNext<0x10; nybNext++) { + + uint8_t nextLumCount = 0; + for (unsigned int i=0; i<4; i++) { + if (nybNext & (1 << i)) { + ++nextLumCount; + } else { + break; + } + } + + for (unsigned int nyb=0x00; nyb<0x10; nyb++) { + + uint8_t color = nyb; + + if (adjustHIRES40) { + // HACK NOTE: mimics old style COLOR_MODE_COLOR setting ... + if (color == IDX_MAGENTA || color == IDX_BROWN) { + color = IDX_ORANGE; + } else if (color == IDX_DARKBLUE) { + color = IDX_MEDBLUE; + } else if (color == 0x0e) { + color = IDX_WHITE; + } else if (color == 0x07) { + color = IDX_WHITE; + } + } + + uint8_t lum1 = (nyb & 0x1); + uint8_t lum2 = (nyb & 0x2); + uint8_t lum4 = (nyb & 0x4); + uint8_t lum8 = (nyb & 0x8); + + uint8_t color1 = lum1 ? color : IDX_BLACK; + uint8_t color2 = lum2 ? color : IDX_BLACK; + uint8_t color4 = lum4 ? color : IDX_BLACK; + uint8_t color8 = lum8 ? color : IDX_BLACK; + + if (adjustHIRES40) { + if (lum1) { + if (prevLumCount > 1) { + color1 = IDX_WHITE; + } else if (lum2 && lum4) { + color1 = color2 = color4 = IDX_WHITE; + } + } + + if (lum2) { + if (lum1 && prevLumCount > 0) { + color1 = color2 = IDX_WHITE; + } else if (lum4 && lum8) { + color2 = color4 = color8 = IDX_WHITE; + } + } + + if (lum8) { + if (nextLumCount > 1) { + color8 = IDX_WHITE; + } else if (lum4 && lum2) { + color2 = color4 = color8 = IDX_WHITE; + } + } + + if (lum4) { + if (lum8 && nextLumCount > 0) { + color4 = color8 = IDX_WHITE; + } else if (lum1 && lum2) { + color1 = color2 = color4 = IDX_WHITE; + } + } + } + + unsigned int idx = (nybNext << 8) | (nyb << 4) | nybPrev; + idx <<= (PIXEL_STRIDE>>1); + + PIXEL_TYPE *pixelsColor = &color_pixels[idx]; + + pixelsColor[0] = (colormap[color1].r << SHIFT_R) | (colormap[color1].g << SHIFT_G) | (colormap[color1].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + pixelsColor[1] = (colormap[color2].r << SHIFT_R) | (colormap[color2].g << SHIFT_G) | (colormap[color2].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + pixelsColor[2] = (colormap[color4].r << SHIFT_R) | (colormap[color4].g << SHIFT_G) | (colormap[color4].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + pixelsColor[3] = (colormap[color8].r << SHIFT_R) | (colormap[color8].g << SHIFT_G) | (colormap[color8].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + + // handle interpolated ... + + if (nyb && nyb != IDX_WHITE) { + // fill in inner black values + lum2 = (lum2 || (lum1 && (lum4 || lum8))) ? 0x2 : 0; + lum4 = (lum4 || (lum8 && (lum1 || lum2))) ? 0x4 : 0; + + if (nyb == nybPrev && nyb == nybNext) { + lum1 = 0x1; + lum2 = 0x2; + lum4 = 0x4; + lum8 = 0x8; + } else { + if (nyb == nybPrev || (adjustHIRES40 && nybPrev == IDX_WHITE)) { + if (lum2 || lum4 || lum8) { + lum1 = 0x1; + } + if (lum4 || lum8) { + lum2 = 0x2; + } + if (lum8) { + lum4 = 0x4; + } + } + if (nyb == nybNext || (adjustHIRES40 && nybNext == IDX_WHITE)) { + if (lum1 || lum2 || lum4) { + lum8 = 0x8; + } + if (lum1 || lum2) { + lum4 = 0x4; + } + if (lum1) { + lum2 = 0x2; + } + } + } + } + color1 = lum1 ? (color1 ? color1 : color) : IDX_BLACK; + color2 = lum2 ? (color2 ? color2 : color) : IDX_BLACK; + color4 = lum4 ? (color4 ? color4 : color) : IDX_BLACK; + color8 = lum8 ? (color8 ? color8 : color) : IDX_BLACK; + + PIXEL_TYPE *interpColor = &interp_pixels[idx]; + interpColor[0] = (colormap[color1].r << SHIFT_R) | (colormap[color1].g << SHIFT_G) | (colormap[color1].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + interpColor[1] = (colormap[color2].r << SHIFT_R) | (colormap[color2].g << SHIFT_G) | (colormap[color2].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + interpColor[2] = (colormap[color4].r << SHIFT_R) | (colormap[color4].g << SHIFT_G) | (colormap[color4].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + interpColor[3] = (colormap[color8].r << SHIFT_R) | (colormap[color8].g << SHIFT_G) | (colormap[color8].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + } + } + } +} + +static void _initialize_display(void) { + + // screen addresses ... + for (unsigned int y = 0; y < TEXT_ROWS; y++) { + for (unsigned int y2 = 0; y2 < FONT_GLYPH_Y; y2++) { + for (unsigned int x = 0; x < 40; x++) { + unsigned int idx = video_line_offset[y] + (0x400*y2) + x; + assert(idx < 8192); + screen_addresses[idx] = ((y*FONT_HEIGHT_PIXELS + 2*y2) * SCANWIDTH) + (x*FONT_WIDTH_PIXELS) + _FB_OFF; + } + } + } + for (unsigned int i = 0; i < 8192; i++) { + assert(screen_addresses[i] != INT_MIN); + } + + // interface/display fonts ... + + _load_interface_font(0x00,0x40,ucase_glyphs); + _load_interface_font(0x40,0x20,ucase_glyphs); + _load_interface_font(0x60,0x20,lcase_glyphs); + _load_interface_font(0x80,0x40,ucase_glyphs); + _load_interface_font(0xC0,0x20,ucase_glyphs); + _load_interface_font(0xE0,0x20,lcase_glyphs); + _load_interface_font(MOUSETEXT_BEGIN,0x20,mousetext_glyphs); + _load_interface_font(ICONTEXT_BEGIN,0x20,interface_glyphs); + + _initialize_colormap(); + _initialize_color_values(general_color, general_interp, /*adjustHIRES40:*/false); + _initialize_color_values(hires40_color, hires40_interp, /*adjustHIRES40:*/true); +} + +static uint8_t _glyph_normal(uint8_t idx, unsigned int row_off) { + unsigned int glyph_base = idx<<3; + uint8_t glyph_bits7 = glyph_map[glyph_base + row_off] /* & 0x7F */; + return glyph_bits7; +} + +static uint8_t _glyph_inverse(uint8_t idx, unsigned int row_off) { + return ~_glyph_normal(idx, row_off); +} + +static uint8_t _glyph_flash(uint8_t idx, unsigned int row_off) { + return flash_getter(idx, row_off); +} + +void display_loadFont(const uint8_t first, const uint8_t quantity, const uint8_t *data, font_mode_t mode) { + uint8_t idx = (first >> 5); + uint8_t len = idx + (quantity >> 5); + for (unsigned int i=idx; i> 2) | (0xFF << SHIFT_A); + return color1; +} + +static PIXEL_TYPE _color_full_scanline(PIXEL_TYPE color0) { + return color0; +} + +static void _plot_oldschool(color_mode_t mode, uint16_t bits14, unsigned int col, PIXEL_TYPE *colors, unsigned int fb_off) { + (void)mode; + + uint8_t shift = 0x8 | ((col & 0x1) << 1); + uint16_t mask = ~(0xFF << shift); // 0xFF or 0x3FF + unsigned int off = ((shift & 0x8) >> 1) + (shift & 0x2); // 4 or 6 + + PIXEL_TYPE *fb_ptr = (&fbFull[0]) + _FB_OFF + fb_off - off; + + extern unsigned int ntsc_signal_bits; // HACK ... + + // 00BB,BBBB BAAA,AAAA dddd,dddc + // -1 -> AAAA,dddd,dddc (redo 4 prior bits) + // 0 -> BAAA,AAAA,dddd + // 1 -> BBBB,BAAA,AAAA + // 2 -> ccBB,BBBB,BAAA xxx + // 3 -> cccc,ccBB,BBBB xxx + + // DDDD,DDDC CCCC,CCbb bbbb,baaa + // 2 -> CCbb,bbbb,baaa (redo 8 prior bits) + // 3 -> CCCC,CCbb,bbbb + // 4 -> DDDC,CCCC,CCbb + // 5 -> DDDD,DDDC,CCCC + // 6 -> aaaa,DDDD,DDDC xxx + // -> 16bits rendered (28bits total) + uint32_t scanbits32 = (bits14 << shift) | (ntsc_signal_bits & mask); + + static unsigned int last_col_shift[6] = { 0, 0, 0, 0, 0, 1 }; + unsigned int count = 3 + (((shift >> 1) & 0x1) << last_col_shift[(col + 1) >> 3]); + assert(count == 3 || count == 4 || count == 5); + if (count == 5) { + assert(true); + } + for (unsigned int i=0; i> (4 * i)) & 0xFFF; + + idx <<= (PIXEL_STRIDE>>1); + PIXEL_TYPE *pixels = &colors[idx]; + + for (unsigned int j=0; j<4; j++) { + fb_ptr[j] = pixels[j]; + fb_ptr[j + SCANWIDTH] = scanline_color[half_scanlines](pixels[j]); + } + + fb_ptr += 4; + } + + shift = ((shift & 0x8) >> 1) | (((shift >> 1) & 0x01) << 1); + mask = ~(0xFFFF << (14-shift)); + ntsc_signal_bits = ((bits14 >> shift) & mask); +} + +static void _plot_direct(color_mode_t mode, uint16_t bits14, unsigned int col, uint32_t *colors16, unsigned int fb_off) { + (void)col; + (void)colors16; + PIXEL_TYPE *fb_ptr = (&fbFull[0]) + _FB_OFF + fb_off; + ntsc_plotBits(mode, bits14, fb_ptr); +} + +static void _flush_nop(void) { + // no-op ... +} + +static void _flush_scanline(void) { + ntsc_flushScanline(); + scan_last_bit = 0x0; } // ---------------------------------------------------------------------------- // lores/char plotting routines - -static inline void _plot_char40(uint8_t **d, uint8_t **s) { - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint16_t *)(*d)) = *((uint16_t *)(*s)); - *d += SCANSTEP; *s -= 12; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint16_t *)(*d)) = *((uint16_t *)(*s)); - *d += SCANSTEP; *s += 4; +static inline void __plot_char80(PIXEL_TYPE **d, uint8_t **s, const unsigned int fb_width) { + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); + *d += 1; *s += 1; + **d = (colormap[(**s)].r << SHIFT_R) | (colormap[(**s)].g << SHIFT_G) | (colormap[(**s)].b << SHIFT_B) | (MAX_SATURATION << SHIFT_A); } -static inline void _plot_char80(uint8_t **d, uint8_t **s, const unsigned int fb_width) { +static inline void _plot_char80(PIXEL_TYPE **d, uint8_t **s, const unsigned int fb_width) { // FIXME : this is implicitly scaling at FONT_GLYPH_SCALE_Y ... make it explicit - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint16_t *)(*d)) = *((uint16_t *)(*s)); - *d += 2; *s += 2; - *((uint8_t *)(*d)) = *((uint8_t *)(*s)); + + __plot_char80(d, s, fb_width); *d += fb_width-6; *s -= 6; - *((uint32_t *)(*d)) = *((uint32_t *)(*s)); - *d += 4; *s += 4; - *((uint16_t *)(*d)) = *((uint16_t *)(*s)); - *d += 2; *s += 2; - *((uint8_t *)(*d)) = *((uint8_t *)(*s)); + + __plot_char80(d, s, fb_width); *d += fb_width-6; *s += 2; } -static inline void _plot_lores40(uint8_t **d, const uint32_t val) { - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint16_t *)(*d)) = (uint16_t)(val & 0xffff); - *d += SCANSTEP; - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint32_t *)(*d)) = val; - *d += 4; - *((uint16_t *)(*d)) = (uint16_t)(val & 0xffff); -} - -static inline void _plot_lores80(uint8_t *d, const uint32_t lo, const uint32_t hi) { - *((uint32_t *)(d)) = lo; - *((uint32_t *)(d + SCANWIDTH)) = lo; - d += 4; - *((uint32_t *)(d)) = hi; - *((uint32_t *)(d + SCANWIDTH)) = hi; -} - static void _plot_char40_scanline(scan_data_t *scandata) { uint8_t *scanline = scandata->scanline; unsigned int scanrow = scandata->scanrow; unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - uint16_t fb_base = video__line_offset[scanrow>>3]; - uint16_t glyph_off = (scanrow&0x7); + uint16_t row_off = (scanrow&0x07); + + uint16_t fb_base = video_line_offset[scanrow>>3]; + unsigned int fb_row = ((row_off<<1) * SCANWIDTH); for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - uint8_t b = scanline[(col<<1)+1]; // MBD data only + uint16_t glyph_bits14 = 0; + { + uint8_t mbd = scanline[(col<<1)+1]; // MBD data only + uint8_t glyph_bits7 = glyph_getter[mbd>>5](mbd, row_off); + for (unsigned int i=0; i<7; i++) { + uint8_t b = glyph_bits7 & (1 << i); + glyph_bits14 |= (b << (i+0)); + glyph_bits14 |= (b << (i+1)); + } + } - unsigned int glyph_base = (b<<7); // *128 - - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((glyph_off<<1) * SCANWIDTH); - uint8_t *font_ptr = video__wider_font + glyph_base + (glyph_off<<4); - - _plot_char40(/*dst:*/&fb_ptr, /*src:*/&font_ptr); + plot[COLOR_MODE_MONO](COLOR_MODE_MONO, glyph_bits14, col, NULL, screen_addresses[fb_base+col] + fb_row); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][COLOR_MODE_MONO](); // filter triggers on scanline completion } static void _plot_char80_scanline(scan_data_t *scandata) { uint8_t *scanline = scandata->scanline; - unsigned int scanrow = scandata->scanrow; - unsigned int scancol = scandata->scancol; + unsigned int scanrow = scandata->scanrow; + unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - uint16_t fb_base = video__line_offset[scanrow>>3]; - uint16_t glyph_off = (scanrow&0x7); + uint16_t row_off = (scanrow&0x07); + + uint16_t fb_base = video_line_offset[scanrow>>3]; + unsigned int fb_row = ((row_off<<1) * SCANWIDTH); for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - for (unsigned int x=0; x<2; x++) { - uint8_t b = scanline[(col<<1)+x]; // AUX, MBD - unsigned int glyph_base = (b<<6); // *64 + uint16_t glyph_bits14 = 0; + { + uint8_t glyph_bits7; - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + (7*x) + ((glyph_off<<1) * SCANWIDTH); - uint8_t *font_ptr = video__font + glyph_base + (glyph_off<<3); + uint8_t aux = scanline[(col<<1)+0]; + glyph_bits7 = glyph_getter[aux>>5](aux, row_off); + glyph_bits14 = glyph_bits7; - _plot_char80(/*dst:*/&fb_ptr, /*src:*/&font_ptr, SCANWIDTH); + uint8_t mbd = scanline[(col<<1)+1]; + glyph_bits7 = glyph_getter[mbd>>5](mbd, row_off); + glyph_bits14 |= glyph_bits7 << 7; } + + plot[COLOR_MODE_MONO](COLOR_MODE_MONO, glyph_bits14, col, NULL, screen_addresses[fb_base+col] + fb_row); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][COLOR_MODE_MONO](); // filter triggers on scanline completion } static void _plot_lores40_scanline(scan_data_t *scandata) { @@ -628,35 +560,30 @@ static void _plot_lores40_scanline(scan_data_t *scandata) { unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - uint16_t fb_base = video__line_offset[scanrow>>3]; - uint16_t block_off = (scanrow&0x7); + uint16_t row_off = (scanrow&0x07); - uint8_t hi_nyb = !!(block_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1 - uint8_t lores_mask = (0x0f << (hi_nyb<<2) ); // 0x0f --or-- 0xf0 - uint8_t lores_shift = ((1-hi_nyb)<<2); // -> 0xi0 + uint8_t hi_nyb = !!(row_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1 + uint8_t lores_shift = (hi_nyb<<2); // -> 0x0i + uint8_t lores_mask = (0x0f << lores_shift ); // 0x0f --or-- 0xf0 + + uint16_t fb_base = video_line_offset[scanrow>>3]; + unsigned int fb_row = ((row_off<<1) * SCANWIDTH); for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((block_off<<1) * SCANWIDTH); + uint8_t mbd = scanline[(col<<1)+1]; // MBD data only + uint8_t val = (mbd & lores_mask) >> lores_shift; + uint8_t rot2 = ((col & 0x1) << 1); // 2 phases at double rotation + val = (val >> rot2) | ((val & 0x03) << rot2); - uint8_t b = scanline[(col<<1)+1]; // MBD data only - uint8_t val = (b & lores_mask) << lores_shift; + uint16_t bits14 = val | (val << 4) | (val << 8) | (val << 12); + bits14 &= 0x3FFF; - uint32_t val32; - if (color_mode == COLOR_MODE_BW) { - uint8_t rot2 = ((col % 2) << 1); // 2 phases at double rotation - val = (val << rot2) | ((val & 0xC0) >> rot2); - val32 = ((val & 0x10) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 0; - val32 |= ((val & 0x20) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8; - val32 |= ((val & 0x40) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16; - val32 |= ((val & 0x80) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24; - } else { - val32 = (val << 24) | (val << 16) | (val << 8) | val; - } - - _plot_lores40(/*dst:*/&fb_ptr, val32); + plot[color_mode](color_mode, bits14, col, general_colors[color_mode], screen_addresses[fb_base+col] + fb_row); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][color_mode](); // filter triggers on scanline completion } static inline uint8_t __shift_block80(uint8_t b) { @@ -673,85 +600,57 @@ static inline uint8_t __shift_block80(uint8_t b) { static void _plot_lores80_scanline(scan_data_t *scandata) { uint8_t *scanline = scandata->scanline; - unsigned int scanrow = scandata->scanrow; - unsigned int scancol = scandata->scancol; + unsigned int scanrow = scandata->scanrow; + unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - uint16_t fb_base = video__line_offset[scanrow>>3]; - uint16_t block_off = (scanrow&0x7); + uint16_t row_off = (scanrow&0x7); - uint8_t hi_nyb = !!(block_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1 - uint8_t lores_mask = (0x0f << (hi_nyb<<2) ); // 0x0f --or-- 0xf0 - uint8_t lores_shift = ((1-hi_nyb)<<2); // -> 0xi0 + uint8_t hi_nyb = !!(row_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1 + uint8_t lores_shift = (hi_nyb<<2); // -> 0x0i + uint8_t lores_mask = (0x0f << lores_shift ); // 0x0f --or-- 0xf0 + + uint16_t fb_base = video_line_offset[scanrow>>3]; + unsigned int fb_row = ((row_off<<1) * SCANWIDTH); for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - { - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((block_off<<1) * SCANWIDTH); + uint16_t bits14; + { unsigned int idx = (col<<1)+0; // AUX uint8_t b = scanline[idx]; b = __shift_block80(b); - uint8_t val = (b & lores_mask) << lores_shift; - uint32_t val32_lo = 0x0; - uint32_t val32_hi = 0x0; - - if (color_mode == COLOR_MODE_BW && val != 0x0) { - val = (val >> 4) | val; - { - uint16_t val16 = val | (val << 8); - val16 = val16 >> (4 - (idx&0x3)); - val = (uint8_t)val16; - } - - val32_lo |= ((val & 0x01) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 0; - val32_lo |= ((val & 0x02) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8; - val32_lo |= ((val & 0x04) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16; - val32_lo |= ((val & 0x08) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24; - val32_hi |= ((uint64_t)((val & 0x10) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 0; - val32_hi |= ((uint64_t)((val & 0x20) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 8; - val32_hi |= ((uint64_t)((val & 0x40) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 16; - } else { - val32_hi = (val << 16) | (val << 8) | val; - val32_lo = (val << 24) | val32_hi; + uint8_t val = (b & lores_mask) >> lores_shift; + val = (val << 4) | val; + { + uint16_t val16 = val | (val << 8); + val16 = val16 >> (4 - (idx&0x3)); + val = (uint8_t)val16; + val &= 0x7F; } - - _plot_lores80(/*dst:*/fb_ptr, val32_lo, val32_hi); + bits14 = val; } { - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + 7 + ((block_off<<1) * SCANWIDTH); - unsigned int idx = (col<<1)+1; // MBD uint8_t b = scanline[idx]; - uint8_t val = (b & lores_mask) << lores_shift; - uint32_t val32_lo = 0x0; - uint32_t val32_hi = 0x0; - - if (color_mode == COLOR_MODE_BW && val != 0x0) { - val = (val >> 4) | val; - { - uint16_t val16 = val | (val << 8); - val16 = val16 >> (4 - (idx&0x3)); - val = (uint8_t)val16; - } - - val32_lo |= ((val & 0x01) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 0; - val32_lo |= ((val & 0x02) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8; - val32_lo |= ((val & 0x04) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16; - val32_lo |= ((val & 0x08) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24; - val32_hi |= ((uint64_t)((val & 0x10) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 0; - val32_hi |= ((uint64_t)((val & 0x20) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 8; - val32_hi |= ((uint64_t)((val & 0x40) ? COLOR_LIGHT_WHITE : COLOR_BLACK)) << 16; - } else { - val32_hi = (val << 16) | (val << 8) | val; - val32_lo = (val << 24) | val32_hi; + uint8_t val = (b & lores_mask) >> lores_shift; + val = (val << 4) | val; + { + uint16_t val16 = val | (val << 8); + val16 = val16 >> (4 - (idx&0x3)); + val = (uint8_t)val16; + val &= 0x7F; } - - _plot_lores80(/*dst:*/fb_ptr, val32_lo, val32_hi); + bits14 |= (val<<7); } + + plot[color_mode](color_mode, bits14, col, general_colors[color_mode], screen_addresses[fb_base+col] + fb_row); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][color_mode](); // filter triggers on scanline completion } static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(scan_data_t*) { @@ -787,8 +686,8 @@ static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(scan_ // ---------------------------------------------------------------------------- // Classic interface and printing HUD messages -static void _display_plotChar(uint8_t *fboff, const unsigned int fbPixWidth, const interface_colorscheme_t cs, const uint8_t c) { - uint8_t *src = video__int_font[cs] + c * (FONT_GLYPH_X*FONT_GLYPH_Y); +static void _display_plotChar(PIXEL_TYPE *fboff, const unsigned int fbPixWidth, const interface_colorscheme_t cs, const uint8_t c) { + uint8_t *src = interface_font[cs] + c * (FONT_GLYPH_X*FONT_GLYPH_Y); _plot_char80(&fboff, &src, fbPixWidth); _plot_char80(&fboff, &src, fbPixWidth); _plot_char80(&fboff, &src, fbPixWidth); @@ -803,13 +702,13 @@ static void _display_plotChar(uint8_t *fboff, const unsigned int fbPixWidth, con void display_plotChar(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c) { assert(col < 80); assert(row < 24); - unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE; - _display_plotChar(fbInterface+off, SCANWIDTH, cs, c); + unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _FB_OFF; + _display_plotChar(fbFull+off, SCANWIDTH, cs, c); video_setDirty(FB_DIRTY_FLAG); } #endif -static void _display_plotLine(uint8_t *fb, const unsigned int fbPixWidth, const unsigned int xAdjust, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *line) { +static void _display_plotLine(PIXEL_TYPE *fb, const unsigned int fbPixWidth, const unsigned int xAdjust, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *line) { for (uint8_t x=col; *line; x++, line++) { char c = *line; unsigned int off = row * fbPixWidth * FONT_HEIGHT_PIXELS + x * FONT80_WIDTH_PIXELS + xAdjust; @@ -819,12 +718,12 @@ static void _display_plotLine(uint8_t *fb, const unsigned int fbPixWidth, const #if INTERFACE_CLASSIC void display_plotLine(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *message) { - _display_plotLine(fbInterface, /*fbPixWidth:*/SCANWIDTH, /*xAdjust:*/_INTERPOLATED_PIXEL_ADJUSTMENT_PRE, col, row, cs, message); + _display_plotLine(fbFull, /*fbPixWidth:*/SCANWIDTH, /*xAdjust:*/_FB_OFF, col, row, cs, message); video_setDirty(FB_DIRTY_FLAG); } #endif -void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows) { +void display_plotMessage(PIXEL_TYPE *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows) { assert(message_cols < 80); assert(message_rows < 24); @@ -837,250 +736,86 @@ void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const ch // ---------------------------------------------------------------------------- // Double-HIRES (HIRES80) graphics -static inline void __plot_hires80_pixels(uint8_t idx, uint8_t *fb_ptr) { - uint8_t bCurr = idx; - - if (color_mode == COLOR_MODE_BW) { - uint32_t b32; - b32 = (bCurr & 0x1) ? COLOR_LIGHT_WHITE : COLOR_BLACK; - b32 |= ((bCurr & 0x2) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8; - b32 |= ((bCurr & 0x4) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16; - b32 |= ((bCurr & 0x8) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24; - *((uint32_t *)fb_ptr) = b32; - *((uint32_t *)(fb_ptr+SCANWIDTH)) = b32; - } else { - // TODO FIXME : handle interpolated here ... - uint32_t b32; - b32 = (bCurr & 0x1) ? bCurr : COLOR_BLACK; - b32 |= ((bCurr & 0x2) ? bCurr : COLOR_BLACK) << 8; - b32 |= ((bCurr & 0x4) ? bCurr : COLOR_BLACK) << 16; - b32 |= ((bCurr & 0x8) ? bCurr : COLOR_BLACK) << 24; - *((uint32_t *)fb_ptr) = b32; - *((uint32_t *)(fb_ptr+SCANWIDTH)) = b32; - } -} - static void _plot_hires80_scanline(scan_data_t *scandata) { uint8_t *scanline = scandata->scanline; unsigned int scanrow = scandata->scanrow; unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - uint16_t fb_base = video__line_offset[scanrow>>3] + (0x400 * (scanrow & 0x7)); + uint16_t fb_base = video_line_offset[scanrow>>3] + (0x400 * (scanrow & 0x07)); + // 8 bytes (without hibit) smushed into 7 total bytes == 56 bits // |AUX 0a|MBD 0b |AUX 1c |MBD 1d |AUX 2e |MBD 2f |AUX 3g |MBD 3h ... // aaaaaaab bbbbbbcc cccccddd ddddeeee eeefffff ffgggggg ghhhhhhh ... // 01234560 12345601 23456012 34560123 45601234 56012345 60123456 for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - uint8_t idx = (col<<1); - uint8_t is_odd = (col & 0x1); - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off]; + uint8_t aux = scanline[idx+0] & 0x7f; // AUX + uint8_t mbd = scanline[idx+1] & 0x7f; // MBD - uint8_t aux = scanline[idx]; // AUX - uint8_t mbd = scanline[idx+1]; // MBD + uint16_t bits14 = (((mbd << 7) | aux) << 1) | scan_last_bit; - if (!is_odd) { - // idx = 0 -> aaaa aaab bbbb 0000 - // idx = 2 -> eeee eeef ffff 0000 + scan_last_bit = (bits14 >> 14) & 0x01; - uint8_t auxLO = (aux & (0x0F<<0)) >> 0; // 0000aaaa -> 0000aaaa - __plot_hires80_pixels(auxLO, fb_ptr); - fb_ptr += 4; - - uint8_t auxHI = (aux & (0x07<<4)) >> 4; // 0aaa0000 -> 00000aaa - uint8_t mbdLO = (mbd & (0x01<<0)) << 3; // 0000000b -> 0000b000 - __plot_hires80_pixels((mbdLO | auxHI), fb_ptr); - fb_ptr += 4; - - uint8_t mbdXX = (mbd & (0x0F<<1)) >> 1; // 000bbbb0 -> 0000bbbb - __plot_hires80_pixels(mbdXX, fb_ptr); - fb_ptr += 4; - - /* - // partial ... overwritten by next even scan ... - uint8_t mbdHI = (mbd & (0x03<<5)) >> 5; // 0bb00000 -> 0000XXbb - __plot_hires80_pixels(mbdHI, fb_ptr); - */ - - } else { - // idx = 1 -> bbcc cccc cddd dddd - // idx = 3 -> ffgg gggg ghhh hhhh - - fb_ptr -= 2; - - uint8_t mb0 = scanline[idx-1]; // MBD-1 - - uint8_t mbdHI = (mb0 & (0x03<<5)) >> 5; // 0bb00000 -> 000000bb - uint8_t auxLO = (aux & (0x03<<0)) << 2; // 000000cc -> 0000cc00 - __plot_hires80_pixels((auxLO | mbdHI), fb_ptr); - fb_ptr += 4; - - uint8_t auxXX = (aux & (0x0F<<2)) >> 2; // 00cccc00 -> 0000cccc - __plot_hires80_pixels(auxXX, fb_ptr); - fb_ptr += 4; - - uint8_t auxHI = (aux & (0x01<<6)) >> 6; // 0c000000 -> 0000000c - uint8_t mbdLO = (mbd & (0x07<<0)) << 1; // 00000ddd -> 0000ddd0 - __plot_hires80_pixels((mbdLO | auxHI), fb_ptr); - fb_ptr += 4; - - uint8_t mbdXX = (mbd & (0x0F<<3)) >> 3; // 0dddd000 -> 0000dddd - __plot_hires80_pixels(mbdXX, fb_ptr); - } + plot[color_mode](color_mode, bits14, col, general_colors[color_mode], screen_addresses[fb_base+col]); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][color_mode](); // filter triggers on scanline completion } // ---------------------------------------------------------------------------- // Hires GRaphics (HIRES40) -static inline void _calculate_interp_color(uint8_t *color_buf, const unsigned int idx, const uint8_t *interp_base, uint8_t b) { - if (color_buf[idx] != 0x0) { - return; - } - uint8_t pixR = color_buf[idx+1]; - if (pixR == 0x0) { - return; - } - uint8_t pixL = color_buf[idx-1]; - if (pixL == 0x0) { - return; - } - - // Calculates the color at the edge of interpolated bytes: called 4 times in little endian order (...7 0...7 0...) - if (pixL == COLOR_LIGHT_WHITE) { - if (pixR == COLOR_LIGHT_WHITE) { - pixL = b; - color_buf[idx] = interp_base[pixL>>7]; - } else { - color_buf[idx] = pixR; - } - } else { - color_buf[idx] = pixL; - } -} - -static inline void _plot_hires_pixels(uint8_t *dst, const uint8_t *src) { - for (unsigned int i=2; i; i--) { - for (unsigned int j=0; jscanline; - unsigned int scanrow = scandata->scanrow; - unsigned int scancol = scandata->scancol; + unsigned int scanrow = scandata->scanrow; + unsigned int scancol = scandata->scancol; unsigned int scanend = scandata->scanend; - assert(scancol < scanend); - assert(scanend > 0); - uint16_t fb_base = video__line_offset[scanrow>>3] + (0x400 * (scanrow & 0x7)); + uint16_t fb_base = video_line_offset[scanrow>>3] + (0x400 * (scanrow & 0x07)); + for (unsigned int col = scancol; col < scanend; col++) { - uint16_t fb_off = fb_base + col; - uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off]; + uint16_t bits14 = 0; + uint8_t shift; + { + uint8_t mbd = scanline[(col<<1)+1]; // MBD data only + shift = (mbd & 0x80) >> 7; - bool is_even = !(col & 0x1); - uint8_t idx = (col<<1)+1; // MBD data only - uint8_t b = scanline[idx]; - - uint8_t _buf[DYNAMIC_SZ] = { 0 }; - uint8_t *color_buf = (uint8_t *)_buf; // <--- work around for -Wstrict-aliasing - - uint8_t *hires_ptr = NULL; - if (is_even) { - hires_ptr = (uint8_t *)&video__hires_even[b<<3]; - } else { - hires_ptr = (uint8_t *)&video__hires_odd[b<<3]; - } - *((uint32_t *)&color_buf[2]) = *((uint32_t *)(hires_ptr+0)); - *((uint16_t *)&color_buf[6]) = *((uint16_t *)(hires_ptr+4)); - *((uint8_t *)&color_buf[8]) = *((uint8_t *)(hires_ptr+6)); - hires_ptr = NULL; - - // copy adjacent pixel bytes - *((uint16_t *)&color_buf[0]) = *((uint16_t *)(fb_ptr-3)); - *((uint16_t *)&color_buf[DYNAMIC_SZ-2]) = *((uint16_t *)(fb_ptr+15)); - - if (color_mode != COLOR_MODE_BW) { - uint8_t *hires_altbase = NULL; - if (is_even) { - hires_altbase = (uint8_t *)&video__hires_odd[0]; - } else { - hires_altbase = (uint8_t *)&video__hires_even[0]; + for (unsigned int i=0; i<7; i++) { + uint8_t b = mbd & (1 << i); + bits14 |= (b << (i+0)); + bits14 |= (b << (i+1)); } - // if right-side color is not black, re-calculate edge values - if (color_buf[DYNAMIC_SZ-2] & 0xff) { - if ((col < CYCLES_VIS-1) && (col < scanend - 1)) { - uint8_t bNext = scanline[idx+2]; - if ((b & 0x40) && (bNext & 0x1)) { - *((uint16_t *)&color_buf[DYNAMIC_SZ-3]) = (uint16_t)0x3737;// COLOR_LIGHT_WHITE - } - } - } - - // if left-side color is not black, re-calculate edge values - if (color_buf[1] & 0xff) { - if (col > 0) { - uint8_t bPrev = scanline[idx-2]; - if ((bPrev & 0x40) && (b & 0x1)) { - *((uint16_t *)&color_buf[1]) = (uint16_t)0x3737;// COLOR_LIGHT_WHITE - } - } - } - - if (color_mode == COLOR_MODE_INTERP) { - uint8_t *interp_base = NULL; - uint8_t *interp_altbase = NULL; - if (is_even) { - interp_base = (uint8_t *)&video__even_colors[0]; - interp_altbase = (uint8_t *)&video__odd_colors[0]; - } else { - interp_base = (uint8_t *)&video__odd_colors[0]; - interp_altbase = (uint8_t *)&video__even_colors[0]; - } - - // calculate interpolated/bleed colors - uint8_t bl = 0x0; - if (col > 0) { - bl = scanline[idx-2]; - } - _calculate_interp_color(color_buf, 1, interp_altbase, bl); - _calculate_interp_color(color_buf, 2, interp_base, b); - _calculate_interp_color(color_buf, 8, interp_base, b); - if (col < CYCLES_VIS-1) { - bl = scanline[idx+2]; - } - _calculate_interp_color(color_buf, 9, interp_altbase, bl); - } + bits14 = (bits14 << shift) | (scan_last_bit >> (1-shift)); + scan_last_bit = (mbd & 0x40) >> 6; + bits14 &= 0x3FFF; } - _plot_hires_pixels(/*dst:*/fb_ptr-4, /*src:*/color_buf); - ////fb_ptr += 7; + plot[color_mode](color_mode, bits14, col, hires40_colors[color_mode], screen_addresses[fb_base+col]); } + + int filter_idx = (scanend >> 3); + flush[filter_idx][color_mode](); // filter triggers on scanline completion } static void (*_hirespage_plotter(uint32_t currswitches))(scan_data_t*) { return ((currswitches & SS_80COL) && (currswitches & SS_DHIRES)) ? _plot_hires80_scanline : _plot_hires40_scanline; } +// ---------------------------------------------------------------------------- + +uint16_t display_getVideoLineOffset(uint8_t txtRow) { + assert(txtRow <= TEXT_ROWS); + return video_line_offset[txtRow]; +} + +#if TESTING uint8_t *display_renderStagingFramebuffer(void) { const uint32_t mainswitches = run_args.softswitches; @@ -1096,7 +831,7 @@ uint8_t *display_renderStagingFramebuffer(void) { uint16_t base = page ? 0x0800 : 0x0400; for (unsigned int row=0; row < TEXT_ROWS-4; row++) { for (unsigned int col=0; col < TEXT_COLS; col++) { - uint16_t off = video__line_offset[row] + col; + uint16_t off = video_line_offset[row] + col; uint16_t ea = base+off; scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD @@ -1118,7 +853,7 @@ uint8_t *display_renderStagingFramebuffer(void) { for (unsigned int row=0; row < TEXT_ROWS-4; row++) { for (unsigned int col=0; col < TEXT_COLS; col++) { for (unsigned int i = 0; i < 8; i++) { - uint16_t off = video__line_offset[row] + (0x400*i) + col; + uint16_t off = video_line_offset[row] + (0x400*i) + col; uint16_t ea = base+off; scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD @@ -1145,7 +880,7 @@ uint8_t *display_renderStagingFramebuffer(void) { uint16_t base = page ? 0x0800 : 0x0400; for (unsigned int row=TEXT_ROWS-4; row < TEXT_ROWS; row++) { for (unsigned int col=0; col < TEXT_COLS; col++) { - uint16_t off = video__line_offset[row] + col; + uint16_t off = video_line_offset[row] + col; uint16_t ea = base+off; scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD @@ -1168,7 +903,7 @@ uint8_t *display_renderStagingFramebuffer(void) { for (unsigned int row=TEXT_ROWS-4; row < TEXT_ROWS; row++) { for (unsigned int col=0; col < TEXT_COLS; col++) { for (unsigned int i = 0; i < 8; i++) { - uint16_t off = video__line_offset[row] + (0x400*i) + col; + uint16_t off = video_line_offset[row] + (0x400*i) + col; uint16_t ea = base+off; scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD @@ -1188,41 +923,6 @@ uint8_t *display_renderStagingFramebuffer(void) { return display_getCurrentFramebuffer(); } -void display_flashText(void) { - static bool normal = false; - normal = !normal; - - if (normal) { - colormap[ COLOR_FLASHING_BLACK].red = 0; - colormap[ COLOR_FLASHING_BLACK].green = 0; - colormap[ COLOR_FLASHING_BLACK].blue = 0; - - colormap[ COLOR_FLASHING_WHITE].red = 0xff; - colormap[ COLOR_FLASHING_WHITE].green = 0xff; - colormap[ COLOR_FLASHING_WHITE].blue = 0xff; - } else { - colormap[ COLOR_FLASHING_BLACK].red = 0xff; - colormap[ COLOR_FLASHING_BLACK].green = 0xff; - colormap[ COLOR_FLASHING_BLACK].blue = 0xff; - - colormap[ COLOR_FLASHING_WHITE].red = 0; - colormap[ COLOR_FLASHING_WHITE].green = 0; - colormap[ COLOR_FLASHING_WHITE].blue = 0; - } - - video_setDirty(FB_DIRTY_FLAG); -} - -uint8_t *display_getCurrentFramebuffer(void) { -#if INTERFACE_CLASSIC - if (interface_isShowing()) { - return fbInterface; - } -#endif - return fbStaging; -} - -#if TESTING // HACK FIXME TODO ... should consolidate this into debugger ... extern pthread_mutex_t interface_mutex; extern pthread_cond_t cpu_thread_cond; @@ -1235,10 +935,28 @@ uint8_t *display_waitForNextCompleteFramebuffer(void) { if ((err = pthread_cond_wait(&dbg_thread_cond, &interface_mutex))) { LOG("pthread_cond_wait : %d", err); } - return display_getCurrentFramebuffer (); + return display_getCurrentFramebuffer(); } #endif + +void display_flashText(void) { + static bool flash_normal = false; + flash_normal = !flash_normal; + + if (flash_normal) { + flash_getter = _glyph_normal; + } else { + flash_getter = _glyph_inverse; + } + + video_setDirty(FB_DIRTY_FLAG); +} + +PIXEL_TYPE *display_getCurrentFramebuffer(void) { + return fbFull; +} + void display_flushScanline(scan_data_t *scandata) { #if TESTING // FIXME TODO ... remove this bracing when video refactoring is done @@ -1288,13 +1006,51 @@ void display_frameComplete(void) { #endif } +static void display_prefsChanged(const char *domain) { + long lVal = 0; + color_mode = prefs_parseLongValue(domain, PREF_COLOR_MODE, &lVal, /*base:*/10) ? getColorMode(lVal) : COLOR_MODE_DEFAULT; + + lVal = MONO_MODE_BW; + mono_mode = prefs_parseLongValue(domain, PREF_MONO_MODE, &lVal, /*base:*/10) ? getMonoMode(lVal) : MONO_MODE_DEFAULT; + + bool bVal = false; + half_scanlines = prefs_parseBoolValue(domain, PREF_SHOW_HALF_SCANLINES, &bVal) ? (bVal ? 1 : 0) : 0; + + _initialize_display(); +} + static void _init_interface(void) { LOG("Initializing display subsystem"); - _initialize_interface_fonts(); - _initialize_hires_values(); - _initialize_row_col_tables(); - _initialize_dhires_values(); - _initialize_color(); + _initialize_display(); + + plot[COLOR_MODE_MONO] = _plot_direct; + plot[COLOR_MODE_COLOR] = _plot_oldschool; + plot[COLOR_MODE_INTERP] = _plot_oldschool; + plot[COLOR_MODE_COLOR_MONITOR] = _plot_direct; + plot[COLOR_MODE_MONO_TV] = _plot_direct; + plot[COLOR_MODE_COLOR_TV] = _plot_direct; + + // scanline filtering + for (unsigned int i=0; i<5; i++) { + for (unsigned int j=0; j= x < 80 } scan_data_t; -#define IDX_BLACK 0x00 -#define IDX_MAGENTA 0x10 -#define IDX_DARKBLUE 0x20 -#define IDX_PURPLE 0x30 -#define IDX_DARKGREEN 0x40 -#define IDX_DARKGREY 0x50 -#define IDX_MEDBLUE 0x60 -#define IDX_LIGHTBLUE 0x70 -#define IDX_BROWN 0x80 -#define IDX_ORANGE 0x90 -#define IDX_LIGHTGREY 0xa0 -#define IDX_PINK 0xb0 -#define IDX_GREEN 0xc0 -#define IDX_YELLOW 0xd0 -#define IDX_AQUA 0xe0 -#define IDX_WHITE 0xf0 +#define IDX_BLACK 0x00 // 0000 +#define IDX_MAGENTA 0x01 // 0001 +#define IDX_DARKBLUE 0x02 // 0010 +#define IDX_PURPLE 0x03 // 0011 - HIRES40 +#define IDX_DARKGREEN 0x04 // 0100 +#define IDX_DARKGREY 0x05 // 0101 +#define IDX_MEDBLUE 0x06 // 0110 - HIRES40 +#define IDX_LIGHTBLUE 0x07 // 0111 +#define IDX_BROWN 0x08 // 1000 +#define IDX_ORANGE 0x09 // 1001 - HIRES40 +#define IDX_LIGHTGREY 0x0a // 1010 +#define IDX_PINK 0x0b // 1011 +#define IDX_GREEN 0x0c // 1100 - HIRES40 +#define IDX_YELLOW 0x0d // 1101 +#define IDX_AQUA 0x0e // 1110 +#define IDX_WHITE 0x0f // 1111 -/* - * Reference to the internal 8bit-indexed color format - */ -extern A2Color_s colormap[]; +// Colors duplicated and muted at this offset +#define IDX_LUMINANCE_HALF 0x40 + +// Interface colors +#define IDX_ICOLOR_R 0x21 +#define IDX_ICOLOR_G 0x22 +#define IDX_ICOLOR_B 0x23 + +typedef enum interface_colorscheme_t { + GREEN_ON_BLACK = 0, + GREEN_ON_BLUE, + RED_ON_BLACK, + BLUE_ON_BLACK, + WHITE_ON_BLACK, + + // WARNING : changing here requires updating display.c ncvideo.c + + BLACK_ON_RED, + + // 16 COLORS -- ncvideo.c + BLACK_ON_BLACK, + BLACK_ON_MAGENTA, + BLACK_ON_DARKBLUE, + BLACK_ON_PURPLE, + BLACK_ON_DARKGREEN, + BLACK_ON_DARKGREY, + BLACK_ON_MEDBLUE, + BLACK_ON_LIGHTBLUE, + BLACK_ON_BROWN, + BLACK_ON_ORANGE, + BLACK_ON_LIGHTGREY, + BLACK_ON_PINK, + BLACK_ON_GREEN, + BLACK_ON_YELLOW, + BLACK_ON_AQUA, + BLACK_ON_WHITE, + + NUM_INTERFACE_COLORSCHEMES, + COLOR16 = 0x80, + INVALID_COLORSCHEME = 0xFF, +} interface_colorscheme_t; /* * Color options */ typedef enum color_mode_t { - COLOR_MODE_BW = 0, + // original modes ... + COLOR_MODE_MONO = 0, COLOR_MODE_COLOR, COLOR_MODE_INTERP, - NUM_COLOROPTS + // NTSC color modes ... + COLOR_MODE_COLOR_MONITOR, + COLOR_MODE_MONO_TV, + COLOR_MODE_COLOR_TV, + NUM_COLOROPTS, } color_mode_t; +#define COLOR_MODE_DEFAULT COLOR_MODE_COLOR_TV +static inline color_mode_t getColorMode(long lVal) { + if (lVal < 0 || lVal >= NUM_COLOROPTS) { + lVal = COLOR_MODE_DEFAULT; + } + return (color_mode_t)lVal; +} + +typedef enum mono_mode_t { + MONO_MODE_BW = 0, + MONO_MODE_GREEN, + NUM_MONOOPTS, +} mono_mode_t; + +#define MONO_MODE_DEFAULT MONO_MODE_BW +static inline mono_mode_t getMonoMode(long lVal) { + if (lVal < 0 || lVal >= NUM_MONOOPTS) { + lVal = MONO_MODE_DEFAULT; + } + return (mono_mode_t)lVal; +} + /* * Font mode */ @@ -87,17 +162,6 @@ typedef enum drawpage_mode_t { NUM_DRAWPAGE_MODES, } drawpage_mode_t; -/* - * Setup the display. This may be called multiple times in a run, and is - * used when graphics parameters may have changed. - * - * This function is responsible for inserting any needed video-specific hooks - * into the 6502 memory indirection table. It should *not* hook the - * soft-switches. - * - */ -void display_reset(void); - /* * Set the font used by the display. QTY characters are loaded starting * with FIRST, from DATA. DATA contains 8 bytes for each character, each @@ -127,7 +191,6 @@ void display_flashText(void); #if INTERFACE_CLASSIC /* * Plot character into interface framebuffer. - * - Pixels in the framebuffer are 8bit indexed color into the colormap array * - This should only be called from video backends/interface */ void display_plotChar(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c); @@ -143,17 +206,21 @@ void display_plotLine(const uint8_t col, const uint8_t row, const interface_colo * Plot multi-line message into given framebuffer (not the interface framebuffer). * - See display_plotChar() ... */ -void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows); +void display_plotMessage(PIXEL_TYPE *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows); + +/* + * Get video line offset for TEXT mode row + */ +uint16_t display_getVideoLineOffset(uint8_t txtRow); // ---------------------------------------------------------------------------- // video generation /* * Called by video backend to get the current complete staging framebuffer. - * - Framebuffer is exactly SCANWIDTH*SCANHEIGHT*sizeof(uint8_t) size - * - Pixels in the framebuffer are 8bit indexed color into the colormap array + * - Framebuffer is exactly SCANWIDTH*SCANHEIGHT*sizeof(PIXEL_TYPE) */ -uint8_t *display_getCurrentFramebuffer(void) CALL_ON_UI_THREAD; +PIXEL_TYPE *display_getCurrentFramebuffer(void) CALL_ON_UI_THREAD; /* * Handler for toggling text flashing @@ -193,10 +260,11 @@ uint8_t *display_waitForNextCompleteFramebuffer(void); #define SCANHEIGHT (TEXT_ROWS * FONT_HEIGHT_PIXELS) // 384 // Extra bytes on each side of internal framebuffers for color interpolation hack -#define _INTERPOLATED_PIXEL_ADJUSTMENT_PRE 4 -#define _INTERPOLATED_PIXEL_ADJUSTMENT_POST 4 +#define _FB_OFF 4 +#define _INTERPOLATED_PIXEL_ADJUSTMENT_PRE _FB_OFF +#define _INTERPOLATED_PIXEL_ADJUSTMENT_POST _FB_OFF #define INTERPOLATED_PIXEL_ADJUSTMENT (_INTERPOLATED_PIXEL_ADJUSTMENT_PRE+_INTERPOLATED_PIXEL_ADJUSTMENT_POST) -#define SCANWIDTH (_SCANWIDTH+INTERPOLATED_PIXEL_ADJUSTMENT) +#define SCANWIDTH (_SCANWIDTH+INTERPOLATED_PIXEL_ADJUSTMENT) // 568 #define FONT_GLYPH_X (7+/*unused*/1) // generated font.c uses a single byte (8bits) per font glyph line #define FONT_GLYPH_Y (FONT_HEIGHT_PIXELS>>1) // ... 8 bytes total for whole glyph @@ -249,42 +317,6 @@ uint8_t *display_waitForNextCompleteFramebuffer(void); #define ICONTEXT_LEFT_TAB (ICONTEXT_KBD_BEGIN+0x..) #define ICONTEXT_RIGHT_TAB (ICONTEXT_KBD_BEGIN+0x..) -#define COLOR_BLACK 0 - -#define COLOR_DARK_RED 35 -#define COLOR_MEDIUM_RED 36 -#define COLOR_LIGHT_RED 37 /* hgr used */ - -#define COLOR_DARK_GREEN 38 -#define COLOR_MEDIUM_GREEN 39 -#define COLOR_LIGHT_GREEN 40 /* hgr used */ - -#define COLOR_DARK_YELLOW 41 -#define COLOR_MEDIUM_YELLOW 42 -#define COLOR_LIGHT_YELLOW 43 - -#define COLOR_DARK_BLUE 44 -#define COLOR_MEDIUM_BLUE 45 -#define COLOR_LIGHT_BLUE 46 /* hgr used */ - -#define COLOR_DARK_PURPLE 47 -#define COLOR_MEDIUM_PURPLE 48 -#define COLOR_LIGHT_PURPLE 49 /* hgr used */ - -#define COLOR_DARK_CYAN 50 -#define COLOR_MEDIUM_CYAN 51 -#define COLOR_LIGHT_CYAN 52 - -#define COLOR_DARK_WHITE 53 -#define COLOR_MEDIUM_WHITE 54 -#define COLOR_LIGHT_WHITE 55 - -#define COLOR_FLASHING_BLACK 56 -#define COLOR_FLASHING_WHITE 57 -#define COLOR_FLASHING_UNGREEN 58 -#define COLOR_FLASHING_GREEN 59 - - /* ---------------------------------- generic graphics globals ---------------------------------- */ diff --git a/src/interface.c b/src/interface.c index 41f86fca..9ce20ea0 100644 --- a/src/interface.c +++ b/src/interface.c @@ -118,7 +118,7 @@ static void _translate_screen_x_y(char *screen, const int xlen, const int ylen) } } -void interface_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows) { +void interface_plotMessage(PIXEL_TYPE *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows) { _translate_screen_x_y(message, message_cols, message_rows); display_plotMessage(fb, cs, message, message_cols, message_rows); } @@ -1083,7 +1083,6 @@ void c_interface_parameters() else if ((ch == kESC) || c_keys_is_interface_key(ch)) { timing_initialize(); - display_reset(); vm_reinitializeAudio(); c_joystick_reset(); #if !TESTING diff --git a/src/interface.h b/src/interface.h index a32b615e..dcd51342 100644 --- a/src/interface.h +++ b/src/interface.h @@ -16,42 +16,10 @@ #ifndef A2_INTERFACE_H #define A2_INTERFACE_H +#include "display.h" + #define INTERFACE_SCREEN_X 80 -typedef enum interface_colorscheme_t { - GREEN_ON_BLACK = 0, - GREEN_ON_BLUE, - RED_ON_BLACK, - BLUE_ON_BLACK, - WHITE_ON_BLACK, - - // WARNING : changing here requires updating display.c ncvideo.c - - BLACK_ON_RED, - - // 16 COLORS -- ncvideo.c - BLACK_ON_BLACK, - BLACK_ON_MAGENTA, - BLACK_ON_DARKBLUE, - BLACK_ON_PURPLE, - BLACK_ON_DARKGREEN, - BLACK_ON_DARKGREY, - BLACK_ON_MEDBLUE, - BLACK_ON_LIGHTBLUE, - BLACK_ON_BROWN, - BLACK_ON_ORANGE, - BLACK_ON_LIGHTGREY, - BLACK_ON_PINK, - BLACK_ON_GREEN, - BLACK_ON_YELLOW, - BLACK_ON_AQUA, - BLACK_ON_WHITE, - - NUM_INTERFACE_COLORSCHEMES, - COLOR16 = 0x80, - INVALID_COLORSCHEME = 0xFF, -} interface_colorscheme_t; - #if INTERFACE_CLASSIC void c_interface_begin(int current_key); void c_interface_print(int x, int y, const interface_colorscheme_t cs, const char *s); @@ -66,7 +34,7 @@ bool interface_isShowing(void); #endif // Plot message into pixel buffer -void interface_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows); +void interface_plotMessage(PIXEL_TYPE *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows); #if INTERFACE_TOUCH typedef enum interface_device_t { diff --git a/src/test/testdisplay.c b/src/test/testdisplay.c index 1043876c..4dbc37c6 100644 --- a/src/test/testdisplay.c +++ b/src/test/testdisplay.c @@ -279,7 +279,7 @@ TEST test_lores_with_80col() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("DED166782E9C529B6D7DB2EEBFF5877AD49C4C1F"); } else { #if !CONFORMANT_TRACKS @@ -301,7 +301,7 @@ TEST test_lores_with_40col() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("270B44B639E062B1DC7C2EB2E561051130F5F790"); } else { ASSERT_SHA_OLD("D7DC78F5718B4CF8716614E79ADABCAB919FCE5D"); @@ -322,7 +322,7 @@ TEST test_lores_with_40col_2() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("A3D5F5DF7A2DF15DDDF7E82F83B756827CD142D3"); } else { WAIT_FOR_FB_SHA("D7CC29D2030230258FAFF3772C8F9AD2B318D190"); @@ -341,7 +341,7 @@ TEST test_lores_40colmix_normal() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("373799861AFCA845826C27571D2FFF7F1CB69BD6"); } else { #if !CONFORMANT_TRACKS @@ -363,7 +363,7 @@ TEST test_lores_40colmix_inverse() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("F9F4757BB751AD47D975D45DC75B3C93C9F2C6C8"); } else { ASSERT_SHA_OLD("5256E8B96CB04F48324B587ECCCF8A435077B5DE"); @@ -384,7 +384,7 @@ TEST test_lores_80colmix_normal() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("DD2A3A05EA38652A86D144FFB5BD98CB24A82FF6"); } else { ASSERT_SHA_OLD("9D5D5382B0A18A71DC135CAD51BEA2665ADB5FB2"); @@ -405,7 +405,7 @@ TEST test_lores_80colmix_inverse() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("0D51D6A375820FE36E4D95127F0E7A8F71495F4A"); } else { ASSERT_SHA_OLD("7936E87BE1F920AACD43268DB288746528E89959"); @@ -433,7 +433,7 @@ TEST test_hires_with_80col() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA(MOIRE_SHA_BW); } else { ASSERT_SHA(MOIRE_SHA); @@ -451,7 +451,7 @@ TEST test_hires_with_40col() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA(MOIRE_SHA_BW); } else { ASSERT_SHA(MOIRE_SHA); @@ -469,7 +469,7 @@ TEST test_hires_with_40col_page2() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA(MOIRE_SHA_BW); } else { WAIT_FOR_FB_SHA(MOIRE_SHA); @@ -487,7 +487,7 @@ TEST test_hires_40colmix_normal() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA("9611721C0F70C5F1FE0172534EC15B977CB099D4"); } else { ASSERT_SHA("37F41F74EB23F8812498F732E6DA34A0EBC4D68A"); @@ -505,7 +505,7 @@ TEST test_hires_40colmix_inverse() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA("5CFA5789735AD09FAB8CC7B6EE44CE22CF64A70D"); } else { ASSERT_SHA("253D1823F5DAC0300B46B3D49C04CD59CC70076F"); @@ -523,7 +523,7 @@ TEST test_hires_80colmix_normal() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("4069102016E4E6AA860A32C6BAC5E4A6C6A45B72"); } else { WAIT_FOR_FB_SHA("8D02F9A7CFC7A7E6D836B01862389F55E877E4E6"); @@ -541,7 +541,7 @@ TEST test_hires_80colmix_inverse() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { WAIT_FOR_FB_SHA("C3C19FB3258E7A58F81BC3DC51C2AEDFFC836285"); } else { #if !CONFORMANT_TRACKS @@ -566,7 +566,7 @@ TEST test_80col_lores() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA("D8238DC3ACC1A0E191CEC06F505A159993C2EBFA"); } else { ASSERT_SHA("5BFF6721FB90B3A6AF88D9021A013C007C4AF23A"); @@ -584,7 +584,7 @@ TEST test_80col_lores_2() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA("5B99DE77F81AD8718FCFB735215A37F7B5ED5DE7"); } else { ASSERT_SHA("98BB7C04854594D9E709302EF29905D2A89F1D34"); @@ -602,7 +602,7 @@ TEST test_80col_hires() { ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED); - if (test_color_mode == COLOR_MODE_BW) { + if (test_color_mode == COLOR_MODE_MONO) { ASSERT_SHA("647F3A377513486121C7609E3F53E97DC6FC456D"); } else { ASSERT_SHA("919EBCBABEA57E932F84E9864B2C35F57F8909B4"); @@ -626,8 +626,8 @@ GREATEST_SUITE(test_suite_display) { static parms_t parmsArray[] = { { COLOR_MODE_COLOR, true }, { COLOR_MODE_COLOR, false }, - { COLOR_MODE_BW, true }, - { COLOR_MODE_BW, false }, + { COLOR_MODE_MONO, true }, + { COLOR_MODE_MONO, false }, }; unsigned int count = sizeof(parmsArray)/sizeof(parmsArray[0]); @@ -755,15 +755,15 @@ GREATEST_SUITE(test_suite_display) { // double-lo/hi RUN_TEST(test_80col_lores); - test_color_mode = COLOR_MODE_BW; + test_color_mode = COLOR_MODE_MONO; RUN_TEST(test_80col_lores); RUN_TEST(test_80col_lores_2); - test_color_mode = COLOR_MODE_BW; + test_color_mode = COLOR_MODE_MONO; RUN_TEST(test_80col_lores_2); RUN_TEST(test_80col_hires); - test_color_mode = COLOR_MODE_BW; + test_color_mode = COLOR_MODE_MONO; RUN_TEST(test_80col_hires); // ... diff --git a/src/video/glalert.c b/src/video/glalert.c index fdd0c0a9..4bd3336e 100644 --- a/src/video/glalert.c +++ b/src/video/glalert.c @@ -92,11 +92,8 @@ static void _alertToModel(char *message, unsigned int messageCols, unsigned int hudElement->tpl = message; hudElement->pixWidth = fbWidth; hudElement->pixHeight = fbHeight; - hudElement->pixels = MALLOC(fbWidth * fbHeight); - if (!hudElement->pixels) { - LOG("OOPS cannot create animation message intermediate framebuffer!"); - break; - } + hudElement->pixels = MALLOC(fbWidth * fbHeight * PIXEL_STRIDE); + assert(hudElement->pixels); glhud_setupDefault(messageModel); if (1) { diff --git a/src/video/glhudmodel.c b/src/video/glhudmodel.c index 26b35984..bb3aa0e2 100644 --- a/src/video/glhudmodel.c +++ b/src/video/glhudmodel.c @@ -134,9 +134,8 @@ void *glhud_createDefault(void) { void *glhud_createCustom(unsigned int sizeofModel) { assert(sizeof(GLModelHUDElement) <= sizeofModel); GLModelHUDElement *hudElement = (GLModelHUDElement *)CALLOC(sizeofModel, 1); - if (hudElement) { - hudElement->glyphMultiplier = 1; - } + assert(hudElement); + hudElement->glyphMultiplier = 1; return hudElement; } @@ -150,7 +149,7 @@ void glhud_setupDefault(GLModel *parent) { char *submenu = (char *)(hudElement->tpl); const unsigned int cols = hudElement->tplWidth; const unsigned int rows = hudElement->tplHeight; - uint8_t *fb = hudElement->pixels; + PIXEL_TYPE *fb = hudElement->pixels; // render template into indexed fb interface_plotMessage(fb, hudElement->colorScheme, submenu, cols, rows); @@ -173,14 +172,11 @@ void glhud_setupDefault(GLModel *parent) { #endif for (unsigned int i=0; iblackIsTransparent) { // black remains transparent } else { - rgba |= ((PIXEL_TYPE)MAX_SATURATION << SHIFT_A); + rgba |= ((PIXEL_TYPE)MAX_SATURATION << SHIFT_A); } // scale glyph data 1x, 2x, ... @@ -208,10 +204,7 @@ void glhud_setupDefault(GLModel *parent) { #endif for (unsigned int i=0; itplHeight = AXIS_TEMPLATE_ROWS; memcpy(hudElement->tpl, axisTemplate, size); - hudElement->pixels = CALLOC(AXIS_FB_WIDTH * AXIS_FB_HEIGHT, 1); + hudElement->pixels = CALLOC(AXIS_FB_WIDTH * AXIS_FB_HEIGHT * PIXEL_STRIDE, 1); hudElement->pixWidth = AXIS_FB_WIDTH; hudElement->pixHeight = AXIS_FB_HEIGHT; } @@ -347,7 +347,7 @@ static void _setup_button_hud(GLModel *parent) { hudElement->tplHeight = BUTTON_TEMPLATE_ROWS; memcpy(hudElement->tpl, buttonTemplate, size); - hudElement->pixels = CALLOC(BUTTON_FB_WIDTH * BUTTON_FB_HEIGHT, 1); + hudElement->pixels = CALLOC(BUTTON_FB_WIDTH * BUTTON_FB_HEIGHT * PIXEL_STRIDE, 1); hudElement->pixWidth = BUTTON_FB_WIDTH; hudElement->pixHeight = BUTTON_FB_HEIGHT; } diff --git a/src/video/gltouchkbd.c b/src/video/gltouchkbd.c index 9c9a60bd..c17b7cbf 100644 --- a/src/video/gltouchkbd.c +++ b/src/video/gltouchkbd.c @@ -511,7 +511,7 @@ static void *_create_touchkbd_hud(GLModel *parent) { const unsigned int size = sizeof(kbdTemplateUCase/* assuming all the same dimensions */); hudKeyboard->tpl = MALLOC(size); - hudKeyboard->pixels = MALLOC(KBD_FB_WIDTH * KBD_FB_HEIGHT); + hudKeyboard->pixels = MALLOC(KBD_FB_WIDTH * KBD_FB_HEIGHT * PIXEL_STRIDE); uint8_t *template = NULL; long lVal = 0; diff --git a/src/video/gltouchmenu.c b/src/video/gltouchmenu.c index f6cc9f4e..7fa309d3 100644 --- a/src/video/gltouchmenu.c +++ b/src/video/gltouchmenu.c @@ -390,7 +390,7 @@ static void *_create_touchmenu_hud(GLModel *parent) { const unsigned int size = sizeof(topMenuTemplate); hudMenu->tpl = CALLOC(size, 1); - hudMenu->pixels = CALLOC(MENU_FB_WIDTH * MENU_FB_HEIGHT, 1); + hudMenu->pixels = CALLOC(MENU_FB_WIDTH * MENU_FB_HEIGHT * PIXEL_STRIDE, 1); _present_menu(parent); diff --git a/src/video/glvideo.c b/src/video/glvideo.c index a238dac7..521508a1 100644 --- a/src/video/glvideo.c +++ b/src/video/glvideo.c @@ -316,21 +316,10 @@ static void glvideo_render(void) { #endif unsigned long wasDirty = video_clearDirty(FB_DIRTY_FLAG); - char *pixels = (char *)crtModel->texPixels; + GLvoid *pixels = crtModel->texPixels; if (wasDirty) { - uint8_t *fb = display_getCurrentFramebuffer(); - SCOPE_TRACE_VIDEO("pixel convert"); - // Update texture from indexed-color Apple //e internal framebuffer - unsigned int count = SCANWIDTH * SCANHEIGHT; - for (unsigned int i=0, j=0; itexPixels, /*src:*/fb, (SCANWIDTH*SCANHEIGHT*sizeof(PIXEL_TYPE))); } glActiveTexture(TEXTURE_ACTIVE_FRAMEBUFFER); @@ -339,7 +328,7 @@ static void glvideo_render(void) { if (wasDirty) { SCOPE_TRACE_VIDEO("glvideo texImage2D"); _HACKAROUND_GLTEXIMAGE2D_PRE(TEXTURE_ACTIVE_FRAMEBUFFER, crtModel->textureName); - glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, SCANWIDTH, SCANHEIGHT, /*border*/0, TEX_FORMAT, TEX_TYPE, (GLvoid *)&pixels[0]); + glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, SCANWIDTH, SCANHEIGHT, /*border*/0, TEX_FORMAT, TEX_TYPE, pixels); } // Bind our vertex array object diff --git a/src/video/ntsc.c b/src/video/ntsc.c new file mode 100644 index 00000000..de817a6e --- /dev/null +++ b/src/video/ntsc.c @@ -0,0 +1,435 @@ +/* + * Apple // emulator for *ix + * + * This software package is subject to the GNU General Public License + * version 3 or later (your choice) as published by the Free Software + * Foundation. + * + * Copyright 2018 Aaron Culliney + * + */ + +// NOTE: routines here are copied from William (Sheldon) Simms's NTSC implementation in AppleWin +// TODO: implement these in a shader? + +/* +AppleWin : An Apple //e emulator for Windows + +Copyright (C) 2010-2011, William S Simms +Copyright (C) 2014-2016, Michael Pohoreski, Tom Charlesworth + +AppleWin 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 2 of the License, or +(at your option) any later version. + +AppleWin 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 AppleWin; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "common.h" +#include "video/ntsc.h" + +#define NTSC_NUM_PHASES 4 +#define NTSC_NUM_SEQUENCES 4096 + +typedef void (*fb_plotter_fn)(color_mode_t, uint16_t, PIXEL_TYPE *); +typedef PIXEL_TYPE (*fb_scanline_fn)(PIXEL_TYPE color0, PIXEL_TYPE color2); + +static uint8_t half_scanlines = false; +static fb_plotter_fn pixelPlotter[NUM_COLOROPTS] = { NULL }; +static fb_scanline_fn getHalfColor[NUM_COLOROPTS][2] = { NULL }; + +static unsigned int ntsc_color_phase = 0; +unsigned int ntsc_signal_bits = 0; + +static PIXEL_TYPE monoPixelsMonitor [NTSC_NUM_SEQUENCES]; +static PIXEL_TYPE monoPixelsTV [NTSC_NUM_SEQUENCES]; + +static PIXEL_TYPE colorPixelsMonitor[NTSC_NUM_PHASES][NTSC_NUM_SEQUENCES]; +static PIXEL_TYPE colorPixelsTV [NTSC_NUM_PHASES][NTSC_NUM_SEQUENCES]; + +// ---------------------------------------------------------------------------- +// TV pixel updates + +static PIXEL_TYPE halfScanlineTV(PIXEL_TYPE color0, PIXEL_TYPE color2) { + PIXEL_TYPE color1; + + int r = (color0 >> SHIFT_R) & 0xFF; + int g = (color0 >> SHIFT_G) & 0xFF; + int b = (color0 >> SHIFT_B) & 0xFF; + uint32_t color2_prime = (color2 & 0xFCFCFCFC) >> 2; + r -= (color2_prime >> SHIFT_R) & 0xFF; if (r<0) { r=0; } // clamp to 0 on underflow + g -= (color2_prime >> SHIFT_G) & 0xFF; if (g<0) { g=0; } // clamp to 0 on underflow + b -= (color2_prime >> SHIFT_B) & 0xFF; if (b<0) { b=0; } // clamp to 0 on underflow + color1 = (r<> 1) + ((color2 & 0xFEFEFEFE) >> 1); // 50% Blend + return color1 | (0xFF<> 2) | (0xFF << SHIFT_A); + return color1; +} + +static PIXEL_TYPE doubleScanlineMonitor(PIXEL_TYPE color0, PIXEL_TYPE color2) { + (void)color2; + return color0; +} + +static void updateFBMonoMonitor(color_mode_t mode, uint16_t signal, PIXEL_TYPE *fb_ptr) { + updateFBCommon(mode, signal, fb_ptr, monoPixelsMonitor); +} + +static void updateFBColorMonitor(color_mode_t mode, uint16_t signal, PIXEL_TYPE *fb_ptr) { + updateFBCommon(mode, signal, fb_ptr, colorPixelsMonitor[ntsc_color_phase]); +} + +// ---------------------------------------------------------------------------- + +void ntsc_plotBits(color_mode_t mode, uint16_t bits14, PIXEL_TYPE *fb_ptr) { + assert(!(mode == COLOR_MODE_COLOR || mode == COLOR_MODE_INTERP)); + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); bits14 >>= 1; ++fb_ptr; + pixelPlotter[mode](mode, bits14 & 1, fb_ptr); ++fb_ptr; +} + +void ntsc_flushScanline(void) { + ntsc_color_phase = 0; + ntsc_signal_bits = 0; +} + +//---------------------------------------------------------------------------- +// pixel color creation + +#define CHROMA_ZEROS 2 +#define CHROMA_POLES 2 +#define CHROMA_GAIN 7.438011255f // Should this be 7.15909 MHz ? +#define CHROMA_0 -0.7318893645f +#define CHROMA_1 1.2336442711f + +#define LUMA_ZEROS 2 +#define LUMA_POLES 2 +#define LUMA_GAIN 13.71331570f // Should this be 14.318180 MHz ? +#define LUMA_0 -0.3961075449f +#define LUMA_1 1.1044202472f + +#define SIGNAL_ZEROS 2 +#define SIGNAL_POLES 2 +#define SIGNAL_GAIN 7.614490548f // Should this be 7.15909 MHz ? +#define SIGNAL_0 -0.2718798058f +#define SIGNAL_1 0.7465656072f + +#if !defined(M_PI) +# error M_PI not defined on this platform? +# define M_PI 3.1415926535898f +#endif + +#define DEG_TO_RAD(x) (M_PI*(x)/180.f) +#define RAD_45 (M_PI*0.25f) +#define RAD_90 (M_PI*0.5f) +#define RAD_360 (M_PI*2.f) + +#define CYCLESTART (DEG_TO_RAD(45)) + +#define clampZeroOne(x) ({ \ + typeof(x) _rc = x; \ + if (_rc < 0.f) { _rc = 0.f; } \ + if (_rc > 1.f) { _rc = 1.f; } \ + _rc; \ +}) + +// What filter is this ?? +// Filter Order: 2 -> poles for low pass +static double initFilterChroma(double z) { + static double x[CHROMA_ZEROS + 1] = {0,0,0}; + static double y[CHROMA_POLES + 1] = {0,0,0}; + + x[0] = x[1]; x[1] = x[2]; x[2] = z / CHROMA_GAIN; + y[0] = y[1]; y[1] = y[2]; y[2] = -x[0] + x[2] + (CHROMA_0*y[0]) + (CHROMA_1*y[1]); // inverted x[0] + + return y[2]; +} + +// Butterworth Lowpass digital filter +// Filter Order: 2 -> poles for low pass +static double initFilterLuma0(double z) { + static double x[LUMA_ZEROS + 1] = { 0,0,0 }; + static double y[LUMA_POLES + 1] = { 0,0,0 }; + + x[0] = x[1]; x[1] = x[2]; x[2] = z / LUMA_GAIN; + y[0] = y[1]; y[1] = y[2]; y[2] = x[0] + x[2] + (2.f*x[1]) + (LUMA_0*y[0]) + (LUMA_1*y[1]); + + return y[2]; +} + +// Butterworth Lowpass digital filter +// Filter Order: 2 -> poles for low pass +static double initFilterLuma1(double z) { + static double x[LUMA_ZEROS + 1] = { 0,0,0}; + static double y[LUMA_POLES + 1] = { 0,0,0}; + + x[0] = x[1]; x[1] = x[2]; x[2] = z / LUMA_GAIN; + y[0] = y[1]; y[1] = y[2]; y[2] = x[0] + x[2] + (2.f*x[1]) + (LUMA_0*y[0]) + (LUMA_1*y[1]); + + return y[2]; +} + +// Butterworth Lowpass digital filter +// Filter Order: 2 -> poles for low pass +static double initFilterSignal(double z) { + static double x[SIGNAL_ZEROS + 1] = { 0,0,0 }; + static double y[SIGNAL_POLES + 1] = { 0,0,0 }; + + x[0] = x[1]; x[1] = x[2]; x[2] = z / SIGNAL_GAIN; + y[0] = y[1]; y[1] = y[2]; y[2] = x[0] + x[2] + (2.f*x[1]) + (SIGNAL_0*y[0]) + (SIGNAL_1*y[1]); + + return y[2]; +} + +// Build the 4 phase chroma lookup table +// The YI'Q' colors are hard-coded +static void initChromaPhaseTables(color_mode_t color_mode, mono_mode_t mono_mode) { + int phase,s,t,n; + double z = 0; + double y0,y1,c,i,q = 0; + double phi,zz; + float brightness; + double r64,g64,b64; + float r32,g32,b32; + + for (phase = 0; phase < 4; phase++) { + phi = (phase * RAD_90) + CYCLESTART; + for (s = 0; s < NTSC_NUM_SEQUENCES; s++) { + t = s; + y0 = y1 = c = i = q = 0.0; + + for (n = 0; n < 12; n++) { + z = (double)(0 != (t & 0x800)); + t = t << 1; + + for (unsigned int k = 0; k < 2; k++) { + //z = z * 1.25; + zz = initFilterSignal(z); + c = initFilterChroma(zz); // "Mostly" correct _if_ CYCLESTART = PI/4 = 45 degrees + y0 = initFilterLuma0 (zz); + y1 = initFilterLuma1 (zz - c); + + c = c * 2.f; + i = i + (c * cos(phi) - i) / 8.f; + q = q + (c * sin(phi) - q) / 8.f; + + phi += RAD_45; + } // k + } // samples + + brightness = clampZeroOne((float)z); + if (color_mode == COLOR_MODE_MONO && mono_mode == MONO_MODE_GREEN) { + monoPixelsMonitor[s] = (((uint8_t)(brightness * 0xFF)) << SHIFT_G) | (0x00 << SHIFT_R) | (0x00 << SHIFT_B) | (0xFF << SHIFT_A); + brightness = clampZeroOne((float)y1); + monoPixelsTV[s] = (((uint8_t)(brightness * 0xFF)) << SHIFT_G) | (0x00 << SHIFT_R) | (0x00 << SHIFT_B) | (0xFF << SHIFT_A); + } else { + monoPixelsMonitor[s] = (((uint8_t)(brightness * 0xFF)) << SHIFT_R) | + (((uint8_t)(brightness * 0xFF)) << SHIFT_G) | + (((uint8_t)(brightness * 0xFF)) << SHIFT_B) | + (0xFF << SHIFT_A); + + brightness = clampZeroOne((float)y1); + monoPixelsTV[s] = (((uint8_t)(brightness * 0xFF)) << SHIFT_R) | + (((uint8_t)(brightness * 0xFF)) << SHIFT_G) | + (((uint8_t)(brightness * 0xFF)) << SHIFT_B) | + (0xFF << SHIFT_A); + } + + /* + YI'V' to RGB + [r g b] = [y i v][ 1 1 1 ] + [0.956 -0.272 -1.105] + [0.621 -0.647 1.702] + [r] [1 0.956 0.621][y] + [g] = [1 -0.272 -0.647][i] + [b] [1 -1.105 1.702][v] + */ + #define I_TO_R 0.956f + #define I_TO_G -0.272f + #define I_TO_B -1.105f + + #define Q_TO_R 0.621f + #define Q_TO_G -0.647f + #define Q_TO_B 1.702f + + r64 = y0 + (I_TO_R * i) + (Q_TO_R * q); + g64 = y0 + (I_TO_G * i) + (Q_TO_G * q); + b64 = y0 + (I_TO_B * i) + (Q_TO_B * q); + + r32 = clampZeroOne((float)r64); + g32 = clampZeroOne((float)g64); + b32 = clampZeroOne((float)b64); + + int color = s & 15; + + // NTSC_REMOVE_WHITE_RINGING + if (color == IDX_WHITE) { + r32 = 1; + g32 = 1; + b32 = 1; + } + + // NTSC_REMOVE_BLACK_GHOSTING + if (color == IDX_BLACK) { + r32 = 0; + g32 = 0; + b32 = 0; + } + + // NTSC_REMOVE_GRAY_CHROMA + if (color == IDX_DARKGREY) { + const float g = (float) 0x83 / (float) 0xFF; + r32 = g; + g32 = g; + b32 = g; + } + + if (color == IDX_LIGHTGREY) { // Gray2 & Gray1 + const float g = (float) 0x78 / (float) 0xFF; + r32 = g; + g32 = g; + b32 = g; + } + + colorPixelsMonitor[phase][s] = (((uint8_t)(r32 * 255)) << SHIFT_R) | + (((uint8_t)(g32 * 255)) << SHIFT_G) | + (((uint8_t)(b32 * 255)) << SHIFT_B) | + (0xFF << SHIFT_A); + + r64 = y1 + (I_TO_R * i) + (Q_TO_R * q); + g64 = y1 + (I_TO_G * i) + (Q_TO_G * q); + b64 = y1 + (I_TO_B * i) + (Q_TO_B * q); + + r32 = clampZeroOne((float)r64); + g32 = clampZeroOne((float)g64); + b32 = clampZeroOne((float)b64); + + // NTSC_REMOVE_WHITE_RINGING + if (color == IDX_WHITE) { + r32 = 1; + g32 = 1; + b32 = 1; + } + + // NTSC_REMOVE_BLACK_GHOSTING + if (color == IDX_BLACK) { + r32 = 0; + g32 = 0; + b32 = 0; + } + + colorPixelsTV[phase][s] = (((uint8_t)(r32 * 255)) << SHIFT_R) | + (((uint8_t)(g32 * 255)) << SHIFT_G) | + (((uint8_t)(b32 * 255)) << SHIFT_B) | + (0xFF << SHIFT_A); + } + } +} + +//---------------------------------------------------------------------------- + +static void ntsc_prefsChanged(const char *domain) { + + long lVal = 0; + color_mode_t color_mode = prefs_parseLongValue(domain, PREF_COLOR_MODE, &lVal, /*base:*/10) ? getColorMode(lVal) : COLOR_MODE_DEFAULT; + + lVal = MONO_MODE_BW; + mono_mode_t mono_mode = prefs_parseLongValue(domain, PREF_MONO_MODE, &lVal, /*base:*/10) ? getMonoMode(lVal) : MONO_MODE_DEFAULT; + + bool bVal = false; + half_scanlines = prefs_parseBoolValue(domain, PREF_SHOW_HALF_SCANLINES, &bVal) ? (bVal ? 1 : 0) : 0; + + initChromaPhaseTables(color_mode, mono_mode); +} + +static void _init_ntsc(void) { + LOG("Initializing NTSC renderer"); + initChromaPhaseTables(COLOR_MODE_DEFAULT, MONO_MODE_DEFAULT); + + pixelPlotter[COLOR_MODE_MONO] = updateFBMonoMonitor; + pixelPlotter[COLOR_MODE_COLOR_MONITOR] = updateFBColorMonitor; + pixelPlotter[COLOR_MODE_MONO_TV] = updateFBMonoTV; + pixelPlotter[COLOR_MODE_COLOR_TV] = updateFBColorTV; + + getHalfColor[COLOR_MODE_MONO][0] = doubleScanlineMonitor; + getHalfColor[COLOR_MODE_MONO][1] = halfScanlineMonitor; + getHalfColor[COLOR_MODE_COLOR_MONITOR][0] = doubleScanlineMonitor; + getHalfColor[COLOR_MODE_COLOR_MONITOR][1] = halfScanlineMonitor; + getHalfColor[COLOR_MODE_MONO_TV][0] = doubleScanlineTV; + getHalfColor[COLOR_MODE_MONO_TV][1] = halfScanlineTV; + getHalfColor[COLOR_MODE_COLOR_TV][0] = doubleScanlineTV; + getHalfColor[COLOR_MODE_COLOR_TV][1] = halfScanlineTV; + + prefs_registerListener(PREF_DOMAIN_VIDEO, &ntsc_prefsChanged); +} + +static __attribute__((constructor)) void __init_ntsc(void) { + emulator_registerStartupCallback(CTOR_PRIORITY_LATE, &_init_ntsc); +} diff --git a/src/video/ntsc.h b/src/video/ntsc.h new file mode 100644 index 00000000..b458a2fe --- /dev/null +++ b/src/video/ntsc.h @@ -0,0 +1,22 @@ +/* + * Apple // emulator for *ix + * + * This software package is subject to the GNU General Public License + * version 3 or later (your choice) as published by the Free Software + * Foundation. + * + * Copyright 2018 Aaron Culliney + * + */ + +#ifndef A2_NTSC_H +#define A2_NTSC_H + +#include "common.h" + +void ntsc_plotBits(color_mode_t mode, uint16_t bits, PIXEL_TYPE *fb_ptr); + +void ntsc_flushScanline(void); + +#endif /* A2_NTSC_H */ + diff --git a/src/video_util/glUtil.h b/src/video_util/glUtil.h index a673c93e..5721cc5e 100644 --- a/src/video_util/glUtil.h +++ b/src/video_util/glUtil.h @@ -48,22 +48,10 @@ #define TEX_FORMAT GL_RGBA #if USE_RGBA4444 -# define PIXEL_TYPE uint16_t -# define MAX_SATURATION 0xf -# define SHIFT_R 12 -# define SHIFT_G 8 -# define SHIFT_B 4 -# define SHIFT_A 0 # define TEX_FORMAT_INTERNAL GL_RGBA4 # define TEX_TYPE GL_UNSIGNED_SHORT_4_4_4_4 #else // assuming RGBA8888 ... -# define PIXEL_TYPE uint32_t -# define MAX_SATURATION 0xff -# define SHIFT_R 0 -# define SHIFT_G 8 -# define SHIFT_B 16 -# define SHIFT_A 24 # define TEX_FORMAT_INTERNAL TEX_FORMAT # define TEX_TYPE GL_UNSIGNED_BYTE #endif diff --git a/src/vm.c b/src/vm.c index 846028c4..5c885191 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1130,9 +1130,6 @@ static void _initialize_tables(void) { } // initialize first text & hires page, which are specially bank switched - // - // display_reset() below substitutes it's own hooks for all visible write locations affect the display, leaving our - // write-functions in place only at the `screen holes', hence the name. for (unsigned int i = 0x400; i < 0x800; i++) { cpu65_vmem_r[i] = iie_read_ram_text_page0; cpu65_vmem_w[i] = video__write_2e_text0; @@ -1143,6 +1140,34 @@ static void _initialize_tables(void) { cpu65_vmem_w[i] = video__write_2e_hgr0; } + // initialize text/lores & hires graphics routines + for (unsigned int y = 0; y < TEXT_ROWS; y++) { + uint16_t row = display_getVideoLineOffset(y); + for (unsigned int x = 0; x < TEXT_COLS; x++) { + unsigned int idx = row + x; + // text/lores pages + if (y < 20) { + cpu65_vmem_w[idx+0x400] = video__write_2e_text0; + cpu65_vmem_w[idx+0x800] = video__write_2e_text1; + } else { + cpu65_vmem_w[idx+0x400] = video__write_2e_text0_mixed; + cpu65_vmem_w[idx+0x800] = video__write_2e_text1_mixed; + } + + // hires/dhires pages + for (unsigned int i = 0; i < 8; i++) { + idx = row + (0x400*i) + x; + if (y < 20) { + cpu65_vmem_w[idx+0x2000] = video__write_2e_hgr0; + cpu65_vmem_w[idx+0x4000] = video__write_2e_hgr1; + } else { + cpu65_vmem_w[idx+0x2000] = video__write_2e_hgr0_mixed; + cpu65_vmem_w[idx+0x4000] = video__write_2e_hgr1_mixed; + } + } + } + } + // softswich rom for (unsigned int i = 0xC000; i < 0xC100; i++) { cpu65_vmem_r[i] = read_unmapped_softswitch; @@ -1297,8 +1322,6 @@ static void _initialize_tables(void) { cpu65_vmem_r[i] = iie_read_slot_expansion; } - display_reset(); - // Peripheral card slot initializations ... // HACK TODO FIXME : this needs to be tied to the UI/configuration system (once we have more/conflicting options)