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
-
+
@@ -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)