Refactor video/display rendering

- Import NTSC video display modes
    - Migrate to using full-color framebuffers
    - Mac and Android builds somewhat working
    - TODO : likely breaks display testing
This commit is contained in:
Aaron Culliney 2018-11-04 14:07:30 -08:00
parent 8757cb2a06
commit 227098ec52
28 changed files with 1705 additions and 1299 deletions

View File

@ -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;

View File

@ -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());
}
});
}

View File

@ -162,8 +162,8 @@
<string name="touch_menu_enable_summary">Aktiviere Softmenü Knöpfe in den oberen Ecken des Bildschirms</string>
<string name="video_configure">Video-Konfiguration…</string>
<string name="video_configure_summary">Farbeinstellungen</string>
<string name="color_configure">Configure color</string>
<string name="color_configure_summary">Color mode</string>
<string name="color_configure">Display mode</string>
<string name="color_configure_summary">Video display emulation mode</string>
<string name="joystick_azimuth_visible">Show joystick/keypad heading</string>
<string name="joystick_azimuth_visible_summary">Shows current axis direction and magnitude</string>
<string name="mode_landscape">Landscape</string>
@ -188,5 +188,13 @@
<string name="color_green_on_black">Green on black</string>
<string name="color_blue_on_black">Blue on black</string>
<string name="color_white_on_black">White on black</string>
<string name="color_monitor">Color Monitor</string>
<string name="color_tv_mono">Monochrome TV</string>
<string name="color_tv">Color TV</string>
<string name="show_half_scanlines">Show half scanlines</string>
<string name="mono_configure">Monocolor mode</string>
<string name="mono_configure_summary">Configure monochrome color</string>
<string name="color_green">Green screen</string>
<string name="color_mono">Monochrome</string>
</resources>

View File

@ -160,8 +160,8 @@
<string name="touch_menu_enable_summary">Los botones del menú en la parte superior de la pantalla</string>
<string name="video_configure">Configurar el video…</string>
<string name="video_configure_summary">Ajustes de color</string>
<string name="color_configure">Configure color</string>
<string name="color_configure_summary">Color mode</string>
<string name="color_configure">Display mode</string>
<string name="color_configure_summary">Video display emulation mode</string>
<string name="joystick_azimuth_visible">Show joystick/keypad heading</string>
<string name="joystick_azimuth_visible_summary">Shows current axis direction and magnitude</string>
<string name="mode_landscape">Landscape</string>
@ -188,5 +188,13 @@
<string name="color_green_on_black">Green on black</string>
<string name="color_blue_on_black">Blue on black</string>
<string name="color_white_on_black">White on black</string>
<string name="color_monitor">Color Monitor</string>
<string name="color_tv_mono">Monochrome TV</string>
<string name="color_tv">Color TV</string>
<string name="show_half_scanlines">Show half scanlines</string>
<string name="mono_configure">Monocolor mode</string>
<string name="mono_configure_summary">Configure monochrome color</string>
<string name="color_green">Green screen</string>
<string name="color_mono">Monochrome</string>
</resources>

View File

@ -160,8 +160,8 @@
<string name="touch_menu_enable_summary">Activation soft des bouton du menu dans les coins en haut de l\'écran</string>
<string name="video_configure">Configuration de la vidéo…</string>
<string name="video_configure_summary">Configuration des couleurs</string>
<string name="color_configure">Configure color</string>
<string name="color_configure_summary">Color mode</string>
<string name="color_configure">Display mode</string>
<string name="color_configure_summary">Video display emulation mode</string>
<string name="joystick_azimuth_visible">Show joystick/keypad heading</string>
<string name="joystick_azimuth_visible_summary">Shows current axis direction and magnitude</string>
<string name="mode_landscape">Landscape</string>
@ -188,5 +188,13 @@
<string name="color_green_on_black">Green on black</string>
<string name="color_blue_on_black">Blue on black</string>
<string name="color_white_on_black">White on black</string>
<string name="color_monitor">Color Monitor</string>
<string name="color_tv_mono">Monochrome TV</string>
<string name="color_tv">Color TV</string>
<string name="show_half_scanlines">Show half scanlines</string>
<string name="mono_configure">Monocolor mode</string>
<string name="mono_configure_summary">Configure monochrome color</string>
<string name="color_green">Green screen</string>
<string name="color_mono">Monochrome</string>
</resources>

View File

@ -25,8 +25,8 @@
<string name="audio_latency">Audio latency</string>
<string name="audio_latency_summary">Audio latency in secs</string>
<string name="cancel">Cancel</string>
<string name="color_configure">Configure color</string>
<string name="color_configure_summary">Color mode</string>
<string name="color_configure">Display mode</string>
<string name="color_configure_summary">Video display emulation mode</string>
<string name="color_bw">Black/white</string>
<string name="color_color">Color</string>
<string name="color_interpolated">Interpolated color</string>
@ -188,5 +188,13 @@
<string name="color_green_on_black">Green on black</string>
<string name="color_blue_on_black">Blue on black</string>
<string name="color_white_on_black">White on black</string>
<string name="color_monitor">Color Monitor</string>
<string name="color_tv_mono">Monochrome TV</string>
<string name="color_tv">Color TV</string>
<string name="show_half_scanlines">Show half scanlines</string>
<string name="mono_configure">Monocolor mode</string>
<string name="mono_configure_summary">Configure monochrome color</string>
<string name="color_green">Green screen</string>
<string name="color_mono">Monochrome</string>
</resources>

View File

@ -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 \

View File

@ -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 = "<group>"; };
4A2636F819FDEDB700DBFB00 /* Apple2Mac.help */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Apple2Mac.help; sourceTree = "<group>"; };
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 = "<group>"; };
4A4B676B1DB47682005028A6 /* Apple2iOSTestUI-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Apple2iOSTestUI-Info.plist"; sourceTree = "<group>"; };
4A589F722157E72B0026A73A /* ntsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ntsc.c; sourceTree = "<group>"; };
4A609CB21D725D4C0066AF38 /* external-disks */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "external-disks"; path = "../external-disks"; sourceTree = "<group>"; };
4A7EDC911AE092680072E98A /* glhudmodel.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glhudmodel.c; sourceTree = "<group>"; };
4A7EDC921AE092680072E98A /* glnode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glnode.c; sourceTree = "<group>"; };
@ -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 */,

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -373,9 +374,10 @@ CA
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="-608" y="-1487"/>
</menu>
<window title="Apple2Mac" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371" userLabel="Window - Apple2Mac" customClass="EmulatorWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" unifiedTitleAndToolbar="YES"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="200" y="200" width="568" height="384"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
@ -428,7 +430,7 @@ CA
<toolbarItem reference="85b-WO-tks"/>
</defaultToolbarItems>
</toolbar>
<point key="canvasLocation" x="-308" y="-1288"/>
<point key="canvasLocation" x="-82" y="-1306"/>
</window>
<customObject id="494" userLabel="EmulatorGLView" customClass="EmulatorGLView"/>
<customObject id="M8b-ga-iOS" customClass="EmulatorWindowController" colorLabel="IBBuiltInLabel-Blue">
@ -472,6 +474,9 @@ CA
<buttonCell key="cell" type="push" title="Choose..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nVq-kA-8RS">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="chooseDriveA:" target="FHO-g2-V3A" id="aXp-cp-feH"/>
@ -613,19 +618,13 @@ CA
<action selector="startupDiskBChoiceChanged:" target="FHO-g2-V3A" id="tQS-5l-DDf"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="7ZU-H6-jQn">
<box verticalHuggingPriority="750" boxType="separator" id="7ZU-H6-jQn">
<rect key="frame" x="12" y="59" width="498" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="864-Ov-2tE">
<box horizontalHuggingPriority="750" boxType="separator" id="864-Ov-2tE">
<rect key="frame" x="259" y="70" width="5" height="217"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button verticalHuggingPriority="750" id="tLd-IJ-Kjl">
<rect key="frame" x="338" y="13" width="85" height="32"/>
@ -633,9 +632,6 @@ CA
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mSV-MT-MmA">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="disksOK:" target="FHO-g2-V3A" id="8qD-fL-VNb"/>
@ -643,7 +639,7 @@ DQ
</button>
</subviews>
</view>
<point key="canvasLocation" x="-139" y="-901"/>
<point key="canvasLocation" x="-530" y="-867"/>
</window>
<customObject id="FHO-g2-V3A" customClass="EmulatorDiskController">
<connections>
@ -656,6 +652,7 @@ DQ
<outlet property="diskInA" destination="cb9-Pc-Ggd" id="chv-2S-Y2R"/>
<outlet property="diskInB" destination="5oU-oN-vUH" id="8fb-s5-EKa"/>
<outlet property="disksWindow" destination="RAk-at-ZT4" id="4wu-vw-EhL"/>
<outlet property="okButton" destination="tLd-IJ-Kjl" id="dvn-gG-jzv"/>
<outlet property="startupLoadDiskA" destination="Ur3-rW-YJG" id="re3-gT-qj3"/>
<outlet property="startupLoadDiskB" destination="Ceo-uO-cRu" id="V0B-0f-YZu"/>
</connections>
@ -736,12 +733,9 @@ DQ
<action selector="peggedChoiceChanged:" target="mUW-Rh-bL1" id="K2H-Vc-15h"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="9U0-v9-wTm">
<box verticalHuggingPriority="750" boxType="separator" id="9U0-v9-wTm">
<rect key="frame" x="17" y="183" width="508" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<slider verticalHuggingPriority="750" id="y10-Rm-oDB">
<rect key="frame" x="15" y="115" width="404" height="27"/>
@ -780,7 +774,7 @@ DQ
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="No soundcard" imagePosition="left" alignment="left" enabled="NO" tag="1" inset="2" id="QsT-B1-n2t">
<buttonCell type="radio" title="No soundcard" imagePosition="left" alignment="left" enabled="NO" state="on" tag="1" inset="2" id="QsT-B1-n2t">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
@ -809,12 +803,12 @@ DQ
<popUpButton verticalHuggingPriority="750" id="1sF-py-jCs">
<rect key="frame" x="115" y="271" width="158" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="eVj-ax-48A">
<popUpButtonCell key="cell" type="push" title="Monochrome" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="PX0-X3-MxY" id="eVj-ax-48A">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="gbp-xU-CmX">
<items>
<menuItem title="Black/white" id="PX0-X3-MxY">
<menuItem title="Monochrome" state="on" id="PX0-X3-MxY">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Color" id="TRN-Jh-rFc">
@ -823,6 +817,15 @@ DQ
<menuItem title="Interpolated" id="RgR-Oe-oo5">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Color Monitor" id="60c-9H-Yr8">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Monochrome TV" id="lQA-Eh-TKs">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Color TV" id="b41-R4-5E3">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</popUpButtonCell>
@ -830,22 +833,8 @@ DQ
<action selector="colorChoiceChanged:" target="mUW-Rh-bL1" id="M58-28-Lmr"/>
</connections>
</popUpButton>
<slider verticalHuggingPriority="750" id="9FG-IJ-hYc">
<rect key="frame" x="115" y="229" width="404" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" enabled="NO" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="below" numberOfTickMarks="5" sliderType="linear" id="Cma-o2-8gh"/>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="RsU-77-Dkx">
<rect key="frame" x="15" y="241" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Graphic Effects :" id="foK-d1-Lrb">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="dak-eg-hHn">
<rect key="frame" x="15" y="277" width="96" height="17"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="dak-eg-hHn">
<rect key="frame" x="15" y="276" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Color :" id="eHo-1T-edz">
<font key="font" metaFont="system"/>
@ -853,6 +842,47 @@ DQ
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="rDh-8p-gf0">
<rect key="frame" x="15" y="245" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Monochrome :" id="6zV-cm-vLN">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" misplaced="YES" id="9bP-5g-S3a">
<rect key="frame" x="115" y="240" width="158" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Black/white" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="7Nj-L6-N5K" id="UkC-75-g1J">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="8h0-lQ-adO">
<items>
<menuItem title="Black/white" state="on" id="7Nj-L6-N5K">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Green Screen" id="8Mv-8d-hgV">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="monochromeColorChoiceChanged:" target="mUW-Rh-bL1" id="FIq-tn-pNv"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="750" misplaced="YES" id="0Av-wg-y41">
<rect key="frame" x="115" y="219" width="142" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Show half scanlines" bezelStyle="regularSquare" imagePosition="left" inset="2" id="q9I-GX-oEP">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="scanlinesChoiceChanged:" target="mUW-Rh-bL1" id="7Uf-Tg-ALt"/>
</connections>
</button>
</subviews>
</view>
</tabViewItem>
@ -861,12 +891,9 @@ DQ
<rect key="frame" x="10" y="33" width="534" height="298"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="Fkg-X3-0XG">
<box horizontalHuggingPriority="750" boxType="separator" id="Fkg-X3-0XG">
<rect key="frame" x="265" y="9" width="5" height="286"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<customView id="OWJ-x7-P0q" customClass="EmulatorJoystickCalibrationView">
<rect key="frame" x="276" y="39" width="256" height="256"/>
@ -1004,7 +1031,7 @@ DQ
</tabView>
</subviews>
</view>
<point key="canvasLocation" x="-286" y="-1308"/>
<point key="canvasLocation" x="-576" y="-1238"/>
</window>
<customObject id="mUW-Rh-bL1" customClass="EmulatorPrefsController">
<connections>
@ -1026,18 +1053,20 @@ DQ
<outlet property="joystickStepLabel" destination="ea3-X4-r6a" id="JKX-kB-tcM"/>
<outlet property="joystickStepper" destination="BrH-bm-tx3" id="PmM-yE-XNH"/>
<outlet property="joystickStepperLabel" destination="e2h-SS-aex" id="I6X-YN-AmA"/>
<outlet property="monochromeColorChoice" destination="9bP-5g-S3a" id="dzI-JR-diH"/>
<outlet property="scanlinesChoice" destination="0Av-wg-y41" id="U1V-Re-wVs"/>
<outlet property="soundCardChoice" destination="3d5-Z5-xDN" id="uHI-Ip-s2E"/>
<outlet property="window" destination="Mzv-VG-jce" id="86q-Ys-9Mt"/>
</connections>
</customObject>
</objects>
<resources>
<image name="CPU" width="32" height="32"/>
<image name="Disks" width="32" height="32"/>
<image name="Fullscreen" width="32" height="32"/>
<image name="CPU" width="64" height="64"/>
<image name="Disks" width="64" height="64"/>
<image name="Fullscreen" width="64" height="64"/>
<image name="NSUser" width="32" height="32"/>
<image name="Prefs" width="32" height="32"/>
<image name="Prefs" width="64" height="64"/>
<image name="Reboot" width="32" height="32"/>
<image name="Stop" width="32" height="32"/>
<image name="Stop" width="64" height="64"/>
</resources>
</document>

View File

@ -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];
}

View File

@ -11,11 +11,6 @@
#import <Cocoa/Cocoa.h>
#define kApple2PrefStartupDiskA @"kApple2PrefStartupDiskA"
#define kApple2PrefStartupDiskAProtected @"kApple2PrefStartupDiskAProtected"
#define kApple2PrefStartupDiskB @"kApple2PrefStartupDiskB"
#define kApple2PrefStartupDiskBProtected @"kApple2PrefStartupDiskBProtected"
@interface EmulatorPrefsController : NSWindowController
@end

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -12,16 +12,27 @@
#ifndef _DISPLAY_H_
#define _DISPLAY_H_
#include "common.h"
#if USE_RGBA4444
#error HACK FIXME TODO : 2018/09/23 currently untested
# 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 RGB_MASK 0xFFF0
#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 RGB_MASK 0xFFFFFF
#endif
/*
* Color structure
*/
typedef struct A2Color_s {
uint8_t red;
uint8_t green;
uint8_t blue;
} A2Color_s;
#define PIXEL_STRIDE (sizeof(PIXEL_TYPE))
/*
* Scanline data
@ -34,38 +45,102 @@ typedef struct scan_data_t {
unsigned int scanend; // 0 >= 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
---------------------------------- */

View File

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

View File

@ -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 {

View File

@ -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);
// ...

View File

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

View File

@ -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; i<fb_h; i++, texIdx+=texSubRowStride) {
for (unsigned int j=0; j<fb_w; j++, srcIdx++, texIdx+=dstPointStride) {
uint8_t value = *(fb + srcIdx);
PIXEL_TYPE rgba = (((PIXEL_TYPE)(colormap[value].red) << SHIFT_R) |
((PIXEL_TYPE)(colormap[value].green) << SHIFT_G) |
((PIXEL_TYPE)(colormap[value].blue) << SHIFT_B));
PIXEL_TYPE rgba = fb[srcIdx] & RGB_MASK;
if (rgba == 0 && hudElement->blackIsTransparent) {
// 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; i<fb_h; i++, texIdx+=texSubRowStride) {
for (unsigned int j=0; j<fb_w; j++, srcIdx++, texIdx+=dstPointStride) {
uint8_t value = *(fb + srcIdx);
PIXEL_TYPE rgb = (((PIXEL_TYPE)(colormap[value].red) << SHIFT_R) |
((PIXEL_TYPE)(colormap[value].green) << SHIFT_G) |
((PIXEL_TYPE)(colormap[value].blue) << SHIFT_B));
PIXEL_TYPE rgb = fb[srcIdx] & RGB_MASK;
unsigned int dstIdx = texIdx;

View File

@ -23,7 +23,7 @@
unsigned int tplWidth; /* template width */ \
unsigned int tplHeight; /* template height */ \
\
uint8_t *pixels; /* raw indexed data */ \
PIXEL_TYPE *pixels; /* raw indexed data */ \
unsigned int pixWidth; /* FB width -- this is the same as GLModel.texWidth if glyphMultiplier is 1 */ \
unsigned int pixHeight; /* FB height -- this is the same as GLModel.texHeight if glyphMultiplier is 1 */ \
unsigned int glyphMultiplier; \

View File

@ -296,7 +296,7 @@ static void _setup_axis_hud(GLModel *parent) {
hudElement->tplHeight = 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;
}

View File

@ -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;

View File

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

View File

@ -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; i<count; i++, j+=sizeof(PIXEL_TYPE)) {
uint8_t index = *(fb + i);
*( (PIXEL_TYPE*)(pixels + j) ) = (PIXEL_TYPE)(
((PIXEL_TYPE)(colormap[index].red) << SHIFT_R) |
((PIXEL_TYPE)(colormap[index].green) << SHIFT_G) |
((PIXEL_TYPE)(colormap[index].blue) << SHIFT_B) |
((PIXEL_TYPE)MAX_SATURATION << SHIFT_A)
);
}
void *fb = display_getCurrentFramebuffer();
memcpy(/*dest:*/crtModel->texPixels, /*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

435
src/video/ntsc.c Normal file
View File

@ -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<<SHIFT_R) | (g<<SHIFT_G) | (b<<SHIFT_B) | (0xFF<<SHIFT_A);
return color1;
}
static PIXEL_TYPE doubleScanlineTV(PIXEL_TYPE color0, PIXEL_TYPE color2) {
PIXEL_TYPE color1 = ((color0 & 0xFEFEFEFE) >> 1) + ((color2 & 0xFEFEFEFE) >> 1); // 50% Blend
return color1 | (0xFF<<SHIFT_A);
}
static void updateFBCommon(color_mode_t mode, uint16_t signal, PIXEL_TYPE *fb_ptr, PIXEL_TYPE *color_table) {
PIXEL_TYPE *fb_ptr0 = fb_ptr;
PIXEL_TYPE *fb_ptr1 = fb_ptr + (SCANWIDTH<<0);
PIXEL_TYPE *fb_ptr2 = fb_ptr + (SCANWIDTH<<1);
PIXEL_TYPE color0;
{
ntsc_signal_bits = ((ntsc_signal_bits << 1) | signal) & 0xFFF; // 12-bit ?
color0 = color_table[ntsc_signal_bits];
}
PIXEL_TYPE color2 = *fb_ptr2;
PIXEL_TYPE color1 = getHalfColor[mode][half_scanlines](color0, color2);
*fb_ptr0 = color0;
*fb_ptr1 = color1;
++ntsc_color_phase;
ntsc_color_phase &= 3;
}
static void updateFBMonoTV(color_mode_t mode, uint16_t signal, PIXEL_TYPE *fb_ptr) {
updateFBCommon(mode, signal, fb_ptr, monoPixelsTV);
}
static void updateFBColorTV(color_mode_t mode, uint16_t signal, PIXEL_TYPE *fb_ptr) {
updateFBCommon(mode, signal, fb_ptr, colorPixelsTV[ntsc_color_phase]);
}
// ----------------------------------------------------------------------------
// Monitor pixel updates
static PIXEL_TYPE halfScanlineMonitor(PIXEL_TYPE color0, PIXEL_TYPE color2) {
(void)color2;
PIXEL_TYPE color1 = ((color0 & 0xFCFCFCFC) >> 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);
}

22
src/video/ntsc.h Normal file
View File

@ -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 */

View File

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

View File

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