commit 5e6e3f2808be9f5e4a8e9cd25eef7b6e36d0f1b1 Author: dgis Date: Mon Nov 12 22:31:04 2018 +0000 Add the non functional and the very first version of Emu48 for Android! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc6ccda --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +/local.properties +/.idea/caches/build_file_checksums.ser +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..3543521 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..d1d62bd --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,46 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.4.1) + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library( # Sets the name of the library. + native-lib + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + #src/main/cpp/native-lib.cpp + src/main/cpp/emu48-jni.c +) + +# Searches for a specified prebuilt library and stores the path as a +# variable. Because CMake includes system libraries in the search path by +# default, you only need to specify the name of the public NDK library +# you want to add. CMake verifies that the library exists before +# completing its build. + +find_library( # Sets the name of the path variable. + log-lib + + # Specifies the name of the NDK library that + # you want CMake to locate. + log) + +# Specifies libraries CMake should link to your target library. You +# can link multiple libraries, such as libraries you define in this +# build script, prebuilt third-party libraries, or system libraries. + +target_link_libraries( # Specifies the target library. + native-lib + + # Links the target library to the log library + # included in the NDK. + ${log-lib}) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..b18d4c5 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,39 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.regis.cosnier.emu48" + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags "" + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:design:28.0.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..6e7ffa9 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/regis/cosnier/emu48/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/regis/cosnier/emu48/ExampleInstrumentedTest.java new file mode 100644 index 0000000..e46de30 --- /dev/null +++ b/app/src/androidTest/java/com/regis/cosnier/emu48/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.regis.cosnier.emu48; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.regis.cosnier.emu48", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fc682a8 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/calculators/BEEP.48 b/app/src/main/assets/calculators/BEEP.48 new file mode 100644 index 0000000..5f2b210 --- /dev/null +++ b/app/src/main/assets/calculators/BEEP.48 @@ -0,0 +1,2 @@ +;01477:6300; disable 10 min auto off (internal, undocumented) +017A6:81B1; =makebeep diff --git a/app/src/main/assets/calculators/REAL48GX.BMP b/app/src/main/assets/calculators/REAL48GX.BMP new file mode 100644 index 0000000..dea927b Binary files /dev/null and b/app/src/main/assets/calculators/REAL48GX.BMP differ diff --git a/app/src/main/assets/calculators/REAL48SX.BMP b/app/src/main/assets/calculators/REAL48SX.BMP new file mode 100644 index 0000000..f91e214 Binary files /dev/null and b/app/src/main/assets/calculators/REAL48SX.BMP differ diff --git a/app/src/main/assets/calculators/ROM.48G b/app/src/main/assets/calculators/ROM.48G new file mode 100644 index 0000000..975ef1f Binary files /dev/null and b/app/src/main/assets/calculators/ROM.48G differ diff --git a/app/src/main/assets/calculators/ROM.48S b/app/src/main/assets/calculators/ROM.48S new file mode 100644 index 0000000..128f4a0 Binary files /dev/null and b/app/src/main/assets/calculators/ROM.48S differ diff --git a/app/src/main/assets/calculators/real48gx.kml b/app/src/main/assets/calculators/real48gx.kml new file mode 100644 index 0000000..c88fc25 --- /dev/null +++ b/app/src/main/assets/calculators/real48gx.kml @@ -0,0 +1,935 @@ +Global + Print "==========================================================" + Print "" + Print "This Real 48GX graphic is by Eric Rechlin." + Print "eric@hpcalc.org http://www.hpcalc.org" + Print "Graphic based on a scan by Hewlett-Packard." + Print "" + Print "Note that contrast support, a touch-sensitive" + Print "screen, and hidden NXT and PREV buttons to the" + Print "right and left of the menu are implemented." + Print "" + Print "==========================================================" + Title "Eric's Real 48GX for 800x600" + Author "Eric Rechlin" + Model "G" + Rom "ROM.48G" + Patch "BEEP.48" + Bitmap "REAL48GX.BMP" + Debug 0 +End + +Background + Offset 0 0 + Size 300 531 +End + +Lcd + Zoom 2 + Offset 19 24 + Color 0 112 157 114 + Color 1 112 157 114 + Color 2 104 145 106 + Color 3 94 131 95 + Color 4 85 119 86 + Color 5 76 106 77 + Color 6 66 92 67 + Color 8 57 79 58 + Color 9 48 67 49 + Color 10 38 53 38 + Color 11 29 40 29 + Color 12 20 28 20 + Color 13 10 13 10 + + Color 14 0 0 0 + + Color 15 0 0 0 + Color 16 0 0 0 + Color 17 0 0 0 + Color 18 0 0 0 + Color 19 0 0 0 + Color 20 0 0 0 + Color 21 0 0 0 + Color 22 0 0 0 + Color 23 0 0 0 + Color 24 0 0 0 + Color 25 0 0 0 + Color 26 0 0 0 + Color 27 0 0 0 + Color 28 0 0 0 + Color 29 0 0 0 + Color 30 0 0 0 + Color 31 0 0 0 + + Color 47 112 157 114 + Color 48 104 145 106 + Color 49 94 131 95 + Color 50 85 119 86 + Color 51 76 106 77 + Color 52 66 92 67 + Color 53 57 79 58 + Color 54 48 67 49 + Color 55 38 53 38 + Color 56 29 40 29 + Color 57 20 28 20 + Color 58 10 13 10 + Color 59 0 0 0 + Color 60 0 0 0 + Color 61 0 0 0 + Color 62 0 0 0 + Color 63 0 0 0 +End + +Annunciator 1 + Size 16 11 + Offset 32 11 + Down 0 531 +End + +Annunciator 2 + Size 16 11 + Offset 75 11 + Down 16 531 +End + +Annunciator 3 + Size 16 11 + Offset 121 11 + Down 32 531 +End + +Annunciator 4 + Size 16 11 + Offset 163 11 + Down 48 531 +End + +Annunciator 5 + Size 16 11 + Offset 207 11 + Down 64 531 +End + +Annunciator 6 + Size 16 11 + Offset 253 11 + Down 80 531 +End + +Button 11 + Type 5 + Size 38 26 + Offset 10 168 + OutIn 1 16 +End + +Button 12 + Type 5 + Size 38 26 + Offset 59 168 + OutIn 8 16 +End + +Button 13 + Type 5 + Size 38 26 + Offset 107 168 + OutIn 8 8 +End + +Button 14 + Type 5 + Size 38 26 + Offset 156 168 + OutIn 8 4 +End + +Button 15 + Type 5 + Size 38 26 + Offset 204 168 + OutIn 8 2 +End + +Button 16 + Type 5 + Size 38 26 + Offset 252 168 + OutIn 8 1 +End + +Button 21 + Type 5 + Size 38 30 + Offset 10 205 + OutIn 2 16 +End + +Button 22 + Type 5 + Size 38 30 + Offset 59 205 + OutIn 7 16 +End + +Button 23 + Type 5 + Size 38 30 + Offset 107 205 + OutIn 7 8 +End + +Button 24 + Type 5 + Size 38 30 + Offset 156 205 + OutIn 7 4 +End + +Button 25 + Type 5 + Size 38 30 + Offset 204 205 + OutIn 7 2 +End + +Button 26 + Type 5 + Size 38 30 + Offset 252 205 + OutIn 7 1 +End + +Button 31 + Type 5 + Size 38 30 + Offset 10 245 + OutIn 0 16 +End + +Button 32 + Type 5 + Size 38 30 + Offset 59 245 + OutIn 6 16 +End + +Button 33 + Type 5 + Size 38 30 + Offset 107 245 + OutIn 6 8 +End + +Button 34 + Type 5 + Size 38 30 + Offset 156 245 + OutIn 6 4 +End + +Button 35 + Type 5 + Size 38 30 + Offset 204 245 + OutIn 6 2 +End + +Button 36 + Type 5 + Size 38 30 + Offset 252 245 + OutIn 6 1 +End + +Button 41 + Type 5 + Size 38 30 + Offset 10 287 + OutIn 3 16 +End + +Button 42 + Type 5 + Size 38 30 + Offset 59 287 + OutIn 5 16 +End + +Button 43 + Type 5 + Size 38 30 + Offset 107 287 + OutIn 5 8 +End + +Button 44 + Type 5 + Size 38 30 + Offset 156 287 + OutIn 5 4 +End + +Button 45 + Type 5 + Size 38 30 + Offset 204 287 + OutIn 5 2 +End + +Button 46 + Type 5 + Size 38 30 + Offset 252 287 + OutIn 5 1 +End + +Button 51 + Type 5 + Size 87 30 + Offset 10 327 + OutIn 4 16 +End + +Button 52 + Type 5 + Size 38 30 + Offset 107 327 + OutIn 4 8 +End + +Button 53 + Type 5 + Size 38 30 + Offset 156 327 + OutIn 4 4 +End + +Button 54 + Type 5 + Size 38 30 + Offset 204 327 + OutIn 4 2 +End + +Button 55 + Type 5 + Size 38 30 + Offset 252 327 + OutIn 4 1 +End + +Button 61 + Type 5 + Size 38 30 + Offset 10 369 + OutIn 3 32 +End + +Button 62 + Type 5 + Size 48 28 + Offset 65 369 + OutIn 3 8 +End + +Button 63 + Type 5 + Size 48 28 + Offset 124 369 + OutIn 3 4 +End + +Button 64 + Type 5 + Size 48 28 + Offset 183 369 + OutIn 3 2 +End + +Button 65 + Type 5 + Size 48 28 + Offset 243 369 + OutIn 3 1 +End + +Button 71 + Type 5 + Size 38 30 + Offset 10 411 + OutIn 2 32 +End + +Button 72 + Type 5 + Size 48 28 + Offset 65 411 + OutIn 2 8 +End + +Button 73 + Type 5 + Size 48 28 + Offset 124 411 + OutIn 2 4 +End + +Button 74 + Type 5 + Size 48 28 + Offset 183 411 + OutIn 2 2 +End + +Button 75 + Type 5 + Size 48 28 + Offset 243 411 + OutIn 2 1 +End + +Button 81 + Type 5 + Size 38 30 + Offset 10 453 + OutIn 1 32 +End + +Button 82 + Type 5 + Size 48 28 + Offset 65 453 + OutIn 1 8 +End + +Button 83 + Type 5 + Size 48 28 + Offset 124 453 + OutIn 1 4 +End + +Button 84 + Type 5 + Size 48 28 + Offset 183 453 + OutIn 1 2 +End + +Button 85 + Type 5 + Size 48 28 + Offset 243 453 + OutIn 1 1 +End + +Button 91 + Type 5 + Size 38 30 + Offset 10 494 + OutIn 0 32768 +End + +Button 92 + Type 5 + Size 48 28 + Offset 65 494 + OutIn 0 8 +End + +Button 93 + Type 5 + Size 48 28 + Offset 124 494 + OutIn 0 4 +End + +Button 94 + Type 5 + Size 48 28 + Offset 183 494 + OutIn 0 2 +End + +Button 95 + Type 5 + Size 48 28 + Offset 243 494 + OutIn 0 1 +End + +Button 101 + Type 3 + Size 42 14 + Offset 19 138 + Virtual + OnDown + Press 11 + End + OnUp + Release 11 + End +End + +Button 102 + Type 3 + Size 42 14 + Offset 63 138 + Virtual + OnDown + Press 12 + End + OnUp + Release 12 + End +End + +Button 103 + Type 3 + Size 42 14 + Offset 107 138 + Virtual + OnDown + Press 13 + End + OnUp + Release 13 + End +End + +Button 104 + Type 3 + Size 42 14 + Offset 151 138 + Virtual + OnDown + Press 14 + End + OnUp + Release 14 + End +End + +Button 105 + Type 3 + Size 42 14 + Offset 195 138 + Virtual + OnDown + Press 15 + End + OnUp + Release 15 + End +End + +Button 106 + Type 3 + Size 42 14 + Offset 239 138 + Virtual + OnDown + Press 16 + End + OnUp + Release 16 + End +End + +Button 107 + Type 5 + Size 16 16 + Offset 0 137 + Virtual + OnDown + Press 71 + Press 26 + End + OnUp + Release 26 + Release 71 + End +End + +Button 108 + Type 5 + Size 16 16 + Offset 284 137 + Virtual + OnDown + Press 26 + End + OnUp + Release 26 + End +End + +Button 109 + Type 4 + Size 262 112 + Offset 19 24 + Down 19 24 + NoHold +End + +Scancode 8 + Map 8 55 +End + +Scancode 13 + Map 13 51 +End + +Scancode 16 + IfPressed 16 + SetFlag 0 + Else + ResetFlag 0 + End +End + +Scancode 17 + IfPressed 17 + SetFlag 1 + Else + ResetFlag 1 + End +End + +Scancode 27 + Map 27 91 +End + +Scancode 32 + Map 32 94 +End + +Scancode 37 + Map 37 34 +End + +Scancode 38 + Map 38 25 +End + +Scancode 39 + Map 39 36 +End + +Scancode 40 + Map 40 35 +End + +Scancode 45 + Map 45 71 +End + +Scancode 46 + Map 46 81 +End + +Scancode 48 + Map 48 92 +End + +Scancode 49 + IfFlag 0 + Map 49 71 + Map 49 54 + Else + Map 49 82 + End +End + +Scancode 50 + Map 50 83 +End + +Scancode 51 + IfFlag 0 + Map 51 81 + Map 51 65 + Else + Map 51 84 + End +End + +Scancode 52 + Map 52 72 +End + +Scancode 53 + Map 53 73 +End + +Scancode 54 + Map 54 74 +End + +Scancode 55 + Map 55 62 +End + +Scancode 56 + IfFlag 0 + Map 56 75 + Else + Map 56 63 + End +End + +Scancode 57 + IfFlag 0 + Map 57 71 + Map 57 65 + Else + Map 57 64 + End +End + +Scancode 65 + Map 65 11 +End + +Scancode 66 + Map 66 12 +End + +Scancode 67 + Map 67 13 +End + +Scancode 68 + Map 68 14 +End + +Scancode 69 + Map 69 15 +End + +Scancode 70 + Map 70 16 +End + +Scancode 71 + Map 71 21 +End + +Scancode 72 + Map 72 22 +End + +Scancode 73 + Map 73 23 +End + +Scancode 74 + Map 74 24 +End + +Scancode 75 + Map 75 25 +End + +Scancode 76 + Map 76 26 +End + +Scancode 77 + Map 77 31 +End + +Scancode 78 + IfFlag 1 + MenuItem 1 + Else + Map 78 32 + End +End + +Scancode 79 + Map 79 33 +End + +Scancode 80 + Map 80 34 +End + +Scancode 81 + Map 81 35 +End + +Scancode 82 + Map 82 36 +End + +Scancode 83 + Map 83 41 +End + +Scancode 84 + Map 84 42 +End + +Scancode 85 + Map 85 43 +End + +Scancode 86 + Map 86 44 +End + +Scancode 87 + Map 87 45 +End + +Scancode 88 + Map 88 46 +End + +Scancode 89 + Map 89 52 +End + +Scancode 90 + Map 90 53 +End + +Scancode 96 + Map 96 92 +End + +Scancode 97 + Map 97 82 +End + +Scancode 98 + Map 98 83 +End + +Scancode 99 + Map 99 84 +End + +Scancode 100 + Map 100 72 +End + +Scancode 101 + Map 101 73 +End + +Scancode 102 + Map 102 74 +End + +Scancode 103 + Map 103 62 +End + +Scancode 104 + Map 104 63 +End + +Scancode 105 + Map 105 64 +End + +Scancode 106 + Map 106 75 +End + +Scancode 107 + Map 107 95 +End + +Scancode 109 + Map 109 85 +End + +Scancode 110 + Map 110 93 +End + +Scancode 111 + Map 111 65 +End + +Scancode 144 + IfPressed 144 + NotFlag 3 + End +End + +Scancode 186 + IfFlag 0 + Map 186 81 + Map 186 95 + End +End + +Scancode 188 + IfFlag 0 + Map 188 71 + Else + Map 188 71 + Map 188 93 + End +End + +Scancode 190 + IfFlag 0 + Map 190 81 + Else + Map 190 93 + End +End + +Scancode 191 + IfFlag 0 + Map 191 71 + Map 191 55 + Else + Map 191 65 + End +End + +Scancode 192 + IfFlag 0 + IfPressed 192 + NotFlag 2 + IfFlag 2 + Press 61 + Else + Release 61 + End + End + Else + Map 192 61 + End +End + +Scancode 219 + IfFlag 0 + Map 219 71 + Map 219 95 + Else + Map 219 71 + Map 219 75 + End +End + +Scancode 220 + Map 220 54 +End + +Scancode 222 + IfFlag 0 + Map 222 81 + Map 222 85 + Else + Map 222 31 + End +End + diff --git a/app/src/main/assets/calculators/real48sx.kml b/app/src/main/assets/calculators/real48sx.kml new file mode 100644 index 0000000..1be5e4c --- /dev/null +++ b/app/src/main/assets/calculators/real48sx.kml @@ -0,0 +1,932 @@ +Global + Print "==========================================================" + Print "" + Print "This Real 48SX graphic is by Eric Rechlin." + Print "eric@hpcalc.org http://www.hpcalc.org" + Print "Graphic based on a scan by Hewlett-Packard." + Print "" + Print "Note that contrast support, a touch-sensitive" + Print "screen, and hidden NXT and PREV buttons to the" + Print "right and left of the menu are implemented." + Print "" + Print "==========================================================" + Title "Eric's Real 48SX for 800x600" + Author "Eric Rechlin" + Model "S" + Rom "ROM.48S" + Patch "BEEP.48" + Bitmap "REAL48SX.BMP" + Debug 0 +End + +Background + Offset 0 0 + Size 300 531 +End + +Lcd + Zoom 2 + Offset 19 24 + Color 0 112 157 114 + Color 1 76 106 77 + Color 2 66 92 67 + Color 3 57 79 58 + Color 4 48 67 49 + Color 5 38 53 38 + Color 6 29 40 29 + Color 7 20 28 20 + Color 8 10 13 10 + + Color 9 0 0 0 + + Color 10 0 0 0 + Color 11 0 0 0 + Color 12 0 0 0 + Color 13 0 0 0 + Color 14 0 0 0 + Color 15 0 0 0 + Color 16 0 0 0 + Color 17 0 0 0 + Color 18 0 0 0 + Color 19 0 0 0 + Color 20 0 0 0 + Color 21 0 0 0 + Color 22 0 0 0 + Color 23 0 0 0 + Color 24 0 0 0 + Color 25 0 0 0 + Color 26 0 0 0 + + Color 43 112 157 114 + Color 44 104 145 106 + Color 45 94 131 95 + Color 46 85 119 86 + Color 47 76 106 77 + Color 48 66 92 67 + Color 49 57 79 58 + Color 50 48 67 49 + Color 51 38 53 38 + Color 52 29 40 29 + Color 53 20 28 20 + Color 54 10 13 10 + Color 55 0 0 0 + Color 56 0 0 0 + Color 57 0 0 0 + Color 58 0 0 0 + Color 59 0 0 0 + Color 60 0 0 0 +End + +Annunciator 1 + Size 16 11 + Offset 32 11 + Down 0 531 +End + +Annunciator 2 + Size 16 11 + Offset 75 11 + Down 16 531 +End + +Annunciator 3 + Size 16 11 + Offset 121 11 + Down 32 531 +End + +Annunciator 4 + Size 16 11 + Offset 163 11 + Down 48 531 +End + +Annunciator 5 + Size 16 11 + Offset 207 11 + Down 64 531 +End + +Annunciator 6 + Size 16 11 + Offset 253 11 + Down 80 531 +End + +Button 11 + Type 5 + Size 38 26 + Offset 11 167 + OutIn 1 16 +End + +Button 12 + Type 5 + Size 38 26 + Offset 59 167 + OutIn 8 16 +End + +Button 13 + Type 5 + Size 38 26 + Offset 107 167 + OutIn 8 8 +End + +Button 14 + Type 5 + Size 38 26 + Offset 156 167 + OutIn 8 4 +End + +Button 15 + Type 5 + Size 38 26 + Offset 204 167 + OutIn 8 2 +End + +Button 16 + Type 5 + Size 38 26 + Offset 252 167 + OutIn 8 1 +End + +Button 21 + Type 5 + Size 38 30 + Offset 11 205 + OutIn 2 16 +End + +Button 22 + Type 5 + Size 38 30 + Offset 59 205 + OutIn 7 16 +End + +Button 23 + Type 5 + Size 38 30 + Offset 107 205 + OutIn 7 8 +End + +Button 24 + Type 5 + Size 38 30 + Offset 156 205 + OutIn 7 4 +End + +Button 25 + Type 5 + Size 38 30 + Offset 204 205 + OutIn 7 2 +End + +Button 26 + Type 5 + Size 38 30 + Offset 252 205 + OutIn 7 1 +End + +Button 31 + Type 5 + Size 38 30 + Offset 11 245 + OutIn 0 16 +End + +Button 32 + Type 5 + Size 38 30 + Offset 59 245 + OutIn 6 16 +End + +Button 33 + Type 5 + Size 38 30 + Offset 107 245 + OutIn 6 8 +End + +Button 34 + Type 5 + Size 38 30 + Offset 156 245 + OutIn 6 4 +End + +Button 35 + Type 5 + Size 38 30 + Offset 204 245 + OutIn 6 2 +End + +Button 36 + Type 5 + Size 38 30 + Offset 252 245 + OutIn 6 1 +End + +Button 41 + Type 5 + Size 38 30 + Offset 11 287 + OutIn 3 16 +End + +Button 42 + Type 5 + Size 38 30 + Offset 59 287 + OutIn 5 16 +End + +Button 43 + Type 5 + Size 38 30 + Offset 107 287 + OutIn 5 8 +End + +Button 44 + Type 5 + Size 38 30 + Offset 156 287 + OutIn 5 4 +End + +Button 45 + Type 5 + Size 38 30 + Offset 204 287 + OutIn 5 2 +End + +Button 46 + Type 5 + Size 38 30 + Offset 252 287 + OutIn 5 1 +End + +Button 51 + Type 5 + Size 87 30 + Offset 11 327 + OutIn 4 16 +End + +Button 52 + Type 5 + Size 38 30 + Offset 107 327 + OutIn 4 8 +End + +Button 53 + Type 5 + Size 38 30 + Offset 156 327 + OutIn 4 4 +End + +Button 54 + Type 5 + Size 38 30 + Offset 204 327 + OutIn 4 2 +End + +Button 55 + Type 5 + Size 38 30 + Offset 252 327 + OutIn 4 1 +End + +Button 61 + Type 5 + Size 38 30 + Offset 11 369 + OutIn 3 32 +End + +Button 62 + Type 5 + Size 48 28 + Offset 65 369 + OutIn 3 8 +End + +Button 63 + Type 5 + Size 48 28 + Offset 124 369 + OutIn 3 4 +End + +Button 64 + Type 5 + Size 48 28 + Offset 183 369 + OutIn 3 2 +End + +Button 65 + Type 5 + Size 48 28 + Offset 243 369 + OutIn 3 1 +End + +Button 71 + Type 5 + Size 38 30 + Offset 11 410 + OutIn 2 32 +End + +Button 72 + Type 5 + Size 48 28 + Offset 65 410 + OutIn 2 8 +End + +Button 73 + Type 5 + Size 48 28 + Offset 124 410 + OutIn 2 4 +End + +Button 74 + Type 5 + Size 48 28 + Offset 183 410 + OutIn 2 2 +End + +Button 75 + Type 5 + Size 48 28 + Offset 243 410 + OutIn 2 1 +End + +Button 81 + Type 5 + Size 38 30 + Offset 11 453 + OutIn 1 32 +End + +Button 82 + Type 5 + Size 48 28 + Offset 65 453 + OutIn 1 8 +End + +Button 83 + Type 5 + Size 48 28 + Offset 124 453 + OutIn 1 4 +End + +Button 84 + Type 5 + Size 48 28 + Offset 183 453 + OutIn 1 2 +End + +Button 85 + Type 5 + Size 48 28 + Offset 243 453 + OutIn 1 1 +End + +Button 91 + Type 5 + Size 38 30 + Offset 11 494 + OutIn 0 32768 +End + +Button 92 + Type 5 + Size 48 28 + Offset 65 494 + OutIn 0 8 +End + +Button 93 + Type 5 + Size 48 28 + Offset 124 494 + OutIn 0 4 +End + +Button 94 + Type 5 + Size 48 28 + Offset 183 494 + OutIn 0 2 +End + +Button 95 + Type 5 + Size 48 28 + Offset 243 494 + OutIn 0 1 +End + +Button 101 + Type 3 + Size 42 14 + Offset 19 138 + Virtual + OnDown + Press 11 + End + OnUp + Release 11 + End +End + +Button 102 + Type 3 + Size 42 14 + Offset 63 138 + Virtual + OnDown + Press 12 + End + OnUp + Release 12 + End +End + +Button 103 + Type 3 + Size 42 14 + Offset 107 138 + Virtual + OnDown + Press 13 + End + OnUp + Release 13 + End +End + +Button 104 + Type 3 + Size 42 14 + Offset 151 138 + Virtual + OnDown + Press 14 + End + OnUp + Release 14 + End +End + +Button 105 + Type 3 + Size 42 14 + Offset 195 138 + Virtual + OnDown + Press 15 + End + OnUp + Release 15 + End +End + +Button 106 + Type 3 + Size 42 14 + Offset 239 138 + Virtual + OnDown + Press 16 + End + OnUp + Release 16 + End +End + +Button 107 + Type 5 + Size 16 16 + Offset 0 137 + Virtual + OnDown + Press 71 + Press 26 + End + OnUp + Release 26 + Release 71 + End +End + +Button 108 + Type 5 + Size 16 16 + Offset 284 137 + Virtual + OnDown + Press 26 + End + OnUp + Release 26 + End +End + +Button 109 + Type 4 + Size 262 112 + Offset 19 24 + Down 19 24 + NoHold +End + +Scancode 8 + Map 8 55 +End + +Scancode 13 + Map 13 51 +End + +Scancode 16 + IfPressed 16 + SetFlag 0 + Else + ResetFlag 0 + End +End + +Scancode 17 + IfPressed 17 + SetFlag 1 + Else + ResetFlag 1 + End +End + +Scancode 27 + Map 27 91 +End + +Scancode 32 + Map 32 94 +End + +Scancode 37 + Map 37 34 +End + +Scancode 38 + Map 38 25 +End + +Scancode 39 + Map 39 36 +End + +Scancode 40 + Map 40 35 +End + +Scancode 45 + Map 45 71 +End + +Scancode 46 + Map 46 81 +End + +Scancode 48 + Map 48 92 +End + +Scancode 49 + IfFlag 0 + Map 49 71 + Map 49 54 + Else + Map 49 82 + End +End + +Scancode 50 + Map 50 83 +End + +Scancode 51 + IfFlag 0 + Map 51 81 + Map 51 65 + Else + Map 51 84 + End +End + +Scancode 52 + Map 52 72 +End + +Scancode 53 + Map 53 73 +End + +Scancode 54 + Map 54 74 +End + +Scancode 55 + Map 55 62 +End + +Scancode 56 + IfFlag 0 + Map 56 75 + Else + Map 56 63 + End +End + +Scancode 57 + IfFlag 0 + Map 57 71 + Map 57 65 + Else + Map 57 64 + End +End + +Scancode 65 + Map 65 11 +End + +Scancode 66 + Map 66 12 +End + +Scancode 67 + Map 67 13 +End + +Scancode 68 + Map 68 14 +End + +Scancode 69 + Map 69 15 +End + +Scancode 70 + Map 70 16 +End + +Scancode 71 + Map 71 21 +End + +Scancode 72 + Map 72 22 +End + +Scancode 73 + Map 73 23 +End + +Scancode 74 + Map 74 24 +End + +Scancode 75 + Map 75 25 +End + +Scancode 76 + Map 76 26 +End + +Scancode 77 + Map 77 31 +End + +Scancode 78 + IfFlag 1 + MenuItem 1 + Else + Map 78 32 + End +End + +Scancode 79 + Map 79 33 +End + +Scancode 80 + Map 80 34 +End + +Scancode 81 + Map 81 35 +End + +Scancode 82 + Map 82 36 +End + +Scancode 83 + Map 83 41 +End + +Scancode 84 + Map 84 42 +End + +Scancode 85 + Map 85 43 +End + +Scancode 86 + Map 86 44 +End + +Scancode 87 + Map 87 45 +End + +Scancode 88 + Map 88 46 +End + +Scancode 89 + Map 89 52 +End + +Scancode 90 + Map 90 53 +End + +Scancode 96 + Map 96 92 +End + +Scancode 97 + Map 97 82 +End + +Scancode 98 + Map 98 83 +End + +Scancode 99 + Map 99 84 +End + +Scancode 100 + Map 100 72 +End + +Scancode 101 + Map 101 73 +End + +Scancode 102 + Map 102 74 +End + +Scancode 103 + Map 103 62 +End + +Scancode 104 + Map 104 63 +End + +Scancode 105 + Map 105 64 +End + +Scancode 106 + Map 106 75 +End + +Scancode 107 + Map 107 95 +End + +Scancode 109 + Map 109 85 +End + +Scancode 110 + Map 110 93 +End + +Scancode 111 + Map 111 65 +End + +Scancode 144 + IfPressed 144 + NotFlag 3 + End +End + +Scancode 186 + IfFlag 0 + Map 186 81 + Map 186 95 + End +End + +Scancode 188 + IfFlag 0 + Map 188 71 + Else + Map 188 71 + Map 188 93 + End +End + +Scancode 190 + IfFlag 0 + Map 190 81 + Else + Map 190 93 + End +End + +Scancode 191 + IfFlag 0 + Map 191 71 + Map 191 55 + Else + Map 191 65 + End +End + +Scancode 192 + IfFlag 0 + IfPressed 192 + NotFlag 2 + IfFlag 2 + Press 61 + Else + Release 61 + End + End + Else + Map 192 61 + End +End + +Scancode 219 + IfFlag 0 + Map 219 71 + Map 219 95 + Else + Map 219 71 + Map 219 75 + End +End + +Scancode 220 + Map 220 54 +End + +Scancode 222 + IfFlag 0 + Map 222 81 + Map 222 85 + Else + Map 222 31 + End +End + diff --git a/app/src/main/cpp/APPLE.C b/app/src/main/cpp/APPLE.C new file mode 100644 index 0000000..ddfd2ff --- /dev/null +++ b/app/src/main/cpp/APPLE.C @@ -0,0 +1,415 @@ +/* + * apple.c + * + * This file is part of Emu48 + * + * Copyright (C) 2005 CdB for HP + * Copyright (C) 2006 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "apple.h" +#include "io.h" // I/O register definitions +#include "i28f160.h" + +#define w Chipset + +#define _KB(s) ((s) * 1024 * 2) + +#pragma intrinsic(memset,memcpy) + +#include "Ops.h" + +// +// ROM buffer access functions +// + +static __inline void WrDirtyPage(DWORD d) +{ + if (pbyRomDirtyPage) // using dirty ROM page table + { + DWORD dwPage = d / ROMPAGESIZE; // this is the page + + _ASSERT(dwPage < dwRomDirtyPageSize); + pbyRomDirtyPage[dwPage] = TRUE; // page is dirty + } + return; +} + +static __inline void EraseBlock(DWORD d,DWORD dwNibSize) +{ + LPBYTE pbyAddr = pbyRom + d; + + while (dwNibSize--) + { + WrDirtyPage(d++); // make page dirty + *pbyAddr++ = 0x0F; // clear address + } + return; +} + +static CONST LPBYTE ppReg[] = { w.A, w.B, w.C, w.D }; + +static QWORD DecodeReg64(LPBYTE R, BYTE byNF) +{ + QWORD qwVal = Npack64(R,16); // generate 64bit number from register + + switch (byNF) // field selector + { + case 0: return (qwVal >> (w.P*4)) & 0xf; // P + case 1: return qwVal & ~((QWORD)~0 << ((w.P+1)*4));// WP + case 2: return (qwVal >> 8) & 0xf; // XS + case 3: return qwVal & 0xfff; // X + case 4: return (qwVal >> 60) & 0xf; // S + case 5: return (qwVal >> 12) & 0x0000ffffffffffff; // M + case 6: return qwVal & 0xff; // B + case 7: return qwVal; // W + case 15: return qwVal & 0xfffff; // A +// default: return qwVal & w.fld[byNF-8]; // F1-F7 + default: return qwVal; + } +} + +static void EncodeReg64(QWORD v, LPBYTE R, BYTE byNF) +{ + if (byNF > 7 && byNF < 15) // user mask area F1-F7 + { +// QWORD qwMask = w.fld[byNF-8]; // F1-F7 +// QWORD qwVal = Npack64(R,16); // original content of register +// v = (v & qwMask) | (qwVal & ~qwMask); // mask operation + byNF = 7; // write W area + } + + Nunpack64(R+F_s[byNF], v, F_l[byNF]); + return; +} + +static QWORD o80BReg164(LPBYTE I) +{ + _ASSERT((I[5] & 3) < ARRAYSIZEOF(ppReg)); + return DecodeReg64(ppReg[I[5] & 3], I[6]); +} + +static QWORD o80BReg264(LPBYTE I) +{ + _ASSERT((I[5] >> 2) < ARRAYSIZEOF(ppReg)); + return DecodeReg64(ppReg[I[5] >> 2], I[6]); +} + +static void o80BRegWrite(QWORD v, LPBYTE I) +{ + _ASSERT((I[5] & 3) < ARRAYSIZEOF(ppReg)); + EncodeReg64(v, ppReg[I[5] & 3], I[6]); + return; +} + +// SETFLDx +VOID o80BF7x(LPBYTE I) +{ + QWORD qwVal; + +_ASSERT(FALSE); // not tested so far + w.pc+=1; // skip x nibble + qwVal = Npack64(w.C,16); // generate 64bit number from C register + w.carry = (qwVal == 0) // set carry if field mask = 0 + || (I[5] < 8) || (I[5] > 14); // or x argument not in range 8..15 + + if (!w.carry) // field mask and argument are valid + { + _ASSERT(I[5] >= 8 && I[5] <= 14); +// w.fld[I[5]-8] = qwVal; + } + return; +} + +// RPL2 (normal LOOP with preserving Carry) +VOID o80B00(VOID) +{ + BYTE p[5]; + + Nread(w.A, w.d0, 5); // A=DAT0 A + w.d0 = (w.d0 + 5) & 0xFFFFF; // D0=D0+ 5 + Nread(p, Npack(w.A,5), 5); // PC=(A) + w.pc = Npack(p,5); + return; +} + +// FALSE +5 +VOID o80B30(VOID) +{ + Ndec(w.D, 5, 0); // D=D-1 A + if (w.carry) w.pc = 0x03A9B; // memerr? + w.d1 -= 5; // D1=D1- 5 (don't care about carry here) + Nwrite(w.A, w.d1, 5); // DAT1=A A + o80B00(); // LOOP + return; +} + +// DOFALSE +VOID o80B40(VOID) +{ + w.P = 0; // P= 0 + PCHANGED; + Nunpack(w.C,0x03AC0,5); // LC(5) =FALSE + memcpy(w.A, w.C, 5); // A=C A + o80B30(); // PC=(A) (call FALSE) + return; +} + +// MOVEDOWN +VOID o80B60(VOID) +{ + BYTE byData[16]; + DWORD dwC,s; + + for (dwC = Npack(w.C,5); dwC > 0; dwC -= s) + { + s = ARRAYSIZEOF(byData); // max size for data + if (dwC < s) s = dwC; + + Npeek(byData,w.d0,s); // read source without CRC update + Nwrite(byData,w.d1,s); // write destination + + w.d0 = (w.d0 + s) & 0xFFFFF; + w.d1 = (w.d1 + s) & 0xFFFFF; + } + + w.P = 0; + PCHANGED; + w.carry = FALSE; + return; +} + +// MOVEUP +VOID o80B70(VOID) +{ + BYTE byData[16]; + DWORD dwC,s; + + for (dwC = Npack(w.C,5); dwC > 0; dwC -= s) + { + s = ARRAYSIZEOF(byData); // max size for data + if (dwC < s) s = dwC; + + w.d0 = (w.d0 - s) & 0xFFFFF; + w.d1 = (w.d1 - s) & 0xFFFFF; + + Npeek(byData,w.d0,s); // read source without CRC update + Nwrite(byData,w.d1,s); // write destination + } + + w.P = 0; + PCHANGED; + w.carry = FALSE; + return; +} + +// CREATETEMP +VOID o80B80(VOID) +{ + DWORD dwC,dwAddress; + + dwC = Npack(w.C,5); // desired size of hole + dwAddress = RPL_CreateTemp(dwC,FALSE); // allocate memory + if (dwAddress) + { + w.d0 = dwAddress; // object position + w.d1 = (w.d0 + dwC) & 0xFFFFF; // link field of hole + w.carry = FALSE; // no error + } + else + { + w.carry = TRUE; // error + } + + dwC += 6; // desired size + link field + Nunpack(w.B,dwC,5); // B[A] = size + 6 + Nunpack(w.C,dwC,5); // C[A] = size + 6 + return; +} + +// setup basic memory configuration +VOID o80B04(VOID) +{ + DWORD a; + + a = Npack(w.C,5); // save C[A] + Reset(); // unconfig all devices + Nunpack(w.C,0x100,5); // IO: 0x00100 + Config(); // addr + Nunpack(w.C,0x80000,5); // RAM: 0x80000 size 256KB + Config(); // size + Config(); // addr + Nunpack(w.C,a,5); // restore C[A] + w.P = 0; + PCHANGED; + return; +} + +// erase flash bank +VOID o80B14(VOID) +{ + DWORD dwStart,dwStop; + + BYTE byBank = w.C[15]; // C[S] = bank to erase + +_ASSERT(FALSE); // not tested so far + // ROM is logically organized in 16 banks with 128KB + dwStart = byBank * _KB(128); // start address + dwStop = dwStart + _KB(128); // last address + if (byBank == 0) dwStart += _KB(64); // skip boot loader + + // clear bank + EraseBlock(dwStart,dwStop-dwStart); + + w.carry = FALSE; // no error + return; +} + +// write bytes to flash +VOID o80B24(VOID) +{ + LPBYTE pbyBuffer; + DWORD dwNib,dwAddr,dwSize; + + dwNib = Npack(w.C,5) * 2; // no. of nibbles to copy + dwAddr = FlashROMAddr(w.d1); // linear addr in flash chip + + dwSize = dwRomSize - dwAddr; // remaining memory size in flash + if (dwNib > dwSize) dwNib = dwSize; // prevent buffer overflow + + pbyBuffer = (LPBYTE) malloc(dwNib); // allocate data buffer + if (pbyBuffer != NULL) + { + DWORD i; + + Npeek(pbyBuffer,w.d0,dwNib); // get data + + for (i = 0; i < dwNib; ++i) + { + WrDirtyPage(dwAddr); // make page dirty + pbyRom[dwAddr++] = pbyBuffer[i]; // write data + } + free(pbyBuffer); + } + + w.d0 += dwNib; // update source register + w.d1 += dwNib; // update destination register + w.carry = FALSE; // no error + return; +} + +// OUTBYT +VOID o80B65(VOID) +{ + // set Transmitting annunciator + BYTE byAnn = w.IORam[ANNCTRL+1] | 0x2; + WriteIO(&byAnn,ANNCTRL+1,1); + + SendByteUdp((BYTE) Npack(w.A,2)); // send data byte + + w.P = 0; + PCHANGED; + w.carry = FALSE; // no error + return; +} + +// CdB for HP: add apples BUSCC commands +VOID o80BExt(LPBYTE I) // Saturnator extentions +{ + DWORD a; + + w.pc+=2; + switch (I[3]+(I[4]<<4)) + { + case 0x00: o80B00(); break; // RPL2 (preserve Carry) + case 0x03: o80B30(); break; // FALSE + case 0x04: o80B40(); break; // DOFALSE + case 0x05: External(&w); PCHANGED; break; // BEEP2 implemented using Emu48's beep + case 0x06: o80B60(); break; // MOVEDOWN + case 0x07: o80B70(); break; // MOVEUP + case 0x08: o80B80(); break; // CREATETEMP + case 0x09: RCKBp(&w); PCHANGED; break; // RCKBp + case 0x0A: break; // KEYDN not implemented + case 0x0B: break; // no doslow implemented + case 0x10: // simulate off function + { + BOOL bShutdn = TRUE; // shut down + + // only shut down when no timer wake up + if (w.IORam[TIMER1_CTRL]&WKE) // WKE bit of timer1 is set + { + if (ReadT1()&0x08) // and MSB of timer1 is set + { + w.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.IORam[TIMER2_CTRL]&WKE) // WKE bit of timer2 is set + { + if (ReadT2()&0x80000000) // and MSB of timer2 is set + { + w.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.in==0 && bShutdn) // shut down only when enabled + { + w.Shutdn = TRUE; // set mode before exit emulation loop + bInterrupt = TRUE; + } + } + break; + case 0x11: w.pc+=2; break; // do not do gettime, just skip the RTN after it to fall in the normal gettime function (only valid in untouched ROM) + case 0x12: break; // do not do settime, fall in the normal settime function (only valid in untouched ROM) + case 0x13: break; // RESETOS not implemented + case 0x14: break; // AUTOTEST not implemented + case 0x15: break; // NATIVE? not implemented + case 0x17: break; // SERIAL not implemented + case 0x28: w.HST |= I[5]; w.pc+=1; break; // HST=1.x + case 0x29: w.A[4]= w.A[3]= w.A[2]= w.A[0]= 0; if (cCurrentRomType=='Q') w.A[1]=5; else w.A[1]=4; break; // screen height = 0x50 = 80 + case 0x2A: w.A[4]= w.A[3]= w.A[2]= 0; w.A[1]=8; w.A[0]=3; break; // screen width = 0x83 = 131 + case 0x2B: w.carry = (cCurrentRomType == '2'); break; // it is medium apple + case 0x2C: w.carry = (cCurrentRomType == 'Q'); break; // it is big apple + case 0x2E: w.carry = (nCurrentClass == 50); break; // it is big apple V2 + case 0x30: w.d0address= Npack(w.C,5)>>12; Map(0,0xff); break; //config_disp0 Ca:address 4K data + case 0x31: w.d0address=0; Map(0,0xff); RefreshDisp0(); break; //unconfig_disp0 does the refresh + case 0x32: RefreshDisp0(); break; //refresh_disp0 force refresh + case 0x33: a= Npack(w.C,2); if (a>(DWORD)SCREENHEIGHT) a= SCREENHEIGHT; /* w.lcounter = (SCREENHEIGHT-a) */; w.d0size= a; RefreshDisp0(); break; //set_lines_disp0 nb in Cb + case 0x34: w.d0offset= Npack(w.C,5); w.d0offset &= 0x7FF; break; //set_offset_disp0 offset to disp in disp0 + case 0x35: Nunpack(w.C,w.d0offset,5); break; // native_get_line_disp0 + case 0x38: w.HST |= I[5]; w.pc+=3; break; // ?HST=1.x not implemented + case 0x40: o80B04(); break; // setup basic memory configuration +// case 0x41: o80B14(); break; // erase flash bank + case 0x42: o80B24(); break; // write bytes into flash +// case 0x43: ??? // format flash bank + case 0x50: break; // REMON not implemented + case 0x51: break; // REMOFF not implemented + case 0x56: o80B65(); break; // OUTBYT + case 0x57: w.D[0]= w.D[1]= 0; break; + case 0x60: break; // ACCESSSD not implemented + case 0x61: break; // PORTTAG? not implemented + case 0x64: w.carry = FALSE; break; // no SD card inserted + case 0x66: w.carry = FALSE; break; // simulate format fail card inserted + case 0x7F: w.carry = TRUE; w.pc+=1; break; // SETFLDn not implemented, set carry for failed + case 0x80: { QWORD b = o80BReg264(I); o80BRegWrite(b, I); w.pc+=2; break; } // r=s + case 0x81: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a+b, I); w.pc+=2; break; } // r=r+s + case 0x82: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a-b, I); w.pc+=2; break; } // r=r-s + case 0x83: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a*b, I); w.pc+=2; break; } // r=r*s + case 0x84: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a/b, I); w.pc+=2; break; } // r=r/s + case 0x85: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a%b, I); w.pc+=2; break; } // r=r%s + case 0x86: { QWORD b = o80BReg264(I); o80BRegWrite(~b, I); w.pc+=2; break; } // r=-r-1 + case 0x87: { QWORD b = o80BReg264(I); o80BRegWrite((QWORD)(-(__int64)b), I); w.pc+=2; break; } // r=-r + case 0x88: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a<>b, I); w.pc+=2; break; } // r=r>s + case 0x8A: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a^b, I); w.pc+=2; break; } // r=r^s + case 0x90: break; // data streamer not implemented + case 0xEE: break; // ARMFLUSH not implemented + case 0xEF: break; // ARMSYS not implemented + case 0xFF: break; // ARMSAT not implemented + default: w.pc-= 2; + } + return; +} diff --git a/app/src/main/cpp/APPLE.H b/app/src/main/cpp/APPLE.H new file mode 100644 index 0000000..c893fbe --- /dev/null +++ b/app/src/main/cpp/APPLE.H @@ -0,0 +1,19 @@ +/* + * apple.h + * + * This file is part of Emu48 + * + * Copyright (C) 2006 Christoph Gießelink + * + */ + +extern VOID o80B00(VOID); +extern VOID o80B30(VOID); +extern VOID o80B40(VOID); +extern VOID o80B60(VOID); +extern VOID o80B70(VOID); +extern VOID o80B80(VOID); +extern VOID o80B04(VOID); +extern VOID o80B14(VOID); +extern VOID o80B24(VOID); +extern VOID o80BExt(LPBYTE I); // Saturnator extentions diff --git a/app/src/main/cpp/COLOR.H b/app/src/main/cpp/COLOR.H new file mode 100644 index 0000000..02f0d4c --- /dev/null +++ b/app/src/main/cpp/COLOR.H @@ -0,0 +1,27 @@ +/* + * color.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +#define COLOR_BLACK 0x00000000 +#define COLOR_MAROON 0x00000080 +#define COLOR_GREEN 0x00008000 +#define COLOR_OLIVE 0x00008080 +#define COLOR_NAVY 0x00800000 +#define COLOR_PURPLE 0x00800080 +#define COLOR_TEAL 0x00808000 +#define COLOR_GRAY 0x00808080 +#define COLOR_SILVER 0x00C0C0C0 +#define COLOR_RED 0x000000FF +#define COLOR_LIME 0x0000FF00 +#define COLOR_YELLOW 0x0000FFFF +#define COLOR_BLUE 0x00FF0000 +#define COLOR_FUCHSIA 0x00FF00FF +#define COLOR_AQUA 0x00FFFF00 +#define COLOR_LTGRAY 0x00C0C0C0 +#define COLOR_DKGRAY 0x00808080 +#define COLOR_WHITE 0x00FFFFFF diff --git a/app/src/main/cpp/CURSOR.C b/app/src/main/cpp/CURSOR.C new file mode 100644 index 0000000..2e80a3a --- /dev/null +++ b/app/src/main/cpp/CURSOR.C @@ -0,0 +1,89 @@ +/* + * Cursor.c + * + * This file is part of Emu48 + * + * Copyright (C) 2004 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +// hand cursor AND mask +static CONST BYTE ANDmaskCursor[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xF3, 0xFF, 0xFF, // 1111 1111 1111 0011 1111 1111 1111 1111 + 0xFF, 0xE1, 0xFF, 0xFF, // 1111 1111 1110 0001 1111 1111 1111 1111 + 0xFF, 0xE1, 0xFF, 0xFF, // 1111 1111 1110 0001 1111 1111 1111 1111 + 0xFF, 0xE1, 0xFF, 0xFF, // 1111 1111 1110 0001 1111 1111 1111 1111 + 0xFF, 0xE1, 0xFF, 0xFF, // 1111 1111 1110 0001 1111 1111 1111 1111 + 0xFF, 0xE0, 0x7F, 0xFF, // 1111 1111 1110 0000 0111 1111 1111 1111 + 0xFF, 0xE0, 0x0F, 0xFF, // 1111 1111 1110 0000 0000 1111 1111 1111 + 0xFF, 0xE0, 0x03, 0xFF, // 1111 1111 1110 0000 0000 0011 1111 1111 + 0xFF, 0xE0, 0x01, 0xFF, // 1111 1111 1110 0000 0000 0001 1111 1111 + 0xFE, 0x20, 0x00, 0xFF, // 1111 1110 0010 0000 0000 0000 1111 1111 + 0xFE, 0x00, 0x00, 0xFF, // 1111 1110 0000 0000 0000 0000 1111 1111 + 0xFE, 0x00, 0x00, 0xFF, // 1111 1110 0000 0000 0000 0000 1111 1111 + 0xFF, 0x00, 0x00, 0xFF, // 1111 1111 0000 0000 0000 0000 1111 1111 + 0xFF, 0x80, 0x00, 0xFF, // 1111 1111 1000 0000 0000 0000 1111 1111 + 0xFF, 0x80, 0x00, 0xFF, // 1111 1111 1000 0000 0000 0000 1111 1111 + 0xFF, 0xC0, 0x00, 0xFF, // 1111 1111 1100 0000 0000 0000 1111 1111 + 0xFF, 0xC0, 0x01, 0xFF, // 1111 1111 1100 0000 0000 0001 1111 1111 + 0xFF, 0xE0, 0x01, 0xFF, // 1111 1111 1110 0000 0000 0001 1111 1111 + 0xFF, 0xE0, 0x01, 0xFF, // 1111 1111 1110 0000 0000 0001 1111 1111 + 0xFF, 0xF0, 0x03, 0xFF, // 1111 1111 1111 0000 0000 0011 1111 1111 + 0xFF, 0xF0, 0x03, 0xFF, // 1111 1111 1111 0000 0000 0011 1111 1111 + 0xFF, 0xF0, 0x03, 0xFF, // 1111 1111 1111 0000 0000 0011 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF, // 1111 1111 1111 1111 1111 1111 1111 1111 + 0xFF, 0xFF, 0xFF, 0xFF // 1111 1111 1111 1111 1111 1111 1111 1111 +}; + +// hand cursor XOR mask +static CONST BYTE XORmaskCursor[] = +{ + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x0C, 0x00, 0x00, // 0000 0000 0000 1100 0000 0000 0000 0000 + 0x00, 0x0C, 0x00, 0x00, // 0000 0000 0000 1100 0000 0000 0000 0000 + 0x00, 0x0C, 0x00, 0x00, // 0000 0000 0000 1100 0000 0000 0000 0000 + 0x00, 0x0C, 0x00, 0x00, // 0000 0000 0000 1100 0000 0000 0000 0000 + 0x00, 0x0C, 0x00, 0x00, // 0000 0000 0000 1100 0000 0000 0000 0000 + 0x00, 0x0D, 0x80, 0x00, // 0000 0000 0000 1101 1000 0000 0000 0000 + 0x00, 0x0D, 0xB0, 0x00, // 0000 0000 0000 1101 1011 0000 0000 0000 + 0x00, 0x0D, 0xB4, 0x00, // 0000 0000 0000 1101 1011 0100 0000 0000 + 0x00, 0x0D, 0xB6, 0x00, // 0000 0000 0000 1101 1011 0110 0000 0000 + 0x00, 0xCF, 0xF6, 0x00, // 0000 0000 1100 1111 1111 0110 0000 0000 + 0x00, 0xEF, 0xFE, 0x00, // 0000 0000 1110 1111 1111 1110 0000 0000 + 0x00, 0x6F, 0xFE, 0x00, // 0000 0000 0110 1111 1111 1110 0000 0000 + 0x00, 0x2F, 0xFE, 0x00, // 0000 0000 0010 1111 1111 1110 0000 0000 + 0x00, 0x3F, 0xFE, 0x00, // 0000 0000 0011 1111 1111 1110 0000 0000 + 0x00, 0x1F, 0xFE, 0x00, // 0000 0000 0001 1111 1111 1110 0000 0000 + 0x00, 0x1F, 0xFC, 0x00, // 0000 0000 0001 1111 1111 1100 0000 0000 + 0x00, 0x0F, 0xFC, 0x00, // 0000 0000 0000 1111 1111 1100 0000 0000 + 0x00, 0x0F, 0xFC, 0x00, // 0000 0000 0000 1111 1111 1100 0000 0000 + 0x00, 0x07, 0xF8, 0x00, // 0000 0000 0000 0111 1111 1000 0000 0000 + 0x00, 0x07, 0xF8, 0x00, // 0000 0000 0000 0111 1111 1000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00, // 0000 0000 0000 0000 0000 0000 0000 0000 + 0x00, 0x00, 0x00, 0x00 // 0000 0000 0000 0000 0000 0000 0000 0000 +}; + +HCURSOR CreateHandCursor(VOID) +{ + return CreateCursor(hApp,12,5,32,32,ANDmaskCursor,XORmaskCursor); +} diff --git a/app/src/main/cpp/DDESERV.C b/app/src/main/cpp/DDESERV.C new file mode 100644 index 0000000..3d219b4 --- /dev/null +++ b/app/src/main/cpp/DDESERV.C @@ -0,0 +1,182 @@ +/* + * DdeServ.c + * + * This file is part of Emu48 + * + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,HCONV hConv, + HSZ hsz1,HSZ hsz2,HDDEDATA hData, + DWORD dwData1,DWORD dwData2) +{ + TCHAR *psz,szBuffer[32]; + HDDEDATA hReturn; + LPBYTE lpData,lpHeader; + DWORD dwAddress,dwSize,dwLoop,dwIndex; + UINT nStkLvl; + BOOL bSuccess; + + // disable stack loading items on HP38G, HP39/40G + BOOL bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add P type + + switch (iType) + { + case XTYP_CONNECT: + // get service name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + if (0 != lstrcmp(szBuffer,szAppName)) + return (HDDEDATA) FALSE; + // get topic name + DdeQueryString(idDdeInst,hsz1,szBuffer,ARRAYSIZEOF(szBuffer),0); + return (HDDEDATA) (INT_PTR) (0 == lstrcmp(szBuffer,szTopic)); + + case XTYP_POKE: + // quit on models without stack or illegal data format or not in running state + if (!bStackEnable || iFmt != uCF_HpObj || nState != SM_RUN) + return (HDDEDATA) DDE_FNOTPROCESSED; + + // get item name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + nStkLvl = _tcstoul(szBuffer,&psz,10); + if (*psz != 0 || nStkLvl < 1) // invalid number format + return (HDDEDATA) DDE_FNOTPROCESSED; + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + if (!(Chipset.IORam[BITOFFSET]&DON)) // HP off + { + // turn on HP + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + hReturn = DDE_FNOTPROCESSED; + goto cancel; + } + + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_SLEEP); + + bSuccess = FALSE; + + // get data and size + lpData = DdeAccessData(hData,&dwSize); + + // has object length header + if (lpData && dwSize >= sizeof(DWORD)) + { + dwIndex = *(LPDWORD) lpData; // object length + + if (dwIndex <= dwSize - sizeof(DWORD)) + { + // reserve unpacked object length memory + LPBYTE pbyMem = (LPBYTE) malloc(dwIndex * 2); + + if (pbyMem != NULL) + { + // copy data and write to stack + CopyMemory(pbyMem+dwIndex,lpData+sizeof(DWORD),dwIndex); + bSuccess = (WriteStack(nStkLvl,pbyMem,dwIndex) == S_ERR_NO); + free(pbyMem); // free memory + } + } + } + + DdeUnaccessData(hData); + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_RUN); + + if (bSuccess == FALSE) + { + hReturn = DDE_FNOTPROCESSED; + goto cancel; + } + + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + // wait for sleep mode + while (Chipset.Shutdn == FALSE) Sleep(0); + hReturn = (HDDEDATA) DDE_FACK; + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return hReturn; + + case XTYP_REQUEST: + // quit on models without stack or illegal data format or not in running state + if (!bStackEnable || iFmt != uCF_HpObj || nState != SM_RUN) + return NULL; + + // get item name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + nStkLvl = _tcstoul(szBuffer,&psz,10); + if (*psz != 0 || nStkLvl < 1) // invalid number format + return NULL; + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + return NULL; + + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_SLEEP); + + dwAddress = RPL_Pick(nStkLvl); // pick address of stack level "item" object + if (dwAddress == 0) + { + SwitchToState(SM_RUN); // run state + return NULL; + } + dwLoop = dwSize = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2; + + lpHeader = (Chipset.type == 'X' || Chipset.type == 'Q' || Chipset.type == '2') ? (LPBYTE) BINARYHEADER49 : (LPBYTE) BINARYHEADER48; + + // length of binary header + dwIndex = (DWORD) strlen((LPCSTR) lpHeader); + + // size of objectsize + header + object + dwSize += dwIndex + sizeof(DWORD); + + // reserve memory + if ((lpData = (LPBYTE) malloc(dwSize)) == NULL) + { + SwitchToState(SM_RUN); // run state + return NULL; + } + + // save data length + *(DWORD *)lpData = dwLoop + dwIndex; + + // copy header + memcpy(lpData + sizeof(DWORD),lpHeader,dwIndex); + + // copy data + for (dwIndex += sizeof(DWORD);dwLoop--;++dwIndex,dwAddress += 2) + lpData[dwIndex] = Read2(dwAddress); + + // write data + hReturn = DdeCreateDataHandle(idDdeInst,lpData,dwSize,0,hsz2,iFmt,0); + free(lpData); + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_RUN); + + return hReturn; + } + return NULL; + UNREFERENCED_PARAMETER(hConv); + UNREFERENCED_PARAMETER(dwData1); + UNREFERENCED_PARAMETER(dwData2); +} diff --git a/app/src/main/cpp/DEBUGDLL.C b/app/src/main/cpp/DEBUGDLL.C new file mode 100644 index 0000000..2f7c8a4 --- /dev/null +++ b/app/src/main/cpp/DEBUGDLL.C @@ -0,0 +1,1104 @@ +/* + * DebugDll.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "ops.h" +#include "debugger.h" + +#include "Emu48Dll.h" + +#define MAXBREAKPOINTS 256 // max. number of breakpoints + +typedef struct // type of breakpoint table +{ + UINT nType; // breakpoint type + DWORD dwAddr; // breakpoint address +} BP_T; + +static DWORD *pdwLinArray; // last instruction linear array + +static DWORD dwRefRstkp; // reference stack pointer content + +static WORD wBreakpointCount = 0; // number of breakpoints +static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table + +static BOOL bDbgNotify = FALSE; // debugger notify flag +static BOOL bDbgRpl; // Flag for ASM or RPL breakpoint + +// callback function notify debugger breakpoint +static VOID (CALLBACK *pEmuDbgNotify)(INT nBreaktype) = NULL; + +// callback function notify hardware stack changed +static BOOL (CALLBACK *pEmuStackNotify)(VOID) = NULL; + + + +//################ +//# +//# Static functions +//# +//################ + +// +// convert nibble register to DWORD +// +static DWORD RegToDWORD(BYTE *pReg, WORD wNib) +{ + WORD i; + DWORD dwRegister = 0; + + for (i = 0;i < wNib;++i) + { + dwRegister <<= 4; + dwRegister += pReg[wNib-i-1]; + } + return dwRegister; +} + +// +// convert DWORD to nibble register +// +static VOID DWORDToReg(BYTE *pReg, WORD wNib, DWORD dwReg) +{ + int i; + + for (i = 0;i < wNib;++i) + { + pReg[i] = (BYTE) (dwReg & 0xF); + dwReg >>= 4; // next nibble + } + return; +} + + +//################ +//# +//# Public internal functions +//# +//################ + +// +// handle upper 32 bit of cpu cycle counter +// +VOID UpdateDbgCycleCounter(VOID) +{ + return; // not necessary here, cycle counter has 64 bit in DLL version +} + +// +// check for code breakpoints +// +BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType) +{ + INT i; + BOOL bBreak = FALSE; // don't break + DWORD dwRomAddr = -1; // no valid ROM address + + // stack changed notify function defined + if (nType == BP_EXEC && pEmuStackNotify != NULL) + { + // hardware stack changed + if (dwRefRstkp != -1 && dwRefRstkp != Chipset.rstkp) + bBreak = pEmuStackNotify(); // inform debugger + + dwRefRstkp = Chipset.rstkp; // save current stack level + } + + // absolute ROM adress breakpoints + if (nType == BP_EXEC) // only on code breakpoints + { + LPBYTE I = FASTPTR(dwAddr); // get opcode stream + + // adress in ROM area + if (I >= pbyRom && I < pbyRom + dwRomSize) + dwRomAddr = I - pbyRom; // linear ROM address + } + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // check for absolute ROM adress breakpoint + if ( sBreakpoint[i].dwAddr >= dwRomAddr && sBreakpoint[i].dwAddr < dwRomAddr + dwRange + && (sBreakpoint[i].nType & BP_ROM) != 0) + return TRUE; + + // check address range and type + if ( sBreakpoint[i].dwAddr >= dwAddr && sBreakpoint[i].dwAddr < dwAddr + dwRange + && (sBreakpoint[i].nType & nType) != 0) + return TRUE; + } + return bBreak; +} + +// +// notify debugger that emulation stopped +// +VOID NotifyDebugger(INT nType) // update registers +{ + nDbgState = DBG_STEPINTO; // state "step into" + dwDbgStopPC = -1; // disable "cursor stop address" + + _ASSERT(pEmuDbgNotify); // notify function defined from caller + pEmuDbgNotify(nType); // notify caller + bDbgNotify = TRUE; // emulation stopped + return; +} + +// +// disable debugger +// +VOID DisableDebugger(VOID) +{ + nDbgState = DBG_OFF; // disable debugger + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + return; +} + + +//################ +//# +//# Dummy functions for linking +//# +//################ + +// +// entry from message loop +// +LRESULT OnToolDebug(VOID) +{ + return 0; +} + +// +// load breakpoint list +// +VOID LoadBreakpointList(HANDLE hFile) +{ + return; + UNREFERENCED_PARAMETER(hFile); +} + +// +// save breakpoint list +// +VOID SaveBreakpointList(HANDLE hFile) +{ + return; + UNREFERENCED_PARAMETER(hFile); +} + +// +// create a copy of the breakpoint list +// +VOID CreateBackupBreakpointList(VOID) +{ + return; +} + +// +// restore the breakpoint list from the copy +// +VOID RestoreBackupBreakpointList(VOID) +{ + return; +} + + +//################ +//# +//# Public external functions +//# +//################ + +/**************************************************************************** +* EmuInitLastInstr +***************************************************************************** +* +* @func init a circular buffer area for saving the last instruction +* addresses +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuInitLastInstr( + WORD wNoInstr, // @parm number of saved instructions, + // 0 = frees the memory buffer + DWORD *pdwArray) // @parm pointer to linear array +{ + if (pdwInstrArray) // circular buffer defined + { + EnterCriticalSection(&csDbgLock); + { + free(pdwInstrArray); // free memory + pdwInstrArray = NULL; + } + LeaveCriticalSection(&csDbgLock); + } + + if (wNoInstr) // new size + { + pdwLinArray = pdwArray; // save address of linear array + wInstrSize = wNoInstr + 1; // size of circular buffer + wInstrWp = wInstrRp = 0; // init write/read pointer + + pdwInstrArray = malloc(wInstrSize*sizeof(*pdwInstrArray)); + if (pdwInstrArray == NULL) // allocation error + return TRUE; + } + return FALSE; +} + +/**************************************************************************** +* EmuGetLastInstr +***************************************************************************** +* +* @func return number of valid entries in the last instruction array, +* each entry contents a PC address, array[0] contents the oldest, +* array[*pwNoEntries-1] the last PC address +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuGetLastInstr( + WORD *pwNoEntries) // @parm return number of valid entries in array +{ + WORD i; + + if (pdwInstrArray == NULL) return TRUE; // circular buffer not defined + + // copy data to linear buffer + *pwNoEntries = 0; + for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize) + pdwLinArray[(*pwNoEntries)++] = pdwInstrArray[i]; + + return FALSE; +} + +/**************************************************************************** +* EmuRun +***************************************************************************** +* +* @func run emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, Emu48 is running +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuRun(VOID) +{ + BOOL bErr = (nDbgState == DBG_RUN); + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_RUN; // state "run" + SetEvent(hEventDebug); // run emulation + } + return bErr; +} + +/**************************************************************************** +* EmuRunPC +***************************************************************************** +* +* @func run emulation until stop address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuRunPC( + DWORD dwAddressPC) // @parm breakpoint address +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + dwDbgStopPC = dwAddressPC; // stop address + EmuRun(); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStep +***************************************************************************** +* +* @func execute one ASM instruction and return to caller +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStep(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_STEPINTO; // state "step into" + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStepOver +***************************************************************************** +* +* @func execute one ASM instruction but skip GOSUB, GOSUBL, GOSBVL +* subroutines +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStepOver(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + LPBYTE I; + + bDbgNotify = FALSE; // reset debugger notify flag + + I = FASTPTR(Chipset.pc); + + dwDbgRstkp = Chipset.rstkp; // save stack level + + nDbgState = DBG_STEPINTO; // state "step into" + if (I[0] == 0x7) // GOSUB 7aaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + if (I[0] == 0x8) // GOSUBL or GOSBVL + { + if (I[1] == 0xE) // GOSUBL 8Eaaaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + if (I[1] == 0xF) // GOSBVL 8Eaaaaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + } + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStepOut +***************************************************************************** +* +* @func run emulation until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, +* RTNYES instruction is found above the current stack level +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStepOut(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + dwDbgRstkp = (Chipset.rstkp-1)&7; // save stack data + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + nDbgState = DBG_STEPOUT; // state "step out" + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStop +***************************************************************************** +* +* @func break emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, no debug notify handler +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuStop(VOID) +{ + if (pEmuDbgNotify && nDbgState != DBG_STEPINTO) // emulation running + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + while (!bDbgNotify) Sleep(0); // wait until emulation stopped + } + return nDbgState != DBG_STEPINTO; +} + +/**************************************************************************** +* EmuCallBackDebugNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on debugger breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackDebugNotify( + VOID (CALLBACK *EmuDbgNotify)(INT nBreaktype)) // @parm CallBack function notify Debug breakpoint +{ + if(EmuDbgNotify != NULL) // debugger enabled + { + dwDbgStopPC = -1; // no stop address for goto cursor + dwDbgRplPC = -1; // no stop address for RPL breakpoint + pEmuDbgNotify = EmuDbgNotify; // set new handler + nDbgState = DBG_RUN; // enable debugger + } + else + { + nDbgState = DBG_OFF; // disable debugger + pEmuDbgNotify = EmuDbgNotify; // remove handler + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + } + return; +} + +/**************************************************************************** +* EmuCallBackStackNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on hardware stack change; +* if the CallBack function return TRUE, emulation stops behind the +* opcode changed hardware stack content, otherwise, if the CallBack +* function return FALSE, no breakpoint is set +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackStackNotify( + BOOL (CALLBACK *EmuStackNotify)(VOID)) // @parm CallBack function notify stack changed +{ + dwRefRstkp = -1; // reference stack pointer not initialized + pEmuStackNotify = EmuStackNotify; // set new handler + return; +} + +/**************************************************************************** +* EmuGetRegister +***************************************************************************** +* +* @func read a 32 bit register +* +* @xref none +* +* @rdesc DWORD: 32 bit value of register +* +****************************************************************************/ + +DECLSPEC DWORD CALLBACK EmuGetRegister( + UINT uRegister) // @parm index of register +{ + DWORD dwResult; + + switch(uRegister) + { + case EMU_REGISTER_PC: + dwResult = Chipset.pc; + break; + case EMU_REGISTER_D0: + dwResult = Chipset.d0; + break; + case EMU_REGISTER_D1: + dwResult = Chipset.d1; + break; + case EMU_REGISTER_DUMMY: + case EMU_REGISTER_R5L: + case EMU_REGISTER_R5H: + case EMU_REGISTER_R6L: + case EMU_REGISTER_R6H: + case EMU_REGISTER_R7L: + case EMU_REGISTER_R7H: + dwResult = 0; // dummy return + break; + case EMU_REGISTER_AL: + dwResult = RegToDWORD(&Chipset.A[0],8); + break; + case EMU_REGISTER_AH: + dwResult = RegToDWORD(&Chipset.A[8],8); + break; + case EMU_REGISTER_BL: + dwResult = RegToDWORD(&Chipset.B[0],8); + break; + case EMU_REGISTER_BH: + dwResult = RegToDWORD(&Chipset.B[8],8); + break; + case EMU_REGISTER_CL: + dwResult = RegToDWORD(&Chipset.C[0],8); + break; + case EMU_REGISTER_CH: + dwResult = RegToDWORD(&Chipset.C[8],8); + break; + case EMU_REGISTER_DL: + dwResult = RegToDWORD(&Chipset.D[0],8); + break; + case EMU_REGISTER_DH: + dwResult = RegToDWORD(&Chipset.D[8],8); + break; + case EMU_REGISTER_R0L: + dwResult = RegToDWORD(&Chipset.R0[0],8); + break; + case EMU_REGISTER_R0H: + dwResult = RegToDWORD(&Chipset.R0[8],8); + break; + case EMU_REGISTER_R1L: + dwResult = RegToDWORD(&Chipset.R1[0],8); + break; + case EMU_REGISTER_R1H: + dwResult = RegToDWORD(&Chipset.R1[8],8); + break; + case EMU_REGISTER_R2L: + dwResult = RegToDWORD(&Chipset.R2[0],8); + break; + case EMU_REGISTER_R2H: + dwResult = RegToDWORD(&Chipset.R2[8],8); + break; + case EMU_REGISTER_R3L: + dwResult = RegToDWORD(&Chipset.R3[0],8); + break; + case EMU_REGISTER_R3H: + dwResult = RegToDWORD(&Chipset.R3[8],8); + break; + case EMU_REGISTER_R4L: + dwResult = RegToDWORD(&Chipset.R4[0],8); + break; + case EMU_REGISTER_R4H: + dwResult = RegToDWORD(&Chipset.R4[8],8); + break; + case EMU_REGISTER_FLAGS: + /** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + dwResult = RegToDWORD(Chipset.ST,4); + dwResult <<= 1; + dwResult |= Chipset.Shutdn ? 1 : 0; + dwResult <<= (3+1); + dwResult |= Chipset.intk ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.inte ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.carry ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.mode_dec ? 1 : 0; + dwResult <<= 4; + dwResult |= Chipset.HST; + dwResult <<= 4; + dwResult |= Chipset.P; + break; + case EMU_REGISTER_OUT: + dwResult = Chipset.out; + break; + case EMU_REGISTER_IN: + dwResult = Chipset.in; + break; + case EMU_REGISTER_VIEW1: + dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) >> 4; + break; + case EMU_REGISTER_VIEW2: + dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) & 0xF; + break; + case EMU_REGISTER_RSTKP: + dwResult = Chipset.rstkp; + break; + case EMU_REGISTER_RSTK0: + dwResult = Chipset.rstk[0]; + break; + case EMU_REGISTER_RSTK1: + dwResult = Chipset.rstk[1]; + break; + case EMU_REGISTER_RSTK2: + dwResult = Chipset.rstk[2]; + break; + case EMU_REGISTER_RSTK3: + dwResult = Chipset.rstk[3]; + break; + case EMU_REGISTER_RSTK4: + dwResult = Chipset.rstk[4]; + break; + case EMU_REGISTER_RSTK5: + dwResult = Chipset.rstk[5]; + break; + case EMU_REGISTER_RSTK6: + dwResult = Chipset.rstk[6]; + break; + case EMU_REGISTER_RSTK7: + dwResult = Chipset.rstk[7]; + break; + case EMU_REGISTER_CLKL: + dwResult = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + break; + case EMU_REGISTER_CLKH: + dwResult = (DWORD) (Chipset.cycles >> 32); + break; + case EMU_REGISTER_CRC: + dwResult = Chipset.crc; + break; + default: + dwResult = 0; // default return + _ASSERT(FALSE); // illegal entry + } + return dwResult; +} + +/**************************************************************************** +* EmuSetRegister +***************************************************************************** +* +* @func write a 32 bit register +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSetRegister( + UINT uRegister, // @parm index of register + DWORD dwValue) // @parm new 32 bit value +{ + switch(uRegister) + { + case EMU_REGISTER_PC: + Chipset.pc = dwValue; + break; + case EMU_REGISTER_D0: + Chipset.d0 = dwValue; + break; + case EMU_REGISTER_D1: + Chipset.d1 = dwValue; + break; + case EMU_REGISTER_DUMMY: + case EMU_REGISTER_R5L: + case EMU_REGISTER_R5H: + case EMU_REGISTER_R6L: + case EMU_REGISTER_R6H: + case EMU_REGISTER_R7L: + case EMU_REGISTER_R7H: + break; // dummy return + case EMU_REGISTER_AL: + DWORDToReg(&Chipset.A[0],8,dwValue); + break; + case EMU_REGISTER_AH: + DWORDToReg(&Chipset.A[8],8,dwValue); + break; + case EMU_REGISTER_BL: + DWORDToReg(&Chipset.B[0],8,dwValue); + break; + case EMU_REGISTER_BH: + DWORDToReg(&Chipset.B[8],8,dwValue); + break; + case EMU_REGISTER_CL: + DWORDToReg(&Chipset.C[0],8,dwValue); + break; + case EMU_REGISTER_CH: + DWORDToReg(&Chipset.C[8],8,dwValue); + break; + case EMU_REGISTER_DL: + DWORDToReg(&Chipset.D[0],8,dwValue); + break; + case EMU_REGISTER_DH: + DWORDToReg(&Chipset.D[8],8,dwValue); + break; + case EMU_REGISTER_R0L: + DWORDToReg(&Chipset.R0[0],8,dwValue); + break; + case EMU_REGISTER_R0H: + DWORDToReg(&Chipset.R0[8],8,dwValue); + break; + case EMU_REGISTER_R1L: + DWORDToReg(&Chipset.R1[0],8,dwValue); + break; + case EMU_REGISTER_R1H: + DWORDToReg(&Chipset.R1[8],8,dwValue); + break; + case EMU_REGISTER_R2L: + DWORDToReg(&Chipset.R2[0],8,dwValue); + break; + case EMU_REGISTER_R2H: + DWORDToReg(&Chipset.R2[8],8,dwValue); + break; + case EMU_REGISTER_R3L: + DWORDToReg(&Chipset.R3[0],8,dwValue); + break; + case EMU_REGISTER_R3H: + DWORDToReg(&Chipset.R3[8],8,dwValue); + break; + case EMU_REGISTER_R4L: + DWORDToReg(&Chipset.R4[0],8,dwValue); + break; + case EMU_REGISTER_R4H: + DWORDToReg(&Chipset.R4[8],8,dwValue); + break; + case EMU_REGISTER_FLAGS: + /** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + Chipset.P = (BYTE) (dwValue & 0xF); + dwValue >>= 4; + Chipset.HST = (BYTE) (dwValue & 0xF); + dwValue >>= 4; + Chipset.mode_dec = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.carry = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.inte = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.intk = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= (1+4); + DWORDToReg(Chipset.ST,sizeof(Chipset.ST),dwValue); + PCHANGED; + break; + case EMU_REGISTER_OUT: + Chipset.out = (WORD) (dwValue & 0xFFFF); + break; + case EMU_REGISTER_IN: + Chipset.in = (WORD) (dwValue & 0xFFFF); + break; + case EMU_REGISTER_VIEW1: + Chipset.Bank_FF &= 0x1F; + Chipset.Bank_FF |= (dwValue & 0x03) << (4+1); + RomSwitch(Chipset.Bank_FF); + break; + case EMU_REGISTER_VIEW2: + Chipset.Bank_FF &= 0x61; + Chipset.Bank_FF |= (dwValue & 0x0F) << 1; + RomSwitch(Chipset.Bank_FF); + break; + case EMU_REGISTER_RSTKP: + Chipset.rstkp = dwValue; + break; + case EMU_REGISTER_RSTK0: + Chipset.rstk[0] = dwValue; + break; + case EMU_REGISTER_RSTK1: + Chipset.rstk[1] = dwValue; + break; + case EMU_REGISTER_RSTK2: + Chipset.rstk[2] = dwValue; + break; + case EMU_REGISTER_RSTK3: + Chipset.rstk[3] = dwValue; + break; + case EMU_REGISTER_RSTK4: + Chipset.rstk[4] = dwValue; + break; + case EMU_REGISTER_RSTK5: + Chipset.rstk[5] = dwValue; + break; + case EMU_REGISTER_RSTK6: + Chipset.rstk[6] = dwValue; + break; + case EMU_REGISTER_RSTK7: + Chipset.rstk[7] = dwValue; + break; + case EMU_REGISTER_CLKL: + case EMU_REGISTER_CLKH: + break; // not allowed to change + case EMU_REGISTER_CRC: + Chipset.crc = (WORD) (dwValue & 0xFFFF); + break; + default: + _ASSERT(FALSE); // illegal entry + } + return; +} + +/**************************************************************************** +* EmuGetMem +***************************************************************************** +* +* @func read one nibble from the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuGetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE *pbyValue) // @parm readed nibble +{ + Npeek(pbyValue,dwMapAddr,1); + return; +} + +/**************************************************************************** +* EmuSetMem +***************************************************************************** +* +* @func write one nibble to the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE byValue) // @parm nibble to write +{ + Nwrite(&byValue,dwMapAddr,1); + return; +} + +/**************************************************************************** +* EmuGetRom +***************************************************************************** +* +* @func return size and base address of mapped ROM +* +* @xref none +* +* @rdesc LPBYTE: base address of ROM (pointer to original data) +* +****************************************************************************/ + +DECLSPEC LPBYTE CALLBACK EmuGetRom( + DWORD *pdwRomSize) // @parm return size of ROM in nibbles +{ + *pdwRomSize = dwRomSize; + return pbyRom; +} + +/**************************************************************************** +* EmuSetBreakpoint +***************************************************************************** +* +* @func set ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint table full +* FALSE = OK, Breakpoint set +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSetBreakpoint( + DWORD dwAddress, // @parm breakpoint address to set + UINT nBreakpointType) // @parm breakpoint type to set +{ + INT i; + + _ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type + || nBreakpointType == BP_READ + || nBreakpointType == BP_WRITE + || nBreakpointType == BP_RPL + || nBreakpointType == BP_ACCESS + || nBreakpointType == BP_ROM); + + for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint + { + // breakpoint already set + if ( sBreakpoint[i].dwAddr == dwAddress + && sBreakpoint[i].nType == nBreakpointType) + return FALSE; + } + + if (wBreakpointCount >= MAXBREAKPOINTS) // breakpoint buffer full + return TRUE; + + sBreakpoint[wBreakpointCount].dwAddr = dwAddress; + sBreakpoint[wBreakpointCount].nType = nBreakpointType; + ++wBreakpointCount; + return FALSE; +} + +/**************************************************************************** +* EmuClearBreakpoint +***************************************************************************** +* +* @func clear ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint not found +* FALSE = OK, Breakpoint cleared +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuClearBreakpoint( + DWORD dwAddress, // @parm breakpoint address to clear + UINT nBreakpointType) // @parm breakpoint type to clear +{ + INT i; + + _ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type + || nBreakpointType == BP_READ + || nBreakpointType == BP_WRITE + || nBreakpointType == BP_RPL + || nBreakpointType == BP_ACCESS + || nBreakpointType == BP_ROM); + + for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint + { + // breakpoint found + if ( sBreakpoint[i].dwAddr == dwAddress + && sBreakpoint[i].nType == nBreakpointType) + { + // move rest to top + for (++i; i < wBreakpointCount; ++i) + sBreakpoint[i-1] = sBreakpoint[i]; + + --wBreakpointCount; + return FALSE; // breakpoint found + } + } + return TRUE; // breakpoint not found +} + +/**************************************************************************** +* EmuClearAllBreakpoints +***************************************************************************** +* +* @func clear all breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuClearAllBreakpoints(VOID) +{ + wBreakpointCount = 0; + return; +} + +/**************************************************************************** +* EmuEnableNop3Breakpoint +***************************************************************************** +* +* @func enable/disable NOP3 breakpoint +* stop emulation at Opcode 420 for GOC + (next line) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableNop3Breakpoint( + BOOL bEnable) // @parm stop on NOP3 opcode +{ + bDbgNOP3 = bEnable; // change stop on NOP3 breakpoint flag + return; +} + +/**************************************************************************** +* EmuEnableDocodeBreakpoint +***************************************************************************** +* +* @func enable/disable DOCODE breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableDoCodeBreakpoint( + BOOL bEnable) // @parm stop on DOCODE entry +{ + bDbgCode = bEnable; // change stop on DOCODE breakpoint flag + return; +} + +/**************************************************************************** +* EmuEnableRplBreakpoint +***************************************************************************** +* +* @func enable/disable RPL breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableRplBreakpoint( + BOOL bEnable) // @parm stop on RPL exit +{ + bDbgRPL = bEnable; // change stop on RPL exit flag + return; +} + +/**************************************************************************** +* EmuEnableSkipInterruptCode +***************************************************************************** +* +* @func enable/disable skip single step execution inside the interrupt +* handler, this option has no effect on code and data breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableSkipInterruptCode( + BOOL bEnable) // @parm TRUE = skip code instructions + // inside interrupt service routine +{ + bDbgSkipInt = bEnable; // change skip interrupt code flag + return; +} diff --git a/app/src/main/cpp/DEBUGGER.C b/app/src/main/cpp/DEBUGGER.C new file mode 100644 index 0000000..5701e5c --- /dev/null +++ b/app/src/main/cpp/DEBUGGER.C @@ -0,0 +1,3638 @@ +/* + * debugger.c + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "ops.h" +#include "color.h" +#include "disrpl.h" +#include "debugger.h" + +#define MAXCODELINES 15 // number of lines in code window +#define MAXMEMLINES 6 // number of lines in memory window +#define MAXMEMITEMS 16 // number of address items in a memory window line +#define MAXBREAKPOINTS 256 // max. number of breakpoints + +#define REG_START IDC_REG_A // first register in register update table +#define REG_STOP IDC_MISC_BS // last register in register update table +#define REG_SIZE (REG_STOP-REG_START+1) // size of register update table + +// assert for register update +#define _ASSERTREG(r) _ASSERT(r >= REG_START && r <= REG_STOP) + +#define WM_UPDATE (WM_USER+0x1000) // update debugger dialog box + +#define MEMWNDMAX (sizeof(nCol) / sizeof(nCol[0])) + +#define RT_TOOLBAR MAKEINTRESOURCE(241) // MFC toolbar resource type + +#define CODELABEL 0x80000000 // label in code window + +typedef struct CToolBarData +{ + WORD wVersion; + WORD wWidth; + WORD wHeight; + WORD wItemCount; + WORD aItems[1]; +} CToolBarData; + +typedef struct // type of breakpoint table +{ + BOOL bEnable; // breakpoint enabled + UINT nType; // breakpoint type + DWORD dwAddr; // breakpoint address +} BP_T; + +static CONST int nCol[] = +{ + IDC_DEBUG_MEM_COL0, IDC_DEBUG_MEM_COL1, IDC_DEBUG_MEM_COL2, IDC_DEBUG_MEM_COL3, + IDC_DEBUG_MEM_COL4, IDC_DEBUG_MEM_COL5, IDC_DEBUG_MEM_COL6, IDC_DEBUG_MEM_COL7 +}; + +static CONST TCHAR cHex[] = { _T('0'),_T('1'),_T('2'),_T('3'), + _T('4'),_T('5'),_T('6'),_T('7'), + _T('8'),_T('9'),_T('A'),_T('B'), + _T('C'),_T('D'),_T('E'),_T('F') }; + +static INT nDbgPosX = 0; // position of debugger window +static INT nDbgPosY = 0; + +static WORD wBreakpointCount = 0; // number of breakpoints +static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table + +static WORD wBackupBreakpointCount = 0; // number of breakpoints +static BP_T sBackupBreakpoint[MAXBREAKPOINTS]; // breakpoint table + +static INT nRplBreak; // RPL breakpoint + +static DWORD dwAdrLine[MAXCODELINES]; // addresses of disassember lines in code window +static DWORD dwAdrMem = 0; // start address of memory window + +static UINT uIDFol = ID_DEBUG_MEM_FNONE; // follow mode +static DWORD dwAdrMemFol = 0; // follow address memory window + +static UINT uIDMap = ID_DEBUG_MEM_MAP; // current memory view mode + +static LONG lCharWidth; // width of a character (is a fix font) + +static HMENU hMenuCode,hMenuMem,hMenuStack;// handle of context menues +static HWND hWndToolbar; // toolbar handle + +static DWORD dwDbgRefCycles; // cpu cycles counter from last opcode + +static CHIPSET OldChipset; // old chipset content +static BOOL bRegUpdate[REG_SIZE]; // register update table + +static HBITMAP hBmpCheckBox; // checked and unchecked bitmap +static HFONT hFontBold; // bold font for symbol labels in code window + +// function prototypes +static BOOL OnMemFind(HWND hDlg); +static BOOL OnProfile(HWND hDlg); +static VOID UpdateRplObjViewWnd(HWND hDlg, DWORD dwAddr); +static BOOL OnRplObjView(HWND hDlg); +static BOOL OnSettings(HWND hDlg); +static INT_PTR OnNewValue(LPTSTR lpszValue); +static VOID OnEnterAddress(HWND hDlg, DWORD *dwValue); +static BOOL OnEditBreakpoint(HWND hDlg); +static BOOL OnInfoIntr(HWND hDlg); +static BOOL OnInfoWoRegister(HWND hDlg); +static VOID UpdateProfileWnd(HWND hDlg); +static BOOL OnMemLoadData(HWND hDlg); +static BOOL OnMemSaveData(HWND hDlg); + +//################ +//# +//# Low level subroutines +//# +//################ + +// +// load external rpl symbol table +// +static BOOL LoadSymbTable(VOID) +{ + TCHAR szSymbFilename[MAX_PATH]; + TCHAR szItemname[16]; + + wsprintf(szItemname,_T("Symb%c"),(TCHAR) cCurrentRomType); + ReadSettingsString(_T("Disassembler"),szItemname,_T(""),szSymbFilename,ARRAYSIZEOF(szSymbFilename)); + + if (*szSymbFilename == 0) // no reference table defined + return FALSE; // no success + + return RplLoadTable(szSymbFilename); // load external reference table +} + +// +// disable menu keys +// +static VOID DisableMenuKeys(HWND hDlg) +{ + HMENU hMenu = GetMenu(hDlg); + + EnableMenuItem(hMenu,ID_DEBUG_RUN,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_RUNCURSOR,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEP,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEPOVER,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEPOUT,MF_GRAYED); + EnableMenuItem(hMenu,ID_INFO_LASTINSTRUCTIONS,MF_GRAYED); + EnableMenuItem(hMenu,ID_INFO_WRITEONLYREG,MF_GRAYED); + + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_BREAK,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUNCURSOR,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEP,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOVER,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOUT,MAKELONG((FALSE),0)); + return; +} + +// +// read edit control and decode content as hex number or if enabled as symbol name +// +static BOOL GetAddr(HWND hDlg,INT nID,DWORD *pdwAddr,DWORD dwMaxAddr,BOOL bSymbEnable) +{ + TCHAR szBuffer[48]; + INT i; + BOOL bSucc = TRUE; + + HWND hWnd = GetDlgItem(hDlg,nID); + + GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer)); + + if (*szBuffer != 0) + { + // if address is not a symbol name decode number + if ( !bSymbEnable || szBuffer[0] != _T('=') + || RplGetAddr(&szBuffer[1],pdwAddr)) + { + // test if valid hex address + for (i = 0; bSucc && i < (LONG) lstrlen(szBuffer); ++i) + { + bSucc = (_istxdigit(szBuffer[i]) != 0); + } + + if (bSucc) // valid characters + { + // convert string to number + *pdwAddr = _tcstoul(szBuffer,NULL,16); + } + } + + // inside address range? + bSucc = bSucc && (*pdwAddr <= dwMaxAddr); + + if (!bSucc) // invalid address + { + SendMessage(hWnd,EM_SETSEL,0,-1); + SetFocus(hWnd); // focus to edit control + } + } + return bSucc; +} + +// +// set mapping menu +// +static VOID SetMappingMenu(HWND hDlg,UINT uID) +{ + enum MEM_MAPPING eMode; + LPTSTR szCaption; + + UINT uEnable = MF_GRAYED; // disable Memory Data menu items + + CheckMenuItem(hMenuMem,uIDMap,MF_UNCHECKED); + + switch (uID) + { + case ID_DEBUG_MEM_MAP: + szCaption = _T("Memory"); + eMode = MEM_MMU; + uEnable = MF_ENABLED; // enable Memory Data menu items + break; + case ID_DEBUG_MEM_NCE1: + szCaption = _T("Memory (NCE1)"); + eMode = MEM_NCE1; + break; + case ID_DEBUG_MEM_NCE2: + szCaption = _T("Memory (NCE2)"); + eMode = MEM_NCE2; + break; + case ID_DEBUG_MEM_CE1: + szCaption = _T("Memory (CE1)"); + eMode = MEM_CE1; + break; + case ID_DEBUG_MEM_CE2: + szCaption = _T("Memory (CE2)"); + eMode = MEM_CE2; + break; + case ID_DEBUG_MEM_NCE3: + szCaption = _T("Memory (NCE3)"); + eMode = MEM_NCE3; + break; + default: _ASSERT(0); + } + + VERIFY(SetMemMapType(eMode)); + + if (uIDMap != uID) dwAdrMem = 0; // view from address 0 + + uIDMap = uID; + CheckMenuItem(hMenuMem,uIDMap,MF_CHECKED); + + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_LOAD,uEnable); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_SAVE,uEnable); + + SetDlgItemText(hDlg,IDC_STATIC_MEMORY,szCaption); + return; +}; + +// +// get address of cursor in memory window +// +static DWORD GetMemCurAddr(HWND hDlg) +{ + INT i,nPos; + DWORD dwAddr = dwAdrMem; + DWORD dwMapDataMask = GetMemDataMask(); + + for (i = 0; i < MEMWNDMAX; ++i) // scan all memory cols + { + // column has cursor + if ((nPos = (INT) SendDlgItemMessage(hDlg,nCol[i],LB_GETCURSEL,0,0)) != LB_ERR) + { + dwAddr += (DWORD) (nPos * MEMWNDMAX + i) * 2; + dwAddr &= dwMapDataMask; + break; + } + } + return dwAddr; +} + +// +// set/reset breakpoint +// +static __inline VOID ToggleBreakpoint(DWORD dwAddr) +{ + INT i; + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // code breakpoint found + if (sBreakpoint[i].nType == BP_EXEC && sBreakpoint[i].dwAddr == dwAddr) + { + if (!sBreakpoint[i].bEnable) // breakpoint disabled + { + sBreakpoint[i].bEnable = TRUE; + return; + } + + while (++i < wBreakpointCount) // purge breakpoint + sBreakpoint[i-1] = sBreakpoint[i]; + --wBreakpointCount; + return; + } + } + + // breakpoint not found + if (wBreakpointCount >= MAXBREAKPOINTS) // breakpoint buffer full + { + AbortMessage(_T("Reached maximum number of breakpoints !")); + return; + } + + sBreakpoint[wBreakpointCount].bEnable = TRUE; + sBreakpoint[wBreakpointCount].nType = BP_EXEC; + sBreakpoint[wBreakpointCount].dwAddr = dwAddr; + ++wBreakpointCount; + return; +} + +// +// init memory mapping table and mapping menu entry +// +static __inline VOID InitMemMap(HWND hDlg) +{ + BOOL bActive; + + SetMemRomType(cCurrentRomType); // set current model + + _ASSERT(hMenuMem); + + // enable menu mappings + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE1,GetMemAvail(MEM_NCE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE2,GetMemAvail(MEM_NCE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE1, GetMemAvail(MEM_CE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE2, GetMemAvail(MEM_CE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE3,GetMemAvail(MEM_NCE3) ? MF_ENABLED : MF_GRAYED); + + bActive = (ID_DEBUG_MEM_NCE1 == uIDMap && GetMemAvail(MEM_NCE1)) + || (ID_DEBUG_MEM_NCE2 == uIDMap && GetMemAvail(MEM_NCE2)) + || (ID_DEBUG_MEM_CE1 == uIDMap && GetMemAvail(MEM_CE1)) + || (ID_DEBUG_MEM_CE2 == uIDMap && GetMemAvail(MEM_CE2)) + || (ID_DEBUG_MEM_NCE3 == uIDMap && GetMemAvail(MEM_NCE3)); + + SetMappingMenu(hDlg,bActive ? uIDMap : ID_DEBUG_MEM_MAP); + return; +} + +// +// init bank switcher area +// +static __inline VOID InitBsArea(HWND hDlg) +{ + // not 38 / 48S // CdB for HP: add apples type + if (cCurrentRomType!='A' && cCurrentRomType!='S') + { + EnableWindow(GetDlgItem(hDlg,IDC_MISC_BS_TXT),TRUE); + EnableWindow(GetDlgItem(hDlg,IDC_MISC_BS),TRUE); + } + return; +} + +// +// convert nibble register to string +// +static LPTSTR RegToStr(BYTE *pReg, WORD wNib) +{ + static TCHAR szBuffer[32]; + + WORD i; + + for (i = 0;i < wNib;++i) + szBuffer[i] = cHex[pReg[wNib-i-1]]; + szBuffer[i] = 0; + + return szBuffer; +} + +// +// convert string to nibble register +// +static VOID StrToReg(BYTE *pReg, WORD wNib, LPTSTR lpszValue) +{ + int i,nValuelen; + + nValuelen = lstrlen(lpszValue); + for (i = wNib - 1;i >= 0;--i) + { + if (i >= nValuelen) // no character in string + { + pReg[i] = 0; // fill with zero + } + else + { + // convert to number + pReg[i] = _totupper(*lpszValue) - _T('0'); + if (pReg[i] > 9) pReg[i] -= _T('A') - _T('9') - 1; + ++lpszValue; + } + } + return; +} + +// +// write code window +// +static INT ViewCodeWnd(HWND hWnd, DWORD dwAddress) +{ + enum MEM_MAPPING eMapMode; + LPCTSTR lpszName; + TCHAR szAddress[64]; + DWORD dwNxtAddr; + INT i,j,nLinePC; + + nLinePC = -1; // PC not shown (no selection) + + eMapMode = GetMemMapType(); // get current map mode + SetMemMapType(MEM_MMU); // disassemble in mapped mode + + dwAddress &= 0xFFFFF; // adjust to Saturn address range + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < MAXCODELINES; ++i) + { + // entry has a name + if (disassembler_symb && (lpszName = RplGetName(dwAddress)) != NULL) + { + // save address as label + dwAdrLine[i] = dwAddress | CODELABEL; + + szAddress[0] = _T('='); + lstrcpyn(&szAddress[1],lpszName,ARRAYSIZEOF(szAddress)-1); + SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szAddress); + if (++i == MAXCODELINES) break; + } + + // remember line of PC + if (dwAddress == Chipset.pc) nLinePC = i; + + dwAdrLine[i] = dwAddress; + j = wsprintf(szAddress, + (dwAddress == Chipset.pc) ? _T("%05lX-%c ") : _T("%05lX "), + dwAddress,nRplBreak ? _T('R') : _T('>')); + + dwNxtAddr = (dwAddress + 5) & 0xFFFFF; + + // check if address content is a PCO (Primitive Code Object) + // make sure when the PC points to such an address that it's + // interpreted as opcode + if ((dwAddress != Chipset.pc) && (Read5(dwAddress) == dwNxtAddr)) + { + if (disassembler_mode == HP_MNEMONICS) + { + _tcscat(&szAddress[j],_T("CON(5) (*)+5")); + } + else + { + wsprintf(&szAddress[j],_T("dcr.5 $%05x"),dwNxtAddr); + } + dwAddress = dwNxtAddr; + } + else + { + dwAddress = disassemble(dwAddress,&szAddress[j]); + } + SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szAddress); + } + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + SetMemMapType(eMapMode); // switch back to old map mode + return nLinePC; +} + +// +// write memory window +// +static VOID ViewMemWnd(HWND hDlg, DWORD dwAddress) +{ + #define TEXTOFF 32 + + INT i,j; + TCHAR szBuffer[16],szItem[4]; + DWORD dwMapDataMask; + BYTE cChar; + + szItem[2] = 0; // end of string + dwAdrMem = dwAddress; // save start address of memory window + dwMapDataMask = GetMemDataMask(); // size mask of data mapping + + // purge all list boxes + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_ADDR,LB_RESETCONTENT,0,0); + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_TEXT,LB_RESETCONTENT,0,0); + for (j = 0; j < MEMWNDMAX; ++j) + SendDlgItemMessage(hDlg,nCol[j],LB_RESETCONTENT,0,0); + + for (i = 0; i < MAXMEMLINES; ++i) + { + BYTE byLineData[MAXMEMITEMS]; + + if (ID_DEBUG_MEM_MAP == uIDMap) // mapped memory content + { + wsprintf(szBuffer,_T("%05lX"),dwAddress); + } + else // module memory content + { + wsprintf(szBuffer,_T("%06lX"),dwAddress); + } + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_ADDR,LB_ADDSTRING,0,(LPARAM) szBuffer); + + // fetch data line + GetMemPeek(byLineData, dwAddress, MAXMEMITEMS); + + for (j = 0; j < MAXMEMITEMS; ++j) + { + // read from fetched data line + szItem[j&0x1] = cHex[byLineData[j]]; + // characters are saved in LBS, MSB order + cChar = (cChar >> 4) | (byLineData[j] << 4); + + if ((j&0x1) != 0) + { + // byte field + _ASSERT(j/2 < MEMWNDMAX); + SendDlgItemMessage(hDlg,nCol[j/2],LB_ADDSTRING,0,(LPARAM) szItem); + + // text field + szBuffer[j/2] = (isprint(cChar) != 0) ? cChar : _T('.'); + } + } + szBuffer[j/2] = 0; // end of text string + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_TEXT,LB_ADDSTRING,0,(LPARAM) szBuffer); + + dwAddress = (dwAddress + MAXMEMITEMS) & dwMapDataMask; + } + return; + #undef TEXTOFF +} + + +//################ +//# +//# High level Window draw routines +//# +//################ + +// +// update code window with scrolling +// +static VOID UpdateCodeWnd(HWND hDlg) +{ + DWORD dwAddress,dwPriorAddr; + INT i,j; + + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + + j = (INT) SendMessage(hWnd,LB_GETCOUNT,0,0); // no. of items in table + + // seach for actual address in code area + for (i = 0; i < j; ++i) + { + if (dwAdrLine[i] == Chipset.pc) // found new pc address line + break; + } + + // redraw code window + dwAddress = dwAdrLine[0]; // redraw list box with modified pc + if (i == j) // address not found + { + dwAddress = Chipset.pc; // begin with actual pc + + // check if current pc is the begin of a PCO, show PCO address + dwPriorAddr = (dwAddress - 5) & 0xFFFFF; + if (Read5(dwPriorAddr) == dwAddress) + dwAddress = dwPriorAddr; + } + else + { + if (i > 10) // cursor near bottom line + { + j = 10; // pc to line 11 + + // new address line is label + if ((dwAdrLine[i-11] & CODELABEL) != 0) + --j; // pc to line 10 + + dwAddress = dwAdrLine[i-j]; // move that pc is in line 11 + } + } + + i = ViewCodeWnd(hWnd,dwAddress); // init code area + SendMessage(hWnd,LB_SETCURSEL,i,0); // set cursor on actual pc + return; +} + +// +// update register window +// +static VOID UpdateRegisterWnd(HWND hDlg) +{ + TCHAR szBuffer[64]; + + _ASSERTREG(IDC_REG_A); + bRegUpdate[IDC_REG_A-REG_START] = memcmp(Chipset.A, OldChipset.A, sizeof(Chipset.A)) != 0; + wsprintf(szBuffer,_T("A= %s"),RegToStr(Chipset.A,16)); + SetDlgItemText(hDlg,IDC_REG_A,szBuffer); + _ASSERTREG(IDC_REG_B); + bRegUpdate[IDC_REG_B-REG_START] = memcmp(Chipset.B, OldChipset.B, sizeof(Chipset.B)) != 0; + wsprintf(szBuffer,_T("B= %s"),RegToStr(Chipset.B,16)); + SetDlgItemText(hDlg,IDC_REG_B,szBuffer); + _ASSERTREG(IDC_REG_C); + bRegUpdate[IDC_REG_C-REG_START] = memcmp(Chipset.C, OldChipset.C, sizeof(Chipset.C)) != 0; + wsprintf(szBuffer,_T("C= %s"),RegToStr(Chipset.C,16)); + SetDlgItemText(hDlg,IDC_REG_C,szBuffer); + _ASSERTREG(IDC_REG_D); + bRegUpdate[IDC_REG_D-REG_START] = memcmp(Chipset.D, OldChipset.D, sizeof(Chipset.D)) != 0; + wsprintf(szBuffer,_T("D= %s"),RegToStr(Chipset.D,16)); + SetDlgItemText(hDlg,IDC_REG_D,szBuffer); + _ASSERTREG(IDC_REG_R0); + bRegUpdate[IDC_REG_R0-REG_START] = memcmp(Chipset.R0, OldChipset.R0, sizeof(Chipset.R0)) != 0; + wsprintf(szBuffer,_T("R0=%s"),RegToStr(Chipset.R0,16)); + SetDlgItemText(hDlg,IDC_REG_R0,szBuffer); + _ASSERTREG(IDC_REG_R1); + bRegUpdate[IDC_REG_R1-REG_START] = memcmp(Chipset.R1, OldChipset.R1, sizeof(Chipset.R1)) != 0; + wsprintf(szBuffer,_T("R1=%s"),RegToStr(Chipset.R1,16)); + SetDlgItemText(hDlg,IDC_REG_R1,szBuffer); + _ASSERTREG(IDC_REG_R2); + bRegUpdate[IDC_REG_R2-REG_START] = memcmp(Chipset.R2, OldChipset.R2, sizeof(Chipset.R2)) != 0; + wsprintf(szBuffer,_T("R2=%s"),RegToStr(Chipset.R2,16)); + SetDlgItemText(hDlg,IDC_REG_R2,szBuffer); + _ASSERTREG(IDC_REG_R3); + bRegUpdate[IDC_REG_R3-REG_START] = memcmp(Chipset.R3, OldChipset.R3, sizeof(Chipset.R3)) != 0; + wsprintf(szBuffer,_T("R3=%s"),RegToStr(Chipset.R3,16)); + SetDlgItemText(hDlg,IDC_REG_R3,szBuffer); + _ASSERTREG(IDC_REG_R4); + bRegUpdate[IDC_REG_R4-REG_START] = memcmp(Chipset.R4, OldChipset.R4, sizeof(Chipset.R4)) != 0; + wsprintf(szBuffer,_T("R4=%s"),RegToStr(Chipset.R4,16)); + SetDlgItemText(hDlg,IDC_REG_R4,szBuffer); + _ASSERTREG(IDC_REG_D0); + bRegUpdate[IDC_REG_D0-REG_START] = Chipset.d0 != OldChipset.d0; + wsprintf(szBuffer,_T("D0=%05X"),Chipset.d0); + SetDlgItemText(hDlg,IDC_REG_D0,szBuffer); + _ASSERTREG(IDC_REG_D1); + bRegUpdate[IDC_REG_D1-REG_START] = Chipset.d1 != OldChipset.d1; + wsprintf(szBuffer,_T("D1=%05X"),Chipset.d1); + SetDlgItemText(hDlg,IDC_REG_D1,szBuffer); + _ASSERTREG(IDC_REG_P); + bRegUpdate[IDC_REG_P-REG_START] = Chipset.P != OldChipset.P; + wsprintf(szBuffer,_T("P=%X"),Chipset.P); + SetDlgItemText(hDlg,IDC_REG_P,szBuffer); + _ASSERTREG(IDC_REG_PC); + bRegUpdate[IDC_REG_PC-REG_START] = Chipset.pc != OldChipset.pc; + wsprintf(szBuffer,_T("PC=%05X"),Chipset.pc); + SetDlgItemText(hDlg,IDC_REG_PC,szBuffer); + _ASSERTREG(IDC_REG_OUT); + bRegUpdate[IDC_REG_OUT-REG_START] = Chipset.out != OldChipset.out; + wsprintf(szBuffer,_T("OUT=%03X"),Chipset.out); + SetDlgItemText(hDlg,IDC_REG_OUT,szBuffer); + _ASSERTREG(IDC_REG_IN); + bRegUpdate[IDC_REG_IN-REG_START] = Chipset.in != OldChipset.in; + wsprintf(szBuffer,_T("IN=%04X"),Chipset.in); + SetDlgItemText(hDlg,IDC_REG_IN,szBuffer); + _ASSERTREG(IDC_REG_ST); + bRegUpdate[IDC_REG_ST-REG_START] = memcmp(Chipset.ST, OldChipset.ST, sizeof(Chipset.ST)) != 0; + wsprintf(szBuffer,_T("ST=%s"),RegToStr(Chipset.ST,4)); + SetDlgItemText(hDlg,IDC_REG_ST,szBuffer); + _ASSERTREG(IDC_REG_CY); + bRegUpdate[IDC_REG_CY-REG_START] = Chipset.carry != OldChipset.carry; + wsprintf(szBuffer,_T("CY=%d"),Chipset.carry); + SetDlgItemText(hDlg,IDC_REG_CY,szBuffer); + _ASSERTREG(IDC_REG_MODE); + bRegUpdate[IDC_REG_MODE-REG_START] = Chipset.mode_dec != OldChipset.mode_dec; + wsprintf(szBuffer,_T("Mode=%c"),Chipset.mode_dec ? _T('D') : _T('H')); + SetDlgItemText(hDlg,IDC_REG_MODE,szBuffer); + _ASSERTREG(IDC_REG_MP); + bRegUpdate[IDC_REG_MP-REG_START] = ((Chipset.HST ^ OldChipset.HST) & MP) != 0; + wsprintf(szBuffer,_T("MP=%d"),(Chipset.HST & MP) != 0); + SetDlgItemText(hDlg,IDC_REG_MP,szBuffer); + _ASSERTREG(IDC_REG_SR); + bRegUpdate[IDC_REG_SR-REG_START] = ((Chipset.HST ^ OldChipset.HST) & SR) != 0; + wsprintf(szBuffer,_T("SR=%d"),(Chipset.HST & SR) != 0); + SetDlgItemText(hDlg,IDC_REG_SR,szBuffer); + _ASSERTREG(IDC_REG_SB); + bRegUpdate[IDC_REG_SB-REG_START] = ((Chipset.HST ^ OldChipset.HST) & SB) != 0; + wsprintf(szBuffer,_T("SB=%d"),(Chipset.HST & SB) != 0); + SetDlgItemText(hDlg,IDC_REG_SB,szBuffer); + _ASSERTREG(IDC_REG_XM); + bRegUpdate[IDC_REG_XM-REG_START] = ((Chipset.HST ^ OldChipset.HST) & XM) != 0; + wsprintf(szBuffer,_T("XM=%d"),(Chipset.HST & XM) != 0); + SetDlgItemText(hDlg,IDC_REG_XM,szBuffer); + return; +} + +// +// update memory window +// +static VOID UpdateMemoryWnd(HWND hDlg) +{ + // check follow mode setting for memory window + switch(uIDFol) + { + case ID_DEBUG_MEM_FNONE: break; + case ID_DEBUG_MEM_FADDR: dwAdrMem = Read5(dwAdrMemFol); break; + case ID_DEBUG_MEM_FPC: dwAdrMem = Chipset.pc; break; + case ID_DEBUG_MEM_FD0: dwAdrMem = Chipset.d0; break; + case ID_DEBUG_MEM_FD1: dwAdrMem = Chipset.d1; break; + default: _ASSERT(FALSE); + } + + ViewMemWnd(hDlg,dwAdrMem); + return; +} + +// +// update stack window +// +static VOID UpdateStackWnd(HWND hDlg) +{ + UINT i; + LONG nPos; + TCHAR szBuffer[64]; + + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + nPos = (LONG) SendMessage(hWnd,LB_GETTOPINDEX,0,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 1; i <= ARRAYSIZEOF(Chipset.rstk); ++i) + { + INT j; + + wsprintf(szBuffer,_T("%d: %05X"), i, Chipset.rstk[(Chipset.rstkp-i)&7]); + j = (INT) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + SendMessage(hWnd,LB_SETITEMDATA,j,Chipset.rstk[(Chipset.rstkp-i)&7]); + } + SendMessage(hWnd,LB_SETTOPINDEX,nPos,0); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return; +} + +// +// update MMU window +// +static VOID UpdateMmuWnd(HWND hDlg) +{ + TCHAR szBuffer[64]; + + if (Chipset.IOCfig) + wsprintf(szBuffer,_T("%05X"),Chipset.IOBase); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_IO_A,szBuffer); + if (Chipset.P0Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P0Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_NCE2_A,szBuffer); + if (Chipset.P0Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P0Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_NCE2_S,szBuffer); + if (Chipset.P1Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P1Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE1_A : IDC_MMU_CE2_A,szBuffer); + if (Chipset.P1Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P1Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE1_S : IDC_MMU_CE2_S,szBuffer); + if (Chipset.P2Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P2Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE2_A : IDC_MMU_NCE3_A,szBuffer); + if (Chipset.P2Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P2Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE2_S : IDC_MMU_NCE3_S,szBuffer); + if (Chipset.BSCfig) + wsprintf(szBuffer,_T("%05X"),Chipset.BSBase<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_NCE3_A : IDC_MMU_CE1_A,szBuffer); + if (Chipset.BSCfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.BSSize^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_NCE3_S : IDC_MMU_CE1_S,szBuffer); + return; +} + +// +// update miscellaneous window +// +static VOID UpdateMiscWnd(HWND hDlg) +{ + _ASSERTREG(IDC_MISC_INT); + bRegUpdate[IDC_MISC_INT-REG_START] = Chipset.inte != OldChipset.inte; + SetDlgItemText(hDlg,IDC_MISC_INT,Chipset.inte ? _T("On ") : _T("Off")); + + _ASSERTREG(IDC_MISC_KEY); + bRegUpdate[IDC_MISC_KEY-REG_START] = Chipset.intk != OldChipset.intk; + SetDlgItemText(hDlg,IDC_MISC_KEY,Chipset.intk ? _T("On ") : _T("Off")); + + _ASSERTREG(IDC_MISC_BS); + bRegUpdate[IDC_MISC_BS-REG_START] = FALSE; + + // not 38/48S // CdB for HP: add Apples type + if (cCurrentRomType!='A' && cCurrentRomType!='S') + { + TCHAR szBuffer[64]; + + bRegUpdate[IDC_MISC_BS-REG_START] = (Chipset.Bank_FF & 0x7F) != (OldChipset.Bank_FF & 0x7F); + wsprintf(szBuffer,_T("%02X"),Chipset.Bank_FF & 0x7F); + SetDlgItemText(hDlg,IDC_MISC_BS,szBuffer); + } + return; +} + +// +// update complete debugger dialog +// +VOID OnUpdate(HWND hDlg) +{ + nDbgState = DBG_STEPINTO; // state "step into" + dwDbgStopPC = -1; // disable "cursor stop address" + + // enable debug buttons + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_RUN,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_RUNCURSOR,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEP,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOVER,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOUT,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_INFO_LASTINSTRUCTIONS,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_INFO_WRITEONLYREG,MF_ENABLED); + + // enable toolbar buttons + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_BREAK,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUNCURSOR,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEP,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOVER,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOUT,MAKELONG((TRUE),0)); + + // update windows + UpdateCodeWnd(hDlg); // update code window + UpdateRegisterWnd(hDlg); // update registers window + UpdateMemoryWnd(hDlg); // update memory window + UpdateStackWnd(hDlg); // update stack window + UpdateMmuWnd(hDlg); // update MMU window + UpdateMiscWnd(hDlg); // update bank switcher window + UpdateProfileWnd(hDlgProfile); // update profiler dialog + ShowWindow(hDlg,SW_RESTORE); // pop up if minimized + SetFocus(hDlg); // set focus to debugger + return; +} + + +//################ +//# +//# Virtual key handler +//# +//################ + +// +// toggle breakpoint key handler (F2) +// +static BOOL OnKeyF2(HWND hDlg) +{ + HWND hWnd; + RECT rc; + LONG i; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + i = (LONG) SendMessage(hWnd,LB_GETCURSEL,0,0); // get selected item + ToggleBreakpoint(dwAdrLine[i]); // toggle breakpoint at address + // update region of toggled item + SendMessage(hWnd,LB_GETITEMRECT,i,(LPARAM)&rc); + InvalidateRect(hWnd,&rc,TRUE); + return -1; // call windows default handler +} + +// +// run key handler (F5) +// +static BOOL OnKeyF5(HWND hDlg) +{ + HWND hWnd; + INT i,nPos; + TCHAR szBuf[64]; + + if (nDbgState != DBG_RUN) // emulation stopped + { + DisableMenuKeys(hDlg); // disable menu keys + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + nPos = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + + // clear "->" in code window + for (i = 0; i < MAXCODELINES; ++i) + { + if (dwAdrLine[i] == Chipset.pc) // PC in window + { + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuf); + szBuf[5] = szBuf[6] = _T(' '); + SendMessage(hWnd,LB_DELETESTRING,i,0); + SendMessage(hWnd,LB_INSERTSTRING,i,(LPARAM) szBuf); + break; + } + } + SendMessage(hWnd,LB_SETCURSEL,nPos,0); + + nDbgState = DBG_RUN; // state "run" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step cursor key handler (F6) +// +static BOOL OnKeyF6(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + // get address of selected item + INT nPos = (INT) SendDlgItemMessage(hDlg,IDC_DEBUG_CODE,LB_GETCURSEL,0,0); + dwDbgStopPC = dwAdrLine[nPos]; + + OnKeyF5(hDlg); // run emulation + } + return -1; // call windows default handler +} + +// +// step into key handler (F7) +// +static BOOL OnKeyF7(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + if (bDbgSkipInt) // skip code in interrupt handler + DisableMenuKeys(hDlg); // disable menu keys + + nDbgState = DBG_STEPINTO; // state "step into" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step over key handler (F8) +// +static BOOL OnKeyF8(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + LPBYTE I = FASTPTR(Chipset.pc); + + if (bDbgSkipInt) // skip code in interrupt handler + DisableMenuKeys(hDlg); // disable menu keys + + dwDbgRstkp = Chipset.rstkp; // save stack level + + // GOSUB 7aaa, GOSUBL 8Eaaaa, GOSBVL 8Faaaaa + if (I[0] == 0x7 || (I[0] == 0x8 && (I[1] == 0xE || I[1] == 0xF))) + { + nDbgState = DBG_STEPOVER; // state "step over" + } + else + { + nDbgState = DBG_STEPINTO; // state "step into" + } + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step out key handler (F9) +// +static BOOL OnKeyF9(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + DisableMenuKeys(hDlg); // disable menu keys + dwDbgRstkp = (Chipset.rstkp-1)&7; // save stack data + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + nDbgState = DBG_STEPOUT; // state "step out" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// break key handler (F11) +// +static BOOL OnKeyF11(HWND hDlg) +{ + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// view of given address in disassembler window +// +static BOOL OnCodeGoAdr(HWND hDlg) +{ + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress != -1) + { + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + ViewCodeWnd(hWnd,dwAddress); + SendMessage(hWnd,LB_SETCURSEL,0,0); + } + return -1; // call windows default handler +} + +// +// view pc in disassembler window +// +static BOOL OnCodeGoPC(HWND hDlg) +{ + UpdateCodeWnd(hDlg); + return 0; +} + +// +// set pc to selection +// +static BOOL OnCodeSetPcToSelection(HWND hDlg) +{ + Chipset.pc = dwAdrLine[SendDlgItemMessage(hDlg,IDC_DEBUG_CODE,LB_GETCURSEL,0,0)]; + return OnCodeGoPC(hDlg); +} + +// +// find PCO object in code window +// +static BOOL OnCodeFindPCO(HWND hDlg,DWORD dwAddr,INT nSearchDir) +{ + DWORD dwCnt; + BOOL bMatch; + + // searching upwards / downwards + _ASSERT(nSearchDir == 1 || nSearchDir == -1); + + dwAddr += nSearchDir; // start address for search + + // scan mapped address area until PCO found + for (dwCnt = 0; dwCnt <= 0xFFFFF; ++dwCnt) + { + // is this a PCO? + bMatch = (Read5(dwAddr & 0xFFFFF) == ((dwAddr + 5) & 0xFFFFF)); + + if (bMatch) + { + // update code window + ViewCodeWnd(GetDlgItem(hDlg,IDC_DEBUG_CODE),dwAddr); + break; + } + + dwAddr += nSearchDir; + } + return 0; +} + +// +// view from address in memory window +// +static BOOL OnMemGoDx(HWND hDlg, DWORD dwAddress) +{ + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_MEM_COL0); + + ViewMemWnd(hDlg, dwAddress); + SendMessage(hWnd,LB_SETCURSEL,0,0); + SetFocus(hWnd); + return -1; // call windows default handler +} + +// +// view of given address in memory window +// +static BOOL OnMemGoAdr(HWND hDlg) +{ + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress != -1) // not Cancel key + OnMemGoDx(hDlg,dwAddress & GetMemDataMask()); + return -1; // call windows default handler +} + +// +// view from address in memory window +// +static BOOL OnMemFollow(HWND hDlg,UINT uID) +{ + if (ID_DEBUG_MEM_FADDR == uID) // ask for follow address + { + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress == -1) return -1; // return at cancel button + + dwAdrMemFol = dwAddress; + } + + CheckMenuItem(hMenuMem,uIDFol,MF_UNCHECKED); + uIDFol = uID; + CheckMenuItem(hMenuMem,uIDFol,MF_CHECKED); + UpdateMemoryWnd(hDlg); // update memory window + return -1; // call windows default handler +} + +// +// clear all breakpoints +// +static BOOL OnClearAll(HWND hDlg) +{ + wBreakpointCount = 0; + // redraw code window + InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); + return 0; +} + +// +// toggle menu selection +// +static BOOL OnToggleMenuItem(HWND hDlg,UINT uIDCheckItem,BOOL *bCheck) +{ + *bCheck = !*bCheck; // toggle flag + CheckMenuItem(GetMenu(hDlg),uIDCheckItem,*bCheck ? MF_CHECKED : MF_UNCHECKED); + return 0; +} + +// +// change memory window mapping style +// +static BOOL OnMemMapping(HWND hDlg,UINT uID) +{ + if (uID == uIDMap) return -1; // same view, call windows default handler + + SetMappingMenu(hDlg,uID); // update menu settings + UpdateMemoryWnd(hDlg); // update memory window + return 0; +} + +// +// push value on hardware stack +// +static BOOL OnStackPush(HWND hDlg) +{ + TCHAR szBuffer[] = _T("00000"); + DWORD dwAddr; + HWND hWnd; + UINT i,j; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (UINT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == (INT) i) return TRUE; // no selection + + if (IDOK != OnNewValue(szBuffer)) // canceled function + return TRUE; + _stscanf(szBuffer,_T("%5X"),&dwAddr); + + // push stack element + for (j = ARRAYSIZEOF(Chipset.rstk); j > i + 1; --j) + { + Chipset.rstk[(Chipset.rstkp-j)&7] = Chipset.rstk[(Chipset.rstkp-j+1)&7]; + } + Chipset.rstk[(Chipset.rstkp-j)&7] = dwAddr; + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// +// pop value from hardware stack +// +static BOOL OnStackPop(HWND hDlg) +{ + HWND hWnd; + UINT i,j; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (UINT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == (INT) i) return TRUE; // no selection + + // pop stack element + for (j = i + 1; j < ARRAYSIZEOF(Chipset.rstk); ++j) + { + Chipset.rstk[(Chipset.rstkp-j)&7] = Chipset.rstk[(Chipset.rstkp-j-1)&7]; + } + Chipset.rstk[Chipset.rstkp] = 0; + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// modify value on hardware stack +static BOOL OnStackModify(HWND hDlg) +{ + TCHAR szBuffer[16]; + HWND hWnd; + INT i; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == i) return TRUE; // no selection + + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuffer); + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.rstk[(Chipset.rstkp-i-1)&7]); + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// +// new register setting +// +static BOOL OnLButtonUp(HWND hDlg, LPARAM lParam) +{ + TCHAR szBuffer[64]; + POINT pt; + HWND hWnd; + INT nId; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); + + // handle of selected window + hWnd = ChildWindowFromPointEx(hDlg,pt,CWP_SKIPDISABLED); + nId = GetDlgCtrlID(hWnd); // control ID of window + + GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer)); + switch (nId) + { + case IDC_REG_A: // A + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.A,16,&szBuffer[3]); + break; + case IDC_REG_B: // B + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.B,16,&szBuffer[3]); + break; + case IDC_REG_C: // C + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.C,16,&szBuffer[3]); + break; + case IDC_REG_D: // D + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.D,16,&szBuffer[3]); + break; + case IDC_REG_R0: // R0 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R0,16,&szBuffer[3]); + break; + case IDC_REG_R1: // R1 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R1,16,&szBuffer[3]); + break; + case IDC_REG_R2: // R2 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R2,16,&szBuffer[3]); + break; + case IDC_REG_R3: // R3 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R3,16,&szBuffer[3]); + break; + case IDC_REG_R4: // R4 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R4,16,&szBuffer[3]); + break; + case IDC_REG_D0: // D0 + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.d0); + break; + case IDC_REG_D1: // D1 + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.d1); + break; + case IDC_REG_P: // P + OnNewValue(&szBuffer[2]); + Chipset.P = _totupper(szBuffer[2]) - _T('0'); + if (Chipset.P > 9) Chipset.P -= 7; + PCHANGED; + break; + case IDC_REG_PC: // PC + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.pc); + break; + case IDC_REG_OUT: // OUT + OnNewValue(&szBuffer[4]); + Chipset.out = (WORD) _tcstoul(&szBuffer[4],NULL,16); + break; + case IDC_REG_IN: // IN + OnNewValue(&szBuffer[3]); + Chipset.in = (WORD) _tcstoul(&szBuffer[3],NULL,16); + break; + case IDC_REG_ST: // ST + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.ST,4,&szBuffer[3]); + break; + case IDC_REG_CY: // CY + Chipset.carry = !Chipset.carry; + break; + case IDC_REG_MODE: // MODE + Chipset.mode_dec = !Chipset.mode_dec; + break; + case IDC_REG_MP: // MP + Chipset.HST ^= MP; + break; + case IDC_REG_SR: // SR + Chipset.HST ^= SR; + break; + case IDC_REG_SB: // SB + Chipset.HST ^= SB; + break; + case IDC_REG_XM: // XM + Chipset.HST ^= XM; + break; + case IDC_MISC_INT: // interrupt status + Chipset.inte = !Chipset.inte; + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + case IDC_MISC_KEY: // 1ms keyboard scan + Chipset.intk = !Chipset.intk; + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + case IDC_MISC_BS: // Bank switcher setting + OnNewValue(szBuffer); + Chipset.Bank_FF = _tcstoul(szBuffer,NULL,16); + Chipset.Bank_FF &= 0x7F; + RomSwitch(Chipset.Bank_FF); // update memory mapping + + UpdateCodeWnd(hDlg); // update code window + UpdateMemoryWnd(hDlg); // update memory window + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + } + UpdateRegisterWnd(hDlg); // update register + return TRUE; +} + +// +// double click in list box area +// +static BOOL OnDblClick(HWND hWnd, WORD wId) +{ + HWND hDlg,hWndCode; + TCHAR szBuffer[4]; + BYTE byData; + INT i; + DWORD dwAddress; + + hDlg = GetParent(hWnd); // dialog window handle + hWndCode = GetDlgItem(hDlg,IDC_DEBUG_CODE); + + if (wId == IDC_DEBUG_STACK) // stack list box + { + // get cpu address of selected item + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + dwAddress = (DWORD) SendMessage(hWnd,LB_GETITEMDATA,i,0); + + ViewCodeWnd(hWndCode,dwAddress); // show code of this address + return TRUE; + } + + for (i = 0; i < MEMWNDMAX; ++i) // scan all Id's + if (nCol[i] == wId) // found ID + break; + + // not IDC_DEBUG_MEM window or module mode -> default handler + if (i == MEMWNDMAX || ID_DEBUG_MEM_MAP != uIDMap) + return FALSE; + + // calculate address of byte + dwAddress = i * 2; + i = (INT) SendMessage(hWnd,LB_GETCARETINDEX,0,0); + dwAddress += MAXMEMITEMS * i + dwAdrMem; + + // enter new value + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuffer); + OnNewValue(szBuffer); + byData = (BYTE) _tcstoul(szBuffer,NULL,16); + byData = (byData >> 4) | (byData << 4); // change nibbles for writing + + Write2(dwAddress,byData); // write data + ViewCodeWnd(hWndCode,dwAdrLine[0]); // update code window + ViewMemWnd(hDlg,dwAdrMem); // update memory window + SendMessage(hWnd,LB_SETCURSEL,i,0); + return FALSE; +} + +// +// request for context menu +// +static VOID OnContextMenu(HWND hDlg, LPARAM lParam, WPARAM wParam) +{ + POINT pt; + INT nId; + + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position + + if (pt.x == -1 && pt.y == -1) // VK_APPS + { + RECT rc; + + GetWindowRect((HWND) wParam,&rc); // get position of active window + pt.x = rc.left + 5; + pt.y = rc.top + 5; + } + + nId = GetDlgCtrlID((HWND) wParam); // control ID of window + + switch(nId) + { + case IDC_DEBUG_CODE: // handle code window + TrackPopupMenu(hMenuCode,0,pt.x,pt.y,0,hDlg,NULL); + break; + + case IDC_DEBUG_MEM_COL0: + case IDC_DEBUG_MEM_COL1: + case IDC_DEBUG_MEM_COL2: + case IDC_DEBUG_MEM_COL3: + case IDC_DEBUG_MEM_COL4: + case IDC_DEBUG_MEM_COL5: + case IDC_DEBUG_MEM_COL6: + case IDC_DEBUG_MEM_COL7: // handle memory window + TrackPopupMenu(hMenuMem,0,pt.x,pt.y,0,hDlg,NULL); + break; + + case IDC_DEBUG_STACK: // handle stack window + TrackPopupMenu(hMenuStack,0,pt.x,pt.y,0,hDlg,NULL); + break; + } + return; +} + +// +// mouse setting for capturing window +// +static BOOL OnSetCursor(HWND hDlg) +{ + // debugger not active but cursor is over debugger window + if (bActFollowsMouse && GetActiveWindow() != hDlg) + { + // force debugger window to foreground + ForceForegroundWindow(GetLastActivePopup(hDlg)); + } + return FALSE; +} + +//################ +//# +//# Dialog handler +//# +//################ + +// +// handle right/left keys in memory window +// +static __inline BOOL OnKeyRightLeft(HWND hWnd, WPARAM wParam) +{ + HWND hWndNew; + WORD wX; + INT nId; + + nId = GetDlgCtrlID(hWnd); // control ID of window + + for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's + if (nCol[wX] == nId) // found ID + break; + + if (wX == MEMWNDMAX) return -1; // not IDC_DEBUG_MEM window, default handler + + // new position + wX = ((LOWORD(wParam) == VK_RIGHT) ? (wX + 1) : (wX + MEMWNDMAX - 1)) % MEMWNDMAX; + + // set new focus + hWndNew = GetDlgItem(GetParent(hWnd),nCol[wX]); + SendMessage(hWndNew,LB_SETCURSEL,HIWORD(wParam),0); + SetFocus(hWndNew); + return -2; +} + +// +// handle (page) up/down keys in memory window +// +static __inline BOOL OnKeyUpDown(HWND hWnd, WPARAM wParam) +{ + INT wX, wY; + INT nId; + + nId = GetDlgCtrlID(hWnd); // control ID of window + + for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's + if (nCol[wX] == nId) // found ID + break; + + if (wX == MEMWNDMAX) return -1; // not IDC_DEBUG_MEM window, default handler + + wY = HIWORD(wParam); // get old focus + + switch(LOWORD(wParam)) + { + case VK_NEXT: + dwAdrMem = (dwAdrMem + MAXMEMITEMS * MAXMEMLINES) & GetMemDataMask(); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + + case VK_PRIOR: + dwAdrMem = (dwAdrMem - MAXMEMITEMS * MAXMEMLINES) & GetMemDataMask(); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + + case VK_DOWN: + if (wY+1 >= MAXMEMLINES) + { + dwAdrMem = (dwAdrMem + MAXMEMITEMS) & GetMemDataMask(); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + } + break; + + case VK_UP: + if (wY == 0) + { + dwAdrMem = (dwAdrMem - MAXMEMITEMS) & GetMemDataMask(); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + } + break; + } + return -1; +} + +// +// handle (page) +/- keys in memory window +// +static __inline BOOL OnKeyPlusMinus(HWND hWnd, WPARAM wParam) +{ + INT wX, wY; + INT nId; + + nId = GetDlgCtrlID(hWnd); // control ID of window + + for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's + if (nCol[wX] == nId) // found ID + break; + + if (wX == MEMWNDMAX) return -1; // not IDC_DEBUG_MEM window, default handler + + wY = HIWORD(wParam); // get focus + + if (LOWORD(wParam) == VK_ADD) // move start address of memory view + dwAdrMem++; + else + dwAdrMem--; + dwAdrMem &= GetMemDataMask(); + + ViewMemWnd(GetParent(hWnd),dwAdrMem); // redraw memory view + SendMessage(hWnd,LB_SETCURSEL,wY,0); // set focus at old position + return -1; +} + +// +// handle keys in code window +// +static __inline BOOL OnKeyCodeWnd(HWND hDlg, WPARAM wParam) +{ + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + WORD wKey = LOWORD(wParam); + WORD wItem = HIWORD(wParam); + + // down key on last line + if ((wKey == VK_DOWN || wKey == VK_NEXT) && wItem == MAXCODELINES - 1) + { + WORD i = ((dwAdrLine[0] & CODELABEL) != 0) ? 2 : 1; + + ViewCodeWnd(hWnd,dwAdrLine[i]); + SendMessage(hWnd,LB_SETCURSEL,wItem-i,0); + } + // up key on first line + if ((wKey == VK_UP || wKey == VK_PRIOR) && wItem == 0) + { + ViewCodeWnd(hWnd,dwAdrLine[0]-1); + } + + if (wKey == _T('G')) return OnCodeGoAdr(GetParent(hWnd)); // goto new address + + return -1; // process key +} + +// +// handle drawing in code window +// +static __inline BOOL OnDrawCodeWnd(LPDRAWITEMSTRUCT lpdis) +{ + TCHAR szBuf[64]; + COLORREF crBkColor; + COLORREF crTextColor; + HFONT hFont; + BOOL bBrk,bPC,bLabel; + + if (lpdis->itemID == -1) // no item in list box + return TRUE; + + // get item text + SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf); + + // line is a label + bLabel = ((dwAdrLine[lpdis->itemID] & CODELABEL) != 0); + + if (!bLabel) + { + // check for codebreakpoint + bBrk = CheckBreakpoint(dwAdrLine[lpdis->itemID],1,BP_EXEC); + bPC = szBuf[5] != _T(' '); // check if line of program counter + + crTextColor = COLOR_WHITE; // standard text color + + if (lpdis->itemState & ODS_SELECTED) // cursor line + { + if (bPC) // PC line + { + crBkColor = bBrk ? COLOR_DKGRAY : COLOR_TEAL; + } + else // normal line + { + crBkColor = bBrk ? COLOR_PURPLE : COLOR_NAVY; + } + } + else // not cursor line + { + if (bPC) // PC line + { + crBkColor = bBrk ? COLOR_OLIVE : COLOR_GREEN; + } + else // normal line + { + if (bBrk) + { + crBkColor = COLOR_MAROON; + } + else + { + crBkColor = COLOR_WHITE; + crTextColor = COLOR_BLACK; + } + } + } + } + else // label + { + crBkColor = COLOR_WHITE; + crTextColor = COLOR_NAVY; + hFont = (HFONT) SelectObject(lpdis->hDC,hFontBold); + } + + // write Text + crBkColor = SetBkColor(lpdis->hDC,crBkColor); + crTextColor = SetTextColor(lpdis->hDC,crTextColor); + + ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+2,(int)(lpdis->rcItem.top), + ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL); + + SetBkColor(lpdis->hDC,crBkColor); + SetTextColor(lpdis->hDC,crTextColor); + + if (bLabel) SelectObject(lpdis->hDC,hFont); + + if (lpdis->itemState & ODS_FOCUS) // redraw focus + DrawFocusRect(lpdis->hDC,&lpdis->rcItem); + + return TRUE; // focus handled here +} + +// +// detect changed register +// +static __inline BOOL OnCtlColorStatic(HWND hWnd) +{ + BOOL bError = FALSE; // not changed + + int nId = GetDlgCtrlID(hWnd); + if (nId >= REG_START && nId <= REG_STOP) // in register area + bError = bRegUpdate[nId-REG_START]; // register changed? + return bError; +} + + +//################ +//# +//# Public functions +//# +//################ + +// +// handle upper 32 bit of cpu cycle counter +// +VOID UpdateDbgCycleCounter(VOID) +{ + // update 64 bit cpu cycle counter + if (Chipset.cycles < dwDbgRefCycles) ++Chipset.cycles_reserved; + dwDbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + return; +} + +// +// check for breakpoints +// +BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType) +{ + INT i; + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // check address range and type + if ( sBreakpoint[i].bEnable + && sBreakpoint[i].dwAddr >= dwAddr && sBreakpoint[i].dwAddr < dwAddr + dwRange + && (sBreakpoint[i].nType & nType) != 0) + return TRUE; + } + return FALSE; +} + +// +// notify debugger that emulation stopped +// +VOID NotifyDebugger(INT nType) // update registers +{ + nRplBreak = nType; // save breakpoint type + _ASSERT(hDlgDebug); // debug dialog box open + PostMessage(hDlgDebug,WM_UPDATE,0,0); + return; +} + +// +// disable debugger +// +VOID DisableDebugger(VOID) +{ + if (hDlgDebug) // debugger running + DestroyWindow(hDlgDebug); // then close debugger to renter emulation + return; +} + + +//################ +//# +//# Debugger Message loop +//# +//################ + +// +// ID_TOOL_DEBUG +// +static __inline HWND CreateToolbar(HWND hWnd) +{ + HRSRC hRes; + HGLOBAL hGlobal; + CToolBarData *pData; + TBBUTTON *ptbb; + INT i,j; + + HWND hWndToolbar = NULL; // toolbar window + + InitCommonControls(); // ensure that common control DLL is loaded + + if ((hRes = FindResource(hApp,MAKEINTRESOURCE(IDR_DEBUG_TOOLBAR),RT_TOOLBAR)) == NULL) + goto quit; + + if ((hGlobal = LoadResource(hApp,hRes)) == NULL) + goto quit; + + if ((pData = (CToolBarData*) LockResource(hGlobal)) == NULL) + goto unlock; + + _ASSERT(pData->wVersion == 1); // toolbar resource version + + // alloc memory for TBBUTTON stucture + if (!(ptbb = (TBBUTTON *) malloc(pData->wItemCount*sizeof(TBBUTTON)))) + goto unlock; + + // fill TBBUTTON stucture with resource data + for (i = j = 0; i < pData->wItemCount; ++i) + { + if (pData->aItems[i]) + { + ptbb[i].iBitmap = j++; + ptbb[i].fsStyle = TBSTYLE_BUTTON; + } + else + { + ptbb[i].iBitmap = 5; // separator width + ptbb[i].fsStyle = TBSTYLE_SEP; + } + ptbb[i].idCommand = pData->aItems[i]; + ptbb[i].fsState = TBSTATE_ENABLED; + ptbb[i].dwData = 0; + ptbb[i].iString = j; + } + + hWndToolbar = CreateToolbarEx(hWnd,WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS, + IDR_DEBUG_TOOLBAR,j,hApp,IDR_DEBUG_TOOLBAR,ptbb,pData->wItemCount, + pData->wWidth,pData->wHeight,pData->wWidth,pData->wHeight, + sizeof(TBBUTTON)); + + free(ptbb); + +unlock: + FreeResource(hGlobal); +quit: + return hWndToolbar; +} + +static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HMENU hMenuMainCode,hMenuMainMem,hMenuMainStack; + + WINDOWPLACEMENT wndpl; + TEXTMETRIC tm; + HDC hDC; + HFONT hFont; + HMENU hSysMenu; + INT i; + + switch (message) + { + case WM_INITDIALOG: + SetWindowLocation(hDlg,nDbgPosX,nDbgPosY); + if (bAlwaysOnTop) SetWindowPos(hDlg,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); + SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM) LoadIcon(hApp,MAKEINTRESOURCE(IDI_EMU48))); + + // add Settings item to sysmenu + _ASSERT((IDM_DEBUG_SETTINGS & 0xFFF0) == IDM_DEBUG_SETTINGS); + _ASSERT(IDM_DEBUG_SETTINGS < 0xF000); + if ((hSysMenu = GetSystemMenu(hDlg,FALSE)) != NULL) + { + VERIFY(AppendMenu(hSysMenu,MF_SEPARATOR,0,NULL)); + VERIFY(AppendMenu(hSysMenu,MF_STRING,IDM_DEBUG_SETTINGS,_T("Debugger Settings..."))); + } + + hWndToolbar = CreateToolbar(hDlg); // add toolbar + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_NOP3, bDbgNOP3 ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_DOCODE,bDbgCode ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_RPL, bDbgRPL ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_INTR_STEPOVERINT, bDbgSkipInt ? MF_CHECKED : MF_UNCHECKED); + hDlgDebug = hDlg; // handle for debugger dialog + hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventDebug == NULL) + { + AbortMessage(_T("Event creation failed !")); + return TRUE; + } + hMenuMainCode = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_CODE)); + _ASSERT(hMenuMainCode); + hMenuCode = GetSubMenu(hMenuMainCode, 0); + _ASSERT(hMenuCode); + hMenuMainMem = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_MEM)); + _ASSERT(hMenuMainMem); + hMenuMem = GetSubMenu(hMenuMainMem, 0); + _ASSERT(hMenuMem); + hMenuMainStack = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_STACK)); + _ASSERT(hMenuMainStack); + hMenuStack = GetSubMenu(hMenuMainStack, 0); + _ASSERT(hMenuStack); + + // bold font for symbol labels in code window + hDC = GetDC(hDlg); + VERIFY(hFontBold = CreateFont(-MulDiv(8,GetDeviceCaps(hDC,LOGPIXELSY),72),0,0,0,FW_BOLD,0,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Arial"))); + ReleaseDC(hDlg,hDC); + + // font settings + SendDlgItemMessage(hDlg,IDC_STATIC_CODE, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_REGISTERS,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MEMORY, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_STACK, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MMU, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MISC, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + // init last instruction circular buffer + pdwInstrArray = (DWORD *) malloc(wInstrSize*sizeof(*pdwInstrArray)); + wInstrWp = wInstrRp = 0; // write/read pointer + + // init "Follow" menu entry in debugger "Memory" context menu + CheckMenuItem(hMenuMem,uIDFol,MF_CHECKED); + + LoadSymbTable(); // load external rpl symbol table + + InitMemMap(hDlg); // init memory mapping table + InitBsArea(hDlg); // init bank switcher list box + DisableMenuKeys(hDlg); // set debug menu keys into run state + + RplReadNibble = GetMemNib; // get nibble function for RPL object viewer + + dwDbgStopPC = -1; // no stop address for goto cursor + dwDbgRplPC = -1; // no stop address for RPL breakpoint + + // init reference cpu cycle counter for 64 bit debug cycle counter + dwDbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + + OldChipset = Chipset; // save chipset values + return TRUE; + + case WM_DESTROY: + // SetHP48Time(); // update time & date + nDbgState = DBG_OFF; // debugger inactive + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + if (pdwInstrArray) // free last instruction circular buffer + { + EnterCriticalSection(&csDbgLock); + { + free(pdwInstrArray); + pdwInstrArray = NULL; + } + LeaveCriticalSection(&csDbgLock); + } + CloseHandle(hEventDebug); + wndpl.length = sizeof(wndpl); // save debugger window position + GetWindowPlacement(hDlg, &wndpl); + nDbgPosX = wndpl.rcNormalPosition.left; + nDbgPosY = wndpl.rcNormalPosition.top; + RplDeleteTable(); // delete rpl symbol table + DeleteObject(hFontBold); // delete bold font + DestroyMenu(hMenuMainCode); + DestroyMenu(hMenuMainMem); + DestroyMenu(hMenuMainStack); + hDlgDebug = NULL; // debugger windows closed + break; + + case WM_CLOSE: + DestroyWindow(hDlg); + break; + + case WM_UPDATE: + OnUpdate(hDlg); + return TRUE; + + case WM_SYSCOMMAND: + if ((wParam & 0xFFF0) == IDM_DEBUG_SETTINGS) + { + return OnSettings(hDlg); + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + return OnDblClick((HWND) lParam, LOWORD(wParam)); + + case LBN_SETFOCUS: + i = (INT) SendMessage((HWND) lParam,LB_GETCARETINDEX,0,0); + SendMessage((HWND) lParam,LB_SETCURSEL,i,0); + return TRUE; + + case LBN_KILLFOCUS: + SendMessage((HWND) lParam,LB_SETCURSEL,-1,0); + return TRUE; + } + + switch (LOWORD(wParam)) + { + case ID_BREAKPOINTS_SETBREAK: return OnKeyF2(hDlg); + case ID_DEBUG_RUN: return OnKeyF5(hDlg); + case ID_DEBUG_RUNCURSOR: return OnKeyF6(hDlg); + case ID_DEBUG_STEP: return OnKeyF7(hDlg); + case ID_DEBUG_STEPOVER: return OnKeyF8(hDlg); + case ID_DEBUG_STEPOUT: return OnKeyF9(hDlg); + case ID_DEBUG_BREAK: return OnKeyF11(hDlg); + case ID_DEBUG_CODE_GOADR: return OnCodeGoAdr(hDlg); + case ID_DEBUG_CODE_GOPC: return OnCodeGoPC(hDlg); + case ID_DEBUG_CODE_SETPCTOSELECT: return OnCodeSetPcToSelection(hDlg); + case ID_DEBUG_CODE_PREVPCO: return OnCodeFindPCO(hDlg,dwAdrLine[0],-1); + case ID_DEBUG_CODE_NEXTPCO: return OnCodeFindPCO(hDlg,dwAdrLine[0],1); + case ID_BREAKPOINTS_CODEEDIT: return OnEditBreakpoint(hDlg); + case ID_BREAKPOINTS_CLEARALL: return OnClearAll(hDlg); + case ID_BREAKPOINTS_NOP3: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgNOP3); + case ID_BREAKPOINTS_DOCODE: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgCode); + case ID_BREAKPOINTS_RPL: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgRPL); + case ID_INFO_LASTINSTRUCTIONS: return OnInfoIntr(hDlg); + case ID_INFO_PROFILE: return OnProfile(hDlg); + case ID_INFO_WRITEONLYREG: return OnInfoWoRegister(hDlg); + case ID_INTR_STEPOVERINT: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgSkipInt); + case ID_DEBUG_MEM_GOADR: return OnMemGoAdr(hDlg); + case ID_DEBUG_MEM_GOPC: return OnMemGoDx(hDlg,Chipset.pc); + case ID_DEBUG_MEM_GOD0: return OnMemGoDx(hDlg,Chipset.d0); + case ID_DEBUG_MEM_GOD1: return OnMemGoDx(hDlg,Chipset.d1); + case ID_DEBUG_MEM_GOSTACK: return OnMemGoDx(hDlg,Chipset.rstk[(Chipset.rstkp-1)&7]); + case ID_DEBUG_MEM_FNONE: + case ID_DEBUG_MEM_FADDR: + case ID_DEBUG_MEM_FPC: + case ID_DEBUG_MEM_FD0: + case ID_DEBUG_MEM_FD1: return OnMemFollow(hDlg,LOWORD(wParam)); + case ID_DEBUG_MEM_FIND: return OnMemFind(hDlg); + case ID_DEBUG_MEM_MAP: + case ID_DEBUG_MEM_NCE1: + case ID_DEBUG_MEM_NCE2: + case ID_DEBUG_MEM_CE1: + case ID_DEBUG_MEM_CE2: + case ID_DEBUG_MEM_NCE3: return OnMemMapping(hDlg,LOWORD(wParam)); + case ID_DEBUG_MEM_LOAD: return OnMemLoadData(hDlg); + case ID_DEBUG_MEM_SAVE: return OnMemSaveData(hDlg); + case ID_DEBUG_MEM_RPLVIEW: return OnRplObjView(hDlg); + case ID_DEBUG_STACK_PUSH: return OnStackPush(hDlg); + case ID_DEBUG_STACK_POP: return OnStackPop(hDlg); + case ID_DEBUG_STACK_MODIFY: return OnStackModify(hDlg); + case ID_DEBUG_CANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + + case WM_VKEYTOITEM: + switch (LOWORD(wParam)) // always valid + { + case VK_F2: return OnKeyF2(hDlg); // toggle breakpoint + case VK_F5: return OnKeyF5(hDlg); // key run + case VK_F6: return OnKeyF6(hDlg); // key step cursor + case VK_F7: return OnKeyF7(hDlg); // key step into + case VK_F8: return OnKeyF8(hDlg); // key step over + case VK_F9: return OnKeyF9(hDlg); // key step out + case VK_F11: return OnKeyF11(hDlg); // key break + } + + switch(GetDlgCtrlID((HWND) lParam)) // calling window + { + // handle code window + case IDC_DEBUG_CODE: + return OnKeyCodeWnd(hDlg, wParam); + + // handle memory window + case IDC_DEBUG_MEM_COL0: + case IDC_DEBUG_MEM_COL1: + case IDC_DEBUG_MEM_COL2: + case IDC_DEBUG_MEM_COL3: + case IDC_DEBUG_MEM_COL4: + case IDC_DEBUG_MEM_COL5: + case IDC_DEBUG_MEM_COL6: + case IDC_DEBUG_MEM_COL7: + switch (LOWORD(wParam)) + { + case _T('G'): return OnMemGoAdr(GetParent((HWND) lParam)); + case _T('F'): return OnMemFind(GetParent((HWND) lParam)); + case VK_RIGHT: + case VK_LEFT: return OnKeyRightLeft((HWND) lParam, wParam); + case VK_NEXT: + case VK_PRIOR: + case VK_DOWN: + case VK_UP: return OnKeyUpDown((HWND) lParam, wParam); + case VK_ADD: + case VK_SUBTRACT: return OnKeyPlusMinus((HWND) lParam, wParam); + } + break; + } + return -1; // default action + + case WM_LBUTTONUP: + return OnLButtonUp(hDlg, lParam); + + case WM_CONTEXTMENU: + OnContextMenu(hDlg, lParam, wParam); + break; + + case WM_SETCURSOR: + return OnSetCursor(hDlg); + + case WM_CTLCOLORSTATIC: // register color highlighting + // highlight text? + if (OnCtlColorStatic((HWND) lParam)) + { + SetTextColor((HDC) wParam, COLOR_RED); + SetBkColor((HDC) wParam, GetSysColor(COLOR_BTNFACE)); + return (INT_PTR) GetStockObject(NULL_BRUSH); // transparent brush + } + break; + + case WM_NOTIFY: + // tooltip for toolbar + if (((LPNMHDR) lParam)->code == TTN_GETDISPINFO) + { + ((LPTOOLTIPTEXT) lParam)->hinst = hApp; + ((LPTOOLTIPTEXT) lParam)->lpszText = MAKEINTRESOURCE(((LPTOOLTIPTEXT) lParam)->hdr.idFrom); + break; + } + break; + + case WM_DRAWITEM: + if (wParam == IDC_DEBUG_CODE) return OnDrawCodeWnd((LPDRAWITEMSTRUCT) lParam); + break; + + case WM_MEASUREITEM: + hDC = GetDC(hDlg); + + // GetTextMetrics from "Courier New 8" font + hFont = CreateFont(-MulDiv(8,GetDeviceCaps(hDC, LOGPIXELSY),72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Courier New")); + + hFont = (HFONT) SelectObject(hDC,hFont); + GetTextMetrics(hDC,&tm); + hFont = (HFONT) SelectObject(hDC,hFont); + DeleteObject(hFont); + + ((LPMEASUREITEMSTRUCT) lParam)->itemHeight = tm.tmHeight; + lCharWidth = tm.tmAveCharWidth; + + ReleaseDC(hDlg,hDC); + return TRUE; + } + return FALSE; +} + +LRESULT OnToolDebug(VOID) // debugger dialogbox call +{ + if ((hDlgDebug = CreateDialog(hApp,MAKEINTRESOURCE(IDD_DEBUG),NULL, + (DLGPROC)Debugger)) == NULL) + AbortMessage(_T("Debugger Dialog Box Creation Error !")); + return 0; +} + + +//################ +//# +//# Find dialog box +//# +//################ + +static __inline BOOL OnFindOK(HWND hDlg,BOOL bASCII,DWORD *pdwAddrLast,INT nSearchDir) +{ + HWND hWnd; + BYTE *lpbySearch; + INT i,j; + DWORD dwCnt,dwAddr,dwMapDataMask; + BOOL bMatch; + + // searching upwards / downwards + _ASSERT(nSearchDir == 1 || nSearchDir == -1); + + hWnd = GetDlgItem(hDlg,IDC_FIND_DATA); + + dwMapDataMask = GetMemDataMask(); // size mask of data mapping + + i = GetWindowTextLength(hWnd) + 1; // text length incl. EOS + j = bASCII ? 2 : sizeof(*(LPTSTR)0); // buffer width + + // allocate search buffer + if ((lpbySearch = (LPBYTE) malloc(i * j)) != NULL) + { + // get search text and real length + i = GetWindowText(hWnd,(LPTSTR) lpbySearch,i); + + // add string to combo box + if (SendMessage(hWnd,CB_FINDSTRINGEXACT,0,(LPARAM) lpbySearch) == CB_ERR) + SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) lpbySearch); + + #if defined _UNICODE + { + // Unicode to byte translation + LPTSTR szTmp = DuplicateString((LPTSTR) lpbySearch); + if (szTmp != NULL) + { + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + szTmp, -1, + (LPSTR) lpbySearch, i+1, NULL, NULL); + free(szTmp); + } + } + #endif + + // convert input format to nibble based format + if (bASCII) // ASCII input + { + // convert ASCII to number + for (j = i - 1; j >= 0; --j) + { + // order LSB, MSB + lpbySearch[j * 2 + 1] = lpbySearch[j] >> 4; + lpbySearch[j * 2] = lpbySearch[j] & 0xF; + } + i *= 2; // no. of nibbles + } + else // hex number input + { + // convert HEX to number + for (i = 0, j = 0; lpbySearch[j] != 0; ++j) + { + if (lpbySearch[j] == ' ') // skip space + continue; + + if (isxdigit(lpbySearch[j])) + { + lpbySearch[i] = toupper(lpbySearch[j]) - '0'; + if (lpbySearch[i] > 9) lpbySearch[i] -= 'A' - '9' - 1; + } + else + { + i = 0; // wrong format, no match + break; + } + ++i; // inc, no. of nibbles + } + } + + bMatch = FALSE; // no match + dwAddr = dwAdrMem; // calculate search start address + if (*pdwAddrLast == dwAddr) + dwAddr += nSearchDir; + + // scan mapping/module until match + for (dwCnt = 0; i && !bMatch && dwCnt <= dwMapDataMask; ++dwCnt) + { + BYTE byC; + + // i = no. of nibbles that have to match + for (bMatch = TRUE, j = 0;bMatch && j < i; ++j) + { + GetMemPeek(&byC,(dwAddr + j) & dwMapDataMask,1); + bMatch = (byC == lpbySearch[j]); + } + dwAddr += nSearchDir; + } + free(lpbySearch); + + // check match result + if (bMatch) + { + // matching address + dwAddr = (dwAddr - nSearchDir) & dwMapDataMask; + + // update memory window + OnMemGoDx(GetParent(hDlg),dwAddr); + + // update rpl obj view dialog + UpdateRplObjViewWnd(hDlgRplObjView,dwAddr); + + *pdwAddrLast = dwAddr; // last matching address + } + else + { + MessageBox(hDlg,_T("Search string not found!"),_T("Find"), + MB_APPLMODAL|MB_OK|MB_ICONEXCLAMATION|MB_SETFOREGROUND); + } + } + return TRUE; +} + +// +// enter find dialog +// +static INT_PTR CALLBACK Find(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static DWORD dwAddrEntry; + static BOOL bASCII = FALSE; + + switch (message) + { + case WM_INITDIALOG: + CheckDlgButton(hDlg,IDC_FIND_ASCII,bASCII); + dwAddrEntry = -1; + return TRUE; + + case WM_DESTROY: + hDlgFind = NULL; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_FIND_ASCII: bASCII = !bASCII; return TRUE; + case IDC_FIND_PREV: return OnFindOK(hDlg,bASCII,&dwAddrEntry,-1); + case IDC_FIND_NEXT: return OnFindOK(hDlg,bASCII,&dwAddrEntry,1); + case IDCANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnMemFind(HWND hDlg) +{ + if (hDlgFind == NULL) // no find dialog, create it + { + if ((hDlgFind = CreateDialog(hApp,MAKEINTRESOURCE(IDD_FIND),hDlg, + (DLGPROC)Find)) == NULL) + AbortMessage(_T("Find Dialog Box Creation Error !")); + } + else + { + SetFocus(hDlgFind); // set focus on find dialog + } + return -1; // call windows default handler +} + + +//################ +//# +//# Profile dialog box +//# +//################ + +// +// update profiler dialog content +// +static VOID UpdateProfileWnd(HWND hDlg) +{ + #define CPU_FREQ 524288 // base CPU frequency + #define SX_RATE 0x0E + #define GX_RATE 0x1B + #define GP_RATE 0x1B*3 // CdB for HP: add high speed apples + #define G2_RATE 0x1B*2 // CdB for HP: add low speed apples + + LPCTSTR pcUnit[] = { _T("s"),_T("ms"),_T("us"),_T("ns") }; + + QWORD lVar; + TCHAR szBuffer[64]; + UINT i; + DWORD dwFreq, dwEndFreq; + + if (hDlg == NULL) return; // dialog not open + + // 64 bit cpu cycle counter + lVar = *(QWORD *)&Chipset.cycles - *(QWORD *)&OldChipset.cycles; + + // cycle counts + _sntprintf(szBuffer,ARRAYSIZEOF(szBuffer),_T("%I64u"),lVar); + SetDlgItemText(hDlg,IDC_PROFILE_LASTCYCLES,szBuffer); + + // CPU frequency + switch (cCurrentRomType) // CdB for HP: add apples speed selection + { + case 'S': dwFreq= ((SX_RATE + 1) * CPU_FREQ / 4); break; + case 'X': case 'G': case 'E': case 'A': dwFreq= ((GX_RATE + 1) * CPU_FREQ / 4); break; + case 'P': case 'Q': dwFreq= ((GP_RATE + 1) * CPU_FREQ / 4); break; + case '2': dwFreq= ((G2_RATE + 1) * CPU_FREQ / 4); break; + } + dwEndFreq = ((999 * 2 - 1) * dwFreq) / (2 * 1000); + + // search for ENG unit + for (i = 0; i < ARRAYSIZEOF(pcUnit) - 1 && lVar <= dwEndFreq; ++i) + { + lVar *= 1000; // next ENG unit + } + + // calculate rounded time + lVar = (2 * lVar + dwFreq) / (2 * dwFreq); + + _ASSERT(i < ARRAYSIZEOF(pcUnit)); + _sntprintf(szBuffer,ARRAYSIZEOF(szBuffer),_T("%I64u %s"),lVar,pcUnit[i]); + SetDlgItemText(hDlg,IDC_PROFILE_LASTTIME,szBuffer); + return; + #undef SX_CLK + #undef GX_CLK + #undef GP_RATE + #undef G2_RATE + #undef CPU_FREQ +} + +// +// enter profile dialog +// +static INT_PTR CALLBACK Profile(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + UpdateProfileWnd(hDlg); + return TRUE; + + case WM_DESTROY: + hDlgProfile = NULL; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnProfile(HWND hDlg) +{ + if (hDlgProfile == NULL) // no profile dialog, create it + { + if ((hDlgProfile = CreateDialog(hApp,MAKEINTRESOURCE(IDD_PROFILE),hDlg, + (DLGPROC)Profile)) == NULL) + AbortMessage(_T("Profile Dialog Box Creation Error !")); + } + else + { + SetFocus(hDlgProfile); // set focus on profile dialog + } + return -1; // call windows default handler +} + + +//################ +//# +//# RPL object viewer dialog box +//# +//################ + +// +// update rpl obj view dialog content +// +static VOID UpdateRplObjViewWnd(HWND hDlg, DWORD dwAddr) +{ + LPTSTR szObjList; + + if (hDlg == NULL) return; // dialog not open + + // show entry point name only in mapped mode + bRplViewName = (GetMemMapType() == MEM_MMU); + + // create view string + szObjList = RplCreateObjView(dwAddr,GetMemDataSize(),TRUE); + SetDlgItemText(hDlg,IDC_RPLVIEW_DATA,szObjList); + free(szObjList); + return; +} + +// +// enter RPL object viewer dialog +// +static INT_PTR CALLBACK RplObjView(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + switch (cCurrentRomType) + { + case 'S': // HP48SX + dwRplPlatform = RPL_P3; // Charlemagne platform + break; + case '6': // HP38G (64K RAM version) + case 'A': // HP38G + case 'G': // HP48GX + dwRplPlatform = RPL_P4; // Alcuin platform + break; + case 'E': // HP39G/40G + case 'X': // HP49G + dwRplPlatform = RPL_P5; // V'ger platform + break; + default: + _ASSERT(FALSE); + } + + bRplViewAddr = TRUE; // show address + bRplViewBin = TRUE; // show binary data + return TRUE; + + case WM_DESTROY: + hDlgRplObjView = NULL; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnRplObjView(HWND hDlg) +{ + // get cursor address of memory view + DWORD dwAddr = GetMemCurAddr(hDlg); + + if (hDlgRplObjView == NULL) // no rpl obj view dialog, create it + { + if ((hDlgRplObjView = CreateDialog(hApp,MAKEINTRESOURCE(IDD_RPLVIEW),hDlg, + (DLGPROC)RplObjView)) == NULL) + AbortMessage(_T("RPL Object View Dialog Box Creation Error !")); + } + + UpdateRplObjViewWnd(hDlgRplObjView,dwAddr); + return -1; // call windows default handler +} + + +//################ +//# +//# Settings dialog box +//# +//################ + +// +// copy edit box content to current combox box selection +// +static VOID CopyEditToCombo(HWND hDlg,HWND hWndComboBox) +{ + TCHAR szSymbFilename[MAX_PATH]; + INT i; + + // get current selection + if ((i = (INT) SendMessage(hWndComboBox,CB_GETCURSEL,0,0)) != CB_ERR) + { + // delete associated name + free((LPVOID) SendMessage(hWndComboBox,CB_GETITEMDATA,i,0)); + + // append actual name + GetDlgItemText(hDlg,IDC_DEBUG_SET_FILE,szSymbFilename,ARRAYSIZEOF(szSymbFilename)); + SendMessage(hWndComboBox,CB_SETITEMDATA,i,(LPARAM) DuplicateString(szSymbFilename)); + } + return; +} + +// +// copy edit box content to current combox box selection +// +static VOID CopyComboToEdit(HWND hDlg,HWND hWndComboBox) +{ + HWND hWnd; + INT i; + + // get current selection + if ((i = (INT) SendMessage(hWndComboBox,CB_GETCURSEL,0,0)) != CB_ERR) + { + // update file edit box + hWnd = GetDlgItem(hDlg,IDC_DEBUG_SET_FILE); + SetWindowText(hWnd,(LPTSTR) SendMessage(hWndComboBox,CB_GETITEMDATA,i,0)); + SendMessage(hWnd,EM_SETSEL,0,-1); + } + return; +} + +// +// settings browse dialog +// +static BOOL OnBrowseSettings(HWND hDlg, HWND hWndFilename) +{ + TCHAR szBuffer[MAX_PATH]; + OPENFILENAME ofn; + + ZeroMemory(&ofn, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = + _T("HP-Tools Linker File (*.O)\0*.O\0") + _T("All Files (*.*)\0*.*\0"); + ofn.lpstrDefExt = _T("O"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn)) + { + SetWindowText(hWndFilename,szBuffer); + SendMessage(hWndFilename,EM_SETSEL,0,-1); + } + return 0; +} + +// +// enter settings dialog +// +static INT_PTR CALLBACK Settings(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND hWnd; + TCHAR szSymbFilename[MAX_PATH]; + TCHAR szItemname[16]; + INT i,nMax; + + switch (message) + { + case WM_INITDIALOG: + // set disassembler mode + CheckDlgButton(hDlg,(disassembler_mode == HP_MNEMONICS) ? IDC_DISASM_HP : IDC_DISASM_CLASS,BST_CHECKED); + // set symbolic enable check button + CheckDlgButton(hDlg,IDC_DEBUG_SET_SYMB,disassembler_symb); + // fill model combo box and corresponding file edit box + { + LPCTSTR lpszModels; + TCHAR cModel[] = _T(" "); + + // fill model combo box + hWnd = GetDlgItem(hDlg,IDC_DEBUG_SET_MODEL); + for (lpszModels = _T(MODELS); *lpszModels != 0; ++lpszModels) + { + cModel[0] = *lpszModels; // string with model character + i = (INT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) cModel); + + // get filename + wsprintf(szItemname,_T("Symb%c"),cModel[0]); + ReadSettingsString(_T("Disassembler"),szItemname,_T(""),szSymbFilename,ARRAYSIZEOF(szSymbFilename)); + + // append filename to model + SendMessage(hWnd,CB_SETITEMDATA,i,(LPARAM) DuplicateString(szSymbFilename)); + } + + // select for actual model + cModel[0] = (TCHAR) cCurrentRomType; + if ((i = (INT) SendMessage(hWnd,CB_SELECTSTRING,0,(LPARAM) cModel)) != CB_ERR) + { + LPTSTR lpszFilename = (LPTSTR) SendMessage(hWnd,CB_GETITEMDATA,i,0); + + // fill file edit box + hWnd = GetDlgItem(hDlg,IDC_DEBUG_SET_FILE); + SetWindowText(hWnd,lpszFilename); + SendMessage(hWnd,EM_SETSEL,0,-1); + } + } + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_DEBUG_SET_MODEL: + // combo box changing item + if (HIWORD(wParam) == CBN_SETFOCUS) + { + // update associated name with file edit box + CopyEditToCombo(hDlg,(HWND) lParam); + } + // new combo box item selected + if (HIWORD(wParam) == CBN_SELENDOK) + { + // update file edit box with associated name + CopyComboToEdit(hDlg,(HWND) lParam); + } + break; + case IDC_DEBUG_SET_BROWSE: + return OnBrowseSettings(hDlg,GetDlgItem(hDlg,IDC_DEBUG_SET_FILE)); + case IDOK: + // set disassembler mode + disassembler_mode = IsDlgButtonChecked(hDlg,IDC_DISASM_HP) ? HP_MNEMONICS : CLASS_MNEMONICS; + // set symbolic enable check button + disassembler_symb = IsDlgButtonChecked(hDlg,IDC_DEBUG_SET_SYMB); + // update associated name with file edit box + hWnd = GetDlgItem(hDlg,IDC_DEBUG_SET_MODEL); + CopyEditToCombo(hDlg,hWnd); + // write all symbol filenames to registry + nMax = (INT) SendMessage(hWnd,CB_GETCOUNT,0,0); + for (i = 0; i < nMax; ++i) + { + LPTSTR lpszFilename; + + SendMessage(hWnd,CB_GETLBTEXT,i,(LPARAM) szSymbFilename); + wsprintf(szItemname,_T("Symb%c"),szSymbFilename[0]); + lpszFilename = (LPTSTR) SendMessage(hWnd,CB_GETITEMDATA,i,0); + if (*lpszFilename == 0) // empty + { + DelSettingsKey(_T("Disassembler"),szItemname); + } + else + { + WriteSettingsString(_T("Disassembler"),szItemname,lpszFilename); + } + } + RplDeleteTable(); // delete rpl symbol table + LoadSymbTable(); // reload external rpl symbol table + // redraw debugger code view + ViewCodeWnd(GetDlgItem(GetParent(hDlg),IDC_DEBUG_CODE),dwAdrLine[0]); + // no break + case IDCANCEL: + // free combo box items + hWnd = GetDlgItem(hDlg,IDC_DEBUG_SET_MODEL); + nMax = (INT) SendMessage(hWnd,CB_GETCOUNT,0,0); + for (i = 0; i < nMax; ++i) + { + LPTSTR lpszFilename = (LPTSTR) SendMessage(hWnd,CB_GETITEMDATA,i,0); + if (lpszFilename != NULL) + { + free(lpszFilename); + } + } + EndDialog(hDlg,LOWORD(wParam)); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnSettings(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DEBUG_SETTINGS), hDlg, (DLGPROC)Settings) == -1) + AbortMessage(_T("Settings Dialog Box Creation Error !")); + return 0; +} + + +//################ +//# +//# New Value dialog box +//# +//################ + +// +// enter new value dialog +// +static INT_PTR CALLBACK NewValue(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static LPTSTR lpszBuffer; // handle of buffer + static int nBufferlen; // length of buffer + + HWND hWnd; + TCHAR szBuffer[64]; + LONG i; + + switch (message) + { + case WM_INITDIALOG: + lpszBuffer = (LPTSTR) lParam; + // length with zero string terminator + nBufferlen = lstrlen(lpszBuffer)+1; + _ASSERT(ARRAYSIZEOF(szBuffer) >= nBufferlen); + SetDlgItemText(hDlg,IDC_NEWVALUE,lpszBuffer); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDOK: + hWnd = GetDlgItem(hDlg,IDC_NEWVALUE); + GetWindowText(hWnd,szBuffer,nBufferlen); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szBuffer); ++i) + { + if (_istxdigit(szBuffer[i]) == 0) + { + SendMessage(hWnd,EM_SETSEL,0,-1); + SetFocus(hWnd); // focus to edit control + return FALSE; + } + } + lstrcpy(lpszBuffer,szBuffer); // copy valid value + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static INT_PTR OnNewValue(LPTSTR lpszValue) +{ + INT_PTR nResult; + + if ((nResult = DialogBoxParam(hApp, + MAKEINTRESOURCE(IDD_NEWVALUE), + hDlgDebug, + (DLGPROC)NewValue, + (LPARAM)lpszValue) + ) == -1) + AbortMessage(_T("Input Dialog Box Creation Error !")); + return nResult; +} + + +//################ +//# +//# Goto Address dialog box +//# +//################ + +// +// enter goto address dialog +// +static INT_PTR CALLBACK EnterAddr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetWindowLongPtr(hDlg,GWLP_USERDATA,(LONG_PTR) lParam); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDOK: + if (!GetAddr(hDlg, + IDC_ENTERADR, + (DWORD *) GetWindowLongPtr(hDlg,GWLP_USERDATA), + 0xFFFFFFFF, + disassembler_symb)) + return FALSE; + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static VOID OnEnterAddress(HWND hDlg, DWORD *dwValue) +{ + if (DialogBoxParam(hApp, MAKEINTRESOURCE(IDD_ENTERADR), hDlg, (DLGPROC)EnterAddr, (LPARAM)dwValue) == -1) + AbortMessage(_T("Address Dialog Box Creation Error !")); +} + + +//################ +//# +//# Breakpoint dialog box +//# +//################ + +// +// enter breakpoint dialog +// +static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static BP_T *sBp; + + DWORD dwAddr; + + switch (message) + { + case WM_INITDIALOG: + sBp = (BP_T *) lParam; + sBp->bEnable = TRUE; + sBp->nType = BP_EXEC; + SendDlgItemMessage(hDlg,IDC_BPCODE,BM_SETCHECK,1,0); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDC_BPCODE: sBp->nType = BP_EXEC; return TRUE; + case IDC_BPRPL: sBp->nType = BP_RPL; return TRUE; + case IDC_BPACCESS: sBp->nType = BP_ACCESS; return TRUE; + case IDC_BPREAD: sBp->nType = BP_READ; return TRUE; + case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE; + case IDOK: + if (!GetAddr(hDlg,IDC_ENTERADR,&dwAddr,0xFFFFF,disassembler_symb)) + return FALSE; + sBp->dwAddr = dwAddr; + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static VOID OnEnterBreakpoint(HWND hDlg, BP_T *sValue) +{ + if (DialogBoxParam(hApp, MAKEINTRESOURCE(IDD_ENTERBREAK), hDlg, (DLGPROC)EnterBreakpoint, (LPARAM)sValue) == -1) + AbortMessage(_T("Breakpoint Dialog Box Creation Error !")); +} + + +//################ +//# +//# Edit breakpoint dialog box +//# +//################ + +// +// handle drawing in breakpoint window +// +static __inline BOOL OnDrawBreakWnd(LPDRAWITEMSTRUCT lpdis) +{ + TCHAR szBuf[64]; + COLORREF crBkColor,crTextColor; + HDC hdcMem; + HBITMAP hBmpOld; + INT i; + + if (lpdis->itemID == -1) // no item in list box + return TRUE; + + if (lpdis->itemState & ODS_SELECTED) // cursor line + { + crBkColor = COLOR_NAVY; + crTextColor = COLOR_WHITE; + } + else + { + crBkColor = COLOR_WHITE; + crTextColor = COLOR_BLACK; + } + + // write Text + crBkColor = SetBkColor(lpdis->hDC,crBkColor); + crTextColor = SetTextColor(lpdis->hDC,crTextColor); + + SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf); + ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+17,(int)(lpdis->rcItem.top), + ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL); + + SetBkColor(lpdis->hDC,crBkColor); + SetTextColor(lpdis->hDC,crTextColor); + + // draw checkbox + i = (INT) SendMessage(lpdis->hwndItem,LB_GETITEMDATA,lpdis->itemID,0); + hdcMem = CreateCompatibleDC(lpdis->hDC); + _ASSERT(hBmpCheckBox); + hBmpOld = (HBITMAP) SelectObject(hdcMem,hBmpCheckBox); + + BitBlt(lpdis->hDC,lpdis->rcItem.left+2,lpdis->rcItem.top+2, + 11,lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem,sBreakpoint[i].bEnable ? 0 : 10,0,SRCCOPY); + + SelectObject(hdcMem,hBmpOld); + DeleteDC(hdcMem); + + if (lpdis->itemState & ODS_FOCUS) // redraw focus + DrawFocusRect(lpdis->hDC,&lpdis->rcItem); + + return TRUE; // focus handled here +} + +// +// toggle breakpoint drawing +// +static BOOL ToggleBreakpointItem(HWND hWnd, INT nItem) +{ + RECT rc; + + // get breakpoint number + INT i = (INT) SendMessage(hWnd,LB_GETITEMDATA,nItem,0); + + sBreakpoint[i].bEnable = !sBreakpoint[i].bEnable; + // update region of toggled item + SendMessage(hWnd,LB_GETITEMRECT,nItem,(LPARAM)&rc); + InvalidateRect(hWnd,&rc,TRUE); + return TRUE; +} + +// +// draw breakpoint type +// +static VOID DrawBreakpoint(HWND hWnd, INT i) +{ + TCHAR *szText,szBuffer[32]; + INT nItem; + + switch(sBreakpoint[i].nType) + { + case BP_EXEC: // code breakpoint + szText = _T("Code"); + break; + case BP_RPL: // RPL breakpoint + szText = _T("RPL"); + break; + case BP_READ: // read memory breakpoint + szText = _T("Memory Read"); + break; + case BP_WRITE: // write memory breakpoint + szText = _T("Memory Write"); + break; + case BP_ACCESS: // memory breakpoint + szText = _T("Memory Access"); + break; + default: // unknown breakpoint type + szText = _T("unknown"); + _ASSERT(0); + } + wsprintf(szBuffer,_T("%05X (%s)"),sBreakpoint[i].dwAddr,szText); + nItem = (INT) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + SendMessage(hWnd,LB_SETITEMDATA,nItem,i); + return; +} + +// +// enter edit breakpoint dialog +// +static INT_PTR CALLBACK EditBreakpoint(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TEXTMETRIC tm; + + HWND hWnd; + HDC hDC; + HFONT hFont; + BP_T sBp; + INT i,nItem; + + switch (message) + { + case WM_INITDIALOG: + // font settings + SendDlgItemMessage(hDlg,IDC_STATIC_BREAKPOINT,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_BREAKEDIT_ADD, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_BREAKEDIT_DELETE, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + hBmpCheckBox = LoadBitmap(hApp,MAKEINTRESOURCE(IDB_CHECKBOX)); + _ASSERT(hBmpCheckBox); + + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < wBreakpointCount; ++i) + DrawBreakpoint(hWnd,i); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return TRUE; + + case WM_DESTROY: + DeleteObject(hBmpCheckBox); + return TRUE; + + case WM_COMMAND: + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + if (LOWORD(wParam) == IDC_BREAKEDIT_WND) + { + if ((nItem = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0)) == LB_ERR) + return FALSE; + + return ToggleBreakpointItem(hWnd,nItem); + } + } + + switch(LOWORD(wParam)) + { + case IDC_BREAKEDIT_ADD: + sBp.dwAddr = -1; // no breakpoint given + OnEnterBreakpoint(hDlg, &sBp); + if (sBp.dwAddr != -1) + { + for (i = 0; i < wBreakpointCount; ++i) + { + if (sBreakpoint[i].dwAddr == sBp.dwAddr) + { + // tried to add used code breakpoint + if (sBreakpoint[i].bEnable && (sBreakpoint[i].nType & sBp.nType & (BP_EXEC | BP_RPL)) != 0) + return FALSE; + + // only modify memory breakpoints + if ( ( sBreakpoint[i].bEnable == FALSE + && (sBreakpoint[i].nType & sBp.nType & (BP_EXEC | BP_RPL)) != 0) + || ((sBreakpoint[i].nType & BP_ACCESS) && (sBp.nType & BP_ACCESS))) + { + // replace breakpoint type + sBreakpoint[i].bEnable = TRUE; + sBreakpoint[i].nType = sBp.nType; + + // redaw breakpoint list + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < wBreakpointCount; ++i) + DrawBreakpoint(hWnd,i); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return FALSE; + } + } + } + + // check for breakpoint buffer full + if (wBreakpointCount >= MAXBREAKPOINTS) + { + AbortMessage(_T("Reached maximum number of breakpoints !")); + return FALSE; + } + + sBreakpoint[wBreakpointCount].bEnable = sBp.bEnable; + sBreakpoint[wBreakpointCount].nType = sBp.nType; + sBreakpoint[wBreakpointCount].dwAddr = sBp.dwAddr; + + DrawBreakpoint(hWnd,wBreakpointCount); + + ++wBreakpointCount; + } + return TRUE; + + case IDC_BREAKEDIT_DELETE: + // scan all breakpoints from top + for (nItem = wBreakpointCount-1; nItem >= 0; --nItem) + { + // item selected + if (SendMessage(hWnd,LB_GETSEL,nItem,0) > 0) + { + INT j; + + // get breakpoint index + i = (INT) SendMessage(hWnd,LB_GETITEMDATA,nItem,0); + SendMessage(hWnd,LB_DELETESTRING,nItem,0); + --wBreakpointCount; + + // update remaining list box references + for (j = 0; j < wBreakpointCount; ++j) + { + INT k = (INT) SendMessage(hWnd,LB_GETITEMDATA,j,0); + if (k > i) SendMessage(hWnd,LB_SETITEMDATA,j,k-1); + } + + // remove breakpoint from breakpoint table + while (++i <= wBreakpointCount) + sBreakpoint[i-1] = sBreakpoint[i]; + } + } + return TRUE; + + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + + case WM_VKEYTOITEM: + if (LOWORD(wParam) == VK_SPACE) + { + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + for (nItem = 0; nItem < wBreakpointCount; ++nItem) + { + // item selected + if (SendMessage(hWnd,LB_GETSEL,nItem,0) > 0) + ToggleBreakpointItem(hWnd,nItem); + } + return -2; + } + return -1; // default action + + case WM_DRAWITEM: + if (wParam == IDC_BREAKEDIT_WND) return OnDrawBreakWnd((LPDRAWITEMSTRUCT) lParam); + break; + + case WM_MEASUREITEM: + hDC = GetDC(hDlg); + + // GetTextMetrics from "Courier New 8" font + hFont = CreateFont(-MulDiv(8,GetDeviceCaps(hDC, LOGPIXELSY),72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Courier New")); + + hFont = (HFONT) SelectObject(hDC,hFont); + GetTextMetrics(hDC,&tm); + hFont = (HFONT) SelectObject(hDC,hFont); + DeleteObject(hFont); + + ((LPMEASUREITEMSTRUCT) lParam)->itemHeight = tm.tmHeight; + + ReleaseDC(hDlg,hDC); + return TRUE; + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnEditBreakpoint(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_BREAKEDIT), hDlg, (DLGPROC)EditBreakpoint) == -1) + AbortMessage(_T("Edit Breakpoint Dialog Box Creation Error !")); + + // update code window + InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); + return -1; +} + + +//################ +//# +//# Last Instruction dialog box +//# +//################ + +// +// view last instructions +// +static INT_PTR CALLBACK InfoIntr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND hWnd; + TCHAR szBuffer[64]; + LONG lIndex; + WORD i,j; + + switch (message) + { + case WM_INITDIALOG: + // font settings + SendDlgItemMessage(hDlg,IDC_INSTR_TEXT, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_INSTR_CLEAR, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_INSTR_COPY, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + lIndex = 0; // init lIndex + hWnd = GetDlgItem(hDlg,IDC_INSTR_CODE); + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize) + { + LPCTSTR lpszName; + + // entry has a name + if (disassembler_symb && (lpszName = RplGetName(pdwInstrArray[i])) != NULL) + { + szBuffer[0] = _T('='); + lstrcpyn(&szBuffer[1],lpszName,ARRAYSIZEOF(szBuffer)-1); + SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + } + + j = wsprintf(szBuffer,_T("%05lX "),pdwInstrArray[i]); + disassemble(pdwInstrArray[i],&szBuffer[j]); + lIndex = (LONG) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + } + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + SendMessage(hWnd,LB_SETCARETINDEX,lIndex,TRUE); + return TRUE; + case WM_COMMAND: + hWnd = GetDlgItem(hDlg,IDC_INSTR_CODE); + switch(LOWORD(wParam)) + { + case IDC_INSTR_COPY: + CopyItemsToClipboard(hWnd); // copy selected items to clipboard + return TRUE; + case IDC_INSTR_CLEAR: // clear instruction buffer + wInstrRp = wInstrWp; + SendMessage(hWnd,LB_RESETCONTENT,0,0); + return TRUE; + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnInfoIntr(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_INSTRUCTIONS), hDlg, (DLGPROC)InfoIntr) == -1) + AbortMessage(_T("Last Instructions Dialog Box Creation Error !")); + return 0; +} + +// +// view write only I/O registers +// +static INT_PTR CALLBACK InfoWoRegister(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TCHAR szBuffer[8]; + + switch (message) + { + case WM_INITDIALOG: + wsprintf(szBuffer,_T("%05X"),Chipset.start1); + SetDlgItemText(hDlg,IDC_ADDR20_24,szBuffer); + wsprintf(szBuffer,_T("%03X"),Chipset.loffset); + SetDlgItemText(hDlg,IDC_ADDR25_27,szBuffer); + wsprintf(szBuffer,_T("%02X"),Chipset.lcounter); + SetDlgItemText(hDlg,IDC_ADDR28_29,szBuffer); + wsprintf(szBuffer,_T("%05X"),Chipset.start2); + SetDlgItemText(hDlg,IDC_ADDR30_34,szBuffer); + return TRUE; + case WM_COMMAND: + if ((LOWORD(wParam) == IDCANCEL)) + { + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnInfoWoRegister(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_WRITEONLYREG), hDlg, (DLGPROC)InfoWoRegister) == -1) + AbortMessage(_T("Write-Only Register Dialog Box Creation Error !")); + return 0; +} + + +//################ +//# +//# Breakpoint list operations +//# +//################ + +// +// load breakpoint list +// +VOID LoadBreakpointList(HANDLE hFile) // NULL = clear breakpoint list +{ + DWORD lBytesRead = 0; + + // read number of breakpoints + if (hFile) ReadFile(hFile, &wBreakpointCount, sizeof(wBreakpointCount), &lBytesRead, NULL); + + // breakpoints found + if (lBytesRead == sizeof(wBreakpointCount) && wBreakpointCount < ARRAYSIZEOF(sBreakpoint)) + { + WORD wBreakpointSize; + + // read size of one breakpoint + ReadFile(hFile, &wBreakpointSize, sizeof(wBreakpointSize), &lBytesRead, NULL); + if (lBytesRead == sizeof(wBreakpointSize) && wBreakpointSize == sizeof(sBreakpoint[0])) + { + // read breakpoints + ReadFile(hFile, sBreakpoint, wBreakpointCount * sizeof(sBreakpoint[0]), &lBytesRead, NULL); + _ASSERT(lBytesRead == wBreakpointCount * sizeof(sBreakpoint[0])); + } + else // changed breakpoint structure + { + wBreakpointCount = 0; // clear breakpoint list + } + } + else // no breakpoints or breakpoint buffer too small + { + wBreakpointCount = 0; // clear breakpoint list + } + return; +} + +// +// save breakpoint list +// +VOID SaveBreakpointList(HANDLE hFile) +{ + if (wBreakpointCount) // defined breakpoints + { + DWORD lBytesWritten; + + WORD wBreakpointSize = sizeof(sBreakpoint[0]); + + _ASSERT(hFile); // valid file pointer? + + // write number of breakpoints + WriteFile(hFile, &wBreakpointCount, sizeof(wBreakpointCount), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == sizeof(wBreakpointCount)); + + // write size of one breakpoint + WriteFile(hFile, &wBreakpointSize, sizeof(wBreakpointSize), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == sizeof(wBreakpointSize)); + + // write breakpoints + WriteFile(hFile, sBreakpoint, wBreakpointCount * sizeof(sBreakpoint[0]), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == wBreakpointCount * sizeof(sBreakpoint[0])); + } + return; +} + +// +// create a copy of the breakpoint list +// +VOID CreateBackupBreakpointList(VOID) +{ + _ASSERT(sizeof(sBackupBreakpoint) == sizeof(sBreakpoint)); + + wBackupBreakpointCount = wBreakpointCount; + + if (wBreakpointCount > 0) // list not empty + { + CopyMemory(sBackupBreakpoint,sBreakpoint,sizeof(sBackupBreakpoint)); + } + return; +} + +// +// restore the breakpoint list from the copy +// +VOID RestoreBackupBreakpointList(VOID) +{ + _ASSERT(sizeof(sBackupBreakpoint) == sizeof(sBreakpoint)); + + wBreakpointCount = wBackupBreakpointCount; + + if (wBreakpointCount > 0) // list not empty + { + CopyMemory(sBreakpoint,sBackupBreakpoint,sizeof(sBreakpoint)); + } + return; +} + + +//################ +//# +//# Load/Save Memory Data +//# +//################ + +static BOOL OnBrowseLoadMem(HWND hDlg) +{ + TCHAR szBuffer[MAX_PATH]; + OPENFILENAME ofn; + + ZeroMemory(&ofn, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = + _T("Memory Dump Files (*.MEM)\0*.MEM\0") + _T("All Files (*.*)\0*.*\0"); + ofn.lpstrDefExt = _T("MEM"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn)) + { + SetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szBuffer); + } + return 0; +} + +static BOOL OnBrowseSaveMem(HWND hDlg) +{ + TCHAR szBuffer[MAX_PATH]; + OPENFILENAME ofn; + + ZeroMemory(&ofn, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = + _T("Memory Dump Files (*.MEM)\0*.MEM\0") + _T("All Files (*.*)\0*.*\0"); + ofn.lpstrDefExt = _T("MEM"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn)) + { + SetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szBuffer); + } + return 0; +} + +// +// write file to memory +// +static BOOL LoadMemData(LPCTSTR lpszFilename,DWORD dwStartAddr) +{ + HANDLE hFile; + DWORD dwRead; + BYTE byData; + + hFile = CreateFile(lpszFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file + return FALSE; + + while (dwStartAddr <= 0xFFFFF) // read until EOF or end of Saturn address space + { + ReadFile(hFile,&byData,sizeof(byData),&dwRead,NULL); + if (dwRead == 0) break; // EOF + + if (dwStartAddr < 0xFFFFF) + { + Write2(dwStartAddr,byData); // write byte in map mode + dwStartAddr += 2; + } + else // special handling to avoid address wrap around + { + byData &= 0xF; + Nwrite(&byData,dwStartAddr,1); // write nibble in map mode + ++dwStartAddr; + } + } + + CloseHandle(hFile); + return TRUE; +} + +// +// write memory data to file +// +static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr) +{ + HANDLE hFile; + DWORD dwAddr,dwWritten; + BYTE byData; + + hFile = CreateFile(lpszFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file + return FALSE; + + for (dwAddr = dwStartAddr; dwAddr <= dwEndAddr; dwAddr += 2) + { + _ASSERT(dwAddr <= 0xFFFFF); + byData = Read2(dwAddr); // read byte in map mode + WriteFile(hFile,&byData,sizeof(byData),&dwWritten,NULL); + } + + CloseHandle(hFile); + return TRUE; +} + +// +// memory load data +// +static INT_PTR CALLBACK DebugMemLoad(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TCHAR szFilename[MAX_PATH]; + DWORD dwStartAddr; + + switch (message) + { + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_DEBUG_DATA_BUT: + return OnBrowseLoadMem(hDlg); + + case IDOK: + dwStartAddr = -1; // no address given + + // get filename + GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename)); + + // decode address field + if ( !GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr,0xFFFFF,FALSE) + || dwStartAddr == -1) + return FALSE; + + _ASSERT(dwStartAddr <= 0xFFFFF); + + // load memory dump file + if (!LoadMemData(szFilename,dwStartAddr)) + return FALSE; + + // update memory window + UpdateMemoryWnd(GetParent(hDlg)); + + // no break + case IDCANCEL: + EndDialog(hDlg,LOWORD(wParam)); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnMemLoadData(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DEBUG_MEMLOAD), hDlg, (DLGPROC)DebugMemLoad) == -1) + AbortMessage(_T("DebugLoad Dialog Box Creation Error !")); + + return -1; +} + +// +// memory save data +// +static INT_PTR CALLBACK DebugMemSave(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TCHAR szFilename[MAX_PATH]; + DWORD dwStartAddr,dwEndAddr; + + switch (message) + { + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_DEBUG_DATA_BUT: + return OnBrowseSaveMem(hDlg); + + case IDOK: + dwStartAddr = dwEndAddr = -1; // no address given + + // get filename + GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename)); + + // decode address fields + if ( !GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr,0xFFFFF,FALSE) + || dwStartAddr == -1) + return FALSE; + if ( !GetAddr(hDlg,IDC_DEBUG_DATA_ENDADDR,&dwEndAddr,0xFFFFF,FALSE) + || dwEndAddr == -1) + return FALSE; + + _ASSERT(dwStartAddr <= 0xFFFFF); + _ASSERT(dwEndAddr <= 0xFFFFF); + + // save memory dump file + if (!SaveMemData(szFilename,dwStartAddr,dwEndAddr)) + return FALSE; + + // no break + case IDCANCEL: + EndDialog(hDlg,LOWORD(wParam)); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnMemSaveData(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DEBUG_MEMSAVE), hDlg, (DLGPROC)DebugMemSave) == -1) + AbortMessage(_T("DebugSave Dialog Box Creation Error !")); + + return -1; +} diff --git a/app/src/main/cpp/DEBUGGER.H b/app/src/main/cpp/DEBUGGER.H new file mode 100644 index 0000000..1a9b694 --- /dev/null +++ b/app/src/main/cpp/DEBUGGER.H @@ -0,0 +1,39 @@ +/* + * debugger.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +// breakpoint type definitions +#define BP_EXEC 0x01 // code breakpoint +#define BP_READ 0x02 // read memory breakpoint +#define BP_WRITE 0x04 // write memory breakpoint +#define BP_RPL 0x08 // RPL breakpoint +#define BP_ACCESS (BP_READ|BP_WRITE) // read/write memory breakpoint + +// breakpoint notify definitions +#define BN_ASM 0 // ASM breakpoint +#define BN_RPL 1 // RPL breakpoint +#define BN_ASM_BT 2 // ASM and RPL breakpoint + +// debugger state definitions +#define DBG_SUSPEND -1 +#define DBG_OFF 0 +#define DBG_RUN 1 +#define DBG_STEPINTO 2 +#define DBG_STEPOVER 3 +#define DBG_STEPOUT 4 + +// debugger.c +extern VOID UpdateDbgCycleCounter(VOID); +extern BOOL CheckBreakpoint(DWORD dwAddr, DWORD wRange, UINT nType); +extern VOID NotifyDebugger(INT nType); +extern VOID DisableDebugger(VOID); +extern LRESULT OnToolDebug(VOID); +extern VOID LoadBreakpointList(HANDLE hFile); +extern VOID SaveBreakpointList(HANDLE hFile); +extern VOID CreateBackupBreakpointList(VOID); +extern VOID RestoreBackupBreakpointList(VOID); diff --git a/app/src/main/cpp/DISASM.C b/app/src/main/cpp/DISASM.C new file mode 100644 index 0000000..71078e0 --- /dev/null +++ b/app/src/main/cpp/DISASM.C @@ -0,0 +1,1845 @@ +/* + * Disasm.c + * + * This file is part of Emu48, a ported version of x48 + * + * Copyright (C) 1994 Eddie C. Dost + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +#pragma inline_depth(0) + +#define TAB_SKIP 8 + +BOOL disassembler_mode = HP_MNEMONICS; +BOOL disassembler_symb = FALSE; + +static LPCTSTR hex[] = +{ + _T("0123456789ABCDEF"), + _T("0123456789abcdef") +}; + +static LPCTSTR opcode_0_tbl[32] = +{ + /* + * HP Mnemonics + */ + _T("RTNSXM"), _T("RTN"), _T("RTNSC"), _T("RTNCC"), + _T("SETHEX"), _T("SETDEC"), _T("RSTK=C"), _T("C=RSTK"), + _T("CLRST"), _T("C=ST"), _T("ST=C"), _T("CSTEX"), + _T("P=P+1"), _T("P=P-1"), _T("(NULL)"), _T("RTI"), + /* + * Class Mnemonics + */ + _T("rtnsxm"), _T("rtn"), _T("rtnsc"), _T("rtncc"), + _T("sethex"), _T("setdec"), _T("push"), _T("pop"), + _T("clr.3 st"), _T("move.3 st, c"), _T("move.3 c, st"), _T("exg.3 c, st"), + _T("inc.1 p"), _T("dec.1 p"), _T("(null)"), _T("rti") +}; + +static LPCTSTR op_str_0[16] = +{ + /* + * HP Mnemonics + */ + _T("A=A%cB"), _T("B=B%cC"), _T("C=C%cA"), _T("D=D%cC"), + _T("B=B%cA"), _T("C=C%cB"), _T("A=A%cC"), _T("C=C%cD"), + /* + * Class Mnemonics + */ + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d"), + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c") +}; + +static LPCTSTR op_str_1[16] = +{ + /* + * HP Mnemonics + */ + _T("DAT0=A"), _T("DAT1=A"), _T("A=DAT0"), _T("A=DAT1"), + _T("DAT0=C"), _T("DAT1=C"), _T("C=DAT0"), _T("C=DAT1"), + /* + * Class Mnemonics + */ + _T("a, (d0)"), _T("a, (d1)"), _T("(d0), a"), _T("(d1), a"), + _T("c, (d0)"), _T("c, (d1)"), _T("(d0), c"), _T("(d1), c") +}; + +static LPCTSTR in_str_80[32] = +{ + /* + * HP Mnemonics + */ + _T("OUT=CS"), _T("OUT=C"), _T("A=IN"), _T("C=IN"), + _T("UNCNFG"), _T("CONFIG"), _T("C=ID"), _T("SHUTDN"), + NULL, _T("C+P+1"), _T("RESET"), _T("BUSCC"), + NULL, NULL, _T("SREQ?"), NULL, + /* + * Class Mnemonics + */ + _T("move.s c, out"), _T("move.3 c, out"), _T("move.4 in, a"), _T("move.4 in, c"), + _T("uncnfg"), _T("config"), _T("c=id"), _T("shutdn"), + NULL, _T("add.a p+1, c"), _T("reset"), _T("buscc"), + NULL, NULL, _T("sreq?"), NULL +}; + +static LPCTSTR in_str_808[32] = +{ + /* + * HP Mnemonics + */ + _T("INTON"), NULL, NULL, _T("BUSCB"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + _T("PC=(A)"), _T("BUSCD"), _T("PC=(C)"), _T("INTOFF"), + /* + * Class Mnemonics + */ + _T("inton"), NULL, NULL, _T("buscb"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + _T("jmp (a)"), _T("buscd"), _T("jmp (c)"), _T("intoff") +}; + +static LPCTSTR op_str_81[8] = +{ + /* + * HP Mnemonics + */ + _T("A"), _T("B"), _T("C"), _T("D"), + /* + * Class Mnemonics + */ + _T("a"), _T("b"), _T("c"), _T("d") +}; + +static LPCTSTR in_str_81b[32] = +{ + /* + * HP Mnemonics + */ + NULL, NULL, _T("PC=A"), _T("PC=C"), + _T("A=PC"), _T("C=PC"), _T("APCEX"), _T("CPCEX"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* + * Class Mnemonics + */ + NULL, NULL, _T("jmp a"), _T("jmp c"), + _T("move.a pc, a"), _T("move.a pc, c"), _T("exg.a a, pc"), _T("exg.a c, pc"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static LPCTSTR in_str_9[16] = +{ + /* + * HP Mnemonics + */ + _T("="), _T("#"), _T("="), _T("#"), + _T(">"), _T("<"), _T(">="), _T("<="), + /* + * Class Mnemonics + */ + _T("eq"), _T("ne"), _T("eq"), _T("ne"), + _T("gt"), _T("lt"), _T("ge"), _T("le") +}; + +static LPCTSTR op_str_9[16] = +{ + /* + * HP Mnemonics + */ + _T("?A%sB"), _T("?B%sC"), _T("?C%sA"), _T("?D%sC"), + _T("?A%s0"), _T("?B%s0"), _T("?C%s0"), _T("?D%s0"), + /* + * Class Mnemonics + */ + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c"), + _T("a, 0"), _T("b, 0"), _T("c, 0"), _T("d, 0") +}; + +static LPCTSTR op_str_af[32] = +{ + /* + * HP Mnemonics + */ + _T("A=A%sB"), _T("B=B%sC"), _T("C=C%sA"), _T("D=D%sC"), + _T("A=A%sA"), _T("B=B%sB"), _T("C=C%sC"), _T("D=D%sD"), + _T("B=B%sA"), _T("C=C%sB"), _T("A=A%sC"), _T("C=C%sD"), + _T("A=B%sA"), _T("B=C%sB"), _T("C=A%sC"), _T("D=C%sD"), + /* + * Class Mnemonics + */ + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d"), + _T("a, a"), _T("b, b"), _T("c, c"), _T("d, d"), + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c"), + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d") +}; + +static LPCTSTR hp_reg_1_af = _T("ABCDABCDBCACABAC"); +static LPCTSTR hp_reg_2_af = _T("0000BCACABCDBCCD"); + +static LPCTSTR field_tbl[32] = +{ + /* + * HP Mnemonics + */ + _T("P"), _T("WP"), _T("XS"), _T("X"), + _T("S"), _T("M"), _T("B"), _T("W"), + _T("P"), _T("WP"), _T("XS"), _T("X"), + _T("S"), _T("M"), _T("B"), _T("A"), + /* + * Class Mnemonics + */ + _T(".p"), _T(".wp"), _T(".xs"), _T(".x"), + _T(".s"), _T(".m"), _T(".b"), _T(".w"), + _T(".p"), _T(".wp"), _T(".xs"), _T(".x"), + _T(".s"), _T(".m"), _T(".b"), _T(".a") +}; + +static LPCTSTR hst_bits[8] = +{ + /* + * HP Mnemonics + */ + _T("XM"), _T("SB"), _T("SR"), _T("MP"), + /* + * Class Mnemonics + */ + _T("xm"), _T("sb"), _T("sr"), _T("mp") +}; + +// general functions + +static BYTE read_nibble (DWORD *p) +{ + return GetMemNib(p); +} + +static int read_int (DWORD *addr, int n) +{ + int i, t; + + for (i = 0, t = 0; i < n; i++) + t |= read_nibble (addr) << (i * 4); + + return t; +} + +static LPTSTR append_str (LPTSTR buf, LPCTSTR str) +{ + while ((*buf = *str++)) + buf++; + return buf; +} + +static LPTSTR append_tab (LPTSTR buf) +{ + int n; + LPTSTR p; + + n = lstrlen (buf); + p = &buf[n]; + n = TAB_SKIP - (n % TAB_SKIP); + while (n--) + *p++ = _T(' '); + *p = 0; + return p; +} + +static __inline LPTSTR append_field (LPTSTR buf, BYTE fn) +{ + return append_str (buf, field_tbl[fn + 16 * disassembler_mode]); +} + +static LPTSTR append_imm_nibble (LPTSTR buf, DWORD *addr, int n) +{ + int i; + BYTE t[16]; + + LPTSTR p = buf; // save start of buffer + + if (disassembler_mode == CLASS_MNEMONICS) + { + *buf++ = _T('#'); + if (n > 1) + *buf++ = _T('$'); + } + else // HP Mnemonics + { + if (n > 1) // hex mode + *buf++ = _T('#'); // insert hex header + } + if (n > 1) + { + DWORD dwAddr = 0; // decoded address + + for (i = 0; i < n; i++) + t[i] = read_nibble (addr); + + for (i = n - 1; i >= 0; i--) + { + dwAddr = (dwAddr << 4) | t[i]; + *buf++ = hex[disassembler_mode][t[i]]; + } + *buf = 0; + + if (n == 5) // 5 nibble address + { + LPCTSTR lpszName; + + if (disassembler_symb && (lpszName = RplGetName(dwAddr)) != NULL) + { + // overwrite number with symbolic name + buf = append_str(p, _T("=")); + buf = append_str(buf, lpszName); + } + } + } + else // single nibble + { + buf += wsprintf (buf, _T("%d"), read_nibble (addr)); + } + return buf; +} + +static LPTSTR append_numaddr (LPTSTR buf, DWORD addr) +{ + int shift; + + if (disassembler_mode == CLASS_MNEMONICS) + { + *buf++ = _T('$'); + } + for (shift = 16; shift >= 0; shift -= 4) + *buf++ = hex[disassembler_mode][(addr >> shift) & 0xF]; + *buf = 0; + return buf; +} + +static LPTSTR append_addr (LPTSTR buf, DWORD addr) +{ + LPCTSTR lpszName; + + if (disassembler_symb && (lpszName = RplGetName(addr)) != NULL) + { + buf = append_str(buf, _T("=")); + buf = append_str(buf, lpszName); + } + else // no symbol + { + buf = append_numaddr (buf, addr); + } + return buf; +} + +static LPTSTR append_r_addr (LPTSTR buf, DWORD *pc, long disp, int n, int offset) +{ + LPCTSTR lpszName; + long sign; + + sign = 1 << (n * 4 - 1); + if (disp & sign) // negative value? + disp |= ~(sign - 1); // expand it to long + *pc = (*pc + disp) & 0xFFFFF; + + if (disassembler_symb && (lpszName = RplGetName(*pc)) != NULL) + { + buf = append_str(buf, _T("=")); // show symbol + buf = append_str(buf, lpszName); + } + else // no symbol + { + if (disp < 0) + { + buf = append_str(buf, _T("-")); + disp = -disp - offset; + } + else + { + buf = append_str(buf, _T("+")); + disp += offset; + } + buf = append_numaddr(buf, disp); // show offset + + buf = append_str(buf, _T(" [")); + buf = append_numaddr(buf, *pc); // show absolute address + buf = append_str(buf, _T("]")); + } + return buf; +} + +static LPTSTR append_hst_bits (LPTSTR buf, int n) +{ + int i; + LPTSTR p = buf; + + switch (disassembler_mode) + { + case HP_MNEMONICS: + for (i = 0; i < 4; i++) + if (n & (1 << i)) + { + p = append_str (p, hst_bits[i + 4 * disassembler_mode]); + } + break; + case CLASS_MNEMONICS: + while (lstrlen (buf) < 4 * TAB_SKIP) + p = append_tab (buf); + p = &buf[lstrlen (buf)]; + p = append_str (p, _T("; hst bits: ")); + + for (buf = p, i = 0; i < 4; i++) + if (n & (1 << i)) + { + if (p != buf) + p = append_str (p, _T(", ")); + p = append_str (p, hst_bits[i + 4 * disassembler_mode]); + } + break; + default: + p = append_str (p, _T("Unknown disassembler mode")); + break; + } + return p; +} + +static LPTSTR disasm_1 (DWORD *addr, LPTSTR out) +{ + BYTE n; + BYTE fn; + LPTSTR p; + TCHAR buf[20]; + TCHAR c; + + p = out; + switch (n = read_nibble (addr)) + { + case 0: + case 1: + fn = read_nibble (addr); + c = (fn < 8); // flag for operand register + fn = (fn & 7); // get register number + if (fn > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) (c ? _T('A') : _T('C')); + if (n == 0) + wsprintf (buf, _T("R%d=%c"), fn, c); + else + wsprintf (buf, _T("%c=R%d"), c, fn); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("move.w")); + p = append_tab (out); + c = (TCHAR) (c ? _T('a') : _T('c')); + if (n == 0) + wsprintf (buf, _T("%c, r%d"), c, fn); + else + wsprintf (buf, _T("r%d, %c"), fn, c); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 2: + fn = read_nibble (addr); + c = (fn < 8); // flag for operand register + fn = (fn & 7); // get register number + if (fn > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) (c ? _T('A') : _T('C')); + wsprintf (buf, _T("%cR%dEX"), c, fn); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("exg.w")); + p = append_tab (out); + c = (TCHAR) (c ? _T('a') : _T('c')); + wsprintf (buf, _T("%c, r%d"), c, fn); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (n & 4) ? _T('C') : _T('A'); + if (n & 2) + { + if (n < 8) + { + wsprintf (buf, _T("%cD%dEX"), c, (n & 1)); + } + else + { + wsprintf (buf, _T("%cD%dXS"), c, (n & 1)); + } + } + else + { + if (n < 8) + { + wsprintf (buf, _T("D%d=%c"), (n & 1), c); + } + else + { + wsprintf (buf, _T("D%d=%cS"), (n & 1), c); + } + } + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n & 2) ? _T("exg.") : _T("move.")); + p = append_str (p, (n < 8) ? _T("a") : _T("4")); + p = append_tab (out); + c = (n & 4) ? _T('c') : _T('a'); + wsprintf (buf, _T("%c, d%d"), c, (n & 1)); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, op_str_1[(fn & 7) + 8 * disassembler_mode]); + p = append_tab (out); + if (n == 4) + { + p = append_str (p, (fn < 8) ? _T("A") : _T("B")); + } + else + { + n = read_nibble (addr); + if (fn < 8) + { + p = append_field (p, n); + } + else + { + wsprintf (buf, _T("%d"), n + 1); + p = append_str (p, buf); + } + } + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("move")); + if (n == 4) + { + p = append_str (p, _T(".")); + p = append_str (p, (fn < 8) ? _T("a") : _T("b")); + } + else + { + n = read_nibble (addr); + if (fn < 8) + { + p = append_field (p, n); + } + else + { + wsprintf (buf, _T(".%d"), n + 1); + p = append_str (p, buf); + } + } + p = append_tab (out); + p = append_str (p, op_str_1[(fn & 7) + 8 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + case 8: + case 0xc: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 6 || n == 8) + p = append_str (out, _T("D0=D0")); + else + p = append_str (out, _T("D1=D1")); + if (n < 8) + p = append_str (p, _T("+")); + else + p = append_str (p, _T("-")); + p = append_tab (out); + wsprintf (buf, _T("%d"), fn + 1); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + if (n < 8) + p = append_str (out, _T("add.a")); + else + p = append_str (out, _T("sub.a")); + p = append_tab (out); + wsprintf (buf, _T("#%d, "), fn + 1); + p = append_str (p, buf); + if (n == 6 || n == 8) + p = append_str (p, _T("d0")); + else + p = append_str (p, _T("d1")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 9: + case 0xa: + case 0xb: + case 0xd: + case 0xe: + case 0xf: + c = (TCHAR) ((n < 0xd) ? _T('0') : _T('1')); + switch (n & 3) + { + case 1: + n = 2; + break; + case 2: + n = 4; + break; + case 3: + n = 5; + break; + } + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("D%c=(%d)"), c, n); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n); + break; + case CLASS_MNEMONICS: + if (n == 5) + { + wsprintf (buf, _T("move.a")); + } + else + if (n == 4) + { + wsprintf (buf, _T("move.as")); + } + else + { + wsprintf (buf, _T("move.b")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n); + wsprintf (buf, _T(", d%c"), c); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + return p; +} + +static LPTSTR disasm_8 (DWORD *addr, LPTSTR out) +{ + BYTE n; + BYTE fn; + LPTSTR p = out; + TCHAR c; + TCHAR buf[20]; + DWORD disp, pc; + + fn = read_nibble (addr); + switch (fn) + { + case 0: + n = read_nibble (addr); + if (NULL != (p = (LPTSTR) in_str_80[n + 16 * disassembler_mode])) + { + p = append_str (out, p); + return p; + } + switch (n) + { + case 8: + fn = read_nibble (addr); + if (NULL != (p = (LPTSTR) in_str_808[fn + 16 * disassembler_mode])) + { + p = append_str (out, p); + return p; + } + switch (fn) + { + case 1: + n = read_nibble (addr); + if (n == 0) + { + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("RSI")); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("rsi")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + } + else + p = out; // illegal opcode, no output + break; + case 2: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n < 5) + { + wsprintf (buf, _T("LA(%d)"), n + 1); + } + else + { + wsprintf (buf, _T("LAHEX")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n + 1); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.%d"), n + 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n + 1); + wsprintf (buf, _T(", a.p")); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + case 8: + case 9: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%cBIT=%d"), (fn & 8) ? _T('C') : _T('A'), + (fn & 1) ? 1 : 0); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn & 1) ? _T("bset") : _T("bclr")); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + p = append_str (p, (fn & 8) ? _T(", c") : _T(", a")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + case 0xa: + case 0xb: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) ((fn < 0xa) ? _T('A') : _T('C')); + wsprintf (buf, _T("?%cBIT=%d"), c, (fn & 1) ? 1 : 0); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 5); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + c = (TCHAR) ((fn < 0xa) ? _T('a') : _T('c')); + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn & 1) ? _T("bs") : _T("bc")); + p = append_tab (out); + wsprintf (buf, _T("#%d, %c"), n, c); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 5); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 0xc: + case 0xd: + case 0xf: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, (n == 0xf) ? _T("%c%cEX") : _T("%c=%c"), + (n == 0xd) ? _T('P') : _T('C'), (n == 0xd) ? _T('C') : _T('P')); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), fn); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n == 0xf) ? _T("exg.1") : _T("move.1")); + p = append_tab (out); + wsprintf (buf, (n == 0xd) ? _T("p, c.%d") : _T("c.%d, p"), fn); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 1: + switch (n = read_nibble (addr)) + { + case 0: + case 1: + case 2: + case 3: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSLC"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("rol.w")); + p = append_tab (out); + p = append_str (p, _T("#4, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + case 6: + case 7: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRC"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("ror.w")); + p = append_tab (out); + p = append_str (p, _T("#4, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + fn = read_nibble (addr); // get number + n = read_nibble (addr); // get register selector + if ((n & 7) > 3) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%s=%s%cCON"), + op_str_81[(n & 3) + 4 * disassembler_mode], + op_str_81[(n & 3) + 4 * disassembler_mode], + (n < 8) ? _T('+') : _T('-')); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + fn = read_nibble (addr); + wsprintf (buf, _T(", %d"), fn + 1); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n < 8) ? _T("add") : _T("sub")); + p = append_field (p, fn); + p = append_tab (out); + fn = read_nibble (addr); + wsprintf (buf, _T("#%d, "), fn + 1); + p = append_str (p, buf); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 9: + fn = read_nibble (addr); // get field selector + n = read_nibble (addr); // get register selector + if (n > 3) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRB.F"), op_str_81[n + 4 * disassembler_mode]); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("lsr")); + p = append_field (p, fn); + p = append_tab (out); + p = append_str (p, _T("#1, ")); + p = append_str (p, op_str_81[n + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xa: + fn = read_nibble (addr); + n = read_nibble (addr); + if (n > 2) // illegal opcode + break; // no output + c = (TCHAR) read_nibble (addr); + if (((int) c & 7) > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 2) + { + wsprintf (buf, _T("%cR%dEX.F"), ((int) c < 8) ? _T('A') : _T('C'), + (int) c & 7); + } + else + if (n == 1) + { + wsprintf (buf, _T("%c=R%d.F"), ((int) c < 8) ? _T('A') : _T('C'), + (int) c & 7); + } + else + { + wsprintf (buf, _T("R%d=%c.F"), (int) c & 7, + ((int) c < 8) ? _T('A') : _T('C')); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n == 2) ? _T("exg") : _T("move")); + p = append_field (p, fn); + p = append_tab (out); + if (n == 1) + { + wsprintf (buf, _T("r%d"), (int) c & 7); + p = append_str (p, buf); + } + else + p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); + p = append_str (p, _T(", ")); + if (n == 1) + p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); + else + { + wsprintf (buf, _T("r%d"), (int) c & 7); + p = append_str (p, buf); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xb: + n = read_nibble (addr); + if ((n < 2) || (n > 7)) // illegal opcode + break; // no output + + p = append_str (out, in_str_81b[n + 16 * disassembler_mode]); + break; + + case 0xc: + case 0xd: + case 0xe: + case 0xf: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRB"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("lsr.w")); + p = append_tab (out); + p = append_str (p, _T("#1, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 2: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 0xf) + { + p = append_str (out, _T("CLRHST")); + } + else + { + // when not only one bit is set the HS=0 opcode is used + if (n != 1 && n != 2 && n != 4 && n != 8) + { + p = append_str (out, _T("HS=0")); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + } + else + { + p = append_hst_bits (out, n); + p = append_str (p, _T("=0")); + } + } + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("clr.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, hst"), n); + p = append_str (p, buf); + p = append_hst_bits (out, n); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n != 1 && n != 2 && n != 4 && n != 8) + { + p = append_str (out, _T("?HS=0")); + p = append_tab (out); + wsprintf (buf, _T("%d, "), n); + p = append_str (p, buf); + } + else + { + p = append_str (out, _T("?")); + p = append_hst_bits (p, n); + p = append_str (p, _T("=0")); + p = append_tab (out); + } + if (disp != 0) + { + p = append_str (p, _T("GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + else + p = append_str (p, _T("RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, _T("eq.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, hst"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + p = append_hst_bits (out, n); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("ST=%d"), (fn == 4) ? 0 : 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 4) ? _T("bclr") : _T("bset")); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + p = append_str (p, _T(", st")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("?ST=%d"), (fn == 6) ? 0 : 1); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn == 6) ? _T("bc") : _T("bs")); + p = append_tab (out); + wsprintf (buf, _T("#%d, st"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + case 9: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("?P%c"), (fn == 8) ? _T('#') : _T('=')); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn == 8) ? _T("ne.1") : _T("eq.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, p"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xc: + case 0xe: + pc = *addr; + if (fn == 0xe) + pc += 4; + disp = read_int (addr, 4); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, (fn == 0xc) ? _T("GOLONG") : _T("GOSUBL")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 0xc) ? _T("bra.4") : _T("bsr.4")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xd: + case 0xf: + pc = read_int (addr, 5); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, (fn == 0xd) ? _T("GOVLNG") : _T("GOSBVL")); + p = append_tab (out); + p = append_addr (p, pc); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 0xd) ? _T("jmp") : _T("jsr")); + p = append_tab (out); + p = append_addr (p, pc); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + return p; +} + + +// public functions + +DWORD disassemble (DWORD addr, LPTSTR out) +{ + BYTE n; + BYTE fn; + LPTSTR p = out; + TCHAR c; + TCHAR buf[20]; + DWORD disp, pc; + + switch (n = read_nibble (&addr)) + { + case 0: + if ((n = read_nibble (&addr)) != 0xe) + { + p = append_str (out, opcode_0_tbl[n + 16 * disassembler_mode]); + break; + } + fn = read_nibble (&addr); + n = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, op_str_0[(n & 7) + 8 * HP_MNEMONICS], + (n < 8) ? _T('&') : _T('!')); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n < 8) ? _T("and") : _T("or")); + p = append_field (p, fn); + p = append_tab (out); + p = append_str (p, op_str_0[(n & 7) + 8 * CLASS_MNEMONICS]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 1: + p = disasm_1 (&addr, out); + break; + + case 2: + n = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("P=")); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.1 #%d, p"), n); + p = append_str (out, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + fn = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (fn < 5) + { + wsprintf (buf, _T("LC(%d)"), fn + 1); + } + else + { + wsprintf (buf, _T("LCHEX")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, &addr, fn + 1); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.%d"), fn + 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, &addr, fn + 1); + wsprintf (buf, _T(", c.p")); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + pc = addr; + disp = read_int (&addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (disp == 2) + { + p = append_str (out, _T("NOP3")); + break; + } + wsprintf (buf, (disp == 0) ? _T("RTN%sC") : _T("GO%sC"), (n == 4) ? _T("") : _T("N")); + p = append_str (out, buf); + if (disp != 0) + { + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 2, 1); + } + break; + + case CLASS_MNEMONICS: + if (disp == 2) + { + p = append_str (out, _T("nop3")); + break; + } + p = append_str (out, (disp == 0) ? _T("rtc") : _T("bc")); + p = append_str (p, (n == 4) ? _T("s") : _T("c")); + if (disp != 0) + { + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 2, 1); + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + pc = addr; + disp = read_int (&addr, 3); // read GOTO distance + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (disp == 3) // special case "GOTO next instruction" + { + p = append_str (out, _T("NOP4")); + break; + } + if (disp == 4) // special case "GOTO to +4 nibbles" + { + addr++; // skipping the fifth nibble in the opcode + p = append_str (out, _T("NOP5")); + break; + } + p = append_str (out, _T("GOTO")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 1); + break; + + case CLASS_MNEMONICS: + if (disp == 3) + { + p = append_str (out, _T("nop4")); + break; + } + if (disp == 4) + { + addr++; // skipping the fifth nibble in the opcode + p = append_str (out, _T("nop5")); + break; + } + p = append_str (out, _T("bra.3")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 1); + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 7: + pc = addr + 3; + disp = read_int (&addr, 3); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("GOSUB")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 4); + break; + + case CLASS_MNEMONICS: + p = append_str (out, _T("bsr.3")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 4); + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + fn = read_nibble (&addr); /* PEEK */ + --addr; + if (fn != 0xa && fn != 0xb) + { + p = disasm_8 (&addr, out); + break; + } + /* Fall through */ + + case 9: + fn = read_nibble (&addr); + if (n == 8) + { + c = (TCHAR) ((fn == 0xa) ? 0 : 1); + fn = 0xf; + } + else + { + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + } + + n = read_nibble (&addr); + pc = addr; + disp = read_int (&addr, 2); + + switch (disassembler_mode) + { + case HP_MNEMONICS: + if ((c == 0) && (n >= 8)) + wsprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS + 4], + in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]); + else + wsprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS], + in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + p = append_str (p, _T(", ")); + p = append_str (p, (disp == 0) ? _T("RTNYES") : _T("GOYES ")); + if (disp != 0) + { + p = append_r_addr (p, &pc, disp, 2, 3); + } + break; + + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, in_str_9[((n >> 2) & 3) + 4 * c + 8 * CLASS_MNEMONICS]); + p = append_field (p, fn); + p = append_tab (out); + if ((c == 0) && (n >= 8)) + p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS + 4]); + else + p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS]); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + switch (n) + { + case 0xa: + fn = read_nibble (&addr); + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + disp = 0xa; + break; + case 0xb: + fn = read_nibble (&addr); + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + disp = 0xb; + break; + case 0xc: + case 0xd: + fn = 0xf; + c = (TCHAR) (n & 1); + disp = 0xa; + break; + case 0xe: + case 0xf: + fn = 0xf; + c = (TCHAR) (n & 1); + disp = 0xb; + break; + default: + fn = 0; + disp = 0; + c = 0; + break; + } + + n = read_nibble (&addr); + pc = 0; + + switch (disp) + { + case 0xa: + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (c == 0) + { + if (n < 0xc) + { + p = _T("+"); + } + else + { + p = _T("%c=%c-1"); + pc = 2; + } + } + else + { + if (n < 4) + { + p = _T("%c=0"); + pc = 1; + } + else + if (n >= 0xc) + { + p = _T("%c%cEX"); + pc = 3; + } + else + { + p = _T("%c=%c"); + pc = 3; + } + } + break; + + case CLASS_MNEMONICS: + if (c == 0) + { + if (n < 0xc) + { + p = _T("add"); + } + else + { + p = _T("dec"); + pc = 1; + } + } + else + { + if (n < 4) + { + p = _T("clr"); + pc = 1; + } + else + if (n >= 0xc) + { + p = _T("exg"); + } + else + { + p = _T("move"); + if (n < 8) + n -= 4; + } + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + return addr; + } + break; + + case 0xb: + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (c == 0) + { + if (n >= 0xc) + { + p = _T("-"); + } + else + if ((n >= 4) && (n <= 7)) + { + p = _T("%c=%c+1"); + pc = 2; + n -= 4; + } + else + { + p = _T("-"); + } + } + else + { + if (n < 4) + { + p = _T("%cSL"); + pc = 1; + } + else + if (n < 8) + { + p = _T("%cSR"); + pc = 1; + } + else + if (n < 0xc) + { + p = _T("%c=-%c"); + pc = 2; + } + else + { + p = _T("%c=-%c-1"); + pc = 2; + } + } + break; + + case CLASS_MNEMONICS: + if (c == 0) + { + if (n >= 0xc) + { + p = _T("subr"); + } + else + if ((n >= 4) && (n <= 7)) + { + p = _T("inc"); + pc = 1; + n -= 4; + } + else + { + p = _T("sub"); + } + } + else + { + pc = 1; + if (n < 4) + { + p = _T("lsl"); + } + else + if (n < 8) + { + p = _T("lsr"); + } + else + if (n < 0xc) + { + p = _T("neg"); + } + else + { + p = _T("not"); + } + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + return addr; + } + break; + + } + + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (pc == 0) + { + wsprintf (buf, op_str_af[n + 16 * HP_MNEMONICS], p); + } + else + if (pc == 1) + { + wsprintf (buf, p, (n & 3) + _T('A')); + } + else + if (pc == 2) + { + wsprintf (buf, p, (n & 3) + _T('A'), (n & 3) + _T('A')); + } + else + { + wsprintf (buf, p, hp_reg_1_af[n], hp_reg_2_af[n]); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + + case CLASS_MNEMONICS: + p = append_str (out, p); + p = append_field (p, fn); + p = append_tab (out); + if (pc == 1) + { + wsprintf (buf, _T("%c"), (n & 3) + _T('a')); + p = append_str (p, buf); + } + else + { + p = append_str (p, op_str_af[n + 16 * CLASS_MNEMONICS]); + } + break; + + default: + p = append_str (p, _T("Unknown disassembler mode")); + break; + } + break; + } + *p = 0; + + return addr; +} diff --git a/app/src/main/cpp/DISMEM.C b/app/src/main/cpp/DISMEM.C new file mode 100644 index 0000000..6cda3e6 --- /dev/null +++ b/app/src/main/cpp/DISMEM.C @@ -0,0 +1,245 @@ +/* + * dismem.c + * + * This file is part of Emu48 + * + * Copyright (C) 2012 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +typedef struct // type of model memory mapping +{ + BYTE byType; // calculator type + CONST LPBYTE *ppbyNCE1; // NCE1 data + CONST DWORD *pdwNCE1Size; // NCE1 size + CONST LPBYTE *ppbyNCE2; // NCE2 data + CONST DWORD *pdwNCE2Size; // NCE2 size + CONST LPBYTE *ppbyCE1; // CE1 data + CONST DWORD *pdwCE1Size; // CE1 size + CONST LPBYTE *ppbyCE2; // CE2 data + CONST DWORD *pdwCE2Size; // CE2 size + CONST LPBYTE *ppbyNCE3; // NCE3 data + CONST DWORD *pdwNCE3Size; // NCE3 size +} MODEL_MAP_T; + +static CONST LPBYTE pbyNoMEM = NULL; // no memory module + +static CONST MODEL_MAP_T MemMap[] = +{ + { + 0, // default + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + '6', // HP38G (64K) + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'A', // HP38G + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'E', // HP39/40G + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM part 1 + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // nc. + &Port2, &Chipset.Port2Size // RAM part 2 + }, + { + 'G', // HP48GX + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size // Card slot 2 + }, + { + 'S', // HP48SX + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size, // Card slot 2 + &pbyNoMEM, NULL // nc. + }, + { + 'X', // HP49G + &pbyRom, &dwRomSize, // Flash + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Port1, &Chipset.Port1Size, // Port 1 part 1 + &Port2, &Chipset.Port2Size // Port 1 part 2 + }, + { // CdB for HP: add Q type + 'Q', // HP49g+ + &pbyRom, &dwRomSize, // Flash + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Port1, &Chipset.Port1Size, // Port 1 part 1 + &Port2, &Chipset.Port2Size // Port 1 part 2 + }, + { // CdB for HP: add 2 type + '2', // HP48gII + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // Port 1 part 1 + &pbyNoMEM, NULL, // Port 1 part 2 + }, + { // CdB for HP: add P type + 'P', // HP39g+/gs + &pbyRom, &dwRomSize, // ROM + &Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // nc. + &Port2, &Chipset.Port2Size // RAM part 2 + } +}; + +static MODEL_MAP_T CONST *pMapping = MemMap; // model specific memory mapping +static enum MEM_MAPPING eMapType = MEM_MMU; // MMU memory mapping + +static LPBYTE pbyMapData = NULL; +static DWORD dwMapDataSize = 0; +static DWORD dwMapDataMask = 0; + +BOOL SetMemRomType(BYTE cCurrentRomType) +{ + UINT i; + + pMapping = MemMap; // init default mapping + + // scan for all table entries + for (i = 0; i < ARRAYSIZEOF(MemMap); ++i) + { + if (MemMap[i].byType == cCurrentRomType) + { + pMapping = &MemMap[i]; // found entry + return TRUE; + } + } + return FALSE; +} + +BOOL SetMemMapType(enum MEM_MAPPING eType) +{ + BOOL bSucc = TRUE; + + eMapType = eType; + + switch (eMapType) + { + case MEM_MMU: + pbyMapData = NULL; // data + dwMapDataSize = 512 * 1024 * 2; // data size + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + case MEM_NCE1: + pbyMapData = *pMapping->ppbyNCE1; + dwMapDataSize = *pMapping->pdwNCE1Size; // ROM size is always in nibbles + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + case MEM_NCE2: + pbyMapData = *pMapping->ppbyNCE2; + dwMapDataSize = *pMapping->pdwNCE2Size * 1024 * 2; + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + case MEM_CE1: + pbyMapData = *pMapping->ppbyCE1; + dwMapDataSize = *pMapping->pdwCE1Size * 1024 * 2; + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + case MEM_CE2: + pbyMapData = *pMapping->ppbyCE2; + dwMapDataSize = *pMapping->pdwCE2Size * 1024 * 2; + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + case MEM_NCE3: + pbyMapData = *pMapping->ppbyNCE3; + dwMapDataSize = *pMapping->pdwNCE3Size * 1024 * 2; + dwMapDataMask = dwMapDataSize - 1; // size mask + break; + default: _ASSERT(FALSE); + pbyMapData = NULL; + dwMapDataSize = 0; + dwMapDataMask = 0; + bSucc = FALSE; + } + return bSucc; +} + +enum MEM_MAPPING GetMemMapType(VOID) +{ + return eMapType; +} + +BOOL GetMemAvail(enum MEM_MAPPING eType) +{ + switch (eType) + { + case MEM_MMU: return TRUE; + case MEM_NCE1: return *pMapping->ppbyNCE1 != NULL; + case MEM_NCE2: return *pMapping->ppbyNCE2 != NULL; + case MEM_CE1: return *pMapping->ppbyCE1 != NULL; + case MEM_CE2: return *pMapping->ppbyCE2 != NULL; + case MEM_NCE3: return *pMapping->ppbyNCE3 != NULL; + default: _ASSERT(FALSE); + } + return FALSE; +} + +DWORD GetMemDataSize(VOID) +{ + return dwMapDataSize; +} + +DWORD GetMemDataMask(VOID) +{ + return dwMapDataMask; +} + +BYTE GetMemNib(DWORD *p) +{ + BYTE byVal; + + if (pbyMapData == NULL) + { + Npeek(&byVal, *p, 1); + } + else + { + byVal = pbyMapData[*p]; + } + *p = (*p + 1) & dwMapDataMask; + return byVal; +} + +VOID GetMemPeek(BYTE *a, DWORD d, UINT s) +{ + if (pbyMapData == NULL) + { + Npeek(a, d, s); + } + else + { + for (; s > 0; --s, ++d) + { + *a++ = pbyMapData[d & dwMapDataMask]; + } + } + return; +} diff --git a/app/src/main/cpp/DISPLAY.C b/app/src/main/cpp/DISPLAY.C new file mode 100644 index 0000000..3dea432 --- /dev/null +++ b/app/src/main/cpp/DISPLAY.C @@ -0,0 +1,746 @@ +/* + * display.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 2002 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" + +// #define DEBUG_DISPLAY // switch for DISPLAY debug purpose + +#define NOCOLORSGRAY 8 // no. of colors in gray scale mode +#define NOCOLORSBW 2 // no. of colors in black and white mode + +#define DISPLAY_FREQ 19 // display update 1/frequency (1/64) in ms (gray scale mode) + +#define B 0x00000000 // black +#define W 0x00FFFFFF // white +#define I 0xFFFFFFFF // ignore + +#define LCD_ROW (36*4) // max. pixel per line + +#define GRAYMASK(c) (((((c)-1)>>1)<<24) \ + |((((c)-1)>>1)<<16) \ + |((((c)-1)>>1)<<8) \ + |((((c)-1)>>1))) + +#define DIBPIXEL4(d,p) *((DWORD*)(d)) = ((*((DWORD*)(d)) & dwGrayMask) << 1) | (p); \ + *((LPBYTE*) &(d)) += 4 + +BOOL bGrayscale = FALSE; +UINT nBackgroundX = 0; +UINT nBackgroundY = 0; +UINT nBackgroundW = 0; +UINT nBackgroundH = 0; +UINT nLcdX = 0; +UINT nLcdY = 0; +UINT nLcdZoom = 1; // memory DC zoom +UINT nGdiXZoom = 1; // GDI x zoom +UINT nGdiYZoom = 1; // GDI y zoom +HDC hLcdDC = NULL; +HDC hMainDC = NULL; +HDC hAnnunDC = NULL; // annunciator DC + +BYTE (*GetLineCounter)(VOID) = NULL; +VOID (*StartDisplay)(BYTE byInitial) = NULL; +VOID (*StopDisplay)(VOID) = NULL; + +static BYTE GetLineCounterGray(VOID); +static BYTE GetLineCounterBW(VOID); +static VOID StartDisplayGray(BYTE byInitial); +static VOID StartDisplayBW(BYTE byInitial); +static VOID StopDisplayGray(VOID); +static VOID StopDisplayBW(VOID); + +static LPBYTE pbyLcd; + +static HBITMAP hLcdBitmap; +static HBITMAP hMainBitmap; +static HBITMAP hAnnunBitmap; + +static DWORD Pattern[16]; +static BYTE Buf[36]; + +static DWORD dwGrayMask; + +static LARGE_INTEGER lLcdRef; // reference time for VBL counter +static UINT uLcdTimerId = 0; + +static BYTE byVblRef = 0; // VBL stop reference + +static DWORD dwKMLColor[64] = // color table loaded by KML script +{ + W,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I +}; + +static struct +{ + BITMAPINFOHEADER Lcd_bmih; + RGBQUAD bmiColors[NOCOLORSGRAY]; +} bmiLcd = +{ + {0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,NOCOLORSGRAY,0} +}; + +static __inline VOID BuildPattern(VOID) +{ + WORD i,j; + for (i=0; i<16; ++i) + { + Pattern[i] = 0; + for (j=8; j>0; j>>=1) + { + Pattern[i] = (Pattern[i] << 8) | ((i&j) != 0); + } + } + return; +} + +VOID UpdateContrast(BYTE byContrast) +{ + RGBQUAD c,b; + INT i,nColors; + + // table for max. 8 colors + const INT nCAdj[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; + + // when display is off use contrast 0 + if ((Chipset.IORam[BITOFFSET] & DON) == 0) byContrast = 0; + + c = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color + b = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color + + // if background color is undefined, use color 0 for compatibility + if (I == *(DWORD*)&b) b = *(RGBQUAD*)&dwKMLColor[0]; + + nColors = bGrayscale ? (NOCOLORSGRAY-1) : (NOCOLORSBW-1); + + _ASSERT(nColors <= ARRAYSIZEOF(nCAdj)); // no. of colors must be smaller than entries in the gray color table + + // fill color palette of bitmap + for (i = 0; i <= nColors; ++i) + { + bmiLcd.bmiColors[i] = b; + bmiLcd.bmiColors[i].rgbRed += ((INT) c.rgbRed - (INT) b.rgbRed) * nCAdj[i] / nCAdj[nColors]; + bmiLcd.bmiColors[i].rgbGreen += ((INT) c.rgbGreen - (INT) b.rgbGreen) * nCAdj[i] / nCAdj[nColors]; + bmiLcd.bmiColors[i].rgbBlue += ((INT) c.rgbBlue - (INT) b.rgbBlue) * nCAdj[i] / nCAdj[nColors]; + } + + // update palette information + _ASSERT(hLcdDC); + SetDIBColorTable(hLcdDC,0,ARRAYSIZEOF(bmiLcd.bmiColors),bmiLcd.bmiColors); + return; +} + +VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue) +{ + dwKMLColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF); + return; +} + +VOID SetLcdMode(BOOL bMode) +{ + if ((bGrayscale = bMode)) + { + // set pixel update mask + dwGrayMask = GRAYMASK(NOCOLORSGRAY); + GetLineCounter = GetLineCounterGray; + StartDisplay = StartDisplayGray; + StopDisplay = StopDisplayGray; + } + else + { + // set pixel update mask + dwGrayMask = GRAYMASK(NOCOLORSBW); + GetLineCounter = GetLineCounterBW; + StartDisplay = StartDisplayBW; + StopDisplay = StopDisplayBW; + } + UpdateContrast(Chipset.contrast); + return; +} + +VOID CreateLcdBitmap(VOID) +{ + // create LCD bitmap + bmiLcd.Lcd_bmih.biWidth = LCD_ROW; + bmiLcd.Lcd_bmih.biHeight = -SCREENHEIGHT; // CdB for HP: add 64/80 line display for apples + _ASSERT(hLcdDC == NULL); + VERIFY(hLcdDC = CreateCompatibleDC(hWindowDC)); + VERIFY(hLcdBitmap = CreateDIBSection(hWindowDC,(BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS,(VOID **)&pbyLcd,NULL,0)); + hLcdBitmap = (HBITMAP) SelectObject(hLcdDC,hLcdBitmap); + _ASSERT(hPalette != NULL); + SelectPalette(hLcdDC,hPalette,FALSE); // set palette for LCD DC + RealizePalette(hLcdDC); // realize palette + BuildPattern(); // build Nibble -> DIB mask pattern + SetLcdMode(bGrayscale); // init display update function pointer + return; +} + +VOID DestroyLcdBitmap(VOID) +{ + // set contrast palette to startup colors + WORD i = 0; dwKMLColor[i++] = W; + while (i < 32) dwKMLColor[i++] = B; + while (i < 64) dwKMLColor[i++] = I; + + GetLineCounter = NULL; + StartDisplay = NULL; + StopDisplay = NULL; + + if (hLcdDC != NULL) + { + // destroy LCD bitmap + DeleteObject(SelectObject(hLcdDC,hLcdBitmap)); + DeleteDC(hLcdDC); + hLcdDC = NULL; + hLcdBitmap = NULL; + } + return; +} + +BOOL CreateMainBitmap(LPCTSTR szFilename) +{ + _ASSERT(hWindowDC != NULL); + VERIFY(hMainDC = CreateCompatibleDC(hWindowDC)); + if (hMainDC == NULL) return FALSE; // quit if failed + hMainBitmap = LoadBitmapFile(szFilename); + if (hMainBitmap == NULL) + { + DeleteDC(hMainDC); + hMainDC = NULL; + return FALSE; + } + hMainBitmap = (HBITMAP) SelectObject(hMainDC,hMainBitmap); + _ASSERT(hPalette != NULL); + VERIFY(SelectPalette(hMainDC,hPalette,FALSE)); + RealizePalette(hMainDC); + return TRUE; +} + +VOID DestroyMainBitmap(VOID) +{ + if (hMainDC != NULL) + { + // destroy Main bitmap + DeleteObject(SelectObject(hMainDC,hMainBitmap)); + DeleteDC(hMainDC); + hMainDC = NULL; + hMainBitmap = NULL; + } + return; +} + +// +// load annunciator bitmap +// +BOOL CreateAnnunBitmap(LPCTSTR szFilename) +{ + _ASSERT(hWindowDC != NULL); + VERIFY(hAnnunDC = CreateCompatibleDC(hWindowDC)); + if (hAnnunDC == NULL) return FALSE; // quit if failed + hAnnunBitmap = LoadBitmapFile(szFilename); + if (hAnnunBitmap == NULL) + { + DeleteDC(hAnnunDC); + hAnnunDC = NULL; + return FALSE; + } + hAnnunBitmap = (HBITMAP) SelectObject(hAnnunDC,hAnnunBitmap); + return TRUE; +} + +// +// destroy annunciator bitmap +// +VOID DestroyAnnunBitmap(VOID) +{ + if (hAnnunDC != NULL) + { + VERIFY(DeleteObject(SelectObject(hAnnunDC,hAnnunBitmap))); + DeleteDC(hAnnunDC); + hAnnunDC = NULL; + hAnnunBitmap = NULL; + } + return; +} + +//**************** +//* +//* LCD functions +//* +//**************** + +VOID UpdateDisplayPointers(VOID) +{ + EnterCriticalSection(&csLcdLock); + { + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Display Pointer\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + // calculate display width + Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE; + Chipset.end1 = Chipset.start1 + MAINSCREENHEIGHT * Chipset.width; + if (Chipset.end1 < Chipset.start1) + { + // calculate first address of main display + Chipset.start12 = Chipset.end1 - Chipset.width; + // calculate last address of main display + Chipset.end1 = Chipset.start1 - Chipset.width; + } + else + { + Chipset.start12 = Chipset.start1; + } + Chipset.end2 = Chipset.start2 + MENUHEIGHT * 34; + } + LeaveCriticalSection(&csLcdLock); + return; +} + +VOID UpdateMainDisplay(VOID) +{ + UINT x, y; + BYTE *p = pbyLcd+(Chipset.d0size*LCD_ROW); // CdB for HP: add 64/80 line display for apples + DWORD d = Chipset.start1; + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Main Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + ZeroMemory(pbyLcd, LCD_ROW * SCREENHEIGHT); + } + else + { + for (y = 0; y < MAINSCREENHEIGHT; ++y) + { + Npeek(Buf,d,36); + for (x=0; x<36; ++x) // every 4 pixel + { + DIBPIXEL4(p,Pattern[Buf[x]]); + // check for display buffer overflow + _ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT); + } + d+=Chipset.width; + } + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // CdB for HP: add 64/80 line display for apples + StretchBlt(hWindowDC, nLcdX, nLcdY+Chipset.d0size*nLcdZoom, 131*nLcdZoom*nGdiXZoom, MAINSCREENHEIGHT*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.boffset, Chipset.d0size, 131, MAINSCREENHEIGHT, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID UpdateMenuDisplay(VOID) +{ + UINT x, y; + BYTE *p; + DWORD d = Chipset.start2; + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Menu Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; + if (MENUHEIGHT==0) return; // menu disabled + + // calculate bitmap offset + p = pbyLcd + ((Chipset.d0size+MAINSCREENHEIGHT)*LCD_ROW); // CdB for HP: add 64/80 line display for apples + for (y = 0; y < MENUHEIGHT; ++y) + { + Npeek(Buf,d,34); // 34 nibbles are viewed + for (x=0; x<34; ++x) // every 4 pixel + { + DIBPIXEL4(p,Pattern[Buf[x]]); + // check for display buffer overflow + _ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT); + } + // adjust pointer to 36 DIBPIXEL drawing calls + p += (36-34) * sizeof(DWORD); + d+=34; + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // CdB for HP: add 64/80 line display for apples + StretchBlt(hWindowDC, + nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, MENUHEIGHT*nLcdZoom*nGdiYZoom, + hLcdDC, + 0, (MAINSCREENHEIGHT+Chipset.d0size), + 131, MENUHEIGHT, + SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +// CdB for HP: add header management +VOID RefreshDisp0() +{ + UINT x, y; + BYTE *p; + BYTE* d = Chipset.d0memory; + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update header Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; + + // calculate bitmap offset + p = pbyLcd; + for (y = 0; y= (INT)Chipset.d0size && y0 < (INT)(MAINSCREENHEIGHT+Chipset.d0size)); + if (!(y0 >= (INT)Chipset.d0size && y0 < (INT)(MAINSCREENHEIGHT+Chipset.d0size))) return; + + while (s--) // loop for nibbles to write + { + if (x<36) // only fill visible area + { + *p = Pattern[*a]; + } + ++a; // next value to write + ++x; // next x position + if (((INT) x==lWidth)&&s) // end of display line + { + x = 0; // first coloumn + ++y; // next row + if (y == (INT) MAINSCREENHEIGHT+Chipset.d0size) break; + // recalculate bitmap memory position of new line + p = (DWORD*) (pbyLcd+y*LCD_ROW); // CdB for HP: add 64/80 line display for apples + } + else + p++; + } + if (y==y0) y++; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + StretchBlt(hWindowDC, nLcdX, nLcdY+y0*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, (y-y0)*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.boffset, y0, 131, y-y0, SRCCOPY); // CdB for HP: add 64/80 line display for apples + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) +{ + UINT x0, x; + UINT y0, y; + DWORD *p; + + if (bGrayscale) return; // no direct writing in grayscale mode + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Write Menu Display %x,%u\n"),Chipset.pc,d,s); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; + if (MENUHEIGHT == 0) return; // menu disabled + + d -= Chipset.start2; + y0 = y = (d / 34) + MAINSCREENHEIGHT+Chipset.d0size; // bitmap row + x0 = x = d % 34; + p = (DWORD*)(pbyLcd + y0*LCD_ROW + x0*sizeof(*p)); + + // outside menu display area +// _ASSERT(y0 >= (INT)(Chipset.d0size+MAINSCREENHEIGHT) && y0 < (INT)(SCREENHEIGHT)); + if (!(y0 >= (UINT)(Chipset.d0size+MAINSCREENHEIGHT) && y0 < (UINT)(SCREENHEIGHT))) return; + + while (s--) // loop for nibbles to write + { + if (x<36) // only fill visible area + { + *p = Pattern[*a]; + } + a++; // next value to write + x++; // next x position + if ((x==34)&&s) // end of display line + { + x = 0; // first coloumn + y++; // next row + if (y == SCREENHEIGHTREAL) break; + // recalculate bitmap memory position of new line + p=(DWORD*)(pbyLcd+y*LCD_ROW); // CdB for HP: add 64/80 ligne display for apples + } else p++; + } + if (y==y0) y++; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + StretchBlt(hWindowDC, nLcdX, nLcdY+y0*nLcdZoom*nGdiYZoom, + (131*nLcdZoom)*nGdiXZoom, (y-y0)*nLcdZoom*nGdiYZoom, + hLcdDC, 0, y0, 131, y-y0, SRCCOPY); // CdB for HP: add 64/80 line display for apples + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID UpdateAnnunciators(VOID) +{ + BYTE c; + + c = (BYTE)(Chipset.IORam[ANNCTRL] | (Chipset.IORam[ANNCTRL+1]<<4)); + // switch annunciators off if timer stopped + if ((c & AON) == 0 || (Chipset.IORam[TIMER2_CTRL] & RUN) == 0) + c = 0; + + DrawAnnunciator(1,c&LA1); + DrawAnnunciator(2,c&LA2); + DrawAnnunciator(3,c&LA3); + DrawAnnunciator(4,c&LA4); + DrawAnnunciator(5,c&LA5); + DrawAnnunciator(6,c&LA6); + return; +} + +VOID ResizeWindow(VOID) +{ + if (hWnd != NULL) // if window created + { + RECT rectWindow; + RECT rectClient; + + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = nBackgroundW; + rectWindow.bottom = nBackgroundH; + + AdjustWindowRect(&rectWindow, + (DWORD) GetWindowLongPtr(hWnd,GWL_STYLE), + GetMenu(hWnd) != NULL || IsRectEmpty(&rectWindow)); + SetWindowPos(hWnd, bAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + SWP_NOMOVE); + + // check if menu bar wrapped to two or more rows + GetClientRect(hWnd, &rectClient); + if (rectClient.bottom < (LONG) nBackgroundH) + { + rectWindow.bottom += (nBackgroundH - rectClient.bottom); + SetWindowPos (hWnd, NULL, 0, 0, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + SWP_NOMOVE | SWP_NOZORDER); + } + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + _ASSERT(hWindowDC); // move origin of destination window + VERIFY(SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL)); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + InvalidateRect(hWnd,NULL,TRUE); + } + return; +} + +//################ +//# +//# functions for gray scale implementation +//# +//################ + +// main display update routine +static VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + EnterCriticalSection(&csLcdLock); + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add header management + } + LeaveCriticalSection(&csLcdLock); + + QueryPerformanceCounter(&lLcdRef); // actual time + + return; + UNREFERENCED_PARAMETER(uEventId); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwUser); + UNREFERENCED_PARAMETER(dw1); + UNREFERENCED_PARAMETER(dw2); +} + +// LCD line counter calculation +static BYTE GetLineCounterGray(VOID) +{ + LARGE_INTEGER lLC; + BYTE byTime; + + if (uLcdTimerId == 0) // display off + return ((Chipset.IORam[LINECOUNT+1] & (LC5|LC4)) << 4) | Chipset.IORam[LINECOUNT]; + + QueryPerformanceCounter(&lLC); // get elapsed time since display update + + // elapsed ticks so far + byTime = (BYTE) (((lLC.QuadPart - lLcdRef.QuadPart) << 12) / lFreq.QuadPart); + + if (byTime > 0x3F) byTime = 0x3F; // all counts made + + return 0x3F - byTime; // update display between VBL counter 0x3F-0x3E +} + +static VOID StartDisplayGray(BYTE byInitial) +{ + if (uLcdTimerId) // LCD update timer running + return; // -> quit + + if (Chipset.IORam[BITOFFSET]&DON) // display on? + { + QueryPerformanceCounter(&lLcdRef); // actual time of top line + + // adjust startup counter to get the right VBL value + _ASSERT(byInitial <= 0x3F); // line counter value 0 - 63 + lLcdRef.QuadPart -= ((LONGLONG) (0x3F - byInitial) * lFreq.QuadPart) >> 12; + + VERIFY(uLcdTimerId = timeSetEvent(DISPLAY_FREQ,0,(LPTIMECALLBACK)&LcdProc,0,TIME_PERIODIC)); + } + return; +} + +static VOID StopDisplayGray(VOID) +{ + BYTE a[2]; + ReadIO(a,LINECOUNT,2,TRUE); // update VBL at display off time + + if (uLcdTimerId == 0) // timer stopped + return; // -> quit + + timeKillEvent(uLcdTimerId); // stop display update + uLcdTimerId = 0; // set flag display update stopped + + EnterCriticalSection(&csLcdLock); // update to last condition + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add header management + } + LeaveCriticalSection(&csLcdLock); + return; +} +//################ +//# +//# functions for black and white implementation +//# +//################ + +// LCD line counter calculation in BW mode +static BYTE F4096Hz(VOID) // get a 6 bit 4096Hz down counter value +{ + LARGE_INTEGER lLC; + + QueryPerformanceCounter(&lLC); // get counter value + + // calculate 4096 Hz frequency down counter value + return -(BYTE)(((lLC.QuadPart - lAppStart.QuadPart) << 12) / lFreq.QuadPart) & 0x3F; +} + +static BYTE GetLineCounterBW(VOID) // get line counter value +{ + _ASSERT(byVblRef < 0x40); + return (0x40 + F4096Hz() - byVblRef) & 0x3F; +} + +static VOID StartDisplayBW(BYTE byInitial) +{ + // get positive VBL difference between now and stop time + byVblRef = (0x40 + F4096Hz() - byInitial) & 0x3F; + return; +} + +static VOID StopDisplayBW(VOID) +{ + BYTE a[2]; + ReadIO(a,LINECOUNT,2,TRUE); // update VBL at display off time + return; +} \ No newline at end of file diff --git a/app/src/main/cpp/DISRPL.C b/app/src/main/cpp/DISRPL.C new file mode 100644 index 0000000..3120b53 --- /dev/null +++ b/app/src/main/cpp/DISRPL.C @@ -0,0 +1,1592 @@ +/* + * disrpl.c + * + * This file is part of Emu48 + * + * Copyright (C) 2008 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "disrpl.h" + +DWORD dwRplPlatform = RPL_P3; // current RPL platform + +//################ +//# +//# String Writing Helper Functions +//# +//################ + +#define ALLOCSIZE 256 // string allocation size + +typedef struct +{ + DWORD dwSize; // size of buffer + LPTSTR szBuffer; // buffer + DWORD dwPos; // position inside buffer +} String; + +static VOID PutSn(String *str, LPCTSTR szVal, DWORD dwLen) +{ + if (str != NULL) // with output + { + if (str->dwSize == 0) // no buffer allocated + { + str->dwSize = ALLOCSIZE; // buffer size + VERIFY(str->szBuffer = (LPTSTR) malloc(str->dwSize * sizeof(TCHAR))); + str->dwPos = 0; + } + + _ASSERT(str->szBuffer != NULL); + + if (dwLen > 0) // actual string length + { + // string buffer to small + if (str->dwPos + dwLen > str->dwSize) + { + DWORD dwMinSize; + dwMinSize = dwLen + str->dwSize - str->dwPos; + dwMinSize = (dwMinSize + ALLOCSIZE - 1) / ALLOCSIZE; + dwMinSize *= ALLOCSIZE; + + str->dwSize += dwMinSize; // new buffer size + VERIFY(str->szBuffer = (LPTSTR) realloc(str->szBuffer,str->dwSize * sizeof(TCHAR))); + } + + CopyMemory(&str->szBuffer[str->dwPos],szVal,dwLen * sizeof(TCHAR)); + str->dwPos += dwLen; + } + } + return; +} + +static VOID PutC(String *str, TCHAR cVal) +{ + PutSn(str,&cVal,1); + return; +} + +static VOID PutS(String *str, LPCTSTR szVal) +{ + PutSn(str,szVal,lstrlen(szVal)); + return; +} + +static VOID __cdecl PutFS(String *str, LPCTSTR lpFormat, ...) +{ + if (str != NULL) // with output + { + TCHAR cOutput[1024]; + va_list arglist; + + va_start(arglist,lpFormat); + wvsprintf(cOutput,lpFormat,arglist); + PutS(str,cOutput); + va_end(arglist); + } + return; +} + +static VOID PutCondSpc(String *str) +{ + if (str != NULL) // with output + { + // write space when not at beginning and prior character isn't a whitespace + if ( str->dwPos > 0 + && _tcschr(_T(" \t\n\r"),str->szBuffer[str->dwPos-1]) == NULL) + PutC(str,_T(' ')); + } + return; +} + +//################ +//# +//# RPL Object Decoding +//# +//################ + +static LPCTSTR cHex = _T("0123456789ABCDEF"); + +// function prototypes +static BOOL (*Getfp(LPCTSTR lpszObject))(DWORD *pdwAddr,String *str,UINT *pnLevel); +static BOOL FetchObj(DWORD *pdwAddr,String *str,UINT *pnLevel); + +static DWORD Readx(DWORD *pdwAddr,DWORD n) +{ + DWORD i, t; + + for (i = 0, t = 0; i < n; ++i) + t |= RplReadNibble(pdwAddr) << (i * 4); + + return t; +} + +static BOOL BCDx(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,String *str) +{ + BYTE byNib; + LONG v,lExp; + BOOL bPflag,bExpflag; + DWORD dwStart; + INT i; + + if (str == NULL) return FALSE; // no output + + dwStart = str->dwPos; // starting pos inside string + + lExp = 0; + for (v = 1; nExpLen--; v *= 10) // fetch exponent + { + lExp += (LONG) *pbyNum++ * v; // calc. exponent + } + + if (lExp > v / 2) lExp -= v; // negative exponent + + lExp -= nMantLen - 1; // set decimal point to end of mantissa + + bPflag = FALSE; // show no decimal point + bExpflag = FALSE; // show no exponent + + // scan mantissa + for (v = (LONG) nMantLen - 1; v >= 0 || bPflag; v--) + { + if (v >= 0L) // still mantissa digits left + byNib = *pbyNum++; + else + byNib = 0; // zero for negative exponent + + if (dwStart == str->dwPos) // still delete zeros at end + { + if (byNib == 0 && lExp && v > 0) // delete zeros + { + lExp++; // adjust exponent + continue; + } + + // TRUE at x.E + bExpflag = v + lExp >= nMantLen || lExp < -nMantLen; + bPflag = !bExpflag && v < -lExp; // decimal point flag at neg. exponent + } + + // set decimal point + if ((bExpflag && v == 0) || (!lExp && (dwStart != str->dwPos))) + { + PutC(str,_T('.')); // write decimal point + if (v < 0) // no mantissa digits any more + { +// PutC(str,_T('0')); // write heading zero + } + bPflag = FALSE; // finished with negative exponents + } + + if (v >= 0 || bPflag) + { + TCHAR cVal = (TCHAR) byNib + _T('0'); + PutC(str,cVal); // write character + } + + lExp++; // next position + } + + if (*pbyNum == 9) // negative number + { + PutC(str,_T('-')); // write sign + } + + i = (str->dwPos - dwStart) / 2; + for (v = 0; v < i; v++) // reverse string + { + // swap chars + TCHAR cNib = str->szBuffer[dwStart+v]; + str->szBuffer[dwStart+v] = str->szBuffer[str->dwPos-v-1]; + str->szBuffer[str->dwPos-v-1] = cNib; + } + + // write number with exponent + if (bExpflag) + { + PutFS(str,_T("E%d"),lExp-1); // write exponent + } + return FALSE; +} + +static BOOL BINx(DWORD *pdwAddr,INT nBinLen,String *str) +{ + LPBYTE pbyNumber; + INT i; + + VERIFY(pbyNumber = (LPBYTE) malloc(nBinLen)); + + for (i = 0; i < nBinLen; ++i) // read data + pbyNumber[i] = RplReadNibble(pdwAddr); + + // strip leading zeros + for (i = nBinLen - 1; pbyNumber[i] == 0 && i > 0; --i) { } + + for (; i >= 0; --i) // write rest of bin + { + PutC(str,cHex[pbyNumber[i]]); + } + + free(pbyNumber); + return FALSE; +} + +static BOOL ASCIC(DWORD *pdwAddr,String *str) +{ + DWORD dwLength; + + dwLength = Readx(pdwAddr,2); // fetch string length in bytes + + for (; dwLength > 0; --dwLength) + { + PutC(str,(TCHAR) Readx(pdwAddr,2)); // write data byte + } + return FALSE; +} + +static BOOL ASCIX(DWORD *pdwAddr,String *str) +{ + BOOL bErr = ASCIC(pdwAddr,str); // decode a ASCIC + Readx(pdwAddr,2); // skip length information at end + return bErr; +} + +// simple HEX output of object +static BOOL DoHexStream(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwLength; + + dwObjStart = *pdwAddr; // remember start position + dwLength = Readx(pdwAddr,5); // fetch code length in nibbles + if (dwLength < 5) // illegal object length + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + + dwLength -= 5; // no. of DOCODE nibbles + PutFS(str,_T("%X"),dwLength); // write length information + if (dwLength > 0) // have data + { + PutC(str,_T(' ')); + } + + for (;dwLength > 0; --dwLength) + { + PutC(str,cHex[RplReadNibble(pdwAddr)]); // write digit + } + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + +// SEMI stream helper function +static BOOL DoSemiStream(DWORD *pdwAddr,String *str,UINT *pnLevel,LPCTSTR lpszSemi) +{ + DWORD dwObjStart; + BOOL bErr; + + UINT nActLevel = *pnLevel; // save actual nesting level + + (*pnLevel)++; // next level + + dwObjStart = *pdwAddr; // remember start position + + do + { + // eval object + bErr = FetchObj(pdwAddr,str,pnLevel); + } + while (!bErr && *pnLevel != nActLevel); + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + } + else + { + // no blank because of prior SEMI + PutS(str,lpszSemi); // write semi replacement character + } + return bErr; +} + +// DOINT stream helper function +static BOOL DoIntStream(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + LPBYTE pbyData; + DWORD dwObjStart,dwLength,i; + + dwObjStart = *pdwAddr; // remember start position + dwLength = Readx(pdwAddr,5); // fetch zint length in nibbles + if (dwLength < 5) // illegal object length + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + + dwLength -= 5; // object length + + VERIFY(pbyData = (LPBYTE) malloc(dwLength)); + + for (i = 0; i < dwLength; ++i) // read data + pbyData[i] = RplReadNibble(pdwAddr); + + if (dwLength <= 1) // special implementation for zero + { + _ASSERT(dwLength == 0 || (dwLength == 1 && pbyData[0] == 0)); + PutC(str,_T('0')); + } + else + { + if (pbyData[--dwLength] == 9) // negative number + PutC(str,_T('-')); + + while (dwLength > 0) // write rest of zint + { + // use only decimal part for translation + PutC(str,cHex[pbyData[--dwLength]]); + } + } + + free(pbyData); + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + + +//################ +//# +//# RPL Object Primitives +//# +//################ + +static BOOL DoBint(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("# ")); + return BINx(pdwAddr,5,str); // BIN5 + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoReal(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BYTE byNumber[16]; + DWORD i; + + PutS(str,_T("% ")); + + // get real object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + return BCDx(byNumber,12,3,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoERel(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BYTE byNumber[21]; + DWORD i; + + PutS(str,_T("%% ")); + + // get extended real object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + return BCDx(byNumber,15,5,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoCmp(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BYTE byNumber[16]; + DWORD i; + + PutS(str,_T("C% ")); + + // get real part of complex object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + BCDx(byNumber,12,3,str); + + PutC(str,_T(' ')); + + // get imaginary part of complex object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + return BCDx(byNumber,12,3,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoECmp(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BYTE byNumber[21]; + DWORD i; + + PutS(str,_T("C%% ")); + + // get real part of extended complex object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + BCDx(byNumber,15,5,str); + + PutC(str,_T(' ')); + + // get imaginary part of extended complex object content + for (i = 0; i < ARRAYSIZEOF(byNumber); ++i) + byNumber[i] = RplReadNibble(pdwAddr); + + return BCDx(byNumber,15,5,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoChar(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwChar = Readx(pdwAddr,2); // character code + + PutS(str,_T("CHR ")); + + if (dwChar == ' ') // special handling for space + { + PutS(str,_T("\" \"")); // write space + } + else + { + if (dwChar >= 0x80) // non ASCII character + { + // print as escape sequence + PutC(str,_T('\\')); // escape sequence prefix + PutC(str,cHex[dwChar >> 4]); // print higher nibble + dwChar = cHex[dwChar & 0xF]; // lower nibble + } + else + { + if (dwChar < ' ') // control character + { + PutC(str,_T('^')); // prefix + dwChar += '@'; // convert to control char + } + } + + PutC(str,(TCHAR) dwChar); // print character + } + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoArry(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BOOL (*pObjHandler)(DWORD *,String *,UINT *pnLevel); + LPCTSTR lpszName; + DWORD i,dwObjStart,dwLength,dwObject,dwDims,dwDimI; + + dwObjStart = *pdwAddr; // remember start position + dwLength = Readx(pdwAddr,5); // fetch object length in nibbles + + // look for object handler + dwObject = Readx(pdwAddr,5); // fetch object + lpszName = RplGetName(dwObject); // name of object + if (lpszName == NULL) // unknown type + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + pObjHandler = Getfp(lpszName); // search for object handler + if (pObjHandler == NULL) // unknown object handler + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + + dwDims = Readx(pdwAddr,5); // number of array dimensions + dwDimI = Readx(pdwAddr,5); // 1st dimension + + PutS(str,_T("ARRY ")); + + if (dwDims > 1) // more than 1 dimension + { + DWORD dwDimJ; + + PutFS(str,_T("%u "),dwDimI); // 1st dimension + + // check other dimensions + for (--dwDims; dwDims > 0; --dwDims) + { + dwDimJ = Readx(pdwAddr,5); // other dimensions + PutFS(str,_T("%u "),dwDimJ); // write other dimension + dwDimI *= dwDimJ; // no. of elements + } + } + + PutC(str,_T('[')); + + for (i = 0; i < dwDimI; ++i) + { + PutC(str,_T(' ')); + pObjHandler(pdwAddr,str,pnLevel); // decode object + } + + PutS(str,_T(" ]")); + + // this assert also fail on illegal objects + _ASSERT(dwObjStart + dwLength == *pdwAddr); + return FALSE; +} + +static BOOL DoLnkArry(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BOOL (*pObjHandler)(DWORD *,String *,UINT *pnLevel); + LPCTSTR lpszName; + DWORD i,dwAddr,dwLength,dwObject,dwDims,dwDimI,dwObjAddr; + + dwAddr = *pdwAddr; // address of array start + dwLength = Readx(&dwAddr,5); // fetch object length in nibbles + + // look for object handler + dwObject = Readx(&dwAddr,5); // fetch object + lpszName = RplGetName(dwObject); // name of object + if (lpszName == NULL) // unknown type + { + return TRUE; // continue decoding behind prolog + } + pObjHandler = Getfp(lpszName); // search for object handler + if (pObjHandler == NULL) // unknown object handler + { + return TRUE; // continue decoding behind prolog + } + + dwDims = Readx(&dwAddr,5); // number of array dimensions + dwDimI = Readx(&dwAddr,5); // 1st dimension + + PutS(str,_T("LNKARRY ")); + + if (dwDims > 1) // more than 1 dimension + { + DWORD dwDimJ; + + PutFS(str,_T("%u "),dwDimI); // 1st dimension + + // check other dimensions + for (--dwDims; dwDims > 0; --dwDims) + { + dwDimJ = Readx(&dwAddr,5); // other dimensions + PutFS(str,_T("%u "),dwDimJ); // write other dimension + dwDimI *= dwDimJ; // no. of elements + } + } + + PutC(str,_T('[')); + + for (i = 0; i < dwDimI; ++i) + { + PutC(str,_T(' ')); + + dwObjAddr = dwAddr; // actual address + dwObject = Readx(&dwAddr,5); // relative link to object + if (dwObject == 0) // empty link + { + PutC(str,_T('_')); + } + else + { + Readx(&dwObjAddr,dwObject); // absolute address + pObjHandler(&dwObjAddr,str,pnLevel); // decode object + } + } + + PutS(str,_T(" ]")); + + Readx(pdwAddr,dwLength); // goto end of link array + return FALSE; +} + +static BOOL DoCStr(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD i,dwObjStart,dwLength,dwC; + + dwObjStart = *pdwAddr; // remember start position + dwLength = Readx(pdwAddr,5); // fetch object length in nibbles + if (dwLength < 5) // illegal object length + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + dwLength = (dwLength - 5) / 2; // string length + + PutS(str,_T("$ \"")); + + for (i = 0; i < dwLength; ++i) + { + dwC = Readx(pdwAddr,2); // character code + + do + { + if (dwC == '\t') // tab + { + PutS(str,_T("\\t")); + break; + } + if (dwC == '\n') // newline + { + PutS(str,_T("\\n")); + break; + } + if (dwC == '\r') // return + { + PutS(str,_T("\\r")); + break; + } + if (dwC < ' ' || dwC >= 0x80) // non ASCII character + { + PutC(str,_T('\\')); // escape sequence prefix + PutC(str,cHex[dwC >> 4]); // print higher nibble + PutC(str,cHex[dwC & 0xF]); // print lower nibble + break; + } + + if (dwC == '"' || dwC == '\\') // double " or \ symbol + PutC(str,(TCHAR) dwC); + + PutC(str,(TCHAR) dwC); // print character + } + while (FALSE); + } + + PutC(str,_T('"')); + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoHxs(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD i,dwObjStart,dwLength; + BOOL bRemove; + BYTE byVal; + + dwObjStart = *pdwAddr; // remember start position + dwLength = Readx(pdwAddr,5); // fetch object length in nibbles + if (dwLength < 5) // illegal object length + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + return TRUE; + } + + PutS(str,_T("HXS ")); + + dwLength -= 5; // no. of HXS + PutFS(str,_T("%X"),dwLength); // write length information + if (dwLength > 0) // have data + { + PutC(str,_T(' ')); + } + + bRemove = TRUE; // remove leading zeros + + for (i = 0; i < dwLength; ++i) + { + byVal = RplReadNibble(pdwAddr); + + // remove leading zeros + if (byVal == 0 && bRemove && i + 1 < dwLength) + continue; + + PutC(str,cHex[byVal]); // write digit + bRemove = FALSE; // got non zero digit + } + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoList(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutC(str,_T('{')); + return DoSemiStream(pdwAddr,str,pnLevel,_T("}")); +} + +static BOOL DoTag(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("TAG ")); + ASCIC(pdwAddr,str); // name + PutC(str,_T(' ')); + return FetchObj(pdwAddr,str,pnLevel); // object +} + +static BOOL DoCol(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + BOOL bErr = FALSE; + + PutS(str,_T("::")); + + if (*pnLevel > 0) // nested objects + { + bErr = DoSemiStream(pdwAddr,str,pnLevel,_T(";")); + } + return bErr; +} + +static BOOL DoCode(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("CODE ")); + return DoHexStream(pdwAddr,str,pnLevel); +} + +static BOOL DoIdnt(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("ID ")); + return ASCIC(pdwAddr,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoLam(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("LAM ")); + return ASCIC(pdwAddr,str); + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoRomp(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + PutS(str,_T("ROMPTR ")); + BINx(pdwAddr,3,str); // BIN3 + PutC(str,_T(' ')); + BINx(pdwAddr,3,str); // BIN3 + return FALSE; + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL Semi(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + if (*pnLevel > 0) // nested objects + { + (*pnLevel)--; // signaling end, caller handles the semi + } + else + { + PutC(str,_T(';')); // write standard semi + } + return FALSE; + UNREFERENCED_PARAMETER(pdwAddr); +} + +static BOOL DoRrp(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwOffset,dwObjStart,dwBufferPos,dwAddr; + BOOL bErr = FALSE; + + dwObjStart = *pdwAddr; // remember start position of RRP + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("DIR")); + Readx(pdwAddr,3); // skip attachments field + + dwAddr = *pdwAddr; // actual address + dwOffset = Readx(pdwAddr,5); // offset-1 + + if (dwOffset != 0) // directory not empty + { + DWORD dwOffsetAddr; + + Readx(&dwAddr,dwOffset); // name-1 + if (dwAddr < *pdwAddr) // address wrap around + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all DIR output + return TRUE; + } + + *pdwAddr = 0; // unknown end + + do + { + PutC(str,_T('\n')); + PutS(str,_T("VARNAME ")); + + // operation is safe because checked for address wrapping before + dwAddr -= 5; // address of next offset + dwOffsetAddr = dwAddr; + + dwOffset = Readx(&dwAddr,5); // read next offset + bErr = ASCIX(&dwAddr,str); // read name + + PutC(str,_T('\n')); + (*pnLevel)++; // treat object as whole + // eval object + bErr = bErr || FetchObj(&dwAddr,str,pnLevel); + (*pnLevel)--; + + if (*pdwAddr == 0) // first name + { + *pdwAddr = dwAddr; // end of dir + } + + dwAddr = dwOffsetAddr; // address of next offset + + // illegal offset + if (dwOffset >= dwAddr - dwObjStart) + { + *pdwAddr = dwObjStart; // continue decoding behind illegal RRP prolog + str->dwPos = dwBufferPos; // throw out all DIR output + return TRUE; + } + + // operation is safe because checked for address wrapping before + dwAddr -= dwOffset; // address of next name + } + while (!bErr && dwOffset != 0); + } + + PutS(str,_T("\nENDDIR")); + return bErr; +} + +// type specific handler +static BOOL DoExt(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + if (dwRplPlatform <= RPL_P2) // DOEXT + { + PutS(str,_T("EXT0 ")); + bErr = DoHexStream(pdwAddr,str,pnLevel); + } + else // Unit + { + PutS(str,_T("UNIT ")); + bErr = DoSemiStream(pdwAddr,str,pnLevel,_T(";")); + } + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoSymb(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("SYMBOL ")); + if ((bErr = DoSemiStream(pdwAddr,str,pnLevel,_T(";")))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoGrob(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("GROB ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoLib(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("LIB ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoBak(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("BAK ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoExt0(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("LIBDAT ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoExt1(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + if (dwRplPlatform <= RPL_P3) // DOEXT1 + { + PutS(str,_T("EXT1 ")); + bErr = DoHexStream(pdwAddr,str,pnLevel); + } + else // DOACPTR + { + PutS(str,_T("ACPTR ")); + bErr = BINx(pdwAddr,5,str); // BIN5 + PutC(str,_T(' ')); + bErr = bErr || BINx(pdwAddr,5,str); // BIN5 + } + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoExt2(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("EXT2 ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoExt3(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("EXT3 ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoExt4(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("EXT4 ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoZint(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("ZINT ")); + if ((bErr = DoIntStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoLngReal(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos,dwExpLen,dwAddr; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + bErr = DoIntStream(pdwAddr,str,pnLevel); + PutC(str,_T('.')); + + dwAddr = *pdwAddr; // copy of address for Readx() + dwExpLen = Readx(&dwAddr,5); // length of exponent + if (dwExpLen > 6) // non zero exponent + { + PutC(str,_T('E')); + bErr = bErr || DoIntStream(pdwAddr,str,pnLevel); + } + else // no exponent + { + Readx(pdwAddr,dwExpLen); // skip exponent + } + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr ; +} + +static BOOL DoLngCmp(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutC(str,_T('(')); + bErr = DoLngReal(pdwAddr,str,pnLevel); + PutC(str,_T(',')); + bErr = bErr || DoLngReal(pdwAddr,str,pnLevel); + PutC(str,_T(')')); + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoFlashPtr(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("FLASHPTR ")); + bErr = BINx(pdwAddr,3,str); // BIN3 + PutC(str,_T(' ')); + bErr = bErr || BINx(pdwAddr,4,str); // BIN4 + + if (bErr) // decoding error + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; + UNREFERENCED_PARAMETER(pnLevel); +} + +static BOOL DoAplet(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("APLET ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoMinifont(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("MINIFONT ")); + if ((bErr = DoHexStream(pdwAddr,str,pnLevel))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static BOOL DoMatrix(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + DWORD dwObjStart,dwBufferPos; + BOOL bErr; + + dwObjStart = *pdwAddr; // remember start position + dwBufferPos = str->dwPos; // insert address of text + + PutS(str,_T("MATRIX")); + if ((bErr = DoSemiStream(pdwAddr,str,pnLevel,_T(";")))) + { + *pdwAddr = dwObjStart; // continue decoding behind prolog + str->dwPos = dwBufferPos; // throw out all output + } + return bErr; +} + +static struct ObjHandler +{ + LPCTSTR lpszName; // object name + // decode function + BOOL (*fp)(DWORD *pdwAddr,String *str,UINT *pnLevel); + DWORD dwType; // calculator type (RPL_P1 = all) +} ObjDecode[] = +{ + _T("DOBINT"), DoBint, RPL_P1, // System Binary + _T("DOREAL"), DoReal, RPL_P1, // Real + _T("DOEREL"), DoERel, RPL_P1, // Long Real + _T("DOCMP"), DoCmp, RPL_P1, // Complex + _T("DOECMP"), DoECmp, RPL_P1, // Long Complex + _T("DOCHAR"), DoChar, RPL_P1, // Character + _T("DOARRY"), DoArry, RPL_P1, // Array + _T("DOLNKARRY"), DoLnkArry, RPL_P1, // Linked Array + _T("DOCSTR"), DoCStr, RPL_P1, // String + _T("DOHSTR"), DoHxs, RPL_P1, // Binary Integer + _T("DOHXS"), DoHxs, RPL_P1, // Binary Integer + _T("DOLIST"), DoList, RPL_P1, // List + _T("DOSYMB"), DoSymb, RPL_P1, // Algebraic + _T("DOCOL"), DoCol, RPL_P1, // Program + _T("DOCODE"), DoCode, RPL_P1, // Code + _T("DOIDNT"), DoIdnt, RPL_P1, // Global Name + _T("DOLAM"), DoLam, RPL_P1, // Local Name + _T("DOROMP"), DoRomp, RPL_P1, // XLIB Name + _T("SEMI"), Semi, RPL_P1, // SEMI + _T("DORRP"), DoRrp, RPL_P2, // Directory + _T("DOEXT"), DoExt, RPL_P2, // Reserved or Unit (HP48 and later) + _T("DOTAG"), DoTag, RPL_P3, // Tagged + _T("DOGROB"), DoGrob, RPL_P3, // Graphic + _T("DOLIB"), DoLib, RPL_P3, // Library + _T("DOBAK"), DoBak, RPL_P3, // Backup + _T("DOEXT0"), DoExt0, RPL_P3, // Library Data + _T("DOEXT1"), DoExt1, RPL_P3, // Ext1 or ACcess PoinTeR + _T("DOACPTR"), DoExt1, RPL_P3, // Ext1 or ACcess PoinTeR + _T("DOEXT2"), DoExt2, RPL_P3, // Reserved 1, Font (HP49G) + _T("DOEXT3"), DoExt3, RPL_P3, // Reserved 2 + _T("DOEXT4"), DoExt4, RPL_P3, // Reserved 3 + _T("DOINT"), DoZint, RPL_P5, // Infinite Precision Integers + _T("DOLNGREAL"), DoLngReal, RPL_P5, // Precision Real + _T("DOLNGCMP"), DoLngCmp, RPL_P5, // Precision Complex + _T("DOFLASHP"), DoFlashPtr, RPL_P5, // Flash Pointer + _T("DOAPLET"), DoAplet, RPL_P5, // Aplet + _T("DOMINIFONT"), DoMinifont, RPL_P5, // Mini Font + _T("DOMATRIX"), DoMatrix, RPL_P5, // Symbolic matrix +}; + +static BOOL (*Getfp(LPCTSTR lpszObject))(DWORD *,String *,UINT *) +{ + UINT i; + + for (i = 0; i < ARRAYSIZEOF(ObjDecode); ++i) + { + if (lstrcmp(lpszObject,ObjDecode[i].lpszName) == 0) + { + // RPL platform type enabled + if ((ObjDecode[i].dwType & dwRplPlatform) == ObjDecode[i].dwType) + return ObjDecode[i].fp; // return object handler + } + } + return NULL; // not found +} + +static BOOL FetchObj(DWORD *pdwAddr,String *str,UINT *pnLevel) +{ + LPCTSTR lpszName; + DWORD dwObject; + BOOL bErr; + + bErr = FALSE; + + PutCondSpc(str); // write blank if necessary + + dwObject = Readx(pdwAddr,5); // fetch object + + lpszName = RplGetName(dwObject); // get name of object + if (lpszName != NULL) + { + // look for object type + BOOL (*fp)(DWORD *,String *,UINT *) = Getfp(lpszName); + + if (fp != NULL) // found an object handler + { + bErr = fp(pdwAddr,str,pnLevel); // call the handler + } + else + { + PutS(str,lpszName); // named entry + } + } + + if (lpszName == NULL || bErr) // no name or illegal object behind prolog + { + PutFS(str,_T("PTR %X"),dwObject); // unnamed entry + } + return bErr; +} + +BYTE (*RplReadNibble)(DWORD *p) = NULL; // get nibble function pointer + +DWORD RplSkipObject(DWORD dwAddr) +{ + UINT nLevel = 1; // nest DOCOL objects + + _ASSERT(RplReadNibble != NULL); // get nibble function defined + FetchObj(&dwAddr,NULL,&nLevel); // decode object without output + return dwAddr; +} + +LPTSTR RplDecodeObject(DWORD dwAddr, DWORD *pdwNxtAddr) +{ + String str = { 0 }; + DWORD dwNxtAddr; + UINT nLevel = 0; // don't nest DOCOL objects + + dwNxtAddr = dwAddr; // init next address + + _ASSERT(RplReadNibble != NULL); // get nibble function defined + FetchObj(&dwNxtAddr,&str,&nLevel); // decode object + + PutC(&str,0); // set EOS + + // release unnecessary allocated buffer memory + VERIFY(str.szBuffer = (LPTSTR) realloc(str.szBuffer,str.dwPos * sizeof(str.szBuffer[0]))); + + // return address of next object + if (pdwNxtAddr != NULL) *pdwNxtAddr = dwNxtAddr; + return str.szBuffer; +} + + +//################ +//# +//# RPL Object Viewer +//# +//################ + +BOOL bRplViewName = TRUE; // show entry point name +BOOL bRplViewAddr = TRUE; // show address +BOOL bRplViewBin = TRUE; // show binary data +BOOL bRplViewAsm = TRUE; // show ASM code instead of hex data + +static VOID PrintHead(DWORD dwStartAddr, DWORD dwEndAddr, String *str) +{ + if (bRplViewAddr) // show address for object + { + PutFS(str,_T("%05X "),dwStartAddr); + } + + if (bRplViewBin) // show object binary data + { + DWORD dwIndex; + + for (dwIndex = 0; dwIndex < 5; ++dwIndex) + { + TCHAR c = _T(' '); + + if (dwStartAddr < dwEndAddr) // still show hex nibble + { + c = cHex[RplReadNibble(&dwStartAddr)]; + } + + PutC(str,c); // write data content + } + + PutS(str,_T(" ")); + } + else // no binary data + { + if (bRplViewAddr) // missing whitespace + { + PutC(str,_T(' ')); + } + } + return; +} + +static VOID PrintTail(DWORD dwStartAddr, DWORD dwEndAddr, String *str) +{ + if (bRplViewBin) // show binary data + { + DWORD dwActAddr,dwRemain; + + if (dwStartAddr < dwEndAddr) // remaining data to show + { + for (dwActAddr = dwStartAddr; dwActAddr < dwEndAddr;) + { + if (bRplViewAddr) // address is visible + { + // spaces instead of show address + PutS(str,_T(" ")); + + // address has 6 digit + if (dwActAddr >= 0x100000) + PutC(str,_T(' ')); + } + + dwRemain = dwEndAddr - dwActAddr; + if (dwRemain > 5) dwRemain = 5; + + for (; dwRemain > 0; --dwRemain) + { + // write data content + PutC(str,cHex[RplReadNibble(&dwActAddr)]); + } + + PutS(str,_T("\r\n")); + } + } + } + return; +} + +static VOID PrintBlank(String *str) +{ + if (bRplViewAddr) // address is visible + { + PutS(str,_T(" ")); // spaces instead of address + } + if (bRplViewBin) // show binary data + { + if (bRplViewAddr) // address is visible + { + PutC(str,_T(' ')); // need separator + } + + PutS(str,_T(" ")); // spaces instead of binary + } + if (bRplViewAddr || bRplViewBin) // any prefix + { + PutS(str,_T(" ")); // need separator + } + return; +} + +static VOID PrintLevel(DWORD dwLevel, String *str) +{ + for (; dwLevel > 0; --dwLevel) // level depending blanks + { + PutS(str,_T(" ")); + } + return; +} + +#if defined HARDWARE // compiled inside emulator sources +static DWORD AssemblyOutput(DWORD dwAddr, DWORD dwEndAddr, DWORD dwLevel, String *str) +{ + LPCTSTR lpszName; + TCHAR cBuffer[64]; + DWORD dwNxtAddr; + + PrintLevel(dwLevel,str); // level depending blanks + PutS(str,_T("CODE\r\n")); // code preamble + + PrintTail(dwAddr+5,dwAddr+10,str); // write code length information + + ++dwLevel; + + dwAddr += 10; // start of assembler code + + for (; dwAddr < dwEndAddr; dwAddr = dwNxtAddr) + { + // entry name enabled and known entry? + if (bRplViewName && (lpszName = RplGetName(dwAddr)) != NULL) + { + PrintHead(dwAddr,dwAddr,str); // write head for assembly label + PutFS(str,_T("=%s\r\n"),lpszName); + } + + // disassemble line + dwNxtAddr = disassemble(dwAddr,cBuffer); + + PrintHead(dwAddr,dwNxtAddr,str); // write head of assembly line + PrintLevel(dwLevel,str); // level depending blanks + PutS(str,cBuffer); // write disassembler text + PutS(str,_T("\r\n")); + + // write additional binary data of opcode + PrintTail(dwAddr+5,dwNxtAddr,str); + } + + --dwLevel; + + PrintBlank(str); // skip address and binary part + PrintLevel(dwLevel,str); // level depending blanks + PutS(str,_T("ENDCODE")); // code postamble + return dwAddr; +} +#endif + +LPTSTR RplCreateObjView(DWORD dwStartAddr, DWORD dwEndAddr, BOOL bSingleObj) +{ + String str = { 0 }; + LPCTSTR lpszName; + LPTSTR lpszObject; + DWORD dwLevel,dwAddr,dwNxtAddr; + + _ASSERT(RplReadNibble != NULL); // get nibble function defined + + lpszObject = NULL; // no memory allocated + dwLevel = 0; // nesting level + + // decode object + for (dwAddr = dwStartAddr;dwAddr < dwEndAddr; dwAddr = dwNxtAddr) + { + lpszObject = RplDecodeObject(dwAddr,&dwNxtAddr); + + if (dwLevel > 0 && lstrcmp(lpszObject,_T(";")) == 0) + --dwLevel; + + // entry name enabled and known entry? + if (bRplViewName && (lpszName = RplGetName(dwAddr)) != NULL) + { + if (bRplViewAddr) // show address for entry + { + PutFS(&str,_T("%05X "),dwAddr); + } + PutFS(&str,_T("=%s\r\n"),lpszName); + } + + PrintHead(dwAddr,dwAddr+5,&str); // write initial head + + do + { + // check for special RRP handling + if (_tcsncmp(lpszObject,_T("DIR\n"),4) == 0) + { + LPCTSTR lpszStart,lpszEnd; + + lpszStart = lpszEnd = lpszObject; + + // decode lines + while (*lpszEnd != 0) + { + // separate lines + if ((lpszEnd = _tcschr(lpszStart,_T('\n'))) == NULL) + { + VERIFY(lpszEnd = _tcschr(lpszStart,0)); + } + + if (dwLevel > 0 && _tcsncmp(lpszStart,_T("ENDDIR"),lpszEnd-lpszStart) == 0) + --dwLevel; + + // level depending blanks + PrintLevel(dwLevel,&str); + + // write line without LF + PutSn(&str,lpszStart,(DWORD) (lpszEnd-lpszStart)); + + if (_tcsncmp(lpszStart,_T("DIR"),lpszEnd-lpszStart) == 0) + ++dwLevel; + + if (*lpszEnd != 0) // more data? + { + PutS(&str,_T("\r\n")); // send CR LF + PrintBlank(&str); + lpszStart = lpszEnd + 1; // next part + } + } + break; + } + + #if defined HARDWARE // compiled inside emulator sources + // check for special CODE handling + if (bRplViewAsm && _tcsncmp(lpszObject,_T("CODE "),5) == 0) + { + // replace object by a disassembler output + dwAddr = AssemblyOutput(dwAddr,dwNxtAddr,dwLevel,&str); + break; + } + #endif + + // default, show the object + PrintLevel(dwLevel,&str); // level depending blanks + PutS(&str,lpszObject); // the object + } + while (FALSE); + PutS(&str,_T("\r\n")); + + PrintTail(dwAddr+5,dwNxtAddr,&str); // write additional binary data + + if (lstrcmp(lpszObject,_T("::")) == 0) + ++dwLevel; + + free(lpszObject); // free object string + lpszObject = NULL; + + if ( (bSingleObj && dwLevel == 0) // single object decoding? + || (dwNxtAddr < dwAddr)) // or address wrap around + break; // stop decoding + } + + _ASSERT(lpszObject == NULL); + + // remove CR LF at end + while ( str.dwPos > 0 + && ( str.szBuffer[str.dwPos-1] == _T('\r') + || str.szBuffer[str.dwPos-1] == _T('\n') + ) + ) + { + str.dwPos--; + } + + PutC(&str,0); // set EOS + + // release unnecessary allocated buffer memory + VERIFY(str.szBuffer = (LPTSTR) realloc(str.szBuffer,str.dwPos * sizeof(str.szBuffer[0]))); + return str.szBuffer; +} diff --git a/app/src/main/cpp/DISRPL.H b/app/src/main/cpp/DISRPL.H new file mode 100644 index 0000000..ba2f5cd --- /dev/null +++ b/app/src/main/cpp/DISRPL.H @@ -0,0 +1,25 @@ +/* + * disrpl.h + * + * This file is part of Emu48 + * + * Copyright (C) 2008 Christoph Gießelink + * + */ + +// RPL platform type +#define RPL_P1 (1<<0) // Clamshell without RRP +#define RPL_P2 (RPL_P1 | (1<<1)) // Pioneer / Clamshell +#define RPL_P3 (RPL_P2 | (1<<2)) // Charlemagne +#define RPL_P4 (RPL_P3 | (1<<3)) // Alcuin +#define RPL_P5 (RPL_P4 | (1<<4)) // V'ger + +extern DWORD dwRplPlatform; // RPL platform +extern BOOL bRplViewName; // show entry point name +extern BOOL bRplViewAddr; // show adress +extern BOOL bRplViewBin; // show binary data +extern BOOL bRplViewAsm; // show ASM code instead of hex data +extern BYTE (*RplReadNibble)(DWORD *p); // read nibble function pointer +extern DWORD RplSkipObject(DWORD dwAddr); +extern LPTSTR RplDecodeObject(DWORD dwAddr, DWORD *pdwNxtAddr); +extern LPTSTR RplCreateObjView(DWORD dwStartAddr, DWORD dwEndAddr, BOOL bSingleObj); diff --git a/app/src/main/cpp/EMU48.C b/app/src/main/cpp/EMU48.C new file mode 100644 index 0000000..d192f67 --- /dev/null +++ b/app/src/main/cpp/EMU48.C @@ -0,0 +1,2288 @@ +/* + * Emu48.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" +#include "debugger.h" + +#define VERSION "1.59+" + +#ifdef _DEBUG +LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug"); +#else +LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION); +#endif +LPTSTR szAppName = _T("Emu48"); // application name for DDE server +LPTSTR szTopic = _T("Stack"); // topic for DDE server +LPTSTR szTitle = NULL; + +static const LPCTSTR szLicence = + _T("This program is free software; you can redistribute it and/or modify\r\n") + _T("it under the terms of the GNU General Public License as published by\r\n") + _T("the Free Software Foundation; either version 2 of the License, or\r\n") + _T("(at your option) any later version.\r\n") + _T("\r\n") + _T("This program is distributed in the hope that it will be useful,\r\n") + _T("but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n") + _T("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n") + _T("See the GNU General Public License for more details.\r\n") + _T("\r\n") + _T("You should have received a copy of the GNU General Public License along\r\n") + _T("with this program; if not, write to the Free Software Foundation, Inc.,\r\n") + _T("51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."); + +static BOOL bOwnCursor = FALSE; +static BOOL bTitleBar = TRUE; + + +CRITICAL_SECTION csGDILock; // critical section for hWindowDC +CRITICAL_SECTION csLcdLock; // critical section for display update +CRITICAL_SECTION csKeyLock; // critical section for key scan +CRITICAL_SECTION csIOLock; // critical section for I/O access +CRITICAL_SECTION csT1Lock; // critical section for timer1 access +CRITICAL_SECTION csT2Lock; // critical section for timer2 access +CRITICAL_SECTION csTxdLock; // critical section for transmit byte +CRITICAL_SECTION csRecvLock; // critical section for receive byte +CRITICAL_SECTION csSlowLock; // critical section for speed slow down +CRITICAL_SECTION csDbgLock; // critical section for debugger purpose +INT nArgc; // no. of command line arguments +LPCTSTR *ppArgv; // command line arguments +LARGE_INTEGER lFreq; // high performance counter frequency +LARGE_INTEGER lAppStart; // high performance counter value at Appl. start +DWORD idDdeInst; // DDE server id +UINT uCF_HpObj; // DDE clipboard format +HANDLE hThread; +HANDLE hEventShutdn; // event handle to stop cpu thread + +HINSTANCE hApp = NULL; +HWND hWnd = NULL; +HWND hDlgDebug = NULL; // handle for debugger dialog +HWND hDlgFind = NULL; // handle for debugger find dialog +HWND hDlgProfile = NULL; // handle for debugger profile dialog +HWND hDlgRplObjView = NULL; // handle for debugger rpl object viewer +HDC hWindowDC = NULL; +HPALETTE hPalette = NULL; +HPALETTE hOldPalette = NULL; // old palette of hWindowDC +DWORD dwTColor = (DWORD) -1; // transparency color +DWORD dwTColorTol = 0; // transparency color tolerance +HRGN hRgn = NULL; +HCURSOR hCursorArrow = NULL; +HCURSOR hCursorHand = NULL; +UINT uWaveDevId = WAVE_MAPPER; // default audio device +DWORD dwWakeupDelay = 200; // ON key hold time to switch on calculator +BOOL bAutoSave = FALSE; +BOOL bAutoSaveOnExit = TRUE; +BOOL bSaveDefConfirm = TRUE; // yes +BOOL bStartupBackup = FALSE; +BOOL bAlwaysDisplayLog = TRUE; +BOOL bLoadObjectWarning = TRUE; +BOOL bShowTitle = TRUE; // show main window title bar +BOOL bShowMenu = TRUE; // show main window menu bar +BOOL bAlwaysOnTop = FALSE; // emulator window always on top +BOOL bActFollowsMouse = FALSE; // emulator window activation follows mouse +BOOL bClientWinMove = FALSE; // emulator window can be moved over client area +BOOL bSingleInstance = FALSE; // multiple emulator instances allowed + + +//################ +//# +//# Window Status +//# +//################ + +VOID SetWindowTitle(LPCTSTR szString) +{ + if (szTitle) free(szTitle); + + _ASSERT(hWnd != NULL); + if (szString) + { + szTitle = DuplicateString(szString); + SetWindowText(hWnd, szTitle); + } + else + { + szTitle = NULL; + SetWindowText(hWnd, szNoTitle); + } + return; +} + +VOID ForceForegroundWindow(HWND hWnd) +{ + // force window to foreground + DWORD dwEmuThreadID = GetCurrentThreadId(); + DWORD dwActThreadID = GetWindowThreadProcessId(GetForegroundWindow(),NULL); + + AttachThreadInput(dwEmuThreadID,dwActThreadID,TRUE); + SetForegroundWindow(hWnd); + AttachThreadInput(dwEmuThreadID,dwActThreadID,FALSE); + return; +} + +static __inline VOID UpdateWindowBars(VOID) +{ + DWORD dwStyle; + HMENU hMenu; + + BOOL bUpdate = FALSE; // no update + + // get current title bar style + dwStyle = (DWORD) GetWindowLongPtr(hWnd,GWL_STYLE); + if ((bTitleBar = (bShowTitle || bDocumentAvail == FALSE))) + { + // title bar off + if ((dwStyle & STYLE_TITLE) != STYLE_TITLE) + { + SetWindowLongPtr(hWnd,GWL_STYLE,(dwStyle & ~STYLE_NOTITLE) | STYLE_TITLE); + bUpdate = TRUE; + } + } + else + { + // title bar on + if ((dwStyle & STYLE_NOTITLE) != STYLE_NOTITLE) + { + SetWindowLongPtr(hWnd,GWL_STYLE,(dwStyle & ~STYLE_TITLE) | STYLE_NOTITLE); + bUpdate = TRUE; + } + } + + hMenu = GetMenu(hWnd); // get system menu + if (bShowMenu || bDocumentAvail == FALSE) + { + if (hMenu == NULL) // menu off + { + // restore menu bar + SetMenu(hWnd,LoadMenu(hApp,MAKEINTRESOURCE(IDR_MENU))); + bUpdate = TRUE; + } + } + else + { + if (hMenu != NULL) // menu on + { + // close menu bar + SetMenu(hWnd,NULL); + VERIFY(DestroyMenu(hMenu)); + bUpdate = TRUE; + } + } + + if (dwTColor != (DWORD) -1) // prepare background bitmap with transparency + { + if (!bTitleBar && GetMenu(hWnd) == NULL) + { + if (hRgn == NULL) + { + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // enable background bitmap transparency + hRgn = CreateRgnFromBitmap((HBITMAP) GetCurrentObject(hMainDC,OBJ_BITMAP), + dwTColor, + dwTColorTol); + if (hRgn != NULL) // region definition successful + { + OffsetRgn(hRgn,-(INT) nBackgroundX,-(INT) nBackgroundY); + SetWindowRgn(hWnd,hRgn,TRUE); + } + else // region definition failed + { + // disable transparency + dwTColor = (DWORD) -1; + } + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + } + } + else + { + if (hRgn != NULL) // region active + { + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // disable background bitmap transparency + SetWindowRgn(hWnd,NULL,TRUE); + hRgn = NULL; + + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + } + } + } + + if (bUpdate) // changed state of title or menu bar + { + ResizeWindow(); // resize & redraw window + } + return; +} + + + +//################ +//# +//# Clipboard Tool +//# +//################ + +VOID CopyItemsToClipboard(HWND hWnd) // save selected Listbox Items to Clipboard +{ + LONG i; + LPINT lpnCount; + + // get number of selections + if ((i = (LONG) SendMessage(hWnd,LB_GETSELCOUNT,0,0)) == 0) + return; // no items selected + + if ((lpnCount = (LPINT) malloc(i * sizeof(INT))) != NULL) + { + LPTSTR lpszData; + HANDLE hClipObj; + LONG j,lMem = 0; + + // get indexes of selected items + i = (LONG) SendMessage(hWnd,LB_GETSELITEMS,i,(LPARAM) lpnCount); + for (j = 0;j < i;++j) // scan all selected items + { + // calculate total amount of characters + lMem += (LONG) SendMessage(hWnd,LB_GETTEXTLEN,lpnCount[j],0) + 2; + } + // allocate clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,(lMem + 1) * sizeof(*lpszData))) != NULL) + { + if ((lpszData = (LPTSTR) GlobalLock(hClipObj))) + { + for (j = 0;j < i;++j) // scan all selected items + { + lpszData += SendMessage(hWnd,LB_GETTEXT,lpnCount[j],(LPARAM) lpszData); + *lpszData++ = _T('\r'); + *lpszData++ = _T('\n'); + } + *lpszData = 0; // set EOS + GlobalUnlock(hClipObj); // unlock memory + } + + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + #if defined _UNICODE + SetClipboardData(CF_UNICODETEXT,hClipObj); + #else + SetClipboardData(CF_TEXT,hClipObj); + #endif + else + GlobalFree(hClipObj); + CloseClipboard(); + } + else // clipboard open failed + { + GlobalFree(hClipObj); + } + } + free(lpnCount); // free item table + } + return; +} + + + +//################ +//# +//# Settings +//# +//################ + +// get R/W state of file +static BOOL IsFileWriteable(LPCTSTR szFilename) +{ + DWORD dwFileAtt; + + BOOL bWriteable = FALSE; + + SetCurrentDirectory(szEmuDirectory); + dwFileAtt = GetFileAttributes(szFilename); + if (dwFileAtt != 0xFFFFFFFF) + bWriteable = ((dwFileAtt & FILE_ATTRIBUTE_READONLY) == 0); + SetCurrentDirectory(szCurrentDirectory); + return bWriteable; +} + +// set listfield for serial combo boxes +static VOID SetCommList(HWND hDlg,LPCTSTR szWireSetting,LPCTSTR szIrSetting) +{ + WPARAM wSelectWire,wSelectIr; + HKEY hKey; + + wSelectWire = wSelectIr = 0; // set selections to disabled + SendDlgItemMessage(hDlg,IDC_WIRE,CB_ADDSTRING,0,(LPARAM) _T(NO_SERIAL)); + SendDlgItemMessage(hDlg,IDC_IR ,CB_ADDSTRING,0,(LPARAM) _T(NO_SERIAL)); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + _T("Hardware\\DeviceMap\\SerialComm"), + 0, + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, + &hKey) == ERROR_SUCCESS) + { + HANDLE hComm; + TCHAR szBuffer[256],cKey[256],cData[256]; + DWORD dwKeySize,dwDataSize; + WPARAM wSelIndexWire,wSelIndexIr; + BOOL bAddWire,bAddIr; + DWORD dwType,dwEnumVal; + LONG lRet; + + wSelIndexWire = wSelIndexIr = 1; // preset selector + + dwEnumVal = 0; + do + { + dwKeySize = ARRAYSIZEOF(cKey); // init return buffer sizes + dwDataSize = sizeof(cData); + + lRet = RegEnumValue(hKey,dwEnumVal++, + cKey,&dwKeySize, + NULL,&dwType, + (LPBYTE) cData,&dwDataSize); + + if (lRet == ERROR_SUCCESS && dwType == REG_SZ) + { + wsprintf(szBuffer,_T("\\\\.\\%s"),cData); + if ((bAddWire = (lstrcmp(&szBuffer[4],szWireSetting) == 0))) + wSelectWire = wSelIndexWire; + if ((bAddIr = (lstrcmp(&szBuffer[4],szIrSetting) == 0))) + wSelectIr = wSelIndexIr; + + // test if COM port is valid + hComm = CreateFile(szBuffer,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); + if (hComm != INVALID_HANDLE_VALUE) + { + VERIFY(CloseHandle(hComm)); + bAddWire = bAddIr = TRUE; + } + + if (bAddWire) // add item to wire combobox + { + SendDlgItemMessage(hDlg,IDC_WIRE,CB_ADDSTRING,0,(LPARAM) &szBuffer[4]); + ++wSelIndexWire; + } + if (bAddIr) // add item to ir combobox + { + SendDlgItemMessage(hDlg,IDC_IR,CB_ADDSTRING,0,(LPARAM) &szBuffer[4]); + ++wSelIndexIr; + } + } + } + while (lRet == ERROR_SUCCESS); + _ASSERT(lRet == ERROR_NO_MORE_ITEMS); + RegCloseKey(hKey); + } + + // set cursors + SendDlgItemMessage(hDlg,IDC_WIRE,CB_SETCURSEL,wSelectWire,0L); + SendDlgItemMessage(hDlg,IDC_IR ,CB_SETCURSEL,wSelectIr ,0L); + return; +} + +static INT_PTR CALLBACK SettingsGeneralProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HWND hWndInsertAfter; + + switch (uMsg) + { + case WM_INITDIALOG: + // init speed checkbox + CheckDlgButton(hDlg,IDC_REALSPEED,bRealSpeed); + CheckDlgButton(hDlg,IDC_GRAYSCALE,bGrayscale); + CheckDlgButton(hDlg,IDC_SHOWTITLE,bShowTitle); + CheckDlgButton(hDlg,IDC_SHOWMENU,bShowMenu); + CheckDlgButton(hDlg,IDC_ALWAYSONTOP,bAlwaysOnTop); + CheckDlgButton(hDlg,IDC_ACTFOLLOWSMOUSE,bActFollowsMouse); + #if defined _USRDLL // DLL version + CheckDlgButton(hDlg,IDC_SINGLEINSTANCE,FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_SINGLEINSTANCE),FALSE); + #else + CheckDlgButton(hDlg,IDC_SINGLEINSTANCE,bSingleInstance); + #endif + CheckDlgButton(hDlg,IDC_AUTOSAVE,bAutoSave); + CheckDlgButton(hDlg,IDC_AUTOSAVEONEXIT,bAutoSaveOnExit); + CheckDlgButton(hDlg,IDC_OBJECTLOADWARNING,bLoadObjectWarning); + CheckDlgButton(hDlg,IDC_ALWAYSDISPLOG,bAlwaysDisplayLog); + + // set disassembler mode + CheckDlgButton(hDlg,(disassembler_mode == HP_MNEMONICS) ? IDC_DISASM_HP : IDC_DISASM_CLASS,BST_CHECKED); + return TRUE; + case WM_NOTIFY: + switch (((LPNMHDR) lParam)->code) + { + case PSN_KILLACTIVE: + // get speed checkbox value + bRealSpeed = IsDlgButtonChecked(hDlg,IDC_REALSPEED); + bShowTitle = IsDlgButtonChecked(hDlg,IDC_SHOWTITLE); + bShowMenu = IsDlgButtonChecked(hDlg,IDC_SHOWMENU); + bAlwaysOnTop = IsDlgButtonChecked(hDlg,IDC_ALWAYSONTOP); + bActFollowsMouse = IsDlgButtonChecked(hDlg,IDC_ACTFOLLOWSMOUSE); + bSingleInstance = IsDlgButtonChecked(hDlg,IDC_SINGLEINSTANCE); + bAutoSave = IsDlgButtonChecked(hDlg,IDC_AUTOSAVE); + bAutoSaveOnExit = IsDlgButtonChecked(hDlg,IDC_AUTOSAVEONEXIT); + bLoadObjectWarning = IsDlgButtonChecked(hDlg,IDC_OBJECTLOADWARNING); + bAlwaysDisplayLog = IsDlgButtonChecked(hDlg,IDC_ALWAYSDISPLOG); + SetSpeed(bRealSpeed); // set speed + + // LCD grayscale checkbox has been changed + if (bGrayscale != (BOOL) IsDlgButtonChecked(hDlg,IDC_GRAYSCALE)) + { + UINT nOldState = SwitchToState(SM_INVALID); + SetLcdMode(!bGrayscale); // set new display mode + SwitchToState(nOldState); + } + + // set disassembler mode + disassembler_mode = IsDlgButtonChecked(hDlg,IDC_DISASM_HP) ? HP_MNEMONICS : CLASS_MNEMONICS; + + // bAlwaysOnTop maybe changed, so set new window Z order + hWndInsertAfter = bAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(hWnd,hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); + if (hDlgDebug != NULL) + { + SetWindowPos(GetLastActivePopup(hDlgDebug),hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); + } + InvalidateRect(hWnd,NULL,TRUE); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static INT_PTR CALLBACK SettingsMemoryProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LPCTSTR szActPort2Filename = _T(""); + + BOOL bPort2CfgChange = FALSE; + BOOL bPort2AttChange = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + // HP48SX/GX + if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0) + { + // init port1 enable checkbox + CheckDlgButton(hDlg,IDC_PORT1EN,(Chipset.cards_status & PORT1_PRESENT) != 0); + CheckDlgButton(hDlg,IDC_PORT1WR,(Chipset.cards_status & PORT1_WRITE) != 0); + + if (nArgc < 3) // port2 filename from Emu48.ini file + { + szActPort2Filename = szPort2Filename; + } + else // port2 filename given from command line + { + szActPort2Filename = ppArgv[2]; + EnableWindow(GetDlgItem(hDlg,IDC_PORT2),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2LOAD),FALSE); + } + + // init port2 shared and writeable checkbox and set port2 filename + CheckDlgButton(hDlg,IDC_PORT2ISSHARED,bPort2IsShared); + CheckDlgButton(hDlg,IDC_PORT2WR,IsFileWriteable(szActPort2Filename)); + SetDlgItemText(hDlg,IDC_PORT2,szActPort2Filename); + if (nState == SM_INVALID) // Invalid State + { + EnableWindow(GetDlgItem(hDlg,IDC_PORT1EN),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE); + } + } + else // HP38G/HP39G/HP40G/HP49G + { + EnableWindow(GetDlgItem(hDlg,IDC_PORT1EN),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2ISSHARED),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2WR),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2LOAD),FALSE); + } + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_PORT2LOAD: + if (GetLoadObjectFilename(_T(BIN_FILTER),_T("BIN"))) + { + TCHAR szFilename[MAX_PATH]; + LPTSTR lpFilePart; + + // check if file path and Emu48 directory path is identical + if (GetFullPathName(szBufferFilename,ARRAYSIZEOF(szFilename),szFilename,&lpFilePart)) + { + *(lpFilePart-1) = 0; // devide path and name + + // name is in the Emu48 directory -> use only name + if (lstrcmpi(szEmuDirectory,szFilename) == 0) + lstrcpy(szBufferFilename,lpFilePart); + } + SetDlgItemText(hDlg,IDC_PORT2,szBufferFilename); + + // adjust R/W checkbox + CheckDlgButton(hDlg,IDC_PORT2WR,IsFileWriteable(szBufferFilename)); + } + return TRUE; + } + break; + case WM_NOTIFY: + switch (((LPNMHDR) lParam)->code) + { + case PSN_KILLACTIVE: + if (Chipset.Port1Size && (cCurrentRomType!='X' || cCurrentRomType!='2' || cCurrentRomType!='Q')) // CdB for HP: add apples + { + UINT nOldState = SwitchToState(SM_SLEEP); + // save old card status + BYTE byCardsStatus = Chipset.cards_status; + + // port1 disabled? + Chipset.cards_status &= ~(PORT1_PRESENT | PORT1_WRITE); + if (IsDlgButtonChecked(hDlg, IDC_PORT1EN)) + { + Chipset.cards_status |= PORT1_PRESENT; + if (IsDlgButtonChecked(hDlg, IDC_PORT1WR)) + Chipset.cards_status |= PORT1_WRITE; + } + + // changed card status in slot1? + if ( ((byCardsStatus ^ Chipset.cards_status) & (PORT1_PRESENT | PORT1_WRITE)) != 0 + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + SwitchToState(nOldState); + } + // HP48SX/GX port2 change settings detection + if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0) + { + TCHAR szFilename[MAX_PATH]; + BOOL bOldPort2IsShared = bPort2IsShared; + + szActPort2Filename = (nArgc < 3) ? szPort2Filename : ppArgv[2]; + + // shared port + bPort2IsShared = IsDlgButtonChecked(hDlg,IDC_PORT2ISSHARED); + if (bPort2IsShared != bOldPort2IsShared) + { + bPort2CfgChange = TRUE; // slot2 configuration changed + } + + if (nArgc < 3) // port2 filename from Emu48.ini file + { + // get current filename and notify difference + GetDlgItemText(hDlg,IDC_PORT2,szFilename,ARRAYSIZEOF(szFilename)); + if (lstrcmp(szPort2Filename,szFilename) != 0) + { + lstrcpyn(szPort2Filename,szFilename,ARRAYSIZEOF(szPort2Filename)); + bPort2CfgChange = TRUE; // slot2 configuration changed + } + } + + // R/W port + if ( *szActPort2Filename != 0 + && (BOOL) IsDlgButtonChecked(hDlg,IDC_PORT2WR) != IsFileWriteable(szActPort2Filename)) + { + bPort2AttChange = TRUE; // slot2 file R/W attribute changed + bPort2CfgChange = TRUE; // slot2 configuration changed + } + } + + if (bPort2CfgChange) // slot2 configuration changed + { + UINT nOldState = SwitchToState(SM_INVALID); + + UnmapPort2(); // unmap port2 + + if (bPort2AttChange) // slot2 R/W mode changed + { + DWORD dwFileAtt; + + SetCurrentDirectory(szEmuDirectory); + dwFileAtt = GetFileAttributes(szActPort2Filename); + if (dwFileAtt != 0xFFFFFFFF) + { + if (IsDlgButtonChecked(hDlg,IDC_PORT2WR)) + dwFileAtt &= ~FILE_ATTRIBUTE_READONLY; + else + dwFileAtt |= FILE_ATTRIBUTE_READONLY; + + SetFileAttributes(szActPort2Filename,dwFileAtt); + } + SetCurrentDirectory(szCurrentDirectory); + } + + if (cCurrentRomType) // ROM defined + { + MapPort2(szActPort2Filename); + + // port2 changed and card detection enabled + if ( (bPort2AttChange || Chipset.wPort2Crc != wPort2Crc) + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + // save fingerprint of port2 + Chipset.wPort2Crc = wPort2Crc; + } + SwitchToState(nOldState); + } + return TRUE; + } + break; + } + return FALSE; +} + +static INT_PTR CALLBACK SettingsPeripheralProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + TCHAR cPort[8]; + LONG i; + UINT uDevId; + + switch (uMsg) + { + case WM_INITDIALOG: + // set sound slider + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETRANGE,FALSE,MAKELONG(0,255)); + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETTICFREQ,256/8,0); + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETPOS,TRUE,dwWaveVol); + + // set sound device + SetSoundDeviceList(GetDlgItem(hDlg,IDC_SOUND_DEVICE),uWaveDevId); + + // UDP infrared printer settings + SetDlgItemText(hDlg,IDC_IR_ADDR,szUdpServer); + wsprintf(cPort,_T("%u"),wUdpPort); + SetDlgItemText(hDlg,IDC_IR_PORT,cPort); + + // set combobox parameter + SetCommList(hDlg,szSerialWire,szSerialIr); + if (bCommInit) // disable when port open + { + EnableWindow(GetDlgItem(hDlg,IDC_WIRE),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE); + } + + if (cCurrentRomType=='X') // HP49G + { + SendDlgItemMessage(hDlg,IDC_IR,CB_RESETCONTENT,0,0); + EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE); + } + return TRUE; + case WM_NOTIFY: + switch (((LPNMHDR) lParam)->code) + { + case PSN_KILLACTIVE: + // set sound data + dwWaveVol = (DWORD) SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_GETPOS,0,0); + i = (LONG) SendDlgItemMessage(hDlg,IDC_SOUND_DEVICE,CB_GETCURSEL,0,0); + uDevId = (UINT) SendDlgItemMessage(hDlg,IDC_SOUND_DEVICE,CB_GETITEMDATA,i,0); + if (uWaveDevId != uDevId) // sound device id changed + { + UINT nActState; + + uWaveDevId = uDevId; // set new sound device id + + nActState = SwitchToState(SM_SLEEP); + + // restart sound engine with new device id + SoundClose(); // close waveform-audio output device + SoundOpen(uWaveDevId); // open waveform-audio output device + + SwitchToState(nActState); + } + // UDP infrared printer settings + GetDlgItemText(hDlg,IDC_IR_ADDR,szUdpServer,ARRAYSIZEOF(szUdpServer)); + GetDlgItemText(hDlg,IDC_IR_PORT,cPort,ARRAYSIZEOF(cPort)); + wUdpPort = (WORD) _ttoi(cPort); + ResetUdp(); // invalidate saved UDP address + // set combobox parameter + GetDlgItemText(hDlg,IDC_WIRE,szSerialWire,ARRAYSIZEOF(szSerialWire)); + if (cCurrentRomType!='X') // HP49G Ir port is not connected + GetDlgItemText(hDlg,IDC_IR,szSerialIr,ARRAYSIZEOF(szSerialIr)); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + + + +//################ +//# +//# Save Helper +//# +//################ + +// +// UINT SaveChanges(BOOL bAuto); +// Return code : +// IDYES File successfuly saved +// IDNO File not saved +// IDCANCEL Cancel command +// +static UINT SaveChanges(BOOL bAuto) +{ + UINT uReply; + + if (bDocumentAvail == FALSE) return IDNO; + + if (bAuto) + uReply = IDYES; + else + { + UINT uStyle = bSaveDefConfirm ? 0 : MB_DEFBUTTON2; + uReply = YesNoCancelMessage(_T("Do you want to save changes?"),uStyle); + } + + if (uReply != IDYES) return uReply; + + if (szCurrentFilename[0] == 0) + { // Save As... + if (GetSaveAsFilename()) + { + if (SaveDocumentAs(szBufferFilename)) + return IDYES; + else + return IDCANCEL; + } + return IDNO; + } + + SaveDocument(); + return IDYES; +} + + + +//################ +//# +//# Message Handlers +//# +//################ + +// +// WM_CREATE +// +static LRESULT OnCreate(HWND hWindow) +{ + InitializeCriticalSection(&csGDILock); + InitializeCriticalSection(&csLcdLock); + InitializeCriticalSection(&csKeyLock); + InitializeCriticalSection(&csIOLock); + InitializeCriticalSection(&csT1Lock); + InitializeCriticalSection(&csT2Lock); + InitializeCriticalSection(&csTxdLock); + InitializeCriticalSection(&csRecvLock); + InitializeCriticalSection(&csSlowLock); + InitializeCriticalSection(&csDbgLock); + + // load cursors + hCursorArrow = LoadCursor(NULL,IDC_ARROW); + hCursorHand = LoadCursor(NULL,IDC_HAND); + if (hCursorHand == NULL) + { + // for Win95, NT4.0 + bOwnCursor = ((hCursorHand = CreateHandCursor()) != NULL); + } + + hWnd = hWindow; + hWindowDC = GetDC(hWnd); + return 0; +} + +// +// WM_DESTROY +// +static LRESULT OnDestroy(HWND hWindow) +{ + DragAcceptFiles(hWnd,FALSE); // no WM_DROPFILES message any more + if (hThread) SwitchToState(SM_RETURN); // exit emulation thread + SetWindowTitle(NULL); // free memory of title + ReleaseDC(hWnd, hWindowDC); + hWindowDC = NULL; // hWindowDC isn't valid any more + hWnd = NULL; + + if (bOwnCursor) // destroy hand cursor + { + DestroyCursor(hCursorHand); + bOwnCursor = FALSE; + } + + DeleteCriticalSection(&csGDILock); + DeleteCriticalSection(&csLcdLock); + DeleteCriticalSection(&csKeyLock); + DeleteCriticalSection(&csIOLock); + DeleteCriticalSection(&csT1Lock); + DeleteCriticalSection(&csT2Lock); + DeleteCriticalSection(&csTxdLock); + DeleteCriticalSection(&csRecvLock); + DeleteCriticalSection(&csSlowLock); + DeleteCriticalSection(&csDbgLock); + + #if defined _USRDLL // DLL version + DLLDestroyWnd(); // cleanup system + #else // EXE version + PostQuitMessage(0); // exit message loop + #endif + return 0; + UNREFERENCED_PARAMETER(hWindow); +} + +// +// WM_PAINT +// +static LRESULT OnPaint(HWND hWindow) +{ + PAINTSTRUCT Paint; + HDC hPaintDC; + + UpdateWindowBars(); // update visibility of title and menu bar + + hPaintDC = BeginPaint(hWindow, &Paint); + if (hMainDC != NULL) + { + RECT rcMainPaint = Paint.rcPaint; + rcMainPaint.left += nBackgroundX; // coordinates in source bitmap + rcMainPaint.top += nBackgroundY; + rcMainPaint.right += nBackgroundX; + rcMainPaint.bottom += nBackgroundY; + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + UINT nLines = MAINSCREENHEIGHT; + + // redraw background bitmap + BitBlt(hPaintDC, Paint.rcPaint.left, Paint.rcPaint.top, + Paint.rcPaint.right-Paint.rcPaint.left, Paint.rcPaint.bottom-Paint.rcPaint.top, + hMainDC, rcMainPaint.left, rcMainPaint.top, SRCCOPY); + + // CdB for HP: add apples display stuff + SetWindowOrgEx(hPaintDC, nBackgroundX, nBackgroundY, NULL); + + // redraw header display area + StretchBlt(hPaintDC, nLcdX, nLcdY, + 131*nLcdZoom*nGdiXZoom, Chipset.d0size*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.d0offset, 0, + 131, Chipset.d0size, SRCCOPY); + // redraw main display area + StretchBlt(hPaintDC, nLcdX, nLcdY+Chipset.d0size*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, nLines*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.boffset, Chipset.d0size, + 131, nLines, SRCCOPY); + // redraw menu display area + StretchBlt(hPaintDC, nLcdX, nLcdY+(nLines+Chipset.d0size)*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, MENUHEIGHT*nLcdZoom*nGdiYZoom, + hLcdDC, 0, (nLines+Chipset.d0size), + 131, MENUHEIGHT, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + UpdateAnnunciators(); + RefreshButtons(&rcMainPaint); + } + EndPaint(hWindow, &Paint); + return 0; +} + +// +// WM_INITMENU +// +static LRESULT OnInitMenu(HMENU hMenu) +{ + // disable stack loading items on HP38G, HP39/40G, HP39G+ + BOOL bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add apples + BOOL bRun = nState == SM_RUN || nState == SM_SLEEP; + + UINT uStackEnable = (bRun && bStackEnable) ? MF_ENABLED : MF_GRAYED; + UINT uRun = bRun ? MF_ENABLED : MF_GRAYED; + UINT uBackup = bBackup ? MF_ENABLED : MF_GRAYED; + + EnableMenuItem(hMenu,ID_FILE_NEW,MF_ENABLED); + EnableMenuItem(hMenu,ID_FILE_OPEN,MF_ENABLED); + EnableMenuItem(hMenu,ID_FILE_SAVE,(bRun && szCurrentFilename[0]) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_FILE_SAVEAS,uRun); + EnableMenuItem(hMenu,ID_FILE_CLOSE,uRun); + EnableMenuItem(hMenu,ID_OBJECT_LOAD,uStackEnable); + EnableMenuItem(hMenu,ID_OBJECT_SAVE,uStackEnable); + EnableMenuItem(hMenu,ID_VIEW_COPY,uRun); + EnableMenuItem(hMenu,ID_STACK_COPY,uStackEnable); + EnableMenuItem(hMenu,ID_STACK_PASTE,uStackEnable); + EnableMenuItem(hMenu,ID_VIEW_RESET,uRun); + EnableMenuItem(hMenu,ID_BACKUP_SAVE,uRun); + EnableMenuItem(hMenu,ID_BACKUP_RESTORE,uBackup); + EnableMenuItem(hMenu,ID_BACKUP_DELETE,uBackup); + EnableMenuItem(hMenu,ID_VIEW_SCRIPT,uRun); + EnableMenuItem(hMenu,ID_TOOL_DISASM,uRun); + EnableMenuItem(hMenu,ID_TOOL_DEBUG,(bRun && nDbgState == DBG_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_RECORD,(bRun && nMacroState == MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_PLAY,(bRun && nMacroState == MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_STOP,(bRun && nMacroState != MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + + MruUpdateMenu(hMenu); // update MRU list + return 0; +} + +// +// WM_DROPFILES +// +static LRESULT OnDropFiles(HDROP hFilesInfo) +{ + TCHAR szFileName[MAX_PATH]; + WORD wNumFiles,wIndex; + BOOL bSuccess = FALSE; + + // get number of files dropped + wNumFiles = DragQueryFile (hFilesInfo,(UINT)-1,NULL,0); + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + // turn on HP + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + } + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + DragFinish (hFilesInfo); + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + // get each name and load it into the emulator + for (wIndex = 0;wIndex < wNumFiles;++wIndex) + { + DragQueryFile (hFilesInfo,wIndex,szFileName,ARRAYSIZEOF(szFileName)); + + // szFileName has file name, now try loading it + if ((bSuccess = LoadObject(szFileName)) == FALSE) + break; + } + + DragFinish (hFilesInfo); + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + + if (bSuccess == FALSE) // data not copied + goto cancel; + + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + // wait for sleep mode + while (Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; +} + +// +// ID_FILE_NEW +// +static LRESULT OnFileNew(VOID) +{ + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (NewDocument()) SetWindowTitle(_T("Untitled")); +cancel: + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_OPEN +// +static LRESULT OnFileOpen(VOID) +{ + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (GetOpenFilename()) + { + if (OpenDocument(szBufferFilename)) + MruAdd(szBufferFilename); + } +cancel: + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_MRU_FILE1 +// +static LRESULT OnFileMruOpen(UINT wID) +{ + LPCTSTR lpszFilename; + + wID -= ID_FILE_MRU_FILE1; // zero based MRU index + lpszFilename = MruFilename(wID); // full filename from MRU list + if (lpszFilename == NULL) return 0; // MRU slot not filled + + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (!OpenDocument(lpszFilename)) // document loading failed + { + MruRemove(wID); // entry not valid any more + } + else + { + MruMoveTop(wID); // move entry to top of MRU list + } +cancel: + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_SAVE +// +static LRESULT OnFileSave(VOID) +{ + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + SaveChanges(TRUE); + SwitchToState(SM_RUN); + } + return 0; +} + +// +// ID_FILE_SAVEAS +// +static LRESULT OnFileSaveAs(VOID) +{ + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + if (GetSaveAsFilename()) + { + if (SaveDocumentAs(szBufferFilename)) + MruAdd(szCurrentFilename); + } + SwitchToState(SM_RUN); + } + return 0; +} + +// +// ID_FILE_CLOSE +// +static LRESULT OnFileClose(VOID) +{ + if (bDocumentAvail) + { + SwitchToState(SM_INVALID); + if (SaveChanges(bAutoSave) != IDCANCEL) + { + ResetDocument(); + SetWindowTitle(NULL); + } + else + { + SwitchToState(SM_RUN); + } + } + return 0; +} + +// +// ID_FILE_EXIT +// +// WM_SYS_CLOSE +// +static LRESULT OnFileExit(VOID) +{ + SwitchToState(SM_INVALID); // hold emulation thread + if (SaveChanges(bAutoSaveOnExit) == IDCANCEL) + { + SwitchToState(SM_RUN); // on cancel restart emulation thread + return 0; + } + DestroyWindow(hWnd); + return 0; +} + +// +// ID_VIEW_COPY +// +static LRESULT OnViewCopy(VOID) +{ + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + { + // DIB bitmap + #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + #define PALVERSION 0x300 + + BITMAP bm; + LPBITMAPINFOHEADER lpbi; + PLOGPALETTE ppal; + HBITMAP hBmp; + HDC hBmpDC; + HANDLE hClipObj; + WORD wBits; + DWORD dwLen, dwSizeImage; + + _ASSERT(nLcdZoom >= 1 && nLcdZoom <= 4); + hBmp = CreateCompatibleBitmap(hLcdDC,131*nLcdZoom*nGdiXZoom,SCREENHEIGHT*nLcdZoom*nGdiYZoom); // CdB for HP: add apples display stuff + hBmpDC = CreateCompatibleDC(hLcdDC); + hBmp = (HBITMAP) SelectObject(hBmpDC,hBmp); + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + UINT nLines = MAINSCREENHEIGHT; + + // copy header display area + StretchBlt(hBmpDC, 0, 0, + 131*nLcdZoom*nGdiXZoom, Chipset.d0size*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.d0offset, 0, + 131, Chipset.d0size, SRCCOPY); + // copy main display area + StretchBlt(hBmpDC, 0, Chipset.d0size*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, nLines*nLcdZoom*nGdiYZoom, + hLcdDC, Chipset.boffset, Chipset.d0size, + 131, nLines, SRCCOPY); + // copy menu display area + StretchBlt(hBmpDC, 0, (nLines+Chipset.d0size)*nLcdZoom*nGdiYZoom, + 131*nLcdZoom*nGdiXZoom, MENUHEIGHT*nLcdZoom*nGdiYZoom, + hLcdDC, 0, (nLines+Chipset.d0size), + 131, MENUHEIGHT, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + hBmp = (HBITMAP) SelectObject(hBmpDC,hBmp); + + // fill BITMAP structure for size information + GetObject(hBmp, sizeof(bm), &bm); + + wBits = bm.bmPlanes * bm.bmBitsPixel; + // make sure bits per pixel is valid + if (wBits <= 1) + wBits = 1; + else if (wBits <= 4) + wBits = 4; + else if (wBits <= 8) + wBits = 8; + else // if greater than 8-bit, force to 24-bit + wBits = 24; + + dwSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * wBits) * bm.bmHeight; + + // calculate memory size to store CF_DIB data + dwLen = sizeof(BITMAPINFOHEADER) + dwSizeImage; + if (wBits != 24) // a 24 bitcount DIB has no color table + { + // add size for color table + dwLen += (DWORD) (1 << wBits) * sizeof(RGBQUAD); + } + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE, dwLen)) != NULL) + { + lpbi = (LPBITMAPINFOHEADER ) GlobalLock(hClipObj); + // initialize BITMAPINFOHEADER + lpbi->biSize = sizeof(BITMAPINFOHEADER); + lpbi->biWidth = bm.bmWidth; + lpbi->biHeight = bm.bmHeight; + lpbi->biPlanes = 1; + lpbi->biBitCount = wBits; + lpbi->biCompression = BI_RGB; + lpbi->biSizeImage = dwSizeImage; + lpbi->biXPelsPerMeter = 0; + lpbi->biYPelsPerMeter = 0; + lpbi->biClrUsed = 0; + lpbi->biClrImportant = 0; + // get bitmap color table and bitmap data + GetDIBits(hBmpDC, hBmp, 0, lpbi->biHeight, (LPBYTE)lpbi + dwLen - dwSizeImage, + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS); + GlobalUnlock(hClipObj); + SetClipboardData(CF_DIB, hClipObj); + + // get number of entries in the logical palette + GetObject(hPalette,sizeof(WORD),&wBits); + + // memory allocation for temporary palette data + if ((ppal = (PLOGPALETTE) calloc(sizeof(LOGPALETTE) + wBits * sizeof(PALETTEENTRY),1)) != NULL) + { + ppal->palVersion = PALVERSION; + ppal->palNumEntries = wBits; + GetPaletteEntries(hPalette, 0, wBits, ppal->palPalEntry); + SetClipboardData(CF_PALETTE, CreatePalette(ppal)); + free(ppal); + } + } + DeleteDC(hBmpDC); + DeleteObject(hBmp); + #undef WIDTHBYTES + #undef PALVERSION + } + CloseClipboard(); + } + return 0; +} + +// +// ID_VIEW_RESET +// +static LRESULT OnViewReset(VOID) +{ + if (nState != SM_RUN) return 0; + if (YesNoMessage(_T("Are you sure you want to press the Reset Button?"))==IDYES) + { + SwitchToState(SM_SLEEP); + CpuReset(); // register setting after Cpu Reset + SwitchToState(SM_RUN); + } + return 0; +} + +// +// ID_VIEW_SETTINGS +// +static INT_PTR CALLBACK PropSheetProc(HWND hwndPropSheet, UINT uMsg, LPARAM lParam) +{ + switch(uMsg) + { + // called before the dialog is created, hwndPropSheet = NULL, lParam points to dialog resource + case PSCB_PRECREATE: + { + LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE) lParam; + if(!(lpTemplate->style & WS_SYSMENU)) + { + lpTemplate->style |= WS_SYSMENU; + } + } + break; + + // called after the dialog is created + case PSCB_INITIALIZED: + break; + } + return 0; + UNREFERENCED_PARAMETER(hwndPropSheet); +} + +static LRESULT OnViewSettings(VOID) +{ + PROPSHEETPAGE psp[3]; + PROPSHEETHEADER psh; + + // not in nState = SM_INVALID or port2 file must be closed from document + _ASSERT(nState != SM_INVALID || pbyPort2 == NULL); + + psp[0].dwSize = sizeof(PROPSHEETPAGE); + psp[0].dwFlags = PSP_DEFAULT; + psp[0].hInstance = hApp; + psp[0].pszTemplate = MAKEINTRESOURCE(IDD_SET_GENERAL); + psp[0].hIcon = NULL; + psp[0].pszTitle = NULL; + psp[0].pfnDlgProc = SettingsGeneralProc; + psp[0].lParam = 0; + psp[0].pfnCallback = NULL; + + psp[1].dwSize = sizeof(PROPSHEETPAGE); + psp[1].dwFlags = PSP_DEFAULT; + psp[1].hInstance = hApp; + psp[1].pszTemplate = MAKEINTRESOURCE(IDD_SET_MEMORY); + psp[1].hIcon = NULL; + psp[1].pszTitle = NULL; + psp[1].pfnDlgProc = SettingsMemoryProc; + psp[1].lParam = 0; + psp[1].pfnCallback = NULL; + + psp[2].dwSize = sizeof(PROPSHEETPAGE); + psp[2].dwFlags = PSP_DEFAULT; + psp[2].hInstance = hApp; + psp[2].pszTemplate = MAKEINTRESOURCE(IDD_SET_PERIPHERAL); + psp[2].hIcon = NULL; + psp[2].pszTitle = NULL; + psp[2].pfnDlgProc = SettingsPeripheralProc; + psp[2].lParam = 0; + psp[2].pfnCallback = NULL; + + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK | PSH_NOAPPLYNOW; + psh.hwndParent = hWnd; + psh.hInstance = hApp; + psh.hIcon = NULL; + psh.pszCaption = _T("Settings"); + psh.nPages = ARRAYSIZEOF(psp); + psh.nStartPage = 0; + psh.ppsp = (LPCPROPSHEETPAGE) &psp; + psh.pfnCallback = (PFNPROPSHEETCALLBACK) PropSheetProc; + + if (PropertySheet(&psh) == -1) + AbortMessage(_T("Settings Property Sheet Creation Error!")); + + WriteSettings(); + return 0; +} + +// +// ID_VIEW_SCRIPT +// +static LRESULT OnViewScript(VOID) +{ + TCHAR szKmlFile[MAX_PATH]; + BOOL bKMLChanged,bSucc; + + BYTE cType = cCurrentRomType; + if (nState != SM_RUN) + { + InfoMessage(_T("You cannot change the KML script when Emu48 is not running.\n") + _T("Use the File,New menu item to create a new calculator.")); + return 0; + } + SwitchToState(SM_INVALID); + + // make a copy of the current KML script file name + _ASSERT(sizeof(szKmlFile) == sizeof(szCurrentKml)); + lstrcpyn(szKmlFile,szCurrentKml,ARRAYSIZEOF(szKmlFile)); + + bKMLChanged = FALSE; // KML script not changed + bSucc = TRUE; // KML script successful loaded + + do + { + if (!DisplayChooseKml(cType)) // quit with Cancel + { + if (!bKMLChanged) // KML script not changed + break; // exit loop with current loaded KML script + + // restore KML script file name + lstrcpyn(szCurrentKml,szKmlFile,ARRAYSIZEOF(szCurrentKml)); + + // try to restore old KML script + if ((bSucc = InitKML(szCurrentKml,FALSE))) + break; // exit loop with success + + // restoring failed, save document + if (IDCANCEL != SaveChanges(bAutoSave)) + break; // exit loop with no success + + _ASSERT(bSucc == FALSE); // for continuing loop + } + else // quit with Ok + { + bKMLChanged = TRUE; // KML script changed + bSucc = InitKML(szCurrentKml,FALSE); + } + } + while (!bSucc); // retry if KML script is invalid + + if (bSucc) + { + if (Chipset.wRomCrc != wRomCrc) // ROM changed + { + CpuReset(); + Chipset.Shutdn = FALSE; // automatic restart + + Chipset.wRomCrc = wRomCrc; // update current ROM fingerprint + } + if (pbyRom) SwitchToState(SM_RUN); // continue emulation + } + else + { + ResetDocument(); // close document + SetWindowTitle(NULL); + } + return 0; +} + +// +// ID_BACKUP_SAVE +// +static LRESULT OnBackupSave(VOID) +{ + UINT nOldState; + if (pbyRom == NULL) return 0; + nOldState = SwitchToState(SM_INVALID); + SaveBackup(); + SwitchToState(nOldState); + return 0; +} + +// +// ID_BACKUP_RESTORE +// +static LRESULT OnBackupRestore(VOID) +{ + SwitchToState(SM_INVALID); + RestoreBackup(); + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_BACKUP_DELETE +// +static LRESULT OnBackupDelete(VOID) +{ + ResetBackup(); + return 0; +} + +// +// ID_OBJECT_LOAD +// +static LRESULT OnObjectLoad(VOID) +{ + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while (Chipset.Shutdn == FALSE) Sleep(0); + } + + if (nState != SM_RUN) + { + InfoMessage(_T("The emulator must be running to load an object.")); + goto cancel; + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + if (bLoadObjectWarning) + { + UINT uReply = YesNoCancelMessage( + _T("Warning: Trying to load an object while the emulator is busy\n") + _T("will certainly result in a memory lost. Before loading an object\n") + _T("you should be sure that the calculator is in idle state.\n") + _T("Do you want to see this warning next time you try to load an object?"),0); + switch (uReply) + { + case IDYES: + break; + case IDNO: + bLoadObjectWarning = FALSE; + break; + case IDCANCEL: + SwitchToState(SM_RUN); + goto cancel; + } + } + + if (!GetLoadObjectFilename(_T(HP_FILTER),_T("HP"))) + { + SwitchToState(SM_RUN); + goto cancel; + } + + if (!LoadObject(szBufferFilename)) + { + SwitchToState(SM_RUN); + goto cancel; + } + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + while (Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; +} + +// +// ID_OBJECT_SAVE +// +static LRESULT OnObjectSave(VOID) +{ + if (nState != SM_RUN) + { + InfoMessage(_T("The emulator must be running to save an object.")); + return 0; + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + return 0; + } + + _ASSERT(nState == SM_SLEEP); + + if (GetSaveObjectFilename(_T(HP_FILTER),_T("HP"))) + { + SaveObject(szBufferFilename); + } + + SwitchToState(SM_RUN); + return 0; +} + +// +// ID_TOOL_DISASM +// +static INT_PTR CALLBACK Disasm(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static DWORD dwAddress,dwAddressMax; + + enum MEM_MAPPING eMode; + LONG i; + DWORD dwNxtAddr; + TCHAR szAddress[256] = _T("0"); + + switch (message) + { + case WM_INITDIALOG: + VERIFY(SetMemRomType(cCurrentRomType)); // set current model + + // set fonts & cursor + SendDlgItemMessage(hDlg,IDC_DISASM_MODULE,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_MODE_TEXT,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_MODE,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_ADDRESS,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_ADR,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_NEXT,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_COPY,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + // fill disassembler mode combo box + { + // disassemble mode window + HWND hWnd = GetDlgItem(hDlg,IDC_DISASM_MODE); + + if (hDlgDebug == NULL) // debugger not open + { + LPCTSTR lpszModes[] = { _T("Map"), _T("NCE1"), _T("NCE2"), _T("CE1"), _T("CE2"), _T("NCE3") }; + + for (eMode = MEM_MMU; eMode <= MEM_NCE3; eMode = (enum MEM_MAPPING) (eMode + 1)) + { + if (GetMemAvail(eMode)) + { + _ASSERT(eMode < ARRAYSIZEOF(lpszModes)); + i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) lpszModes[eMode]); + SendMessage(hWnd,CB_SETITEMDATA,i,(LPARAM) eMode); + } + } + VERIFY(SendMessage(hWnd,CB_SETCURSEL,0,0) != LB_ERR); + + // disassemble with mapped modules + VERIFY(SetMemMapType(MEM_MMU)); + } + else // debugger open + { + EnableWindow(hWnd,FALSE); + } + } + + SetDlgItemText(hDlg,IDC_DISASM_ADR,szAddress); + dwAddressMax = GetMemDataSize(); + dwAddress = _tcstoul(szAddress,NULL,16); + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + // decode memory mode combo box + case IDC_DISASM_MODE: + // new combo box item selected + if (HIWORD(wParam) == CBN_SELENDOK) + { + HWND hWnd = GetDlgItem(hDlg,IDC_DISASM_MODE); + i = (LONG) SendMessage(hWnd,CB_GETCURSEL,0,0); + eMode = (enum MEM_MAPPING) SendMessage(hWnd,CB_GETITEMDATA,i,0); + VERIFY(SetMemMapType(eMode)); + dwAddressMax = GetMemDataSize(); + } + break; + case IDOK: + SendDlgItemMessage(hDlg,IDC_DISASM_ADR,EM_SETSEL,0,-1); + GetDlgItemText(hDlg,IDC_DISASM_ADR,szAddress,ARRAYSIZEOF(szAddress)); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szAddress); ++i) + { + if (_istxdigit(szAddress[i]) == FALSE) + return FALSE; + } + dwAddress = _tcstoul(szAddress,NULL,16); + // no break + case IDC_DISASM_NEXT: + if (dwAddress >= dwAddressMax) + return FALSE; + i = wsprintf(szAddress,(dwAddress <= 0xFFFFF) ? _T("%05lX ") : _T("%06lX "),dwAddress); + // check if address content is a PCO (Primitive Code Object) + dwNxtAddr = (dwAddress + 5) & 0xFFFFF; + if (Read5(dwAddress) == dwNxtAddr) + { + if (disassembler_mode == HP_MNEMONICS) + { + _tcscat(&szAddress[i],_T("CON(5) (*)+5")); + } + else + { + wsprintf(&szAddress[i],_T("dcr.5 $%05x"),dwNxtAddr); + } + dwAddress = dwNxtAddr; + } + else + { + dwAddress = disassemble(dwAddress,&szAddress[i]); + } + i = (LONG) SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_ADDSTRING,0,(LPARAM) szAddress); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SELITEMRANGE,FALSE,MAKELPARAM(0,i)); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SETSEL,TRUE,i); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SETTOPINDEX,i,0); + return TRUE; + case IDC_DISASM_COPY: + // copy selected items to clipboard + CopyItemsToClipboard(GetDlgItem(hDlg,IDC_DISASM_WIN)); + return TRUE; + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +// +// ID_ABOUT +// +static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg,IDC_VERSION,szNoTitle); + SetDlgItemText(hDlg,IDC_LICENSE,szLicence); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + if ((wParam==IDOK)||(wParam==IDCANCEL)) + { + EndDialog(hDlg, wParam); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static LRESULT OnToolDisasm(VOID) // disasm dialogbox call +{ + if (pbyRom) SwitchToState(SM_SLEEP); + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DISASM), hWnd, (DLGPROC)Disasm) == -1) + AbortMessage(_T("Disassembler Dialog Box Creation Error!")); + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +static LRESULT OnTopics(VOID) +{ + ShellExecute(hWnd,_T("open"),_T("Emu48.htm"),NULL,szEmuDirectory,SW_SHOWNORMAL); + return 0; +} + +static LRESULT OnAbout(VOID) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)About) == -1) + AbortMessage(_T("About Dialog Box Creation Error!")); + return 0; +} + +static VOID OnContextMenu(LPARAM lParam) +{ + HMENU hMenu; + POINT pt,ptc; + + if (GetMenu(hWnd) == NULL) // no main window menu + { + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position + + if (pt.x == -1 && pt.y == -1) // VK_APPS + { + pt.x = 15; // open context help at client position 15,15 + pt.y = 15; + VERIFY(ClientToScreen(hWnd,&pt)); + } + + ptc = pt; + VERIFY(ScreenToClient(hWnd,&ptc)); // convert mouse into client position + + // in client area not over a button + if (ptc.y >= 0 && !MouseIsButton(ptc.x,ptc.y)) + { + // load the popup menu resource + if ((hMenu = LoadMenu(hApp,MAKEINTRESOURCE(IDM_MENU))) != NULL) + { + // display the popup menu + TrackPopupMenu(GetSubMenu(hMenu,0), + TPM_LEFTALIGN | TPM_LEFTBUTTON, + pt.x, pt.y, 0, hWnd, NULL); + + DestroyMenu(hMenu); // destroy the menu + } + } + } + return; +} + +static BOOL OnNcHitTest(LPARAM lParam) +{ + if (!bTitleBar || bClientWinMove) // no title bar or window movement over client enabled + { + POINT pt; + + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position + VERIFY(ScreenToClient(hWnd,&pt)); // convert mouse into client position + + if (pt.y >= 0) // client area + { + // hit area not over a button + return !MouseIsButton(pt.x,pt.y); + } + } + return FALSE; +} + +static LRESULT OnLButtonDown(UINT nFlags, WORD x, WORD y) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseButtonDownAt(nFlags, x,y); + return 0; +} + +static LRESULT OnLButtonUp(UINT nFlags, WORD x, WORD y) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseButtonUpAt(nFlags, x,y); + return 0; +} + +static LRESULT OnMouseMove(UINT nFlags, WORD x, WORD y) +{ + // emulator not active but cursor is over emulator window + if (bActFollowsMouse && GetActiveWindow() != hWnd) + { + ForceForegroundWindow(hWnd); // force emulator window to foreground + } + + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseMovesTo(nFlags, x,y); + return 0; +} + +static LRESULT OnNcMouseMove(UINT nFlags, WORD x, WORD y) +{ + // emulator not active but cursor is over emulator window + if (bActFollowsMouse && GetActiveWindow() != hWnd) + { + ForceForegroundWindow(hWnd); // force emulator window to foreground + } + return 0; + UNREFERENCED_PARAMETER(nFlags); + UNREFERENCED_PARAMETER(x); + UNREFERENCED_PARAMETER(y); +} + +static LRESULT OnKeyDown(int nVirtKey, LPARAM lKeyData) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + // call RunKey() only once (suppress autorepeat feature) + if (nState == SM_RUN && (lKeyData & 0x40000000) == 0) + RunKey((BYTE)nVirtKey, TRUE); + return 0; +} + +static LRESULT OnKeyUp(int nVirtKey, LPARAM lKeyData) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) RunKey((BYTE)nVirtKey, FALSE); + return 0; + UNREFERENCED_PARAMETER(lKeyData); +} + +static LRESULT OnCopyData(PCOPYDATASTRUCT psCDS) +{ + switch (psCDS->dwData) + { + case CDID_FILENAME: + // current instance has document loaded and got a wide-character file name + if (bDocumentAvail && psCDS->cbData > 0 && psCDS->lpData != NULL) + { + TCHAR szActFilename[MAX_PATH]; + LPTSTR lpFilePart; // address of file name in path + + #if defined _UNICODE + { + // get full path file name for requested state file + GetFullPathName((LPCTSTR) psCDS->lpData,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart); + } + #else + { + CHAR szAscFilename[MAX_PATH]; + + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + (LPCWSTR) psCDS->lpData, -1, + szAscFilename, ARRAYSIZEOF(szAscFilename), NULL, NULL); + + // get full path file name for requested state file + GetFullPathName(szAscFilename,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart); + } + #endif + + // get full path file name for actual state file + GetFullPathName(szCurrentFilename,ARRAYSIZEOF(szActFilename),szActFilename,&lpFilePart); + + // check if both file names are unequal + if (lstrcmpi(szBufferFilename,szActFilename) != 0) + { + UINT nCurState; + + if (pbyRom) + { + nCurState = SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (OpenDocument(szBufferFilename)) // open new file + { + MruAdd(szBufferFilename); + } +cancel: + if (pbyRom) SwitchToState(nCurState); + } + } + break; + default: + return FALSE; // message not processed + } + return TRUE; // message processed +} + +LRESULT CALLBACK MainWndProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: return OnCreate(hWindow); + case WM_DESTROY: return OnDestroy(hWindow); + case WM_PAINT: return OnPaint(hWindow); + case WM_INITMENU: return OnInitMenu((HMENU) wParam); + case WM_DROPFILES: return OnDropFiles((HDROP) wParam); + case WM_ACTIVATE: + if (LOWORD(wParam)==WA_INACTIVE) break; + case WM_QUERYNEWPALETTE: + if (hPalette) + { + SelectPalette(hWindowDC, hPalette, FALSE); + if (RealizePalette(hWindowDC)) + { + InvalidateRect(hWindow,NULL,TRUE); + return TRUE; + } + } + return FALSE; + case WM_PALETTECHANGED: + if ((HWND)wParam == hWindow) break; + if (hPalette) + { + SelectPalette(hWindowDC, hPalette, FALSE); + if (RealizePalette(hWindowDC)) + { + // UpdateColors(hWindowDC); + InvalidateRect(hWindow,NULL,TRUE); + } + } + return FALSE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_FILE_NEW: return OnFileNew(); + case ID_FILE_OPEN: return OnFileOpen(); + case ID_FILE_SAVE: return OnFileSave(); + case ID_FILE_SAVEAS: return OnFileSaveAs(); + case ID_FILE_CLOSE: return OnFileClose(); + case ID_FILE_EXIT: return OnFileExit(); + case ID_STACK_COPY: return OnStackCopy(); + case ID_STACK_PASTE: return OnStackPaste(); + case ID_VIEW_COPY: return OnViewCopy(); + case ID_VIEW_RESET: return OnViewReset(); + case ID_VIEW_SETTINGS: return OnViewSettings(); + case ID_VIEW_SCRIPT: return OnViewScript(); + case ID_BACKUP_SAVE: return OnBackupSave(); + case ID_BACKUP_RESTORE: return OnBackupRestore(); + case ID_BACKUP_DELETE: return OnBackupDelete(); + case ID_OBJECT_LOAD: return OnObjectLoad(); + case ID_OBJECT_SAVE: return OnObjectSave(); + case ID_TOOL_DISASM: return OnToolDisasm(); + case ID_TOOL_DEBUG: return OnToolDebug(); + case ID_TOOL_MACRO_RECORD: return OnToolMacroNew(); + case ID_TOOL_MACRO_PLAY: return OnToolMacroPlay(); + case ID_TOOL_MACRO_STOP: return OnToolMacroStop(); + case ID_TOOL_MACRO_SETTINGS: return OnToolMacroSettings(); + case ID_HELP_TOPICS: return OnTopics(); + case ID_ABOUT: return OnAbout(); + } + // check if command ID belongs to MRU file area + if ( (LOWORD(wParam) >= ID_FILE_MRU_FILE1) + && (LOWORD(wParam) < ID_FILE_MRU_FILE1 + MruEntries())) + return OnFileMruOpen(LOWORD(wParam)); + break; + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) + { + case SC_CLOSE: return OnFileExit(); + } + break; + case WM_CONTEXTMENU: + case WM_NCRBUTTONUP: + OnContextMenu(lParam); + break; + case WM_NCHITTEST: + if (OnNcHitTest(lParam)) return HTCAPTION; + break; + case WM_RBUTTONDOWN: + case WM_LBUTTONDOWN: return OnLButtonDown((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_LBUTTONUP: return OnLButtonUp((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_MOUSEMOVE: return OnMouseMove((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_NCMOUSEMOVE: return OnNcMouseMove((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_KEYUP: return OnKeyUp((int) wParam, lParam); + case WM_KEYDOWN: return OnKeyDown((int) wParam, lParam); + #if !defined _USRDLL // not in DLL version + case WM_COPYDATA: return OnCopyData((PCOPYDATASTRUCT) lParam); + #endif + } + return DefWindowProc(hWindow, uMsg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) +{ + typedef DWORD (WINAPI *LPFN_STIP)(HANDLE hThread,DWORD dwIdealProcessor); + + MSG msg; + WNDCLASS wc; + ATOM classAtom; + RECT rectWindow; + HACCEL hAccel; + DWORD dwThreadId; + LPFN_STIP fnSetThreadIdealProcessor; + DWORD dwProcessor; + HSZ hszService, hszTopic; // variables for DDE server + LPTSTR lpFilePart; + + // enable memory leak detection + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + + hApp = hInst; + #if defined _UNICODE + { + ppArgv = (LPCTSTR*) CommandLineToArgvW(GetCommandLine(),&nArgc); + } + #else + { + nArgc = __argc; // no. of command line arguments + ppArgv = (LPCTSTR*) __argv; // command line arguments + } + #endif + + wc.style = CS_BYTEALIGNCLIENT; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_EMU48)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wc.lpszClassName = _T("CEmu48"); + + if (!(classAtom = RegisterClass(&wc))) + { + AbortMessage( + _T("CEmu48 class registration failed.\n") + _T("This application will now terminate.")); + return FALSE; + } + + // read emulator settings + GetCurrentDirectory(ARRAYSIZEOF(szCurrentDirectory),szCurrentDirectory); + ReadSettings(); + + // running an instance of me? + if (bSingleInstance && (hWnd = FindWindow(MAKEINTATOM(classAtom),NULL)) != NULL) + { + COPYDATASTRUCT sCDS; + + if (IsIconic(hWnd)) // window minimized + ShowWindow(hWnd,SW_RESTORE); // show window + + // put the window into foreground + ForceForegroundWindow(GetLastActivePopup(hWnd)); + + if (nArgc >= 2) // use decoded parameter line + { + LPTSTR lpFilePart; // address of file name in path + DWORD dwLength; // file name length + + // get full path file name + GetFullPathName(ppArgv[1],ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart); + + // size of file name incl. EOS + dwLength = lstrlen(szBufferFilename) + 1; + sCDS.cbData = dwLength * sizeof(WCHAR); + + #if defined _UNICODE + { + sCDS.lpData = szBufferFilename; + } + #else + { + sCDS.lpData = _alloca(sCDS.cbData); + if (sCDS.lpData != NULL) + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szBufferFilename, dwLength, + (LPWSTR) sCDS.lpData, sCDS.cbData); + } + else + { + sCDS.cbData = 0; // size of file name + } + } + #endif + } + else + { + sCDS.lpData = NULL; // file name + sCDS.cbData = 0; // size of file name + } + + // fill the COPYDATA structure and send file name to other instance + sCDS.dwData = CDID_FILENAME; // function identifier + SendMessage(hWnd,WM_COPYDATA,(WPARAM) NULL,(LPARAM) &sCDS); + return 0; // quit program + } + + // Create window + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = 256; + rectWindow.bottom = 0; + AdjustWindowRect(&rectWindow, STYLE_TITLE, TRUE); + + hWnd = CreateWindow(MAKEINTATOM(classAtom),_T("Emu48"), + STYLE_TITLE, + CW_USEDEFAULT, CW_USEDEFAULT, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + NULL,NULL,hApp,NULL); + + if (hWnd == NULL) + { + AbortMessage(_T("Window creation failed.")); + return FALSE; + } + + VERIFY(hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_MENU))); + + // initialization + QueryPerformanceFrequency(&lFreq); // init high resolution counter + QueryPerformanceCounter(&lAppStart); + + szCurrentKml[0] = 0; // no KML file selected + SetSpeed(bRealSpeed); // set speed + MruInit(4); // init MRU entries + + // create auto event handle + hEventShutdn = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventShutdn == NULL) + { + AbortMessage(_T("Event creation failed.")); + DestroyWindow(hWnd); + return FALSE; + } + + nState = SM_RUN; // init state must be <> nNextState + nNextState = SM_INVALID; // go into invalid state + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&WorkerThread, NULL, CREATE_SUSPENDED, &dwThreadId); + if (hThread == NULL) + { + CloseHandle(hEventShutdn); // close event handle + AbortMessage(_T("Thread creation failed.")); + DestroyWindow(hWnd); + return FALSE; + } + + // SetThreadIdealProcessor() is available since Windows NT4.0 + fnSetThreadIdealProcessor = (LPFN_STIP) GetProcAddress(GetModuleHandle(_T("kernel32")), + "SetThreadIdealProcessor"); + + // bind Saturn CPU emulation thread to current ideal processor + dwProcessor = (fnSetThreadIdealProcessor != NULL) // running on NT4.0 or later + ? fnSetThreadIdealProcessor(hThread,MAXIMUM_PROCESSORS) // get ideal processor no. + : 0; // select 1st processor + + // on multiprocessor machines for QueryPerformanceCounter() + VERIFY(SetThreadAffinityMask(hThread,(DWORD_PTR) (1 << dwProcessor))); + ResumeThread(hThread); // start thread + while (nState!=nNextState) Sleep(0); // wait for thread initialized + + idDdeInst = 0; // initialize DDE server + if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback, + APPCLASS_STANDARD | + CBF_FAIL_EXECUTES | CBF_FAIL_ADVISES | + CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS,0)) + { + TerminateThread(hThread, 0); // kill emulation thread + CloseHandle(hEventShutdn); // close event handle + AbortMessage(_T("Could not initialize server!")); + DestroyWindow(hWnd); + return FALSE; + } + + // init clipboard format and name service + uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ)); + hszService = DdeCreateStringHandle(idDdeInst,szAppName,0); + hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0); + DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER); + + SoundOpen(uWaveDevId); // open waveform-audio output device + + _ASSERT(hWnd != NULL); + _ASSERT(hWindowDC != NULL); + + if (nArgc >= 2) // use decoded parameter line + lstrcpyn(szBufferFilename,ppArgv[1],ARRAYSIZEOF(szBufferFilename)); + else // use last document setting + ReadLastDocument(szBufferFilename, ARRAYSIZEOF(szBufferFilename)); + + if (szBufferFilename[0]) // given default document + { + TCHAR szTemp[MAX_PATH+8] = _T("Loading "); + RECT rectClient; + + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szBufferFilename,&szTemp[8],MAX_PATH,rectClient.right/11); + SetWindowTitle(szTemp); + if (OpenDocument(szBufferFilename)) + { + MruAdd(szCurrentFilename); + ShowWindow(hWnd,nCmdShow); + goto start; + } + } + + SetWindowTitle(_T("New Document")); + ShowWindow(hWnd,nCmdShow); // show emulator menu + + // no default document, ask for new one + if (NewDocument()) SetWindowTitle(_T("Untitled")); + +start: + if (bStartupBackup) SaveBackup(); // make a RAM backup at startup + if (pbyRom) SwitchToState(SM_RUN); + + while (GetMessage(&msg, NULL, 0, 0)) + { + if ( !TranslateAccelerator(hWnd, hAccel, &msg) + && (hDlgDebug == NULL || !IsDialogMessage(hDlgDebug, &msg)) + && (hDlgFind == NULL || !IsDialogMessage(hDlgFind, &msg)) + && (hDlgProfile == NULL || !IsDialogMessage(hDlgProfile, &msg)) + && (hDlgRplObjView == NULL || !IsDialogMessage(hDlgRplObjView, &msg))) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // clean up DDE server + DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER); + DdeFreeStringHandle(idDdeInst, hszService); + DdeFreeStringHandle(idDdeInst, hszTopic); + DdeUninitialize(idDdeInst); + + SoundClose(); // close waveform-audio output device + + // get full path name of szCurrentFilename + if (GetFullPathName(szCurrentFilename,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart) == 0) + szBufferFilename[0] = 0; // no last document name + + WriteLastDocument(szBufferFilename); // save last document setting + WriteSettings(); // save emulation settings + + CloseHandle(hThread); // close thread handle + CloseHandle(hEventShutdn); // close event handle + _ASSERT(nState == SM_RETURN); // emulation thread down? + ResetDocument(); + ResetBackup(); + MruCleanup(); + _ASSERT(pbyRom == NULL); // rom file unmapped + _ASSERT(pbyPort2 == NULL); // port2 file unmapped + _ASSERT(pKml == NULL); // KML script not closed + _ASSERT(szTitle == NULL); // freed allocated memory + _ASSERT(hPalette == NULL); // freed resource memory + + return (int) msg.wParam; + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(hPrevInst); +} diff --git a/app/src/main/cpp/EMU48.H b/app/src/main/cpp/EMU48.H new file mode 100644 index 0000000..46a6f51 --- /dev/null +++ b/app/src/main/cpp/EMU48.H @@ -0,0 +1,459 @@ +/* + * Emu48.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "types.h" + +#define HARDWARE "Yorke" // emulator hardware +#define MODELS "26AEGPQSX" // valid calculator models +#define APPLEHARD "2PQ" // Apple platform calculator models + +#define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) + +// cards status +#define PORT1_PRESENT ((cCurrentRomType=='S')?P1C:P2C) +#define PORT1_WRITE ((cCurrentRomType=='S')?P1W:P2W) +#define PORT2_PRESENT ((cCurrentRomType=='S')?P2C:P1C) +#define PORT2_WRITE ((cCurrentRomType=='S')?P2W:P1W) + +#define BINARYHEADER48 "HPHP48-W" +#define BINARYHEADER49 "HPHP49-W" + +#define BIN_FILTER "Port Data File (*.BIN)\0*.BIN\0All Files (*.*)\0*.*\0" +#define HP_FILTER "HP Binary Object (*.HP;*.LIB)\0*.HP;*.LIB\0All Files (*.*)\0*.*\0" + +#define CF_HPOBJ "CF_HPOBJ" // clipboard format for DDE + +// CPU cycles in 16384 Hz time frame +#define T2CYCLES ((cCurrentRomType=='S')?dwSXCycles:(cCurrentRomType=='G')?dwGXCycles:(cCurrentRomType=='P')?dwGPCycles:(cCurrentRomType=='Q')?dwGPCycles:dwG2Cycles) // CdB for HP: add apples + +#define SM_RUN 0 // states of cpu emulation thread +#define SM_INVALID 1 +#define SM_RETURN 2 +#define SM_SLEEP 3 + +#define S_ERR_NO 0 // stack errorcodes +#define S_ERR_OBJECT 1 +#define S_ERR_BINARY 2 +#define S_ERR_ASCII 3 + +#define BAD_OB (0xFFFFFFFF) // bad object + +#define NO_SERIAL "disabled" // port not open + +#define HP_MNEMONICS FALSE // disassembler mnenomics mode +#define CLASS_MNEMONICS TRUE + +#define MACRO_OFF 0 // macro recorder off +#define MACRO_NEW 1 +#define MACRO_PLAY 2 + +#define DISP_POINTER 0x01 // defines for display area +#define DISP_MAIN 0x02 +#define DISP_MENUE 0x04 +#define DISP_ANNUN 0x08 + +#define ROMPAGESIZE (1<<12) // ROM dirty page size in nibbles + +// window styles +#define STYLE_TITLE (WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED) +#define STYLE_NOTITLE (WS_POPUP|WS_SYSMENU|WS_MINIMIZEBOX|WS_CLIPSIBLINGS) + +// WM_COPYDATA identifier +#define CDID_FILENAME 1 // send file name + +// macro to check for valid calculator model +#define isModelValid(m) (m != 0 && strchr(MODELS,m) != NULL) +#define isModelApple(m) (m != 0 && strchr(APPLEHARD,m) != NULL) + +// values for mapping area +enum MMUMAP { M_IO, M_ROM, M_RAM, M_P1, M_P2, M_BS }; + +// values for disassembler memory mapping modes +enum MEM_MAPPING { MEM_MMU, MEM_NCE1, MEM_NCE2, MEM_CE1, MEM_CE2, MEM_NCE3 }; + +// Emu48.c +extern HPALETTE hPalette; +extern HPALETTE hOldPalette; +extern HANDLE hEventShutdn; +extern LPTSTR szAppName; +extern LPTSTR szTopic; +extern LPTSTR szTitle; +extern CRITICAL_SECTION csGDILock; +extern CRITICAL_SECTION csLcdLock; +extern CRITICAL_SECTION csKeyLock; +extern CRITICAL_SECTION csIOLock; +extern CRITICAL_SECTION csT1Lock; +extern CRITICAL_SECTION csT2Lock; +extern CRITICAL_SECTION csTxdLock; +extern CRITICAL_SECTION csRecvLock; +extern CRITICAL_SECTION csSlowLock; +extern CRITICAL_SECTION csDbgLock; +extern INT nArgc; +extern LPCTSTR *ppArgv; +extern LARGE_INTEGER lFreq; +extern LARGE_INTEGER lAppStart; +extern DWORD idDdeInst; +extern UINT uCF_HpObj; +extern HINSTANCE hApp; +extern HWND hWnd; +extern HWND hDlgDebug; +extern HWND hDlgFind; +extern HWND hDlgProfile; +extern HWND hDlgRplObjView; +extern HDC hWindowDC; +extern DWORD dwTColor; +extern DWORD dwTColorTol; +extern HRGN hRgn; +extern HCURSOR hCursorArrow; +extern HCURSOR hCursorHand; +extern UINT uWaveDevId; +extern DWORD dwWakeupDelay; +extern BOOL bAutoSave; +extern BOOL bAutoSaveOnExit; +extern BOOL bSaveDefConfirm; +extern BOOL bStartupBackup; +extern BOOL bAlwaysDisplayLog; +extern BOOL bLoadObjectWarning; +extern BOOL bShowTitle; +extern BOOL bShowMenu; +extern BOOL bAlwaysOnTop; +extern BOOL bActFollowsMouse; +extern BOOL bClientWinMove; +extern BOOL bSingleInstance; +extern HANDLE hThread; +extern VOID SetWindowTitle(LPCTSTR szString); +extern VOID ForceForegroundWindow(HWND hWnd); +extern VOID CopyItemsToClipboard(HWND hWnd); + +// mru.c +extern BOOL MruInit(UINT nNum); +extern VOID MruCleanup(VOID); +extern VOID MruAdd(LPCTSTR lpszEntry); +extern VOID MruRemove(UINT nIndex); +extern VOID MruMoveTop(UINT nIndex); +extern UINT MruEntries(VOID); +extern LPCTSTR MruFilename(UINT nIndex); +extern VOID MruUpdateMenu(HMENU hMenu); +extern VOID MruWriteList(VOID); +extern VOID MruReadList(VOID); + +// Settings.c +extern VOID ReadSettings(VOID); +extern VOID WriteSettings(VOID); +extern VOID ReadLastDocument(LPTSTR szFileName, DWORD nSize); +extern VOID WriteLastDocument(LPCTSTR szFilename); +extern VOID ReadSettingsString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpDefault, LPTSTR lpData, DWORD dwSize); +extern VOID WriteSettingsString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPTSTR lpData); +extern INT ReadSettingsInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nDefault); +extern VOID WriteSettingsInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nValue); +extern VOID DelSettingsKey(LPCTSTR lpszSection, LPCTSTR lpszEntry); + +// Display.c +extern BOOL bGrayscale; +extern UINT nBackgroundX; +extern UINT nBackgroundY; +extern UINT nBackgroundW; +extern UINT nBackgroundH; +extern UINT nLcdX; +extern UINT nLcdY; +extern UINT nLcdZoom; +extern UINT nGdiXZoom; +extern UINT nGdiYZoom; +extern HDC hLcdDC; +extern HDC hMainDC; +extern HDC hAnnunDC; +extern BYTE (*GetLineCounter)(VOID); +extern VOID (*StartDisplay)(BYTE byInitial); +extern VOID (*StopDisplay)(VOID); +extern VOID UpdateContrast(BYTE byContrast); +extern VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue); +extern VOID SetLcdMode(BOOL bMode); +extern VOID CreateLcdBitmap(VOID); +extern VOID DestroyLcdBitmap(VOID); +extern BOOL CreateMainBitmap(LPCTSTR szFilename); +extern VOID DestroyMainBitmap(VOID); +extern BOOL CreateAnnunBitmap(LPCTSTR szFilename); +extern VOID DestroyAnnunBitmap(VOID); +extern VOID UpdateDisplayPointers(VOID); +extern VOID UpdateMainDisplay(VOID); +extern VOID UpdateMenuDisplay(VOID); +extern VOID RefreshDisp0(); // CdB for HP: add apples display management +extern VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s); +extern VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s); +extern VOID UpdateAnnunciators(VOID); +extern VOID ResizeWindow(VOID); + +// Engine.c +extern BOOL bInterrupt; +extern UINT nState; +extern UINT nNextState; +extern BOOL bEnableSlow; +extern BOOL bRealSpeed; +extern BOOL bKeySlow; +extern BOOL bSoundSlow; +extern UINT nOpcSlow; +extern BOOL bCommInit; +extern CHIPSET Chipset; +extern TCHAR szSerialWire[16]; +extern TCHAR szSerialIr[16]; +extern DWORD dwSXCycles; +extern DWORD dwGXCycles; +extern DWORD dwGPCycles; // CdB for HP: add apples speed +extern DWORD dwG2Cycles; // CdB for HP: add apples speed +extern HANDLE hEventDebug; +extern BOOL bDbgAutoStateCtrl; +extern INT nDbgState; +extern BOOL bDbgNOP3; +extern BOOL bDbgCode; +extern BOOL bDbgRPL; +extern BOOL bDbgSkipInt; +extern DWORD dwDbgStopPC; +extern DWORD dwDbgRplPC; +extern DWORD dwDbgRstkp; +extern DWORD dwDbgRstk; +extern DWORD *pdwInstrArray; +extern WORD wInstrSize; +extern WORD wInstrWp; +extern WORD wInstrRp; +extern VOID SuspendDebugger(VOID); +extern VOID ResumeDebugger(VOID); +extern VOID CheckSerial(VOID); +extern VOID InitAdjustSpeed(VOID); +extern VOID AdjKeySpeed(VOID); +extern VOID SetSpeed(BOOL bAdjust); +extern VOID UpdateKdnBit(VOID); +extern BOOL WaitForSleepState(VOID); +extern UINT SwitchToState(UINT nNewState); +extern UINT WorkerThread(LPVOID pParam); + +// Fetch.c +extern VOID EvalOpcode(LPBYTE I); + +// Files.c +extern TCHAR szEmuDirectory[MAX_PATH]; +extern TCHAR szRomDirectory[MAX_PATH]; +extern TCHAR szCurrentDirectory[MAX_PATH]; +extern TCHAR szCurrentKml[MAX_PATH]; +extern TCHAR szBackupKml[MAX_PATH]; +extern TCHAR szCurrentFilename[MAX_PATH]; +extern TCHAR szBackupFilename[MAX_PATH]; +extern TCHAR szBufferFilename[MAX_PATH]; +extern TCHAR szPort2Filename[MAX_PATH]; +extern BOOL bDocumentAvail; +extern BYTE cCurrentRomType; +extern UINT nCurrentClass; +extern LPBYTE Port0; +extern LPBYTE Port1; +extern LPBYTE Port2; +extern LPBYTE pbyRom; +extern BOOL bRomWriteable; +extern DWORD dwRomSize; +extern LPBYTE pbyRomDirtyPage; +extern DWORD dwRomDirtyPageSize; +extern WORD wRomCrc; +extern LPBYTE pbyPort2; +extern BOOL bPort2Writeable; +extern BOOL bPort2IsShared; +extern DWORD dwPort2Size; +extern DWORD dwPort2Mask; +extern WORD wPort2Crc; +extern BOOL bBackup; +extern VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY); +extern DWORD GetCutPathName(LPCTSTR szFileName,LPTSTR szBuffer,DWORD dwBufferLength,INT nCutLength); +extern VOID SetWindowPathTitle(LPCTSTR szFileName); +extern VOID UpdatePatches(BOOL bPatch); +extern BOOL PatchRom(LPCTSTR szFilename); +extern BOOL CrcRom(WORD *pwChk); +extern BOOL MapRom(LPCTSTR szFilename); +extern VOID UnmapRom(VOID); +extern BOOL CrcPort2(WORD *pwCrc); +extern BOOL MapPort2(LPCTSTR szFilename); +extern VOID UnmapPort2(VOID); +extern VOID ResetDocument(VOID); +extern BOOL NewDocument(VOID); +extern BOOL OpenDocument(LPCTSTR szFilename); +extern BOOL SaveDocument(VOID); +extern BOOL SaveDocumentAs(LPCTSTR szFilename); +extern BOOL SaveBackup(VOID); +extern BOOL RestoreBackup(VOID); +extern BOOL ResetBackup(VOID); +extern BOOL GetOpenFilename(VOID); +extern BOOL GetSaveAsFilename(VOID); +extern BOOL GetLoadObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt); +extern BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt); +extern WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize); +extern BOOL LoadObject(LPCTSTR szFilename); +extern BOOL SaveObject(LPCTSTR szFilename); +extern BOOL LoadIconFromFile(LPCTSTR szFilename); +extern VOID LoadIconDefault(VOID); +extern HBITMAP LoadBitmapFile(LPCTSTR szFilename); +extern HRGN CreateRgnFromBitmap(HBITMAP hBmp,COLORREF color,DWORD dwTol); + +// Timer.c +extern VOID SetHP48Time(VOID); +extern VOID StartTimers(VOID); +extern VOID StopTimers(VOID); +extern DWORD ReadT2(VOID); +extern VOID SetT2(DWORD dwValue); +extern BYTE ReadT1(VOID); +extern VOID SetT1(BYTE byValue); + +// Mops.c +extern BOOL bFlashRomArray; +extern BYTE disp; +extern LPBYTE RMap[256]; +extern LPBYTE WMap[256]; +extern DWORD FlashROMAddr(DWORD d); +extern VOID Map(BYTE a, BYTE b); +extern VOID RomSwitch(DWORD adr); +extern VOID Config(VOID); +extern VOID Uncnfg(VOID); +extern VOID Reset(VOID); +extern VOID C_Eq_Id(VOID); +extern enum MMUMAP MapData(DWORD d); +extern VOID CpuReset(VOID); +extern VOID Npeek(BYTE *a, DWORD d, UINT s); +extern VOID Nread(BYTE *a, DWORD d, UINT s); +extern VOID Nwrite(BYTE *a, DWORD d, UINT s); +extern BYTE Read2(DWORD d); +extern DWORD Read5(DWORD d); +extern VOID Write5(DWORD d, DWORD n); +extern VOID Write2(DWORD d, BYTE n); +extern VOID IOBit(DWORD d, BYTE b, BOOL s); +extern VOID ReadIO(BYTE *a, DWORD b, DWORD s, BOOL bUpdate); +extern VOID WriteIO(BYTE *a, DWORD b, DWORD s); + +// Lowbat.c +extern BOOL bLowBatDisable; +extern VOID StartBatMeasure(VOID); +extern VOID StopBatMeasure(VOID); +extern VOID GetBatteryState(BOOL *pbLBI, BOOL *pbVLBI); + +// Keyboard.c +extern DWORD dwKeyMinDelay; +extern VOID ScanKeyboard(BOOL bActive, BOOL bReset); +extern VOID KeyboardEvent(BOOL bPress, UINT out, UINT in); + +// Keymacro.c +extern INT nMacroState; +extern INT nMacroTimeout; +extern BOOL bMacroRealSpeed; +extern DWORD dwMacroMinDelay; +extern VOID KeyMacroRecord(BOOL bPress, UINT out, UINT in); +extern LRESULT OnToolMacroNew(VOID); +extern LRESULT OnToolMacroPlay(VOID); +extern LRESULT OnToolMacroStop(VOID); +extern LRESULT OnToolMacroSettings(VOID); + +// Redeye.c +extern VOID IrPrinter(BYTE c); + +// Udp.c +extern TCHAR szUdpServer[1024]; +extern WORD wUdpPort; +extern VOID ResetUdp(VOID); +extern BOOL SendByteUdp(BYTE byData); + +// Stack.c +extern BOOL bDetectClpObject; +extern LRESULT OnStackCopy(VOID); +extern LRESULT OnStackPaste(VOID); + +// RPL.c +extern BOOL RPL_GetSystemFlag(INT nFlag); +extern DWORD RPL_SkipOb(DWORD d); +extern DWORD RPL_ObjectSize(BYTE *o,DWORD s); +extern DWORD RPL_CreateTemp(DWORD l,BOOL bGarbageCol); +extern UINT RPL_Depth(VOID); +extern DWORD RPL_Pick(UINT l); +extern VOID RPL_Replace(DWORD n); +extern VOID RPL_Push(UINT l,DWORD n); + +// External.c +extern VOID External(CHIPSET* w); +extern VOID RCKBp(CHIPSET* w); + +// SndEnum.c +extern VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID); + +// Sound.c +extern DWORD dwWaveVol; +extern DWORD dwWaveTime; +extern BOOL SoundAvailable(UINT uDeviceID); +extern BOOL SoundGetDeviceID(UINT *puDeviceID); +extern BOOL SoundOpen(UINT uDeviceID); +extern VOID SoundClose(VOID); +extern VOID SoundOut(CHIPSET* w, WORD wOut); +extern VOID SoundBeep(DWORD dwFrequency, DWORD dwDuration); + +// DDEserv.c +extern HDDEDATA CALLBACK DdeCallback(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); + +// Dismem.c +extern BOOL SetMemRomType(BYTE cCurrentRomType); +extern BOOL SetMemMapType(enum MEM_MAPPING eType); +extern enum MEM_MAPPING GetMemMapType(VOID); +extern BOOL GetMemAvail(enum MEM_MAPPING eType); +extern DWORD GetMemDataSize(VOID); +extern DWORD GetMemDataMask(VOID); +extern BYTE GetMemNib(DWORD *p); +extern VOID GetMemPeek(BYTE *a, DWORD d, UINT s); + +// Disasm.c +extern BOOL disassembler_mode; +extern BOOL disassembler_symb; +extern DWORD disassemble(DWORD addr, LPTSTR out); + +// Symbfile.c +extern BOOL RplTableEmpty(VOID); +extern BOOL RplLoadTable(LPCTSTR lpszFilename); +extern VOID RplDeleteTable(VOID); +extern LPCTSTR RplGetName(DWORD dwAddr); +extern BOOL RplGetAddr(LPCTSTR lpszName, DWORD *pdwAddr); + +// Serial.c +extern BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort); +extern VOID CommClose(VOID); +extern VOID CommSetBaud(VOID); +extern BOOL UpdateUSRQ(VOID); +extern VOID CommTxBRK(VOID); +extern VOID CommTransmit(VOID); +extern VOID CommReceive(VOID); + +// Cursor.c +extern HCURSOR CreateHandCursor(VOID); + +#if defined _USRDLL // DLL version +// Emu48dll.c +extern VOID (CALLBACK *pEmuDocumentNotify)(LPCTSTR lpszFilename); +extern BOOL DLLCreateWnd(LPCTSTR lpszFilename, LPCTSTR lpszPort2Name); +extern BOOL DLLDestroyWnd(VOID); + +// Symbfile.c +#define RplGetName(a) NULL // for linking +#endif + +// Message Boxes +static __inline int InfoMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND);} +static __inline int AbortMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);} +static __inline int YesNoMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_YESNO|MB_ICONEXCLAMATION|MB_SETFOREGROUND);} +static __inline int YesNoCancelMessage(LPCTSTR szMessage,UINT uStyle) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_YESNOCANCEL|MB_ICONEXCLAMATION|MB_SETFOREGROUND|uStyle);} + +// Missing Win32 API calls +static __inline LPTSTR DuplicateString(LPCTSTR szString) +{ + UINT uLength = lstrlen(szString) + 1; + LPTSTR szDup = (LPTSTR) malloc(uLength*sizeof(szDup[0])); + lstrcpy(szDup,szString); + return szDup; +} + +#define SCREENHEIGHT (cCurrentRomType=='Q' ? 80 : 64) // CdB for HP: add apples display management +#define SCREENHEIGHTREAL ((cCurrentRomType=='Q') ? (80-Chipset.d0size) : 64) +#define MAINSCREENHEIGHT (((Chipset.lcounter) == 0) ? SCREENHEIGHTREAL : SCREENHEIGHTREAL-64+((Chipset.lcounter)+1)) +#define MENUHEIGHT (Chipset.lcounter==0?0:64-(Chipset.lcounter+1)) diff --git a/app/src/main/cpp/EMU48DLL.C b/app/src/main/cpp/EMU48DLL.C new file mode 100644 index 0000000..981b9f7 --- /dev/null +++ b/app/src/main/cpp/EMU48DLL.C @@ -0,0 +1,638 @@ +/* + * Emu48Dll.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" +#include "debugger.h" + +#include "Emu48Dll.h" + +static LPCTSTR pArgv[3]; // command line memory +static ATOM classAtom = INVALID_ATOM; // window class atom +static HACCEL hAccel; // accelerator table + +static HSZ hszService, hszTopic; // variables for DDE server + +extern LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM); + +// callback function notify document filename +VOID (CALLBACK *pEmuDocumentNotify)(LPCTSTR lpszFilename) = NULL; + +// callback function notify Emu48 closed +static VOID (CALLBACK *pEmuClose)(VOID) = NULL; + +//################ +//# +//# Public internal functions +//# +//################ + +// +// DllMain +// +BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) +{ + BOOL bSucc = TRUE; + + if (fdwReason == DLL_PROCESS_ATTACH) + { + WNDCLASS wc; + + wc.style = CS_BYTEALIGNCLIENT; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinstDLL; + wc.hIcon = LoadIcon(hApp, MAKEINTRESOURCE(IDI_EMU48)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wc.lpszClassName = _T("CEmu48"); + VERIFY(bSucc = ((classAtom = RegisterClass(&wc)) != INVALID_ATOM)); + } + + if (fdwReason == DLL_PROCESS_DETACH) + { + if (INVALID_ATOM != classAtom) + { + VERIFY(UnregisterClass(MAKEINTATOM(classAtom),hinstDLL)); + classAtom = INVALID_ATOM; + } + } + + return bSucc; + UNREFERENCED_PARAMETER(lpvReserved); +} + +// +// DLLCreateWnd +// +BOOL DLLCreateWnd(LPCTSTR lpszFilename, LPCTSTR lpszPort2Name) +{ + typedef DWORD (WINAPI *LPFN_STIP)(HANDLE hThread,DWORD dwIdealProcessor); + + RECT rectWindow; + DWORD dwThreadId; + LPFN_STIP fnSetThreadIdealProcessor; + DWORD dwProcessor; + + BOOL bFileExist = FALSE; // state file don't exist + + hApp = GetModuleHandle(_T("EMU48.DLL")); + if (hApp == NULL) return TRUE; + + nArgc = 1; // no argument + if (lpszFilename[0]) + { + // try to open given filename + HANDLE hFile = CreateFile(lpszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + if (bFileExist = (hFile != INVALID_HANDLE_VALUE)) + CloseHandle(hFile); + + ppArgv = pArgv; // command line arguments + nArgc = 2; // one argument: state file, no port2 file + pArgv[1] = lpszFilename; // name of state file + + if (lpszPort2Name) // port2 filename + { + nArgc = 3; // two arguments: state file, port2 file + pArgv[2] = lpszPort2Name; // name of port2 file + } + } + + // read emulator settings + GetCurrentDirectory(ARRAYSIZEOF(szCurrentDirectory),szCurrentDirectory); + ReadSettings(); + + // Create window + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = 256; + rectWindow.bottom = 0; + AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); + + hWnd = CreateWindow(MAKEINTATOM(classAtom),_T("Emu48"), + WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + NULL,NULL,hApp,NULL + ); + + if (hWnd == NULL) + { + return TRUE; + } + + VERIFY(hAccel = LoadAccelerators(hApp,MAKEINTRESOURCE(IDR_MENU))); + + // remove debugger menu entry from resource + DeleteMenu(GetMenu(hWnd),ID_TOOL_DEBUG,MF_BYCOMMAND); + + // initialization + EmuClearAllBreakpoints(); + QueryPerformanceFrequency(&lFreq); // init high resolution counter + + szCurrentKml[0] = 0; // no KML file selected + SetSpeed(bRealSpeed); // set speed + MruInit(0); // init MRU entries + + // create shutdown auto event handle + hEventShutdn = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventShutdn == NULL) + { + DestroyWindow(hWnd); + return TRUE; + } + + // create debugger auto event handle + hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventDebug == NULL) + { + CloseHandle(hEventShutdn); // close shutdown event handle + DestroyWindow(hWnd); + return TRUE; + } + + nState = SM_RUN; // init state must be <> nNextState + nNextState = SM_INVALID; // go into invalid state + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&WorkerThread, NULL, CREATE_SUSPENDED, &dwThreadId); + if (hThread == NULL) + { + CloseHandle(hEventDebug); // close debugger event handle + CloseHandle(hEventShutdn); // close event handle + DestroyWindow(hWnd); + return TRUE; + } + + // SetThreadIdealProcessor() is available since Windows NT4.0 + fnSetThreadIdealProcessor = (LPFN_STIP) GetProcAddress(GetModuleHandle(_T("kernel32")), + "SetThreadIdealProcessor"); + + // bind Saturn CPU emulation thread to current ideal processor + dwProcessor = (fnSetThreadIdealProcessor != NULL) // running on NT4.0 or later + ? fnSetThreadIdealProcessor(hThread,MAXIMUM_PROCESSORS) // get ideal processor no. + : 0; // select 1st processor + + // on multiprocessor machines for QueryPerformanceCounter() + VERIFY(SetThreadAffinityMask(hThread,(DWORD_PTR) (1 << dwProcessor))); + ResumeThread(hThread); // start thread + while (nState!=nNextState) Sleep(0); // wait for thread initialized + + idDdeInst = 0; // initialize DDE server + if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback, + APPCLASS_STANDARD | + CBF_FAIL_EXECUTES | CBF_FAIL_ADVISES | + CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS,0)) + { + TerminateThread(hThread, 0); // kill emulation thread + CloseHandle(hEventDebug); // close debugger event handle + CloseHandle(hEventShutdn); // close event handle + DestroyWindow(hWnd); + return TRUE; + } + + // init clipboard format and name service + uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ)); + hszService = DdeCreateStringHandle(idDdeInst,szAppName,0); + hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0); + DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER); + + SoundOpen(uWaveDevId); // open waveform-audio output device + + _ASSERT(hWnd != NULL); + _ASSERT(hWindowDC != NULL); + + szBufferFilename[0] = 0; + if (bFileExist) // open existing file + { + lstrcpyn(szBufferFilename,ppArgv[1],ARRAYSIZEOF(szBufferFilename)); + } + if (nArgc == 1) // no argument + { + ReadLastDocument(szBufferFilename,ARRAYSIZEOF(szBufferFilename)); + } + + if (szBufferFilename[0]) // given default document + { + TCHAR szTemp[MAX_PATH+8] = _T("Loading "); + RECT rectClient; + + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szBufferFilename,&szTemp[8],MAX_PATH,rectClient.right/11); + SetWindowTitle(szTemp); + if (OpenDocument(szBufferFilename)) + { + MruAdd(szCurrentFilename); + ShowWindow(hWnd,SW_SHOWNORMAL); + goto start; + } + } + + SetWindowTitle(_T("New Document")); + ShowWindow(hWnd,SW_SHOWNORMAL); + + if (NewDocument()) + { + if (nArgc >= 2) + SaveDocumentAs(ppArgv[1]); + else + SetWindowTitle(_T("Untitled")); + goto start; + } + + DestroyWindow(hWnd); // clean up system + return TRUE; + +start: + if (bStartupBackup) SaveBackup(); // make a RAM backup at startup + if (pbyRom) SwitchToState(SM_RUN); + return FALSE; +} + +// +// DLLDestroyWnd +// +BOOL DLLDestroyWnd(VOID) +{ + LPTSTR lpFilePart; + + // clean up DDE server + DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER); + DdeFreeStringHandle(idDdeInst, hszService); + DdeFreeStringHandle(idDdeInst, hszTopic); + DdeUninitialize(idDdeInst); + + SoundClose(); // close waveform-audio output device + + // get full path name of szCurrentFilename + if (GetFullPathName(szCurrentFilename,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart) == 0) + szBufferFilename[0] = 0; // no last document name + + WriteLastDocument(szBufferFilename); // save last document setting + WriteSettings(); // save variable settings + + CloseHandle(hThread); // close emulation thread handle + CloseHandle(hEventShutdn); // close shutdown event handle + CloseHandle(hEventDebug); // close debugger event handle + _ASSERT(nState == SM_RETURN); // emulation thread down? + ResetDocument(); + ResetBackup(); + MruCleanup(); + _ASSERT(pbyRom == NULL); // rom file unmapped + _ASSERT(pbyPort2 == NULL); // port2 file unmapped + _ASSERT(pKml == NULL); // KML script not closed + _ASSERT(szTitle == NULL); // freed allocated memory + _ASSERT(hPalette == NULL); // freed resource memory + if (pEmuClose) pEmuClose(); // call notify function + return FALSE; +} + + +//################ +//# +//# Public external functions +//# +//################ + +/**************************************************************************** +* EmuCreate +***************************************************************************** +* +* @func start Emu48 and load Ram file into emulator, if Ram file don't +* exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuCreate( + LPCTSTR lpszFilename) // @parm String with RAM filename +{ + return DLLCreateWnd(lpszFilename, NULL); +} + +/**************************************************************************** +* EmuCreateEx +***************************************************************************** +* +* @func start Emu48 and load Ram and Port2 file into emulator, if Ram file +* don't exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuCreateEx( + LPCTSTR lpszFilename, // @parm String with RAM filename + LPCTSTR lpszPort2Name) // @parm String with Port2 filename + // or NULL for using name inside INI file +{ + return DLLCreateWnd(lpszFilename, lpszPort2Name); +} + +/**************************************************************************** +* EmuDestroy +***************************************************************************** +* +* @func close Emu48, free all memory +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuDestroy(VOID) +{ + if (hWnd == NULL) return TRUE; // Emu48 closed + + // close Emu48 via exit + SendMessage(hWnd,WM_SYSCOMMAND,SC_CLOSE,0); + return FALSE; +} + +/**************************************************************************** +* EmuAcceleratorTable +***************************************************************************** +* +* @func load accelerator table of emulator +* +* @xref none +* +* @rdesc HACCEL: handle of the loaded accelerator table +* +****************************************************************************/ + +DECLSPEC HACCEL CALLBACK EmuAcceleratorTable( + HWND *phEmuWnd) // @parm return of emulator window handle +{ + *phEmuWnd = hWnd; + return hAccel; +} + +/**************************************************************************** +* EmuCallBackClose +***************************************************************************** +* +* @func init CallBack handler to notify caller when Emu48 window close +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackClose( + VOID (CALLBACK *EmuClose)(VOID)) // @parm CallBack function notify caller Emu48 closed +{ + pEmuClose = EmuClose; // set new handler + return; +} + +/**************************************************************************** +* EmuCallBackDocumentNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller for actual document file +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackDocumentNotify( + VOID (CALLBACK *EmuDocumentNotify)(LPCTSTR lpszFilename)) // @parm CallBack function notify document filename +{ + pEmuDocumentNotify = EmuDocumentNotify; // set new handler + return; +} + +/**************************************************************************** +* EmuLoadRamFile +***************************************************************************** +* +* @func load Ram file into emulator +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuLoadRamFile( + LPCTSTR lpszFilename) // @parm String with RAM filename +{ + BOOL bErr; + + if (pbyRom) SwitchToState(SM_INVALID); // stop emulation thread + bErr = !OpenDocument(lpszFilename); // load state file + if (pbyRom) SwitchToState(SM_RUN); // restart emulation thread + return bErr; +} + +/**************************************************************************** +* EmuSaveRamFile +***************************************************************************** +* +* @func save the current emulator Ram to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSaveRamFile(VOID) +{ + BOOL bErr; + + if (pbyRom == NULL) return TRUE; // fail + SwitchToState(SM_INVALID); // stop emulation thread + bErr = !SaveDocument(); // save current state file + SwitchToState(SM_RUN); // restart emulation thread + return bErr; +} + +/**************************************************************************** +* EmuLoadObject +***************************************************************************** +* +* @func load object file to stack +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuLoadObject( + LPCTSTR lpszObjectFilename) // @parm String with object filename +{ + HANDLE hFile; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPBYTE lpBuf; + WORD wError = S_ERR_BINARY; // set into error state + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + if (!(Chipset.IORam[BITOFFSET]&DON)) // calculator off, turn on + { + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + Sleep(dwWakeupDelay); + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + } + + if (nState != SM_RUN) goto cancel; // emulator must run to load on object + if (WaitForSleepState()) goto cancel; // wait for cpu SHUTDN then sleep state + + _ASSERT(nState == SM_SLEEP); + + hFile = CreateFile(lpszObjectFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + SwitchToState(SM_RUN); // run state + goto cancel; + } + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { // file is too large. + SwitchToState(SM_RUN); // run state + CloseHandle(hFile); + goto cancel; + } + lpBuf = malloc(dwFileSizeLow*2); + if (lpBuf == NULL) + { + SwitchToState(SM_RUN); // run state + CloseHandle(hFile); + goto cancel; + } + ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL); + CloseHandle(hFile); + + wError = WriteStack(1,lpBuf,dwFileSizeLow); + + free(lpBuf); + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return wError != S_ERR_NO; +} + +/**************************************************************************** +* EmuSaveObject +***************************************************************************** +* +* @func save object on stack to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSaveObject( + LPCTSTR lpszObjectFilename) // @parm String with object filename +{ + BOOL bErr; + + if (nState != SM_RUN) return TRUE; // emulator must run to load on object + if (WaitForSleepState()) return TRUE; // wait for cpu SHUTDN then sleep state + _ASSERT(nState == SM_SLEEP); + bErr = !SaveObject(lpszObjectFilename); + SwitchToState(SM_RUN); + return bErr; +} + +/**************************************************************************** +* EmuCalculatorType +***************************************************************************** +* +* @func get ID of current calculator type +* +* @xref none +* +* @rdesc BYTE: '6' = HP38G with 64KB RAM +* 'A' = HP38G +* 'E' = HP39/40G +* 'S' = HP48SX +* 'G' = HP48GX +* 'X' = HP49G +* 'P' = HP39G+ +* '2' = HP48GII +* 'Q' = HP49G+ +* +****************************************************************************/ + +DECLSPEC BYTE CALLBACK EmuCalculatorType(VOID) +{ + return Chipset.type; +} + +/**************************************************************************** +* EmuSimulateKey +***************************************************************************** +* +* @func simulate a key action +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSimulateKey( + BOOL bKeyState, // @parm TRUE = pressed, FALSE = released + UINT out, // @parm key out line + UINT in) // @parm key in line +{ + KeyboardEvent(bKeyState,out,in); +} + +/**************************************************************************** +* EmuPressOn +***************************************************************************** +* +* @func press On key (emulation must run) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuPressOn( + BOOL bKeyState) // @parm TRUE = pressed, FALSE = released +{ + KeyboardEvent(bKeyState,0,0x8000); +} \ No newline at end of file diff --git a/app/src/main/cpp/EMU48DLL.H b/app/src/main/cpp/EMU48DLL.H new file mode 100644 index 0000000..6baa01a --- /dev/null +++ b/app/src/main/cpp/EMU48DLL.H @@ -0,0 +1,642 @@ +/* + * Emu48Dll.h + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#define DECLSPEC __declspec(dllexport) + +////////////////////////////////// +// +// breakpoint type definitions +// +////////////////////////////////// + +#define BP_EXEC 0x01 // code breakpoint +#define BP_READ 0x02 // read memory breakpoint +#define BP_WRITE 0x04 // write memory breakpoint +#define BP_RPL 0x08 // RPL breakpoint +#define BP_ACCESS (BP_READ|BP_WRITE) // read/write memory breakpoint + +#define BP_ROM 0x8000 // absolute ROM adress breakpoint + +////////////////////////////////// +// +// REGISTER ACCESS API +// +////////////////////////////////// + +#define EMU_REGISTER_PC 0 +#define EMU_REGISTER_D0 1 +#define EMU_REGISTER_D1 2 +#define EMU_REGISTER_DUMMY 3 +#define EMU_REGISTER_AL 4 +#define EMU_REGISTER_AH 5 +#define EMU_REGISTER_BL 6 +#define EMU_REGISTER_BH 7 +#define EMU_REGISTER_CL 8 +#define EMU_REGISTER_CH 9 +#define EMU_REGISTER_DL 10 +#define EMU_REGISTER_DH 11 +#define EMU_REGISTER_R0L 12 +#define EMU_REGISTER_R0H 13 +#define EMU_REGISTER_R1L 14 +#define EMU_REGISTER_R1H 15 +#define EMU_REGISTER_R2L 16 +#define EMU_REGISTER_R2H 17 +#define EMU_REGISTER_R3L 18 +#define EMU_REGISTER_R3H 19 +#define EMU_REGISTER_R4L 20 +#define EMU_REGISTER_R4H 21 +#define EMU_REGISTER_R5L 22 +#define EMU_REGISTER_R5H 23 +#define EMU_REGISTER_R6L 24 +#define EMU_REGISTER_R6H 25 +#define EMU_REGISTER_R7L 26 +#define EMU_REGISTER_R7H 27 +#define EMU_REGISTER_FLAGS 28 +#define EMU_REGISTER_OUT 29 +#define EMU_REGISTER_IN 30 +#define EMU_REGISTER_VIEW1 31 +#define EMU_REGISTER_VIEW2 32 +#define EMU_REGISTER_RSTKP 63 +#define EMU_REGISTER_RSTK0 64 +#define EMU_REGISTER_RSTK1 65 +#define EMU_REGISTER_RSTK2 66 +#define EMU_REGISTER_RSTK3 67 +#define EMU_REGISTER_RSTK4 68 +#define EMU_REGISTER_RSTK5 69 +#define EMU_REGISTER_RSTK6 70 +#define EMU_REGISTER_RSTK7 71 +#define EMU_REGISTER_CLKL 72 +#define EMU_REGISTER_CLKH 73 +#define EMU_REGISTER_CRC 74 + +/** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + +/**************************************************************************** +* EmuCreate +***************************************************************************** +* +* @func start Emu48 and load Ram file into emulator, if Ram file don't +* exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuCreate( + LPCTSTR lpszFilename); // @parm String with RAM filename + +/**************************************************************************** +* EmuCreateEx +***************************************************************************** +* +* @func start Emu48 and load Ram and Port2 file into emulator, if Ram file +* don't exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuCreateEx( + LPCTSTR lpszFilename, // @parm String with RAM filename + LPCTSTR lpszPort2Name); // @parm String with Port2 filename + // or NULL for using name inside INI file + +/**************************************************************************** +* EmuDestroy +***************************************************************************** +* +* @func close Emu48, free all memory +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuDestroy(VOID); + +/**************************************************************************** +* EmuAcceleratorTable +***************************************************************************** +* +* @func load accelerator table of emulator +* +* @xref none +* +* @rdesc HACCEL: handle of the loaded accelerator table +* +****************************************************************************/ + +EXTERN_C DECLSPEC HACCEL CALLBACK EmuAcceleratorTable( + HWND *phEmuWnd); // @parm return of emulator window handle + +/**************************************************************************** +* EmuCallBackClose +***************************************************************************** +* +* @func init CallBack handler to notify caller when Emu48 window close +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackClose( + VOID (CALLBACK *EmuClose)(VOID)); // @parm CallBack function notify caller Emu48 closed + +/**************************************************************************** +* EmuCallBackDocumentNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller for actual document file +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackDocumentNotify( + VOID (CALLBACK *EmuDocumentNotify)(LPCTSTR lpszFilename)); // @parm CallBack function notify document filename + +/**************************************************************************** +* EmuLoadRamFile +***************************************************************************** +* +* @func load Ram file into emulator +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuLoadRamFile( + LPCTSTR lpszFilename); // @parm String with RAM filename + +/**************************************************************************** +* EmuSaveRamFile +***************************************************************************** +* +* @func save the current emulator Ram to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSaveRamFile(VOID); + +/**************************************************************************** +* EmuLoadObject +***************************************************************************** +* +* @func load object file to stack +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuLoadObject( + LPCTSTR lpszObjectFilename); // @parm String with object filename + +/**************************************************************************** +* EmuSaveObject +***************************************************************************** +* +* @func save object on stack to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSaveObject( + LPCTSTR lpszObjectFilename); // @parm String with object filename + +/**************************************************************************** +* EmuCalculatorType +***************************************************************************** +* +* @func get ID of current calculator type +* +* @xref none +* +* @rdesc BYTE: '6' = HP38G with 64KB RAM +* 'A' = HP38G +* 'E' = HP39/40G +* 'S' = HP48SX +* 'G' = HP48GX +* 'X' = HP49G +* 'P' = HP39G+ +* '2' = HP48GII +* 'Q' = HP49G+ +* +****************************************************************************/ + +EXTERN_C DECLSPEC BYTE CALLBACK EmuCalculatorType(VOID); + +/**************************************************************************** +* EmuSimulateKey +***************************************************************************** +* +* @func simulate a key action +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSimulateKey( + BOOL bKeyState, // @parm TRUE = pressed, FALSE = released + UINT out, // @parm key out line + UINT in); // @parm key in line + +/**************************************************************************** +* EmuPressOn +***************************************************************************** +* +* @func press On key (emulation must run) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuPressOn( + BOOL bKeyState); // @parm TRUE = pressed, FALSE = released + +/**************************************************************************** +* EmuInitLastInstr +***************************************************************************** +* +* @func init a circular buffer area for saving the last instruction +* addresses +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuInitLastInstr( + WORD wNoInstr, // @parm number of saved instructions, + // 0 = frees the memory buffer + DWORD *pdwArray); // @parm pointer to linear array + +/**************************************************************************** +* EmuGetLastInstr +***************************************************************************** +* +* @func return number of valid entries in the last instruction array, +* each entry contents a PC address, array[0] contents the oldest, +* array[*pwNoEntries-1] the last PC address +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuGetLastInstr( + WORD *pwNoEntries); // @parm return number of valid entries in array + +/**************************************************************************** +* EmuRun +***************************************************************************** +* +* @func run emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, Emu48 is running +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuRun(VOID); + +/**************************************************************************** +* EmuRunPC +***************************************************************************** +* +* @func run emulation until stop address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuRunPC( + DWORD dwAddressPC); // @parm breakpoint address + +/**************************************************************************** +* EmuStep +***************************************************************************** +* +* @func execute one ASM instruction and return to caller +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStep(VOID); + +/**************************************************************************** +* EmuStepOver +***************************************************************************** +* +* @func execute one ASM instruction but skip GOSUB, GOSUBL, GOSBVL +* subroutines +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStepOver(VOID); + +/**************************************************************************** +* EmuStepOut +***************************************************************************** +* +* @func run emulation until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, +* RTNYES instruction is found above the current stack level +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStepOut(VOID); + +/**************************************************************************** +* EmuStop +***************************************************************************** +* +* @func break emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, no debug notify handler +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuStop(VOID); + +/**************************************************************************** +* EmuCallBackDebugNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on debugger breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackDebugNotify( + VOID (CALLBACK *EmuDbgNotify)(INT nBreaktype)); // @parm CallBack function notify Debug breakpoint + +/**************************************************************************** +* EmuCallBackStackNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on hardware stack change; +* if the CallBack function return TRUE, emulation stops behind the +* opcode changed hardware stack content, otherwise, if the CallBack +* function return FALSE, no breakpoint is set +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackStackNotify( + BOOL (CALLBACK *EmuStackNotify)(VOID)); // @parm CallBack function notify stack changed + +/**************************************************************************** +* EmuGetRegister +***************************************************************************** +* +* @func read a 32 bit register +* +* @xref none +* +* @rdesc DWORD: 32 bit value of register +* +****************************************************************************/ + +EXTERN_C DECLSPEC DWORD CALLBACK EmuGetRegister( + UINT uRegister); // @parm index of register + +/**************************************************************************** +* EmuSetRegister +***************************************************************************** +* +* @func write a 32 bit register +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSetRegister( + UINT uRegister, // @parm index of register + DWORD dwValue); // @parm new 32 bit value + +/**************************************************************************** +* EmuGetMem +***************************************************************************** +* +* @func read one nibble from the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuGetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE *pbyValue); // @parm readed nibble + +/**************************************************************************** +* EmuSetMem +***************************************************************************** +* +* @func write one nibble to the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE byValue); // @parm nibble to write + +/**************************************************************************** +* EmuGetRom +***************************************************************************** +* +* @func return size and base address of mapped ROM +* +* @xref none +* +* @rdesc LPBYTE: base address of ROM (pointer to original data) +* +****************************************************************************/ + +EXTERN_C DECLSPEC LPBYTE CALLBACK EmuGetRom( + DWORD *pdwRomSize); // @parm return size of ROM in nibbles + +/**************************************************************************** +* EmuSetBreakpoint +***************************************************************************** +* +* @func set ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint table full +* FALSE = OK, Breakpoint set +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSetBreakpoint( + DWORD dwAddress, // @parm breakpoint address to set + UINT nBreakpointType); // @parm breakpoint type to set + +/**************************************************************************** +* EmuClearBreakpoint +***************************************************************************** +* +* @func clear ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint not found +* FALSE = OK, Breakpoint cleared +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuClearBreakpoint( + DWORD dwAddress, // @parm breakpoint address to clear + UINT nBreakpointType); // @parm breakpoint type to clear + +/**************************************************************************** +* EmuClearAllBreakpoints +***************************************************************************** +* +* @func clear all breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuClearAllBreakpoints(VOID); + +/**************************************************************************** +* EmuEnableNop3Breakpoint +***************************************************************************** +* +* @func enable/disable NOP3 breakpoint +* stop emulation at Opcode 420 for GOC + (next line) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableNop3Breakpoint( + BOOL bEnable); // @parm stop on NOP3 opcode + +/**************************************************************************** +* EmuEnableDocodeBreakpoint +***************************************************************************** +* +* @func enable/disable DOCODE breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableDoCodeBreakpoint( + BOOL bEnable); // @parm stop on DOCODE entry + +/**************************************************************************** +* EmuEnableRplBreakpoint +***************************************************************************** +* +* @func enable/disable RPL breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableRplBreakpoint( + BOOL bEnable); // @parm stop on RPL exit + +/**************************************************************************** +* EmuEnableSkipInterruptCode +***************************************************************************** +* +* @func enable/disable skip single step execution inside the interrupt +* handler, this option has no effect on code and data breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableSkipInterruptCode( + BOOL bEnable); // @parm TRUE = skip code instructions + // inside interrupt service routine diff --git a/app/src/main/cpp/ENGINE.C b/app/src/main/cpp/ENGINE.C new file mode 100644 index 0000000..6123270 --- /dev/null +++ b/app/src/main/cpp/ENGINE.C @@ -0,0 +1,645 @@ +/* + * engine.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "io.h" +#include "debugger.h" + +#define SAMPLE 16384 // speed adjust sample frequency + +BOOL bInterrupt = FALSE; +UINT nState = SM_INVALID; +UINT nNextState = SM_RUN; +BOOL bEnableSlow = TRUE; // slow down is enabled +BOOL bRealSpeed = FALSE; +BOOL bKeySlow = FALSE; // slow down for key emulation +BOOL bSoundSlow = FALSE; // slow down for sound emulation +UINT nOpcSlow = 0; // no. of opcodes to slow down +BOOL bCommInit = FALSE; // COM port not open + +CHIPSET Chipset; + +TCHAR szSerialWire[16]; // devicename for wire port +TCHAR szSerialIr[16]; // devicename for IR port + +DWORD dwSXCycles = 82; // SX cpu cycles in interval +DWORD dwGXCycles = 123; // GX cpu cycles in interval +DWORD dwGPCycles = 123*3; // g+ cpu cycles in interval // CdB for HP: add apples display management +DWORD dwG2Cycles = 123*2; // gII cpu cycles in interval // CdB for HP: add apples display management + +// variables for debugger engine +HANDLE hEventDebug; // event handle to stop cpu thread + +BOOL bDbgAutoStateCtrl = TRUE; // debugger suspend control by SwitchToState() +INT nDbgState = DBG_OFF; // state of debugger + +BOOL bDbgNOP3 = FALSE; // halt on NOP3 (#420 opcode) +BOOL bDbgRPL = FALSE; // halt on RPL entry +BOOL bDbgCode = FALSE; // halt on DOCODE entry + +BOOL bDbgSkipInt = FALSE; // execute interrupt handler + +DWORD dwDbgStopPC = -1; // stop address for goto cursor +DWORD dwDbgRplPC = -1; // stop address for RPL breakpoint + +DWORD dwDbgRstkp; // stack recursion level of step over end +DWORD dwDbgRstk; // possible return address + +DWORD *pdwInstrArray = NULL; // last instruction array +WORD wInstrSize = 256; // size of last instruction array +WORD wInstrWp; // write pointer of instruction array +WORD wInstrRp; // read pointer of instruction array + +static INT nDbgRplBreak = BN_ASM; // flag for RPL breakpoint detection +static INT nDbgOldState = DBG_OFF; // old state of debugger for suspend/resume + +static BOOL bCpuSlow = FALSE; // enable/disable real speed + +static DWORD dwEDbgT2 = 0; // debugger timer2 emulation +static DWORD dwEDbgCycles = 0; // debugger cycle counter + +static DWORD dwOldCyc; // cpu cycles at last event +static DWORD dwSpeedRef; // timer value at last event +static DWORD dwTickRef; // sample timer ticks + +#include "Ops.h" + +// save last instruction in circular instruction buffer +static __inline VOID SaveInstrAddr(DWORD dwAddr) +{ + EnterCriticalSection(&csDbgLock); + { + if (pdwInstrArray) // circular buffer allocated + { + pdwInstrArray[wInstrWp] = dwAddr; + wInstrWp = (wInstrWp + 1) % wInstrSize; + if (wInstrWp == wInstrRp) + wInstrRp = (wInstrRp + 1) % wInstrSize; + } + } + LeaveCriticalSection(&csDbgLock); + return; +} + +static __inline VOID Debugger(VOID) // debugger part +{ + LARGE_INTEGER lDummyInt; // sample timer ticks + BOOL bStopEmulation; + LPBYTE I = FASTPTR(Chipset.pc); // get opcode stream + + UpdateDbgCycleCounter(); // update 64 bit cpu cycle counter + + SaveInstrAddr(Chipset.pc); // save pc in last instruction buffer + + nDbgRplBreak = BN_ASM; // notify ASM breakpoint + + // check for code breakpoints + bStopEmulation = CheckBreakpoint(Chipset.pc, 1, BP_EXEC); + + // check for memory breakpoints, opcode #14x or #15x + if (I[0] == 0x1 && (I[1] == 0x4 || I[1] == 0x5)) + { + DWORD dwData = (I[2] & 0x1) ? Chipset.d1 : Chipset.d0; + UINT nType = (I[2] & 0x2) ? BP_READ : BP_WRITE; + + DWORD dwRange; + if (I[1] == 0x4) // B,A + { + dwRange = (I[2] & 0x8) ? 2 : 5; + } + else // number of nibbles, (P,WP,XS,X,S,M,W) + { + dwRange = (I[2] & 0x8) ? (I[3]+1) : (F_l[I[3]]); + } + #if defined DEBUG_DEBUGGER + { + TCHAR buffer[256]; + wsprintf(buffer,_T("Memory breakpoint %.5lx, %u\n",dwData,dwRange)); + OutputDebugString(buffer); + } + #endif + bStopEmulation |= CheckBreakpoint(dwData, dwRange, nType); + } + + // check for step cursor + bStopEmulation |= (dwDbgStopPC == Chipset.pc); + + // NOP3, opcode #420 (GOC) + if (bDbgNOP3 && I[0] == 0x4 && I[1] == 0x2 && I[2] == 0x0) + bStopEmulation = TRUE; + + // stop on first instruction of DOCODE object + if (bDbgCode && (Chipset.pc == 0x02DDE || Chipset.pc == 0x02E3C)) + { + // return address + DWORD dwAddr = Chipset.rstk[(Chipset.rstkp-1)&7]; + + _ASSERT(I[0] == 0 && I[1] == 1); // stopped at RTN opcode + + if (MapData(dwAddr) != M_ROM) // address not in ROM + dwDbgStopPC = dwAddr; // then stop + } + + // check for RPL breakpoint + if (dwDbgRplPC == Chipset.pc) + { + dwDbgRplPC = -1; + nDbgRplBreak = bStopEmulation ? BN_ASM_BT : BN_RPL; + bStopEmulation = TRUE; + } + + // RPL breakpoints, PC=(A), opcode #808C or PC=(C), opcode #808E + if (I[0] == 0x8 && I[1] == 0x0 && I[2] == 0x8 && (I[3] == 0xC || I[3] == 0xE )) + { + // get next RPL entry + DWORD dwAddr = Npack((I[3] == 0xC) ? Chipset.A : Chipset.C,5); + + if (bDbgRPL || CheckBreakpoint(dwAddr, 1, BP_RPL)) + { + BYTE byRplPtr[5]; + + Npeek(byRplPtr,dwAddr,5); // get PC address of next opcode + dwDbgRplPC = Npack(byRplPtr,5); // set RPL breakpoint + } + } + + // step over interrupt execution + if (bDbgSkipInt && !bStopEmulation && !Chipset.inte) + return; + + // check for step into + bStopEmulation |= (nDbgState == DBG_STEPINTO); + + // check for step over + bStopEmulation |= (nDbgState == DBG_STEPOVER) && dwDbgRstkp == Chipset.rstkp; + + // check for step out, something was popped from hardware stack + if (nDbgState == DBG_STEPOUT && dwDbgRstkp == Chipset.rstkp) + { + _ASSERT(bStopEmulation == FALSE); + if ((bStopEmulation = (Chipset.pc == dwDbgRstk)) == FALSE) + { + // it was C=RSTK, check for next object popped from hardware stack + dwDbgRstkp = (Chipset.rstkp-1)&7; + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + } + } + + if (bStopEmulation) // stop condition + { + StopTimers(); // hold timer values when emulator is stopped + if (Chipset.IORam[TIMER2_CTRL]&RUN) // check if timer running + { + if (dwEDbgT2 == Chipset.t2) + { + // cpu cycles for one timer2 tick elapsed + if ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwEDbgCycles + >= (SAMPLE / 8192) * (DWORD) T2CYCLES) + { + --Chipset.t2; + // adjust cycles reference + dwEDbgCycles += (SAMPLE / 8192) * T2CYCLES; + } + } + else // new timer2 value + { + // new cycle reference + dwEDbgCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + } + + // check rising edge of Bit 8 of timer2 + if ((dwEDbgT2 & 0x100) == 0 && (Chipset.t2 & 0x100) != 0) + Chipset.t1 = (Chipset.t1 - 1) & 0xF; + } + dwEDbgT2 = Chipset.t2; // timer2 check reference value + + // redraw debugger window and stop + NotifyDebugger(nDbgRplBreak); + WaitForSingleObject(hEventDebug,INFINITE); + + StartTimers(); // continue timers + + if (nDbgState >= DBG_OFF) // if debugger is active + { + Chipset.Shutdn = FALSE; + Chipset.bShutdnWake = FALSE; + } + + // init slow down part + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + } + return; +} + +VOID SuspendDebugger(VOID) +{ + // auto control enabled, emulation halted by debugger + if (bDbgAutoStateCtrl && nDbgState > DBG_OFF) + { + nDbgOldState = nDbgState; // save old state + nDbgState = DBG_SUSPEND; // suspend state + SetEvent(hEventDebug); // exit debugger + } + return; +} + +VOID ResumeDebugger(VOID) +{ + // auto control enabled, debugger is suspended + if (bDbgAutoStateCtrl && nDbgState == DBG_SUSPEND) + { + // active RPL breakpoint + if (nDbgRplBreak) dwDbgRplPC = Chipset.pc; + nDbgState = nDbgOldState; // set to old debugger state + if (Chipset.Shutdn) // inside shutdown + SetEvent(hEventShutdn); // leave it to call debugger + } + return; +} + +static __inline VOID CheckDisp(BOOL bSync) +{ + if (disp == 0) return; // no display update need + + // update display when drawing top line or display is off + if (bSync && GetLineCounter() != 0x3F && (Chipset.IORam[0x00]&8)) + return; + + _ASSERT((disp & DISP_POINTER) == 0); // display pointer already updated + if (disp & DISP_MAIN) UpdateMainDisplay(); + if (disp & DISP_MENUE) UpdateMenuDisplay(); + _ASSERT((disp & DISP_ANNUN) == 0); // annunciators already updated + disp = 0; // display updated + return; +} + +static __inline VOID AdjustSpeed(VOID) // adjust emulation speed +{ + // emulation slow down + if ( bEnableSlow + && (bCpuSlow || bKeySlow || bSoundSlow || nOpcSlow > 0)) + { + DWORD dwCycles,dwTicks; + + EnterCriticalSection(&csSlowLock); + { + // cycles elapsed for next check + if ((dwCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF)-dwOldCyc) >= (DWORD) T2CYCLES) + { + LARGE_INTEGER lAct; + do + { + VERIFY(QueryPerformanceCounter(&lAct)); + + // get time difference + dwTicks = lAct.LowPart - dwSpeedRef; + } + // ticks elapsed or negative number (workaround for QueryPerformanceCounter() in Win2k) + while (dwTicks <= dwTickRef || (dwTicks & 0x80000000) != 0); + + dwOldCyc += T2CYCLES; // adjust cycles reference + dwSpeedRef += dwTickRef; // adjust reference time + } + + if (nOpcSlow > 0) --nOpcSlow; // decr. slow down opcode counter + } + LeaveCriticalSection(&csSlowLock); + } + return; +} + +VOID CheckSerial(VOID) +{ + // COM port closed and serial on + if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0) + { + bCommInit = CommOpen(szSerialWire,szSerialIr); // open COM ports + } + + // COM port opened and serial off + if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0) + { + CommClose(); // close COM port + bCommInit = FALSE; + } + return; +} + +VOID InitAdjustSpeed(VOID) +{ + // slow down function not initalized + if (!bEnableSlow || (!bCpuSlow && !bKeySlow && !bSoundSlow && nOpcSlow == 0)) + { + LARGE_INTEGER lTime; // sample timer ticks + // save reference cycles + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lTime); // get timer ticks + dwSpeedRef = lTime.LowPart; // save reference time + } + return; +} + +VOID AdjKeySpeed(VOID) // slow down key repeat +{ + WORD i; + BOOL bKey; + + bKey = FALSE; // search for a pressed key + for (i = 0;i < ARRAYSIZEOF(Chipset.Keyboard_Row) && !bKey;++i) + bKey = (Chipset.Keyboard_Row[i] != 0); + + EnterCriticalSection(&csSlowLock); + { + if (bKey) // key pressed + { + InitAdjustSpeed(); // init variables if necessary + } + bKeySlow = bKey; // save new state + } + LeaveCriticalSection(&csSlowLock); + return; +} + +VOID SetSpeed(BOOL bAdjust) // set emulation speed +{ + EnterCriticalSection(&csSlowLock); + { + if (bAdjust) // switch to real speed + { + InitAdjustSpeed(); // init variables if necessary + } + bCpuSlow = bAdjust; // save emulation speed + } + LeaveCriticalSection(&csSlowLock); + return; +} + +VOID UpdateKdnBit(VOID) // update KDN bit +{ + if ( Chipset.intk + && (Chipset.IORam[TIMER2_CTRL]&RUN) != 0 + && (DWORD) (Chipset.cycles & 0xFFFFFFFF) - Chipset.dwKdnCycles > (DWORD) T2CYCLES * 16) + IOBit(SRQ2,KDN,Chipset.in != 0); + return; +} + +BOOL WaitForSleepState(VOID) // wait for cpu SHUTDN then sleep state +{ + DWORD dwRefTime; + + SuspendDebugger(); // suspend debugger + + dwRefTime = timeGetTime(); + // wait for the SHUTDN command with 1.5 sec timeout + while (timeGetTime() - dwRefTime < 1500L && !Chipset.Shutdn) + Sleep(0); + + if (Chipset.Shutdn) // not timeout, cpu is down + SwitchToState(SM_SLEEP); // go to sleep state + else + ResumeDebugger(); // timeout, resume to debugger + + return SM_SLEEP != nNextState; // state not changed, emulator was busy +} + +UINT SwitchToState(UINT nNewState) +{ + UINT nOldState = nState; + + if (nState == nNewState) return nOldState; + switch (nState) + { + case SM_RUN: // Run + switch (nNewState) + { + case SM_INVALID: // -> Invalid + nNextState = SM_INVALID; + if (Chipset.Shutdn) + SetEvent(hEventShutdn); + else + bInterrupt = TRUE; + SuspendDebugger(); // suspend debugger + while (nState!=nNextState) Sleep(0); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_INVALID; + if (Chipset.Shutdn) + SetEvent(hEventShutdn); + else + bInterrupt = TRUE; + while (nState!=nNextState) Sleep(0); + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + break; + case SM_SLEEP: // -> Sleep + nNextState = SM_SLEEP; + bInterrupt = TRUE; // exit main loop + SuspendDebugger(); // suspend debugger + SetEvent(hEventShutdn); // exit shutdown + while (nState!=nNextState) Sleep(0); + bInterrupt = FALSE; + ResetEvent(hEventDebug); + ResetEvent(hEventShutdn); + break; + } + break; + case SM_INVALID: // Invalid + switch (nNewState) + { + case SM_RUN: // -> Run + nNextState = SM_RUN; + // don't enter opcode loop on interrupt request + bInterrupt = Chipset.Shutdn || Chipset.SoftInt; + ResumeDebugger(); + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + break; + case SM_SLEEP: // -> Sleep + nNextState = SM_SLEEP; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + break; + } + break; + case SM_SLEEP: // Sleep + switch (nNewState) + { + case SM_RUN: // -> Run + nNextState = SM_RUN; + // don't enter opcode loop on interrupt request + bInterrupt = (nDbgState == DBG_OFF) && (Chipset.Shutdn || Chipset.SoftInt); + ResumeDebugger(); + SetEvent(hEventShutdn); // leave sleep state + break; + case SM_INVALID: // -> Invalid + nNextState = SM_INVALID; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_INVALID; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + break; + } + break; + } + return nOldState; +} + +UINT WorkerThread(LPVOID pParam) +{ + LARGE_INTEGER lDummyInt; // sample timer ticks + QueryPerformanceFrequency(&lDummyInt); // init timer ticks + lDummyInt.QuadPart /= SAMPLE; // calculate sample ticks + dwTickRef = lDummyInt.LowPart; // sample timer ticks + _ASSERT(dwTickRef); // tick resolution error + +loop: + while (nNextState == SM_INVALID) // go into invalid state + { + OnToolMacroStop(); // close open keyboard macro handler + CommClose(); // close COM port + bCommInit = FALSE; // COM port not open + nState = SM_INVALID; // in invalid state + WaitForSingleObject(hEventShutdn,INFINITE); + if (nNextState == SM_RETURN) // go into return state + { + nState = SM_RETURN; // in return state + return 0; // kill thread + } + CheckSerial(); // test if UART on + } + while (nNextState == SM_RUN) + { + if (nState != SM_RUN) + { + nState = SM_RUN; + // clear port2 status bits + Chipset.cards_status &= ~(PORT2_PRESENT | PORT2_WRITE); + if (pbyPort2 || Port2) // card plugged in port2 + { + Chipset.cards_status |= PORT2_PRESENT; + + if (bPort2Writeable) // is card writeable + Chipset.cards_status |= PORT2_WRITE; + } + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + BOOL bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + BOOL bNINT = (Chipset.IORam[CARDCTL] & SMP) == 0; + + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + + IOBit(SRQ2,NINT2,bNINT2); + IOBit(SRQ2,NINT,bNINT); + } + RomSwitch(Chipset.Bank_FF); // select HP49G ROM bank and update memory mapping + UpdateContrast(Chipset.contrast); + UpdateDisplayPointers(); + UpdateMainDisplay(); + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add apples display management + UpdateAnnunciators(); + // init speed reference + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + SetHP48Time(); // update HP48 time & date + // start display counter/update engine + StartDisplay((BYTE)(((Chipset.IORam[LINECOUNT+1]<<4)|Chipset.IORam[LINECOUNT])&0x3F)); + StartBatMeasure(); // start battery measurement + StartTimers(); + } + PCHANGED; + while (!bInterrupt) + { + if (nDbgState > DBG_OFF) // debugger active + { + Debugger(); + + // if suspended skip next opcode execution + if (nDbgState == DBG_SUSPEND) + { + if (Chipset.Shutdn) break; + continue; + } + } + + EvalOpcode(FASTPTR(Chipset.pc)); // execute opcode + + // check for display update in BW mode + if (!bGrayscale) CheckDisp(!Chipset.Shutdn); + AdjustSpeed(); // adjust emulation speed + } + bInterrupt = FALSE; // be sure to reenter opcode loop + + // enter SHUTDN handler only in RUN mode + if (Chipset.Shutdn && !(nDbgState == DBG_STEPINTO || nDbgState == DBG_STEPOVER)) + { + if (!Chipset.SoftInt) // ignore SHUTDN on interrupt request + WaitForSingleObject(hEventShutdn,INFINITE); + else + Chipset.bShutdnWake = TRUE; // waked by interrupt + + if (Chipset.bShutdnWake) // waked up by timer, keyboard or serial + { + Chipset.bShutdnWake = FALSE; + Chipset.Shutdn = FALSE; + // init speed reference + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + nOpcSlow = 0; // no opcodes to slow down + } + } + if (Chipset.SoftInt) + { + Chipset.SoftInt = FALSE; + if (Chipset.inte) + { + Chipset.inte = FALSE; + rstkpush(Chipset.pc); + Chipset.pc = 0xf; + } + } + } + _ASSERT(nNextState != SM_RUN); + + StopDisplay(); // stop display counter/update + StopBatMeasure(); // stop battery measurement + StopTimers(); + + while (nNextState == SM_SLEEP) // go into sleep state + { + nState = SM_SLEEP; // in sleep state + WaitForSingleObject(hEventShutdn,INFINITE); + } + goto loop; + UNREFERENCED_PARAMETER(pParam); +} diff --git a/app/src/main/cpp/EXTERNAL.C b/app/src/main/cpp/EXTERNAL.C new file mode 100644 index 0000000..9193d19 --- /dev/null +++ b/app/src/main/cpp/EXTERNAL.C @@ -0,0 +1,100 @@ +/* + * external.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 2005 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" + +//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name +//#F0E4F #80F0F #80F0F #706D2 #80850 #80F0F =SFLAG53_56 + +// memory address for flags -53 to -56 + // CdB for HP: add apples beep management +#define SFLAG53_56 ( (cCurrentRomType=='6') \ + ? 0xE0E4F \ + : ( (cCurrentRomType=='A') \ + ? 0xF0E4F \ + : ( (cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='P' && cCurrentRomType!='2' && cCurrentRomType!='Q') \ + ? ( (cCurrentRomType=='S') \ + ? 0x706D2 \ + : 0x80850 \ + ) \ + : 0x80F0F \ + ) \ + ) \ + ) + +VOID External(CHIPSET* w) // Beep patch +{ + BYTE fbeep; + DWORD freq,dur; + + freq = Npack(w->D,5); // frequency in Hz + dur = Npack(w->C,5); // duration in ms + Nread(&fbeep,SFLAG53_56,1); // fetch system flags -53 to -56 + + w->carry = TRUE; // setting of no beep + if (!(fbeep & 0x8) && freq) // bit -56 clear and frequency > 0 Hz + { + if (freq > 4400) freq = 4400; // high limit of HP (SX) + + SoundBeep(freq,dur); // beeping + + // estimate cpu cycles for beeping time (2MHz / 4MHz) + w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000); + + // original routine return with... + w->P = 0; // P=0 + w->intk = TRUE; // INTON + w->carry = FALSE; // RTNCC + } + w->pc = rstkpop(); + return; +} + +VOID RCKBp(CHIPSET* w) // ROM Check Beep patch +{ + DWORD dw2F,dwCpuFreq; + DWORD freq,dur; + BYTE f,d; + + f = w->C[1]; // f = freq ctl + d = w->C[0]; // d = duration ctl + + if (cCurrentRomType == 'S') // Clarke chip with 48S ROM + { + // CPU strobe frequency @ RATE 14 = 1.97MHz + dwCpuFreq = ((14 + 1) * 524288) >> 2; + + dw2F = f * 126 + 262; // F=f*63+131 + } + else // York chip with 48G and later ROM + { + // CPU strobe frequency @ RATE 27 = 3.67MHz + // CPU strobe frequency @ RATE 29 = 3.93MHz + dwCpuFreq = ((27 + 1) * 524288) >> 2; + + dw2F = f * 180 + 367; // F=f*90+183.5 + } + + freq = dwCpuFreq / dw2F; + dur = (dw2F * (256 - 16 * d)) * 1000 / 2 / dwCpuFreq; + + if (freq > 4400) freq = 4400; // high limit of HP + + SoundBeep(freq,dur); // beeping + + // estimate cpu cycles for beeping time (2MHz / 4MHz) + w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000); + + w->P = 0; // P=0 + w->carry = FALSE; // RTNCC + w->pc = rstkpop(); + return; +} diff --git a/app/src/main/cpp/FETCH.C b/app/src/main/cpp/FETCH.C new file mode 100644 index 0000000..f67c362 --- /dev/null +++ b/app/src/main/cpp/FETCH.C @@ -0,0 +1,778 @@ +/* + * fetch.c + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "Opcodes.h" + +#define F 0xFF // F = function + +typedef const struct +{ + LPCVOID pLnk; + const DWORD dwTyp; +} JMPTAB, *PJMPTAB; + +// jump tables +static JMPTAB oF_[] = +{ + (LPCVOID) oF0, F, + (LPCVOID) oF1, F, + (LPCVOID) oF2, F, + (LPCVOID) oF3, F, + (LPCVOID) oF4, F, + (LPCVOID) oF5, F, + (LPCVOID) oF6, F, + (LPCVOID) oF7, F, + (LPCVOID) oF8, F, + (LPCVOID) oF9, F, + (LPCVOID) oFA, F, + (LPCVOID) oFB, F, + (LPCVOID) oFC, F, + (LPCVOID) oFD, F, + (LPCVOID) oFE, F, + (LPCVOID) oFF, F +}; + +static JMPTAB oE_[] = +{ + (LPCVOID) oE0, F, + (LPCVOID) oE1, F, + (LPCVOID) oE2, F, + (LPCVOID) oE3, F, + (LPCVOID) oE4, F, + (LPCVOID) oE5, F, + (LPCVOID) oE6, F, + (LPCVOID) oE7, F, + (LPCVOID) oE8, F, + (LPCVOID) oE9, F, + (LPCVOID) oEA, F, + (LPCVOID) oEB, F, + (LPCVOID) oEC, F, + (LPCVOID) oED, F, + (LPCVOID) oEE, F, + (LPCVOID) oEF, F +}; + +static JMPTAB oD_[] = +{ + (LPCVOID) oD0, F, + (LPCVOID) oD1, F, + (LPCVOID) oD2, F, + (LPCVOID) oD3, F, + (LPCVOID) oD4, F, + (LPCVOID) oD5, F, + (LPCVOID) oD6, F, + (LPCVOID) oD7, F, + (LPCVOID) oD8, F, + (LPCVOID) oD9, F, + (LPCVOID) oDA, F, + (LPCVOID) oDB, F, + (LPCVOID) oDC, F, + (LPCVOID) oDD, F, + (LPCVOID) oDE, F, + (LPCVOID) oDF, F +}; + +static JMPTAB oC_[] = +{ + (LPCVOID) oC0, F, + (LPCVOID) oC1, F, + (LPCVOID) oC2, F, + (LPCVOID) oC3, F, + (LPCVOID) oC4, F, + (LPCVOID) oC5, F, + (LPCVOID) oC6, F, + (LPCVOID) oC7, F, + (LPCVOID) oC8, F, + (LPCVOID) oC9, F, + (LPCVOID) oCA, F, + (LPCVOID) oCB, F, + (LPCVOID) oCC, F, + (LPCVOID) oCD, F, + (LPCVOID) oCE, F, + (LPCVOID) oCF, F +}; + +static JMPTAB oBb_[] = +{ + (LPCVOID) oBb0, F, + (LPCVOID) oBb1, F, + (LPCVOID) oBb2, F, + (LPCVOID) oBb3, F, + (LPCVOID) oBb4, F, + (LPCVOID) oBb5, F, + (LPCVOID) oBb6, F, + (LPCVOID) oBb7, F, + (LPCVOID) oBb8, F, + (LPCVOID) oBb9, F, + (LPCVOID) oBbA, F, + (LPCVOID) oBbB, F, + (LPCVOID) oBbC, F, + (LPCVOID) oBbD, F, + (LPCVOID) oBbE, F, + (LPCVOID) oBbF, F +}; + +static JMPTAB oBa_[] = +{ + (LPCVOID) oBa0, F, + (LPCVOID) oBa1, F, + (LPCVOID) oBa2, F, + (LPCVOID) oBa3, F, + (LPCVOID) oBa4, F, + (LPCVOID) oBa5, F, + (LPCVOID) oBa6, F, + (LPCVOID) oBa7, F, + (LPCVOID) oBa8, F, + (LPCVOID) oBa9, F, + (LPCVOID) oBaA, F, + (LPCVOID) oBaB, F, + (LPCVOID) oBaC, F, + (LPCVOID) oBaD, F, + (LPCVOID) oBaE, F, + (LPCVOID) oBaF, F +}; + +static JMPTAB oB_[] = +{ + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBa_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2, + (LPCVOID) oBb_, 2 +}; + +static JMPTAB oAb_[] = +{ + (LPCVOID) oAb0, F, + (LPCVOID) oAb1, F, + (LPCVOID) oAb2, F, + (LPCVOID) oAb3, F, + (LPCVOID) oAb4, F, + (LPCVOID) oAb5, F, + (LPCVOID) oAb6, F, + (LPCVOID) oAb7, F, + (LPCVOID) oAb8, F, + (LPCVOID) oAb9, F, + (LPCVOID) oAbA, F, + (LPCVOID) oAbB, F, + (LPCVOID) oAbC, F, + (LPCVOID) oAbD, F, + (LPCVOID) oAbE, F, + (LPCVOID) oAbF, F +}; + +static JMPTAB oAa_[] = +{ + (LPCVOID) oAa0, F, + (LPCVOID) oAa1, F, + (LPCVOID) oAa2, F, + (LPCVOID) oAa3, F, + (LPCVOID) oAa4, F, + (LPCVOID) oAa5, F, + (LPCVOID) oAa6, F, + (LPCVOID) oAa7, F, + (LPCVOID) oAa8, F, + (LPCVOID) oAa9, F, + (LPCVOID) oAaA, F, + (LPCVOID) oAaB, F, + (LPCVOID) oAaC, F, + (LPCVOID) oAaD, F, + (LPCVOID) oAaE, F, + (LPCVOID) oAaF, F +}; + +static JMPTAB oA_[] = +{ + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAa_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2, + (LPCVOID) oAb_, 2 +}; + +static JMPTAB o9b_[] = +{ + (LPCVOID) o9b0, F, + (LPCVOID) o9b1, F, + (LPCVOID) o9b2, F, + (LPCVOID) o9b3, F, + (LPCVOID) o9b4, F, + (LPCVOID) o9b5, F, + (LPCVOID) o9b6, F, + (LPCVOID) o9b7, F, + (LPCVOID) o9b8, F, + (LPCVOID) o9b9, F, + (LPCVOID) o9bA, F, + (LPCVOID) o9bB, F, + (LPCVOID) o9bC, F, + (LPCVOID) o9bD, F, + (LPCVOID) o9bE, F, + (LPCVOID) o9bF, F +}; + +static JMPTAB o9a_[] = +{ + (LPCVOID) o9a0, F, + (LPCVOID) o9a1, F, + (LPCVOID) o9a2, F, + (LPCVOID) o9a3, F, + (LPCVOID) o9a4, F, + (LPCVOID) o9a5, F, + (LPCVOID) o9a6, F, + (LPCVOID) o9a7, F, + (LPCVOID) o9a8, F, + (LPCVOID) o9a9, F, + (LPCVOID) o9aA, F, + (LPCVOID) o9aB, F, + (LPCVOID) o9aC, F, + (LPCVOID) o9aD, F, + (LPCVOID) o9aE, F, + (LPCVOID) o9aF, F +}; + +static JMPTAB o9_[] = +{ + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9a_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2, + (LPCVOID) o9b_, 2 +}; + +static JMPTAB o8B_[] = +{ + (LPCVOID) o8B0, F, + (LPCVOID) o8B1, F, + (LPCVOID) o8B2, F, + (LPCVOID) o8B3, F, + (LPCVOID) o8B4, F, + (LPCVOID) o8B5, F, + (LPCVOID) o8B6, F, + (LPCVOID) o8B7, F, + (LPCVOID) o8B8, F, + (LPCVOID) o8B9, F, + (LPCVOID) o8BA, F, + (LPCVOID) o8BB, F, + (LPCVOID) o8BC, F, + (LPCVOID) o8BD, F, + (LPCVOID) o8BE, F, + (LPCVOID) o8BF, F +}; + +static JMPTAB o8A_[] = +{ + (LPCVOID) o8A0, F, + (LPCVOID) o8A1, F, + (LPCVOID) o8A2, F, + (LPCVOID) o8A3, F, + (LPCVOID) o8A4, F, + (LPCVOID) o8A5, F, + (LPCVOID) o8A6, F, + (LPCVOID) o8A7, F, + (LPCVOID) o8A8, F, + (LPCVOID) o8A9, F, + (LPCVOID) o8AA, F, + (LPCVOID) o8AB, F, + (LPCVOID) o8AC, F, + (LPCVOID) o8AD, F, + (LPCVOID) o8AE, F, + (LPCVOID) o8AF, F +}; + +static JMPTAB o81B_[] = +{ + (LPCVOID) o_invalid4, F, + (LPCVOID) o81B1, F, // normally o_invalid4, beep patch, Apple: LOOP + (LPCVOID) o81B2, F, + (LPCVOID) o81B3, F, + (LPCVOID) o81B4, F, + (LPCVOID) o81B5, F, + (LPCVOID) o81B6, F, + (LPCVOID) o81B7, F, + (LPCVOID) o_invalid4, F, // Apple: SKPTOP + (LPCVOID) o_invalid4, F, // Apple: SKBOT + (LPCVOID) o_invalid4, F, // Apple: SKIP + (LPCVOID) o_invalid4, F, // Apple: SKPLEN + (LPCVOID) o_invalid4, F, // Apple: SKPID + (LPCVOID) o_invalid4, F, // Apple: $SEMI + (LPCVOID) o_invalid4, F, // Apple: DOCOL + (LPCVOID) o_invalid4, F +}; + +static JMPTAB o81Af2_[] = +{ + (LPCVOID) o81Af20, F, + (LPCVOID) o81Af21, F, + (LPCVOID) o81Af22, F, + (LPCVOID) o81Af23, F, + (LPCVOID) o81Af24, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o81Af28, F, + (LPCVOID) o81Af29, F, + (LPCVOID) o81Af2A, F, + (LPCVOID) o81Af2B, F, + (LPCVOID) o81Af2C, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F +}; + +static JMPTAB o81Af1_[] = +{ + (LPCVOID) o81Af10, F, + (LPCVOID) o81Af11, F, + (LPCVOID) o81Af12, F, + (LPCVOID) o81Af13, F, + (LPCVOID) o81Af14, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o81Af18, F, + (LPCVOID) o81Af19, F, + (LPCVOID) o81Af1A, F, + (LPCVOID) o81Af1B, F, + (LPCVOID) o81Af1C, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F +}; + +static JMPTAB o81Af0_[] = +{ + (LPCVOID) o81Af00, F, + (LPCVOID) o81Af01, F, + (LPCVOID) o81Af02, F, + (LPCVOID) o81Af03, F, + (LPCVOID) o81Af04, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o81Af08, F, + (LPCVOID) o81Af09, F, + (LPCVOID) o81Af0A, F, + (LPCVOID) o81Af0B, F, + (LPCVOID) o81Af0C, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F +}; + +static JMPTAB o81A_[] = +{ + (LPCVOID) o81Af0_, 5, + (LPCVOID) o81Af1_, 5, + (LPCVOID) o81Af2_, 5, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F +}; + +static JMPTAB o819_[] = +{ + (LPCVOID) o819f0, F, + (LPCVOID) o819f1, F, + (LPCVOID) o819f2, F, + (LPCVOID) o819f3, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F +}; + +static JMPTAB o818_[] = +{ + (LPCVOID) o818f0x, F, + (LPCVOID) o818f1x, F, + (LPCVOID) o818f2x, F, + (LPCVOID) o818f3x, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o818f8x, F, + (LPCVOID) o818f9x, F, + (LPCVOID) o818fAx, F, + (LPCVOID) o818fBx, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F, + (LPCVOID) o_invalid6, F +}; + +static JMPTAB o81_[] = +{ + (LPCVOID) o810, F, + (LPCVOID) o811, F, + (LPCVOID) o812, F, + (LPCVOID) o813, F, + (LPCVOID) o814, F, + (LPCVOID) o815, F, + (LPCVOID) o816, F, + (LPCVOID) o817, F, + (LPCVOID) o818_, 4, + (LPCVOID) o819_, 4, + (LPCVOID) o81A_, 4, + (LPCVOID) o81B_, 3, + (LPCVOID) o81C, F, + (LPCVOID) o81D, F, + (LPCVOID) o81E, F, + (LPCVOID) o81F, F +}; + +static JMPTAB o8081_[] = +{ + (LPCVOID) o80810, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F, + (LPCVOID) o_invalid5, F +}; + +static JMPTAB o808_[] = +{ + (LPCVOID) o8080, F, + (LPCVOID) o8081_, 4, + (LPCVOID) o8082X, F, + (LPCVOID) o8083, F, + (LPCVOID) o8084n, F, + (LPCVOID) o8085n, F, + (LPCVOID) o8086n, F, + (LPCVOID) o8087n, F, + (LPCVOID) o8088n, F, + (LPCVOID) o8089n, F, + (LPCVOID) o808An, F, + (LPCVOID) o808Bn, F, + (LPCVOID) o808C, F, + (LPCVOID) o808D, F, + (LPCVOID) o808E, F, + (LPCVOID) o808F, F +}; + +static JMPTAB o80_[] = +{ + (LPCVOID) o800, F, + (LPCVOID) o801, F, + (LPCVOID) o802, F, + (LPCVOID) o803, F, + (LPCVOID) o804, F, + (LPCVOID) o805, F, + (LPCVOID) o806, F, + (LPCVOID) o807, F, + (LPCVOID) o808_, 3, + (LPCVOID) o809, F, + (LPCVOID) o80A, F, + (LPCVOID) o80B, F, + (LPCVOID) o80Cn, F, + (LPCVOID) o80Dn, F, + (LPCVOID) o80E, F, + (LPCVOID) o80Fn, F +}; + +static JMPTAB o8_[] = +{ + (LPCVOID) o80_, 2, + (LPCVOID) o81_, 2, + (LPCVOID) o82n, F, + (LPCVOID) o83n, F, + (LPCVOID) o84n, F, + (LPCVOID) o85n, F, + (LPCVOID) o86n, F, + (LPCVOID) o87n, F, + (LPCVOID) o88n, F, + (LPCVOID) o89n, F, + (LPCVOID) o8A_, 2, + (LPCVOID) o8B_, 2, + (LPCVOID) o8Cd4, F, + (LPCVOID) o8Dd5, F, + (LPCVOID) o8Ed4, F, + (LPCVOID) o8Fd5, F +}; + +static JMPTAB o15_[] = +{ + (LPCVOID) o150a, F, + (LPCVOID) o151a, F, + (LPCVOID) o152a, F, + (LPCVOID) o153a, F, + (LPCVOID) o154a, F, + (LPCVOID) o155a, F, + (LPCVOID) o156a, F, + (LPCVOID) o157a, F, + (LPCVOID) o158x, F, + (LPCVOID) o159x, F, + (LPCVOID) o15Ax, F, + (LPCVOID) o15Bx, F, + (LPCVOID) o15Cx, F, + (LPCVOID) o15Dx, F, + (LPCVOID) o15Ex, F, + (LPCVOID) o15Fx, F +}; + +static JMPTAB o14_[] = +{ + (LPCVOID) o140, F, + (LPCVOID) o141, F, + (LPCVOID) o142, F, + (LPCVOID) o143, F, + (LPCVOID) o144, F, + (LPCVOID) o145, F, + (LPCVOID) o146, F, + (LPCVOID) o147, F, + (LPCVOID) o148, F, + (LPCVOID) o149, F, + (LPCVOID) o14A, F, + (LPCVOID) o14B, F, + (LPCVOID) o14C, F, + (LPCVOID) o14D, F, + (LPCVOID) o14E, F, + (LPCVOID) o14F, F +}; + +static JMPTAB o13_[] = +{ + (LPCVOID) o130, F, + (LPCVOID) o131, F, + (LPCVOID) o132, F, + (LPCVOID) o133, F, + (LPCVOID) o134, F, + (LPCVOID) o135, F, + (LPCVOID) o136, F, + (LPCVOID) o137, F, + (LPCVOID) o138, F, + (LPCVOID) o139, F, + (LPCVOID) o13A, F, + (LPCVOID) o13B, F, + (LPCVOID) o13C, F, + (LPCVOID) o13D, F, + (LPCVOID) o13E, F, + (LPCVOID) o13F, F +}; + +static JMPTAB o12_[] = +{ + (LPCVOID) o120, F, + (LPCVOID) o121, F, + (LPCVOID) o122, F, + (LPCVOID) o123, F, + (LPCVOID) o124, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o128, F, + (LPCVOID) o129, F, + (LPCVOID) o12A, F, + (LPCVOID) o12B, F, + (LPCVOID) o12C, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F +}; + +static JMPTAB o11_[] = +{ + (LPCVOID) o110, F, + (LPCVOID) o111, F, + (LPCVOID) o112, F, + (LPCVOID) o113, F, + (LPCVOID) o114, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o118, F, + (LPCVOID) o119, F, + (LPCVOID) o11A, F, + (LPCVOID) o11B, F, + (LPCVOID) o11C, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F +}; + +static JMPTAB o10_[] = +{ + (LPCVOID) o100, F, + (LPCVOID) o101, F, + (LPCVOID) o102, F, + (LPCVOID) o103, F, + (LPCVOID) o104, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o108, F, + (LPCVOID) o109, F, + (LPCVOID) o10A, F, + (LPCVOID) o10B, F, + (LPCVOID) o10C, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F, + (LPCVOID) o_invalid3, F +}; + +static JMPTAB o1_[] = +{ + (LPCVOID) o10_, 2, + (LPCVOID) o11_, 2, + (LPCVOID) o12_, 2, + (LPCVOID) o13_, 2, + (LPCVOID) o14_, 2, + (LPCVOID) o15_, 2, + (LPCVOID) o16x, F, + (LPCVOID) o17x, F, + (LPCVOID) o18x, F, + (LPCVOID) o19d2, F, + (LPCVOID) o1Ad4, F, + (LPCVOID) o1Bd5, F, + (LPCVOID) o1Cx, F, + (LPCVOID) o1Dd2, F, + (LPCVOID) o1Ed4, F, + (LPCVOID) o1Fd5, F +}; + +static JMPTAB o0E_[] = +{ + (LPCVOID) o0Ef0, F, + (LPCVOID) o0Ef1, F, + (LPCVOID) o0Ef2, F, + (LPCVOID) o0Ef3, F, + (LPCVOID) o0Ef4, F, + (LPCVOID) o0Ef5, F, + (LPCVOID) o0Ef6, F, + (LPCVOID) o0Ef7, F, + (LPCVOID) o0Ef8, F, + (LPCVOID) o0Ef9, F, + (LPCVOID) o0EfA, F, + (LPCVOID) o0EfB, F, + (LPCVOID) o0EfC, F, + (LPCVOID) o0EfD, F, + (LPCVOID) o0EfE, F, + (LPCVOID) o0EfF, F +}; + +static JMPTAB o0_[] = +{ + (LPCVOID) o00, F, + (LPCVOID) o01, F, + (LPCVOID) o02, F, + (LPCVOID) o03, F, + (LPCVOID) o04, F, + (LPCVOID) o05, F, + (LPCVOID) o06, F, + (LPCVOID) o07, F, + (LPCVOID) o08, F, + (LPCVOID) o09, F, + (LPCVOID) o0A, F, + (LPCVOID) o0B, F, + (LPCVOID) o0C, F, + (LPCVOID) o0D, F, + (LPCVOID) o0E_, 3, + (LPCVOID) o0F, F +}; + +static JMPTAB o_[] = +{ + (LPCVOID) o0_, 1, + (LPCVOID) o1_, 1, + (LPCVOID) o2n, F, + (LPCVOID) o3X, F, + (LPCVOID) o4d2, F, + (LPCVOID) o5d2, F, + (LPCVOID) o6d3, F, + (LPCVOID) o7d3, F, + (LPCVOID) o8_, 1, + (LPCVOID) o9_, 1, + (LPCVOID) oA_, 1, + (LPCVOID) oB_, 1, + (LPCVOID) oC_, 1, + (LPCVOID) oD_, 1, + (LPCVOID) oE_, 1, + (LPCVOID) oF_, 1 +}; + +// opcode dispatcher +VOID EvalOpcode(LPBYTE I) +{ + DWORD dwIndex = 0; + PJMPTAB pJmpTab = o_; + + do + { + _ASSERT(I[dwIndex] <= 0xf); // found packed data + pJmpTab = &pJmpTab[I[dwIndex]]; // table entry by opcode + dwIndex = pJmpTab->dwTyp; // next pointer type + pJmpTab = (PJMPTAB) pJmpTab->pLnk; // next pointer to table/function + } + while (dwIndex != F); // reference to table? -> again + + ((VOID (*)(LPBYTE)) pJmpTab)(I); // call function + return; +} diff --git a/app/src/main/cpp/FILES.C b/app/src/main/cpp/FILES.C new file mode 100644 index 0000000..b235e72 --- /dev/null +++ b/app/src/main/cpp/FILES.C @@ -0,0 +1,2727 @@ +/* + * files.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" // I/O register definitions +#include "kml.h" +#include "i28f160.h" // flash support +#include "debugger.h" +#include "lodepng.h" + +#pragma intrinsic(abs,labs) + +TCHAR szEmuDirectory[MAX_PATH]; +TCHAR szRomDirectory[MAX_PATH]; +TCHAR szCurrentDirectory[MAX_PATH]; +TCHAR szCurrentKml[MAX_PATH]; +TCHAR szBackupKml[MAX_PATH]; +TCHAR szCurrentFilename[MAX_PATH]; +TCHAR szBackupFilename[MAX_PATH]; +TCHAR szBufferFilename[MAX_PATH]; +TCHAR szPort2Filename[MAX_PATH]; + +BOOL bDocumentAvail = FALSE; // document not available + +BYTE cCurrentRomType = 0; // Model -> hardware +UINT nCurrentClass = 0; // Class -> derivate + +LPBYTE Port0 = NULL; +LPBYTE Port1 = NULL; +LPBYTE Port2 = NULL; + +LPBYTE pbyRom = NULL; +BOOL bRomWriteable = TRUE; // flag if ROM writeable +DWORD dwRomSize = 0; +LPBYTE pbyRomDirtyPage = NULL; +DWORD dwRomDirtyPageSize = 0; +WORD wRomCrc = 0; // fingerprint of patched ROM + +LPBYTE pbyPort2 = NULL; +BOOL bPort2Writeable = FALSE; +BOOL bPort2IsShared = FALSE; +DWORD dwPort2Size = 0; // size of mapped port2 +DWORD dwPort2Mask = 0; +WORD wPort2Crc = 0; // fingerprint of port2 + +BOOL bBackup = FALSE; + +static HANDLE hRomFile = NULL; +static HANDLE hRomMap = NULL; +static HANDLE hPort2File = NULL; +static HANDLE hPort2Map = NULL; + +// document signatures +static BYTE pbySignatureA[16] = "Emu38 Document\xFE"; +static BYTE pbySignatureB[16] = "Emu39 Document\xFE"; +static BYTE pbySignatureE[16] = "Emu48 Document\xFE"; +static BYTE pbySignatureW[16] = "Win48 Document\xFE"; +static BYTE pbySignatureV[16] = "Emu49 Document\xFE"; +static HANDLE hCurrentFile = NULL; + +static CHIPSET BackupChipset; +static LPBYTE BackupPort0; +static LPBYTE BackupPort1; +static LPBYTE BackupPort2; +static BOOL bRomPacked; + +//################ +//# +//# Window Position Tools +//# +//################ + +VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY) +{ + WINDOWPLACEMENT wndpl; + RECT *pRc = &wndpl.rcNormalPosition; + + wndpl.length = sizeof(wndpl); + GetWindowPlacement(hWnd,&wndpl); + pRc->right = pRc->right - pRc->left + nPosX; + pRc->bottom = pRc->bottom - pRc->top + nPosY; + pRc->left = nPosX; + pRc->top = nPosY; + SetWindowPlacement(hWnd,&wndpl); + return; +} + + + +//################ +//# +//# Filename Title Helper Tool +//# +//################ + +DWORD GetCutPathName(LPCTSTR szFileName, LPTSTR szBuffer, DWORD dwBufferLength, INT nCutLength) +{ + TCHAR cPath[_MAX_PATH]; // full filename + TCHAR cDrive[_MAX_DRIVE]; + TCHAR cDir[_MAX_DIR]; + TCHAR cFname[_MAX_FNAME]; + TCHAR cExt[_MAX_EXT]; + + _ASSERT(nCutLength >= 0); // 0 = only drive and name + + // split original filename into parts + _tsplitpath(szFileName,cDrive,cDir,cFname,cExt); + + if (*cDir != 0) // contain directory part + { + LPTSTR lpFilePart; // address of file name in path + INT nNameLen,nPathLen,nMaxPathLen; + + GetFullPathName(szFileName,ARRAYSIZEOF(cPath),cPath,&lpFilePart); + _tsplitpath(cPath,cDrive,cDir,cFname,cExt); + + // calculate size of drive/name and path + nNameLen = lstrlen(cDrive) + lstrlen(cFname) + lstrlen(cExt); + nPathLen = lstrlen(cDir); + + // maximum length for path + nMaxPathLen = nCutLength - nNameLen; + + if (nPathLen > nMaxPathLen) // have to cut path + { + TCHAR cDirTemp[_MAX_DIR] = _T(""); + LPTSTR szPtr; + + // UNC name + if (cDir[0] == _T('\\') && cDir[1] == _T('\\')) + { + // skip server + if ((szPtr = _tcschr(cDir + 2,_T('\\'))) != NULL) + { + // skip share + if ((szPtr = _tcschr(szPtr + 1,_T('\\'))) != NULL) + { + INT nLength = (INT) (szPtr - cDir); + + *szPtr = 0; // set EOS behind share + + // enough room for \\server\\share and "\...\" + if (nLength + 5 <= nMaxPathLen) + { + lstrcpyn(cDirTemp,cDir,ARRAYSIZEOF(cDirTemp)); + nMaxPathLen -= nLength; + } + + } + } + } + + lstrcat(cDirTemp,_T("\\...")); + nMaxPathLen -= 5; // need 6 chars for additional "\..." + "\" + if (nMaxPathLen < 0) nMaxPathLen = 0; + + // get earliest possible '\' character + szPtr = &cDir[nPathLen - nMaxPathLen]; + szPtr = _tcschr(szPtr,_T('\\')); + // not found + if (szPtr == NULL) szPtr = _T(""); + + lstrcat(cDirTemp,szPtr); // copy path with preample to dir buffer + lstrcpyn(cDir,cDirTemp,ARRAYSIZEOF(cDir)); + } + } + + _tmakepath(cPath,cDrive,cDir,cFname,cExt); + lstrcpyn(szBuffer,cPath,dwBufferLength); + return lstrlen(szBuffer); +} + +VOID SetWindowPathTitle(LPCTSTR szFileName) +{ + TCHAR cPath[MAX_PATH]; + RECT rectClient; + + if (*szFileName != 0) // set new title + { + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szFileName,cPath,ARRAYSIZEOF(cPath),rectClient.right/11); + SetWindowTitle(cPath); + } + return; +} + + + +//################ +//# +//# Patch +//# +//################ + +static __inline BYTE Asc2Nib(BYTE c) +{ + if (c<'0') return 0; + if (c<='9') return c-'0'; + if (c<'A') return 0; + if (c<='F') return c-'A'+10; + if (c<'a') return 0; + if (c<='f') return c-'a'+10; + return 0; +} + +// functions to restore ROM patches +typedef struct tnode +{ + BOOL bPatch; // TRUE = ROM address patched + DWORD dwAddress; // patch address + BYTE byROM; // original ROM value + BYTE byPatch; // patched ROM value + struct tnode *prev; // previous node + struct tnode *next; // next node +} TREENODE, *PTREENODE; + +static TREENODE *nodePatch = NULL; + +static BOOL PatchNibble(DWORD dwAddress, BYTE byPatch) +{ + PTREENODE p; + + _ASSERT(pbyRom); // ROM defined + if ((p = (PTREENODE) malloc(sizeof(TREENODE))) == NULL) + return TRUE; + + p->bPatch = TRUE; // address patched + p->dwAddress = dwAddress; // save current values + p->byROM = pbyRom[dwAddress]; + p->byPatch = byPatch; + p->prev = NULL; + p->next = nodePatch; // save node + + if (nodePatch) nodePatch->prev = p; // add as previous element + nodePatch = p; + + pbyRom[dwAddress] = byPatch; // patch ROM + return FALSE; +} + +static VOID RestorePatches(VOID) +{ + TREENODE *p; + + _ASSERT(pbyRom); // ROM defined + while (nodePatch != NULL) + { + // restore original data + pbyRom[nodePatch->dwAddress] = nodePatch->byROM; + + p = nodePatch->next; // save pointer to next node + free(nodePatch); // free node + nodePatch = p; // new node + } + return; +} + +VOID UpdatePatches(BOOL bPatch) +{ + TREENODE *p = nodePatch; + + _ASSERT(pbyRom); // ROM defined + if (bPatch) // patch ROM + { + if (p) // something in patch list + { + // goto last element in list + for (; p->next != NULL; p = p->next) {} + + do + { + if (!p->bPatch) // patch only if not patched + { + // use original data for patch restore + p->byROM = pbyRom[p->dwAddress]; + + // restore patch data + pbyRom[p->dwAddress] = p->byPatch; + p->bPatch = TRUE; // address patched + } + else + { + _ASSERT(FALSE); // call ROM patch on a patched ROM + } + + p = p->prev; + } + while (p != NULL); + } + } + else // restore ROM + { + for (; p != NULL; p = p->next) + { + // restore original data + pbyRom[p->dwAddress] = p->byROM; + p->bPatch = FALSE; // address not patched + } + } + return; +} + +BOOL PatchRom(LPCTSTR szFilename) +{ + HANDLE hFile = NULL; + DWORD dwFileSizeLow = 0; + DWORD dwFileSizeHigh = 0; + DWORD lBytesRead = 0; + PSZ lpStop,lpBuf = NULL; + DWORD dwAddress = 0; + UINT nPos = 0; + BOOL bSucc = TRUE; + + if (pbyRom == NULL) return FALSE; + SetCurrentDirectory((*szRomDirectory == 0) ? szEmuDirectory : szRomDirectory); + hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) return FALSE; + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0 || dwFileSizeLow == 0) + { // file is too large or empty + CloseHandle(hFile); + return FALSE; + } + lpBuf = (PSZ) malloc(dwFileSizeLow+1); + if (lpBuf == NULL) + { + CloseHandle(hFile); + return FALSE; + } + ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL); + CloseHandle(hFile); + lpBuf[dwFileSizeLow] = 0; + nPos = 0; + while (lpBuf[nPos]) + { + // skip whitespace characters + nPos += (UINT) strspn(&lpBuf[nPos]," \t\n\r"); + + if (lpBuf[nPos] == ';') // comment? + { + do + { + nPos++; + if (lpBuf[nPos] == '\n') + { + nPos++; + break; + } + } while (lpBuf[nPos]); + continue; + } + dwAddress = strtoul(&lpBuf[nPos], &lpStop, 16); + nPos = (UINT) (lpStop - lpBuf); // position of lpStop + + if (*lpStop != 0) // data behind address + { + if (*lpStop != ':') // invalid syntax + { + // skip to end of line + while (lpBuf[nPos] != '\n' && lpBuf[nPos] != 0) + { + ++nPos; + } + bSucc = FALSE; + continue; + } + + while (lpBuf[++nPos]) + { + if (isxdigit(lpBuf[nPos]) == FALSE) break; + if (dwAddress < dwRomSize) // patch ROM + { + // patch ROM and save original nibble + PatchNibble(dwAddress, Asc2Nib(lpBuf[nPos])); + } + ++dwAddress; + } + } + } + _ASSERT(nPos <= dwFileSizeLow); // buffer overflow? + free(lpBuf); + return bSucc; +} + + + +//################ +//# +//# ROM +//# +//################ + +BOOL CrcRom(WORD *pwChk) // calculate fingerprint of ROM +{ + DWORD *pdwData,dwSize; + DWORD dwChk = 0; + + if (pbyRom == NULL) return TRUE; // ROM CRC isn't available + + _ASSERT(pbyRom); // view on ROM + pdwData = (DWORD *) pbyRom; + + _ASSERT((dwRomSize % sizeof(*pdwData)) == 0); + dwSize = dwRomSize / sizeof(*pdwData); // file size in DWORD's + + // use checksum, because it's faster + while (dwSize-- > 0) + { + DWORD dwData = *pdwData++; + if ((dwData & 0xF0F0F0F0) != 0) // data packed? + return FALSE; + dwChk += dwData; + } + + *pwChk = (WORD) ((dwChk >> 16) + (dwChk & 0xFFFF)); + return TRUE; +} + +BOOL MapRom(LPCTSTR szFilename) +{ + DWORD dwSize,dwFileSize,dwRead; + + // open ROM for writing + BOOL bRomRW = (cCurrentRomType == 'X' || cCurrentRomType == 'Q') ? bRomWriteable : FALSE; // CdB for HP: add apples + + if (pbyRom != NULL) + { + return FALSE; + } + SetCurrentDirectory((*szRomDirectory == 0) ? szEmuDirectory : szRomDirectory); + if (bRomRW) // ROM writeable + { + hRomFile = CreateFile(szFilename, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hRomFile == INVALID_HANDLE_VALUE) + { + bRomRW = FALSE; // ROM not writeable + hRomFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + } + } + else // writing ROM disabled + { + hRomFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + } + SetCurrentDirectory(szCurrentDirectory); + if (hRomFile == INVALID_HANDLE_VALUE) + { + hRomFile = NULL; + return FALSE; + } + dwRomSize = GetFileSize(hRomFile, NULL); + + // read the first 4 bytes + ReadFile(hRomFile,&dwSize,sizeof(dwSize),&dwRead,NULL); + if (dwRead < sizeof(dwSize)) + { // file is too small. + CloseHandle(hRomFile); + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + + dwFileSize = dwRomSize; // calculate ROM image buffer size + bRomPacked = (dwSize & 0xF0F0F0F0) != 0; // ROM image packed + if (bRomPacked) dwRomSize *= 2; // unpacked ROM image has double size + + pbyRom = (LPBYTE) malloc(dwRomSize); + if (pbyRom == NULL) + { + CloseHandle(hRomFile); + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + + *(DWORD *) pbyRom = dwSize; // save first 4 bytes + + // load rest of file content + ReadFile(hRomFile,&pbyRom[sizeof(dwSize)],dwFileSize - sizeof(dwSize),&dwRead,NULL); + _ASSERT(dwFileSize - sizeof(dwSize) == dwRead); + + if (bRomRW) // ROM is writeable + { + // no. of dirty pages + dwRomDirtyPageSize = dwRomSize / ROMPAGESIZE; + + // alloc dirty page table + pbyRomDirtyPage = (LPBYTE) calloc(dwRomDirtyPageSize,sizeof(*pbyRomDirtyPage)); + if (pbyRomDirtyPage == NULL) + { + free(pbyRom); // free ROM image + CloseHandle(hRomFile); + dwRomDirtyPageSize = 0; + pbyRom = NULL; + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + } + else + { + dwRomDirtyPageSize = 0; + CloseHandle(hRomFile); + hRomFile = NULL; + } + + if (bRomPacked) // packed ROM image + { + dwSize = dwRomSize; // destination start address + while (dwFileSize > 0) // unpack source + { + BYTE byValue = pbyRom[--dwFileSize]; + pbyRom[--dwSize] = byValue >> 4; + pbyRom[--dwSize] = byValue & 0xF; + } + } + return TRUE; +} + +VOID UnmapRom(VOID) +{ + if (pbyRom == NULL) return; // ROM not mapped + RestorePatches(); // restore ROM patches + if (hRomFile) // ROM file still open (only in R/W case) + { + DWORD i; + + _ASSERT(pbyRomDirtyPage != NULL); + + // scan for every dirty page + for (i = 0; i < dwRomDirtyPageSize; ++i) + { + if (pbyRomDirtyPage[i]) // page dirty + { + DWORD dwSize,dwLinPos,dwFilePos,dwWritten; + + dwLinPos = i * ROMPAGESIZE; // position inside emulator memory + + dwSize = ROMPAGESIZE; // bytes to write + while (i+1 < dwRomDirtyPageSize && pbyRomDirtyPage[i+1]) + { + dwSize += ROMPAGESIZE; // next page is also dirty + ++i; // skip next page in outer loop + } + + dwFilePos = dwLinPos; // ROM file position + + if (bRomPacked) // repack data + { + LPBYTE pbySrc,pbyDest; + DWORD j; + + dwSize /= 2; // adjust no. of bytes to write + dwFilePos /= 2; // linear pos in packed file + + // pack data in page + pbySrc = pbyDest = &pbyRom[dwLinPos]; + for (j = 0; j < dwSize; j++) + { + *pbyDest = *pbySrc++; + *pbyDest |= *pbySrc++ << 4; + pbyDest++; + } + } + + SetFilePointer(hRomFile,dwFilePos,NULL,FILE_BEGIN); + WriteFile(hRomFile,&pbyRom[dwLinPos],dwSize,&dwWritten,NULL); + } + } + + free(pbyRomDirtyPage); + CloseHandle(hRomFile); + pbyRomDirtyPage = NULL; + dwRomDirtyPageSize = 0; + hRomFile = NULL; + } + + free(pbyRom); // free ROM image + pbyRom = NULL; + dwRomSize = 0; + wRomCrc = 0; + return; +} + + + +//################ +//# +//# Port2 +//# +//################ + +BOOL CrcPort2(WORD *pwCrc) // calculate fingerprint of port2 +{ + DWORD dwCount; + DWORD dwFileSize; + + *pwCrc = 0; + + // port2 CRC isn't available + if (pbyPort2 == NULL) return TRUE; + + dwFileSize = GetFileSize(hPort2File, &dwCount); // get real filesize + _ASSERT(dwCount == 0); // isn't created by MapPort2() + + for (dwCount = 0;dwCount < dwFileSize; ++dwCount) + { + if ((pbyPort2[dwCount] & 0xF0) != 0) // data packed? + return FALSE; + + *pwCrc = (*pwCrc >> 4) ^ (((*pwCrc ^ ((WORD) pbyPort2[dwCount])) & 0xf) * 0x1081); + } + return TRUE; +} + +BOOL MapPort2(LPCTSTR szFilename) +{ + DWORD dwFileSizeLo,dwFileSizeHi; + + if (pbyPort2 != NULL) return FALSE; + bPort2Writeable = TRUE; + dwPort2Size = 0; // reset size of port2 + + SetCurrentDirectory(szEmuDirectory); + hPort2File = CreateFile(szFilename, + GENERIC_READ|GENERIC_WRITE, + bPort2IsShared ? FILE_SHARE_READ : 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hPort2File == INVALID_HANDLE_VALUE) + { + bPort2Writeable = FALSE; + hPort2File = CreateFile(szFilename, + GENERIC_READ, + bPort2IsShared ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hPort2File == INVALID_HANDLE_VALUE) + { + SetCurrentDirectory(szCurrentDirectory); + hPort2File = NULL; + return FALSE; + } + } + SetCurrentDirectory(szCurrentDirectory); + dwFileSizeLo = GetFileSize(hPort2File, &dwFileSizeHi); + + // size not 32, 128, 256, 512, 1024, 2048 or 4096 KB + if ( dwFileSizeHi != 0 + || dwFileSizeLo == 0 + || (dwFileSizeLo & (dwFileSizeLo - 1)) != 0 + || (dwFileSizeLo & 0xFF02FFFF) != 0) + { + UnmapPort2(); + return FALSE; + } + + hPort2Map = CreateFileMapping(hPort2File, NULL, bPort2Writeable ? PAGE_READWRITE : PAGE_READONLY, + 0, dwFileSizeLo, NULL); + if (hPort2Map == NULL) + { + UnmapPort2(); + return FALSE; + } + pbyPort2 = (LPBYTE) MapViewOfFile(hPort2Map, bPort2Writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, dwFileSizeLo); + if (pbyPort2 == NULL) + { + UnmapPort2(); + return FALSE; + } + + dwPort2Mask = (dwFileSizeLo - 1) >> 18; // mask for valid address lines of the BS-FF + dwPort2Size = dwFileSizeLo / 2048; // mapping size of port2 + + if (CrcPort2(&wPort2Crc) == FALSE) // calculate fingerprint of port2 + { + UnmapPort2(); // free memory + AbortMessage(_T("Packed Port 2 image detected!")); + return FALSE; + } + return TRUE; +} + +VOID UnmapPort2(VOID) +{ + if (pbyPort2 != NULL) + { + UnmapViewOfFile(pbyPort2); + pbyPort2 = NULL; + } + if (hPort2Map != NULL) + { + CloseHandle(hPort2Map); + hPort2Map = NULL; + } + if (hPort2File != NULL) + { + CloseHandle(hPort2File); + hPort2File = NULL; + } + dwPort2Size = 0; // reset size of port2 + dwPort2Mask = 0; + bPort2Writeable = FALSE; + wPort2Crc = 0; + return; +} + + + +//################ +//# +//# Documents +//# +//################ + +static BOOL IsDataPacked(VOID *pMem, DWORD dwSize) +{ + DWORD *pdwMem = (DWORD *) pMem; + + _ASSERT((dwSize % sizeof(DWORD)) == 0); + if ((dwSize % sizeof(DWORD)) != 0) return TRUE; + + for (dwSize /= sizeof(DWORD); dwSize-- > 0;) + { + if ((*pdwMem++ & 0xF0F0F0F0) != 0) + return TRUE; + } + return FALSE; +} + +VOID ResetDocument(VOID) +{ + DisableDebugger(); + if (szCurrentKml[0]) + { + KillKML(); + } + if (hCurrentFile) + { + CloseHandle(hCurrentFile); + hCurrentFile = NULL; + } + szCurrentKml[0] = 0; + szCurrentFilename[0]=0; + if (Port0) { free(Port0); Port0 = NULL; } + if (Port1) { free(Port1); Port1 = NULL; } + if (Port2) { free(Port2); Port2 = NULL; } else UnmapPort2(); + ZeroMemory(&Chipset,sizeof(Chipset)); + ZeroMemory(&RMap,sizeof(RMap)); // delete MMU mappings + ZeroMemory(&WMap,sizeof(WMap)); + bDocumentAvail = FALSE; // document not available + return; +} + +BOOL NewDocument(VOID) +{ + SaveBackup(); + ResetDocument(); + + if (!DisplayChooseKml(0)) goto restore; + if (!InitKML(szCurrentKml,FALSE)) goto restore; + Chipset.type = cCurrentRomType; + + if (Chipset.type == '6' || Chipset.type == 'A') // HP38G + { + Chipset.Port0Size = (Chipset.type == 'A') ? 32 : 64; + Chipset.Port1Size = 0; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0x0; + } + if (Chipset.type == 'E' || Chipset.type == 'P') // HP39/40G/HP39G+ // CdB for HP: add apples + { + Chipset.Port0Size = 128; + Chipset.Port1Size = 0; + Chipset.Port2Size = 128; + + Chipset.cards_status = 0xF; + + bPort2Writeable = TRUE; // port2 is writeable + } + if (Chipset.type == 'S') // HP48SX + { + Chipset.Port0Size = 32; + Chipset.Port1Size = 128; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0x5; + + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (Chipset.type == 'G') // HP48GX + { + Chipset.Port0Size = 128; + Chipset.Port1Size = 128; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0xA; + + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (Chipset.type == 'X' || Chipset.type == '2' || Chipset.type == 'Q') // HP49G/HP48gII/HP49g+/50g // CdB for HP: add apples + { + Chipset.Port0Size = 256; + Chipset.Port1Size = 128; + Chipset.Port2Size = 128; + + Chipset.cards_status = 0xF; + bPort2Writeable = TRUE; // port2 is writeable + + FlashInit(); // init flash structure + } + + if (Chipset.type == 'Q') // HP49g+/50g // CdB for HP: add apples + { + Chipset.d0size = 16; + } + Chipset.IORam[LPE] = RST; // set ReSeT bit at power on reset + + // allocate port memory + if (Chipset.Port0Size) + { + Port0 = (LPBYTE) calloc(Chipset.Port0Size*2048,sizeof(*Port0)); + _ASSERT(Port0 != NULL); + } + if (Chipset.Port1Size) + { + Port1 = (LPBYTE) calloc(Chipset.Port1Size*2048,sizeof(*Port1)); + _ASSERT(Port1 != NULL); + } + if (Chipset.Port2Size) + { + Port2 = (LPBYTE) calloc(Chipset.Port2Size*2048,sizeof(*Port1)); + _ASSERT(Port2 != NULL); + } + LoadBreakpointList(NULL); // clear debugger breakpoint list + RomSwitch(0); // boot ROM view of HP49G and map memory + bDocumentAvail = TRUE; // document available + return TRUE; +restore: + RestoreBackup(); + ResetBackup(); + + // HP48SX/GX + if (Chipset.type == 'S' || Chipset.type == 'G') + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (pbyRom) + { + Map(0x00,0xFF); + } + return FALSE; +} + +BOOL OpenDocument(LPCTSTR szFilename) +{ + #define CHECKAREA(s,e) (offsetof(CHIPSET,e)-offsetof(CHIPSET,s)+sizeof(((CHIPSET *)NULL)->e)) + + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD lBytesRead,lSizeofChipset; + BYTE pbyFileSignature[16]; + LPBYTE pbySig; + UINT ctBytesCompared; + UINT nLength; + + // Open file + if (lstrcmpi(szCurrentFilename,szFilename) == 0) + { + if (YesNoMessage(_T("Do you want to reload this document?")) == IDNO) + return TRUE; + } + + SaveBackup(); + ResetDocument(); + + hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + AbortMessage(_T("This file is missing or already loaded in another instance of Emu48.")); + goto restore; + } + + // Read and Compare signature + ReadFile(hFile, pbyFileSignature, 16, &lBytesRead, NULL); + switch (pbyFileSignature[0]) + { + case 'E': + pbySig = (pbyFileSignature[3] == '3') + ? ((pbyFileSignature[4] == '8') ? pbySignatureA : pbySignatureB) + : ((pbyFileSignature[4] == '8') ? pbySignatureE : pbySignatureV); + for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++) + { + if (pbyFileSignature[ctBytesCompared]!=pbySig[ctBytesCompared]) + { + AbortMessage(_T("This file is not a valid Emu48 document.")); + goto restore; + } + } + break; + case 'W': + for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++) + { + if (pbyFileSignature[ctBytesCompared]!=pbySignatureW[ctBytesCompared]) + { + AbortMessage(_T("This file is not a valid Win48 document.")); + goto restore; + } + } + break; + default: + AbortMessage(_T("This file is not a valid document.")); + goto restore; + } + + switch (pbyFileSignature[14]) + { + case 0xFE: // Win48 2.1 / Emu4x 0.99.x format + // read length of KML script name + ReadFile(hFile,&nLength,sizeof(nLength),&lBytesRead,NULL); + // KML script name too long for file buffer + if (nLength >= ARRAYSIZEOF(szCurrentKml)) goto read_err; + #if defined _UNICODE + { + LPSTR szTmp = (LPSTR) malloc(nLength); + if (szTmp == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + ReadFile(hFile, szTmp, nLength, &lBytesRead, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTmp, lBytesRead, + szCurrentKml, ARRAYSIZEOF(szCurrentKml)); + free(szTmp); + } + #else + { + ReadFile(hFile, szCurrentKml, nLength, &lBytesRead, NULL); + } + #endif + if (nLength != lBytesRead) goto read_err; + szCurrentKml[nLength] = 0; + break; + case 0xFF: // Win48 2.05 format + break; + default: + AbortMessage(_T("This file is for an unknown version of Emu48.")); + goto restore; + } + + // read chipset size inside file + ReadFile(hFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesRead, NULL); + if (lBytesRead != sizeof(lSizeofChipset)) goto read_err; + if (lSizeofChipset <= sizeof(Chipset)) // actual or older chipset version + { + // read chipset content + ZeroMemory(&Chipset,sizeof(Chipset)); // init chipset + ReadFile(hFile, &Chipset, lSizeofChipset, &lBytesRead, NULL); + } + else // newer chipset version + { + // read my used chipset content + ReadFile(hFile, &Chipset, sizeof(Chipset), &lBytesRead, NULL); + + // skip rest of chipset + SetFilePointer(hFile, lSizeofChipset-sizeof(Chipset), NULL, FILE_CURRENT); + lSizeofChipset = sizeof(Chipset); + } + if (lBytesRead != lSizeofChipset) goto read_err; + + if (!isModelValid(Chipset.type)) // check for valid model in emulator state file + { + AbortMessage(_T("Emulator state file with invalid calculator model.")); + goto restore; + } + + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); + + while (TRUE) + { + if (szCurrentKml[0]) // KML file name + { + BOOL bOK; + + bOK = InitKML(szCurrentKml,FALSE); + bOK = bOK && (cCurrentRomType == Chipset.type); + if (bOK) break; + + KillKML(); + } + if (!DisplayChooseKml(Chipset.type)) + goto restore; + } + // reload old button state + ReloadButtons(Chipset.Keyboard_Row,ARRAYSIZEOF(Chipset.Keyboard_Row)); + + FlashInit(); // init flash structure + + if (Chipset.Port0Size) + { + Port0 = (LPBYTE) malloc(Chipset.Port0Size*2048); + if (Port0 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Port0, Chipset.Port0Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port0Size*2048) goto read_err; + + if (IsDataPacked(Port0,Chipset.Port0Size*2048)) goto read_err; + } + + if (Chipset.Port1Size) + { + Port1 = (LPBYTE) malloc(Chipset.Port1Size*2048); + if (Port1 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Port1, Chipset.Port1Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port1Size*2048) goto read_err; + + if (IsDataPacked(Port1,Chipset.Port1Size*2048)) goto read_err; + } + + // HP48SX/GX + if (cCurrentRomType=='S' || cCurrentRomType=='G') + { + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + // port2 changed and card detection enabled + if ( Chipset.wPort2Crc != wPort2Crc + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + } + else // HP38G, HP39/40G, HP49G + { + if (Chipset.Port2Size) + { + Port2 = (LPBYTE) malloc(Chipset.Port2Size*2048); + if (Port2 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Port2, Chipset.Port2Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port2Size*2048) goto read_err; + + if (IsDataPacked(Port2,Chipset.Port2Size*2048)) goto read_err; + + bPort2Writeable = TRUE; + Chipset.cards_status = 0xF; + } + } + + RomSwitch(Chipset.Bank_FF); // reload ROM view of HP49G and map memory + + if (Chipset.wRomCrc != wRomCrc) // ROM changed + { + CpuReset(); + Chipset.Shutdn = FALSE; // automatic restart + } + + // check CPU main registers + if (IsDataPacked(Chipset.A,CHECKAREA(A,R4))) goto read_err; + + LoadBreakpointList(hFile); // load debugger breakpoint list + + lstrcpy(szCurrentFilename, szFilename); + _ASSERT(hCurrentFile == NULL); + hCurrentFile = hFile; + #if defined _USRDLL // DLL version + // notify main proc about current document file + if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename); + #endif + SetWindowPathTitle(szCurrentFilename); // update window title line + bDocumentAvail = TRUE; // document available + return TRUE; + +read_err: + AbortMessage(_T("This file must be truncated, and cannot be loaded.")); +restore: + if (INVALID_HANDLE_VALUE != hFile) // close if valid handle + CloseHandle(hFile); + RestoreBackup(); + ResetBackup(); + + // HP48SX/GX + if (cCurrentRomType=='S' || cCurrentRomType=='G') + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + return FALSE; + #undef CHECKAREA +} + +BOOL SaveDocument(VOID) +{ + DWORD lBytesWritten; + DWORD lSizeofChipset; + UINT nLength; + WINDOWPLACEMENT wndpl; + + if (hCurrentFile == NULL) return FALSE; + + _ASSERT(hWnd); // window open + wndpl.length = sizeof(wndpl); // update saved window position + GetWindowPlacement(hWnd, &wndpl); + Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left; + Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top; + + SetFilePointer(hCurrentFile,0,NULL,FILE_BEGIN); + + if (!WriteFile(hCurrentFile, pbySignatureE, sizeof(pbySignatureE), &lBytesWritten, NULL)) + { + AbortMessage(_T("Could not write into file !")); + return FALSE; + } + + CrcRom(&Chipset.wRomCrc); // save fingerprint of ROM + CrcPort2(&Chipset.wPort2Crc); // save fingerprint of port2 + + nLength = lstrlen(szCurrentKml); + WriteFile(hCurrentFile, &nLength, sizeof(nLength), &lBytesWritten, NULL); + #if defined _UNICODE + { + LPSTR szTmp = (LPSTR) malloc(nLength); + if (szTmp != NULL) + { + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + szCurrentKml, nLength, + szTmp, nLength, NULL, NULL); + WriteFile(hCurrentFile, szTmp, nLength, &lBytesWritten, NULL); + free(szTmp); + } + } + #else + { + WriteFile(hCurrentFile, szCurrentKml, nLength, &lBytesWritten, NULL); + } + #endif + lSizeofChipset = sizeof(CHIPSET); + WriteFile(hCurrentFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesWritten, NULL); + WriteFile(hCurrentFile, &Chipset, lSizeofChipset, &lBytesWritten, NULL); + if (Chipset.Port0Size) WriteFile(hCurrentFile, Port0, Chipset.Port0Size*2048, &lBytesWritten, NULL); + if (Chipset.Port1Size) WriteFile(hCurrentFile, Port1, Chipset.Port1Size*2048, &lBytesWritten, NULL); + if (Chipset.Port2Size) WriteFile(hCurrentFile, Port2, Chipset.Port2Size*2048, &lBytesWritten, NULL); + SaveBreakpointList(hCurrentFile); // save debugger breakpoint list + SetEndOfFile(hCurrentFile); // cut the rest + return TRUE; +} + +BOOL SaveDocumentAs(LPCTSTR szFilename) +{ + HANDLE hFile; + + if (hCurrentFile) // already file in use + { + CloseHandle(hCurrentFile); // close it, even it's same, so data always will be saved + hCurrentFile = NULL; + } + hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file + { + AbortMessage(_T("This file must be currently used by another instance of Emu48.")); + return FALSE; + } + lstrcpy(szCurrentFilename, szFilename); // save new file name + hCurrentFile = hFile; // and the corresponding handle + #if defined _USRDLL // DLL version + // notify main proc about current document file + if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename); + #endif + SetWindowPathTitle(szCurrentFilename); // update window title line + return SaveDocument(); // save current content +} + + + +//################ +//# +//# Backup +//# +//################ + +BOOL SaveBackup(VOID) +{ + WINDOWPLACEMENT wndpl; + + if (!bDocumentAvail) return FALSE; + + _ASSERT(nState != SM_RUN); // emulation engine is running + + // save window position + _ASSERT(hWnd); // window open + wndpl.length = sizeof(wndpl); // update saved window position + GetWindowPlacement(hWnd, &wndpl); + Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left; + Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top; + + lstrcpy(szBackupFilename, szCurrentFilename); + lstrcpy(szBackupKml, szCurrentKml); + if (BackupPort0) { free(BackupPort0); BackupPort0 = NULL; } + if (BackupPort1) { free(BackupPort1); BackupPort1 = NULL; } + if (BackupPort2) { free(BackupPort2); BackupPort2 = NULL; } + CopyMemory(&BackupChipset, &Chipset, sizeof(Chipset)); + if (Port0 && Chipset.Port0Size) + { + BackupPort0 = (LPBYTE) malloc(Chipset.Port0Size*2048); + CopyMemory(BackupPort0,Port0,Chipset.Port0Size*2048); + } + if (Port1 && Chipset.Port1Size) + { + BackupPort1 = (LPBYTE) malloc(Chipset.Port1Size*2048); + CopyMemory(BackupPort1,Port1,Chipset.Port1Size*2048); + } + if (Port2 && Chipset.Port2Size) // internal port2 + { + BackupPort2 = (LPBYTE) malloc(Chipset.Port2Size*2048); + CopyMemory(BackupPort2,Port2,Chipset.Port2Size*2048); + } + CreateBackupBreakpointList(); + bBackup = TRUE; + return TRUE; +} + +BOOL RestoreBackup(VOID) +{ + BOOL bDbgOpen; + + if (!bBackup) return FALSE; + + bDbgOpen = (nDbgState != DBG_OFF); // debugger window open? + ResetDocument(); + // need chipset for contrast setting in InitKML() + Chipset.contrast = BackupChipset.contrast; + if (!InitKML(szBackupKml,TRUE)) + { + InitKML(szCurrentKml,TRUE); + return FALSE; + } + lstrcpy(szCurrentKml, szBackupKml); + lstrcpy(szCurrentFilename, szBackupFilename); + if (szCurrentFilename[0]) + { + hCurrentFile = CreateFile(szCurrentFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hCurrentFile == INVALID_HANDLE_VALUE) + { + hCurrentFile = NULL; + szCurrentFilename[0] = 0; + } + } + CopyMemory(&Chipset, &BackupChipset, sizeof(Chipset)); + if (BackupPort0 && Chipset.Port0Size) + { + Port0 = (LPBYTE) malloc(Chipset.Port0Size*2048); + CopyMemory(Port0,BackupPort0,Chipset.Port0Size*2048); + } + if (BackupPort1 && Chipset.Port1Size) + { + Port1 = (LPBYTE) malloc(Chipset.Port1Size*2048); + CopyMemory(Port1,BackupPort1,Chipset.Port1Size*2048); + } + if (BackupPort2 && Chipset.Port2Size) // internal port2 + { + Port2 = (LPBYTE) malloc(Chipset.Port2Size*2048); + CopyMemory(Port2,BackupPort2,Chipset.Port2Size*2048); + } + // map port2 + else + { + if (cCurrentRomType=='S' || cCurrentRomType=='G') // HP48SX/GX + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + } + Map(0x00,0xFF); // update memory mapping + SetWindowPathTitle(szCurrentFilename); // update window title line + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); + RestoreBackupBreakpointList(); // restore the debugger breakpoint list + if (bDbgOpen) OnToolDebug(); // reopen the debugger + bDocumentAvail = TRUE; // document available + return TRUE; +} + +BOOL ResetBackup(VOID) +{ + if (!bBackup) return FALSE; + szBackupFilename[0] = 0; + szBackupKml[0] = 0; + if (BackupPort0) { free(BackupPort0); BackupPort0 = NULL; } + if (BackupPort1) { free(BackupPort1); BackupPort1 = NULL; } + if (BackupPort2) { free(BackupPort2); BackupPort2 = NULL; } + ZeroMemory(&BackupChipset,sizeof(BackupChipset)); + bBackup = FALSE; + return TRUE; +} + + + +//################ +//# +//# Open File Common Dialog Boxes +//# +//################ + +static VOID InitializeOFN(LPOPENFILENAME ofn) +{ + ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME)); + ofn->lStructSize = sizeof(OPENFILENAME); + ofn->hwndOwner = hWnd; + ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY; + return; +} + +BOOL GetOpenFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Emu48 Files (*.e38;*.e39;*.e48;*.e49)\0") + _T("*.e38;*.e39;*.e48;*.e49\0") + _T("HP-38 Files (*.e38)\0*.e38\0") + _T("HP-39 Files (*.e39)\0*.e39\0") + _T("HP-48 Files (*.e48)\0*.e48\0") + _T("HP-49 Files (*.e49)\0*.e49\0") + _T("Win48 Files (*.w48)\0*.w48\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetSaveAsFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("HP-38 Files (*.e38)\0*.e38\0") + _T("HP-39 Files (*.e39)\0*.e39\0") + _T("HP-48 Files (*.e48)\0*.e48\0") + _T("HP-49 Files (*.e49)\0*.e49\0"); + ofn.lpstrDefExt = _T("e48"); // HP48SX/GX + ofn.nFilterIndex = 3; + if (cCurrentRomType=='6' || cCurrentRomType=='A') // HP38G + { + ofn.lpstrDefExt = _T("e38"); + ofn.nFilterIndex = 1; + } + if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40 // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("e39"); + ofn.nFilterIndex = 2; + } + if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48gII/HP49g+/HP50g // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("e49"); + ofn.nFilterIndex = 4; + } + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetLoadObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = lpstrFilter; + ofn.lpstrDefExt = lpstrDefExt; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = lpstrFilter; + ofn.lpstrDefExt = NULL; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + if (ofn.nFileExtension == 0) // given filename has no extension + { + // actual name length + UINT nLength = lstrlen(szBufferFilename); + + // destination buffer has room for the default extension + if (nLength + 1 + lstrlen(lpstrDefExt) < ARRAYSIZEOF(szBufferFilename)) + { + // add default extension + szBufferFilename[nLength++] = _T('.'); + lstrcpy(&szBufferFilename[nLength], lpstrDefExt); + } + } + return TRUE; +} + + + +//################ +//# +//# Load and Save HP48 Objects +//# +//################ + +WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize) // separated from LoadObject() +{ + BOOL bBinary; + DWORD dwAddress, i; + + bBinary = ((lpBuf[dwSize+0]=='H') + && (lpBuf[dwSize+1]=='P') + && (lpBuf[dwSize+2]=='H') + && (lpBuf[dwSize+3]=='P') + && (lpBuf[dwSize+4]=='4') + && (lpBuf[dwSize+5]==((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') ? '9' : '8')) // CdB for HP: add apples + && (lpBuf[dwSize+6]=='-')); + + for (dwAddress = 0, i = 0; i < dwSize; i++) + { + BYTE byTwoNibs = lpBuf[i+dwSize]; + lpBuf[dwAddress++] = (BYTE)(byTwoNibs&0xF); + lpBuf[dwAddress++] = (BYTE)(byTwoNibs>>4); + } + + dwSize = dwAddress; // unpacked buffer size + + if (bBinary == TRUE) + { // load as binary + dwSize = RPL_ObjectSize(lpBuf+16,dwSize-16); + if (dwSize == BAD_OB) return S_ERR_OBJECT; + dwAddress = RPL_CreateTemp(dwSize,TRUE); + if (dwAddress == 0) return S_ERR_BINARY; + Nwrite(lpBuf+16,dwAddress,dwSize); + } + else + { // load as string + dwAddress = RPL_CreateTemp(dwSize+10,TRUE); + if (dwAddress == 0) return S_ERR_ASCII; + Write5(dwAddress,0x02A2C); // String + Write5(dwAddress+5,dwSize+5); // length of String + Nwrite(lpBuf,dwAddress+10,dwSize); // data + } + RPL_Push(nStkLevel,dwAddress); + return S_ERR_NO; +} + +BOOL LoadObject(LPCTSTR szFilename) // separated stack writing part +{ + HANDLE hFile; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPBYTE lpBuf; + WORD wError; + + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) return FALSE; + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { // file is too large. + CloseHandle(hFile); + return FALSE; + } + lpBuf = (LPBYTE) malloc(dwFileSizeLow*2); + if (lpBuf == NULL) + { + CloseHandle(hFile); + return FALSE; + } + ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL); + CloseHandle(hFile); + + wError = WriteStack(1,lpBuf,dwFileSizeLow); + + if (wError == S_ERR_OBJECT) + AbortMessage(_T("This isn't a valid binary file.")); + + if (wError == S_ERR_BINARY) + AbortMessage(_T("The calculator does not have enough\nfree memory to load this binary file.")); + + if (wError == S_ERR_ASCII) + AbortMessage(_T("The calculator does not have enough\nfree memory to load this text file.")); + + free(lpBuf); + return (wError == S_ERR_NO); +} + +BOOL SaveObject(LPCTSTR szFilename) // separated stack reading part +{ + HANDLE hFile; + LPBYTE pbyHeader; + DWORD lBytesWritten; + DWORD dwAddress; + DWORD dwLength; + + dwAddress = RPL_Pick(1); + if (dwAddress == 0) + { + AbortMessage(_T("Too Few Arguments.")); + return FALSE; + } + dwLength = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2; + + hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + AbortMessage(_T("Cannot open file.")); + return FALSE; + } + + pbyHeader = ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q')) + ? (LPBYTE) BINARYHEADER49 + : (LPBYTE) BINARYHEADER48; + + WriteFile(hFile, pbyHeader, 8, &lBytesWritten, NULL); + + while (dwLength--) + { + BYTE byByte = Read2(dwAddress); + WriteFile(hFile, &byByte, 1, &lBytesWritten, NULL); + dwAddress += 2; + } + CloseHandle(hFile); + return TRUE; +} + + + +//################ +//# +//# Load Icon +//# +//################ + +BOOL LoadIconFromFile(LPCTSTR szFilename) +{ + HANDLE hIcon; + + SetCurrentDirectory(szEmuDirectory); + // not necessary to destroy because icon is shared + hIcon = LoadImage(NULL, szFilename, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE|LR_SHARED); + SetCurrentDirectory(szCurrentDirectory); + + if (hIcon) + { + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon); + } + return hIcon != NULL; +} + +VOID LoadIconDefault(VOID) +{ + // use window class icon + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) NULL); + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) NULL); + return; +} + + + +//################ +//# +//# Load Bitmap +//# +//################ + +#define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4) + +typedef struct _BmpFile +{ + DWORD dwPos; // actual reading pos + DWORD dwFileSize; // file size + LPBYTE pbyFile; // buffer +} BMPFILE, FAR *LPBMPFILE, *PBMPFILE; + +static __inline WORD DibNumColors(BITMAPINFOHEADER CONST *lpbi) +{ + if (lpbi->biClrUsed != 0) return (WORD) lpbi->biClrUsed; + + /* a 24 bitcount DIB has no color table */ + return (lpbi->biBitCount <= 8) ? (1 << lpbi->biBitCount) : 0; +} + +static HPALETTE CreateBIPalette(BITMAPINFOHEADER CONST *lpbi) +{ + LOGPALETTE* pPal; + HPALETTE hpal = NULL; + WORD wNumColors; + BYTE red; + BYTE green; + BYTE blue; + UINT i; + RGBQUAD* pRgb; + + if (!lpbi) + return NULL; + + if (lpbi->biSize != sizeof(BITMAPINFOHEADER)) + return NULL; + + // Get a pointer to the color table and the number of colors in it + pRgb = (RGBQUAD FAR *)((LPBYTE)lpbi + (WORD)lpbi->biSize); + wNumColors = DibNumColors(lpbi); + + if (wNumColors) + { + // Allocate for the logical palette structure + pPal = (PLOGPALETTE) malloc(sizeof(LOGPALETTE) + wNumColors * sizeof(PALETTEENTRY)); + if (!pPal) return NULL; + + pPal->palVersion = 0x300; + pPal->palNumEntries = wNumColors; + + // Fill in the palette entries from the DIB color table and + // create a logical color palette. + for (i = 0; i < pPal->palNumEntries; i++) + { + pPal->palPalEntry[i].peRed = pRgb[i].rgbRed; + pPal->palPalEntry[i].peGreen = pRgb[i].rgbGreen; + pPal->palPalEntry[i].peBlue = pRgb[i].rgbBlue; + pPal->palPalEntry[i].peFlags = 0; + } + hpal = CreatePalette(pPal); + free(pPal); + } + else + { + // create halftone palette for 16, 24 and 32 bitcount bitmaps + + // 16, 24 and 32 bitcount DIB's have no color table entries so, set the + // number of to the maximum value (256). + wNumColors = 256; + pPal = (PLOGPALETTE) malloc(sizeof(LOGPALETTE) + wNumColors * sizeof(PALETTEENTRY)); + if (!pPal) return NULL; + + pPal->palVersion = 0x300; + pPal->palNumEntries = wNumColors; + + red = green = blue = 0; + + // Generate 256 (= 8*8*4) RGB combinations to fill the palette + // entries. + for (i = 0; i < pPal->palNumEntries; i++) + { + pPal->palPalEntry[i].peRed = red; + pPal->palPalEntry[i].peGreen = green; + pPal->palPalEntry[i].peBlue = blue; + pPal->palPalEntry[i].peFlags = 0; + + if (!(red += 32)) + if (!(green += 32)) + blue += 64; + } + hpal = CreatePalette(pPal); + free(pPal); + } + return hpal; +} + +static HBITMAP DecodeBmp(LPBMPFILE pBmp) +{ + LPBITMAPFILEHEADER pBmfh; + LPBITMAPINFO pBmi; + HBITMAP hBitmap; + DWORD dwFileSize; + + hBitmap = NULL; + + // size of bitmap header information + dwFileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + if (pBmp->dwFileSize < dwFileSize) return NULL; + + // check for bitmap + pBmfh = (LPBITMAPFILEHEADER) pBmp->pbyFile; + if (pBmfh->bfType != 0x4D42) return NULL; // "BM" + + pBmi = (LPBITMAPINFO) (pBmp->pbyFile + sizeof(BITMAPFILEHEADER)); + + // size with color table + if (pBmi->bmiHeader.biCompression == BI_BITFIELDS) + { + dwFileSize += 3 * sizeof(DWORD); + } + else + { + dwFileSize += DibNumColors(&pBmi->bmiHeader) * sizeof(RGBQUAD); + } + if (dwFileSize != pBmfh->bfOffBits) return NULL; + + // size with bitmap data + if (pBmi->bmiHeader.biCompression != BI_RGB) + { + dwFileSize += pBmi->bmiHeader.biSizeImage; + } + else + { + dwFileSize += WIDTHBYTES(pBmi->bmiHeader.biWidth * pBmi->bmiHeader.biBitCount) + * labs(pBmi->bmiHeader.biHeight); + } + if (pBmp->dwFileSize < dwFileSize) return NULL; + + VERIFY(hBitmap = CreateDIBitmap( + hWindowDC, + &pBmi->bmiHeader, + CBM_INIT, + pBmp->pbyFile + pBmfh->bfOffBits, + pBmi, DIB_RGB_COLORS)); + if (hBitmap == NULL) return NULL; + + if (hPalette == NULL) + { + hPalette = CreateBIPalette(&pBmi->bmiHeader); + // save old palette + hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE); + RealizePalette(hWindowDC); + } + return hBitmap; +} + +static BOOL ReadGifByte(LPBMPFILE pGif, INT *n) +{ + // outside GIF file + if (pGif->dwPos >= pGif->dwFileSize) + return TRUE; + + *n = pGif->pbyFile[pGif->dwPos++]; + return FALSE; +} + +static BOOL ReadGifWord(LPBMPFILE pGif, INT *n) +{ + // outside GIF file + if (pGif->dwPos + 1 >= pGif->dwFileSize) + return TRUE; + + *n = pGif->pbyFile[pGif->dwPos++]; + *n |= (pGif->pbyFile[pGif->dwPos++] << 8); + return FALSE; +} + +static HBITMAP DecodeGif(LPBMPFILE pBmp,DWORD *pdwTransparentColor) +{ + // this implementation base on the GIF image file + // decoder engine of Free42 (c) by Thomas Okken + + HBITMAP hBitmap; + + typedef struct cmap + { + WORD biBitCount; // bits used in color map + DWORD biClrUsed; // no of colors in color map + RGBQUAD bmiColors[256]; // color map + } CMAP; + + BOOL bHasGlobalCmap; + CMAP sGlb; // data of global color map + + INT nWidth,nHeight,nInfo,nBackground,nZero; + LONG lBytesPerLine; + + LPBYTE pbyPixels; + + BITMAPINFO bmi; // global bitmap info + + BOOL bDecoding = TRUE; + + hBitmap = NULL; + + pBmp->dwPos = 6; // position behind GIF header + + /* Bits 6..4 of info contain one less than the "color resolution", + * defined as the number of significant bits per RGB component in + * the source image's color palette. If the source image (from + * which the GIF was generated) was 24-bit true color, the color + * resolution is 8, so the value in bits 6..4 is 7. If the source + * image had an EGA color cube (2x2x2), the color resolution would + * be 2, etc. + * Bit 3 of info must be zero in GIF87a; in GIF89a, if it is set, + * it indicates that the global colormap is sorted, the most + * important entries being first. In PseudoColor environments this + * can be used to make sure to get the most important colors from + * the X server first, to optimize the image's appearance in the + * event that not all the colors from the colormap can actually be + * obtained at the same time. + * The 'zero' field is always 0 in GIF87a; in GIF89a, it indicates + * the pixel aspect ratio, as (PAR + 15) : 64. If PAR is zero, + * this means no aspect ratio information is given, PAR = 1 means + * 1:4 (narrow), PAR = 49 means 1:1 (square), PAR = 255 means + * slightly over 4:1 (wide), etc. + */ + + if ( ReadGifWord(pBmp,&nWidth) + || ReadGifWord(pBmp,&nHeight) + || ReadGifByte(pBmp,&nInfo) + || ReadGifByte(pBmp,&nBackground) + || ReadGifByte(pBmp,&nZero) + || nZero != 0) + goto quit; + + ZeroMemory(&bmi,sizeof(bmi)); // init bitmap info + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = nWidth; + bmi.bmiHeader.biHeight = nHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; // create a true color DIB + bmi.bmiHeader.biCompression = BI_RGB; + + ZeroMemory(&sGlb,sizeof(sGlb)); // init global color map + bHasGlobalCmap = (nInfo & 0x80) != 0; + + sGlb.biBitCount = (nInfo & 7) + 1; // bits used in global color map + sGlb.biClrUsed = (1 << sGlb.biBitCount); // no of colors in global color map + + // color table should not exceed 256 colors + _ASSERT(sGlb.biClrUsed <= ARRAYSIZEOF(sGlb.bmiColors)); + + if (bHasGlobalCmap) // global color map + { + DWORD i; + + for (i = 0; i < sGlb.biClrUsed; ++i) + { + int r, g, b; + + if (ReadGifByte(pBmp,&r) || ReadGifByte(pBmp,&g) || ReadGifByte(pBmp,&b)) + goto quit; + + sGlb.bmiColors[i].rgbRed = r; + sGlb.bmiColors[i].rgbGreen = g; + sGlb.bmiColors[i].rgbBlue = b; + } + } + else // no color map + { + DWORD i; + + for (i = 0; i < sGlb.biClrUsed; ++i) + { + BYTE k = (BYTE) ((i * sGlb.biClrUsed) / (sGlb.biClrUsed - 1)); + + sGlb.bmiColors[i].rgbRed = k; + sGlb.bmiColors[i].rgbGreen = k; + sGlb.bmiColors[i].rgbBlue = k; + } + } + + // bitmap dimensions + lBytesPerLine = WIDTHBYTES(bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount); + bmi.bmiHeader.biSizeImage = lBytesPerLine * bmi.bmiHeader.biHeight; + + // create top-down DIB + bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight; + + // allocate buffer for pixels + VERIFY(hBitmap = CreateDIBSection(hWindowDC, + &bmi, + DIB_RGB_COLORS, + (VOID **)&pbyPixels, + NULL, + 0)); + if (hBitmap == NULL) goto quit; + + // fill pixel buffer with background color + for (nHeight = 0; nHeight < labs(bmi.bmiHeader.biHeight); ++nHeight) + { + LPBYTE pbyLine = pbyPixels + nHeight * lBytesPerLine; + + for (nWidth = 0; nWidth < bmi.bmiHeader.biWidth; ++nWidth) + { + *pbyLine++ = sGlb.bmiColors[nBackground].rgbBlue; + *pbyLine++ = sGlb.bmiColors[nBackground].rgbGreen; + *pbyLine++ = sGlb.bmiColors[nBackground].rgbRed; + } + + _ASSERT((DWORD) (pbyLine - pbyPixels) <= bmi.bmiHeader.biSizeImage); + } + + while (bDecoding) + { + INT nBlockType; + + if (ReadGifByte(pBmp,&nBlockType)) goto quit; + + switch (nBlockType) + { + case ',': // image + { + CMAP sAct; // data of actual color map + + INT nLeft,nTop,nWidth,nHeight; + INT i,nInfo; + + BOOL bInterlaced; + INT h,v; + INT nCodesize; // LZW codesize in bits + INT nBytecount; + + SHORT prefix_table[4096]; + SHORT code_table[4096]; + + INT nMaxcode; + INT nClearCode; + INT nEndCode; + + INT nCurrCodesize; + INT nCurrCode; + INT nOldCode; + INT nBitsNeeded; + BOOL bEndCodeSeen; + + // read image dimensions + if ( ReadGifWord(pBmp,&nLeft) + || ReadGifWord(pBmp,&nTop) + || ReadGifWord(pBmp,&nWidth) + || ReadGifWord(pBmp,&nHeight) + || ReadGifByte(pBmp,&nInfo)) + goto quit; + + if ( nTop + nHeight > labs(bmi.bmiHeader.biHeight) + || nLeft + nWidth > bmi.bmiHeader.biWidth) + goto quit; + + /* Bit 3 of info must be zero in GIF87a; in GIF89a, if it + * is set, it indicates that the local colormap is sorted, + * the most important entries being first. In PseudoColor + * environments this can be used to make sure to get the + * most important colors from the X server first, to + * optimize the image's appearance in the event that not + * all the colors from the colormap can actually be + * obtained at the same time. + */ + + if ((nInfo & 0x80) == 0) // using global color map + { + sAct = sGlb; + } + else // using local color map + { + DWORD i; + + sAct.biBitCount = (nInfo & 7) + 1; // bits used in color map + sAct.biClrUsed = (1 << sAct.biBitCount); // no of colors in color map + + for (i = 0; i < sAct.biClrUsed; ++i) + { + int r, g, b; + + if (ReadGifByte(pBmp,&r) || ReadGifByte(pBmp,&g) || ReadGifByte(pBmp,&b)) + goto quit; + + sAct.bmiColors[i].rgbRed = r; + sAct.bmiColors[i].rgbGreen = g; + sAct.bmiColors[i].rgbBlue = b; + } + } + + // interlaced image + bInterlaced = (nInfo & 0x40) != 0; + + h = 0; + v = 0; + if ( ReadGifByte(pBmp,&nCodesize) + || ReadGifByte(pBmp,&nBytecount)) + goto quit; + + nMaxcode = (1 << nCodesize); + + // preset LZW table + for (i = 0; i < nMaxcode + 2; ++i) + { + prefix_table[i] = -1; + code_table[i] = i; + } + nClearCode = nMaxcode++; + nEndCode = nMaxcode++; + + nCurrCodesize = nCodesize + 1; + nCurrCode = 0; + nOldCode = -1; + nBitsNeeded = nCurrCodesize; + bEndCodeSeen = FALSE; + + while (nBytecount != 0) + { + for (i = 0; i < nBytecount; ++i) + { + INT nCurrByte; + INT nBitsAvailable; + + if (ReadGifByte(pBmp,&nCurrByte)) + goto quit; + + if (bEndCodeSeen) continue; + + nBitsAvailable = 8; + while (nBitsAvailable != 0) + { + INT nBitsCopied = (nBitsNeeded < nBitsAvailable) + ? nBitsNeeded + : nBitsAvailable; + + INT nBits = nCurrByte >> (8 - nBitsAvailable); + + nBits &= 0xFF >> (8 - nBitsCopied); + nCurrCode |= nBits << (nCurrCodesize - nBitsNeeded); + nBitsAvailable -= nBitsCopied; + nBitsNeeded -= nBitsCopied; + + if (nBitsNeeded == 0) + { + BYTE byExpanded[4096]; + INT nExplen; + + do + { + if (nCurrCode == nEndCode) + { + bEndCodeSeen = TRUE; + break; + } + + if (nCurrCode == nClearCode) + { + nMaxcode = (1 << nCodesize) + 2; + nCurrCodesize = nCodesize + 1; + nOldCode = -1; + break; + } + + if (nCurrCode < nMaxcode) + { + if (nMaxcode < 4096 && nOldCode != -1) + { + INT c = nCurrCode; + while (prefix_table[c] != -1) + c = prefix_table[c]; + c = code_table[c]; + prefix_table[nMaxcode] = nOldCode; + code_table[nMaxcode] = c; + nMaxcode++; + if (nMaxcode == (1 << nCurrCodesize) && nCurrCodesize < 12) + nCurrCodesize++; + } + } + else + { + INT c; + + if (nCurrCode > nMaxcode || nOldCode == -1) goto quit; + + _ASSERT(nCurrCode == nMaxcode); + + /* Once maxcode == 4096, we can't get here + * any more, because we refuse to raise + * nCurrCodeSize above 12 -- so we can + * never read a bigger code than 4095. + */ + + c = nOldCode; + while (prefix_table[c] != -1) + c = prefix_table[c]; + c = code_table[c]; + prefix_table[nMaxcode] = nOldCode; + code_table[nMaxcode] = c; + nMaxcode++; + + if (nMaxcode == (1 << nCurrCodesize) && nCurrCodesize < 12) + nCurrCodesize++; + } + nOldCode = nCurrCode; + + // output nCurrCode! + nExplen = 0; + while (nCurrCode != -1) + { + _ASSERT(nExplen < ARRAYSIZEOF(byExpanded)); + byExpanded[nExplen++] = (BYTE) code_table[nCurrCode]; + nCurrCode = prefix_table[nCurrCode]; + } + _ASSERT(nExplen > 0); + + while (--nExplen >= 0) + { + // get color map index + BYTE byColIndex = byExpanded[nExplen]; + + LPBYTE pbyRgbr = pbyPixels + (lBytesPerLine * (nTop + v) + 3 * (nLeft + h)); + + _ASSERT(pbyRgbr + 2 < pbyPixels + bmi.bmiHeader.biSizeImage); + _ASSERT(byColIndex < sAct.biClrUsed); + + *pbyRgbr++ = sAct.bmiColors[byColIndex].rgbBlue; + *pbyRgbr++ = sAct.bmiColors[byColIndex].rgbGreen; + *pbyRgbr = sAct.bmiColors[byColIndex].rgbRed; + + if (++h == nWidth) + { + h = 0; + if (bInterlaced) + { + switch (v & 7) + { + case 0: + v += 8; + if (v < nHeight) + break; + /* Some GIF en/decoders go + * straight from the '0' + * pass to the '4' pass + * without checking the + * height, and blow up on + * 2/3/4 pixel high + * interlaced images. + */ + if (nHeight > 4) + v = 4; + else + if (nHeight > 2) + v = 2; + else + if (nHeight > 1) + v = 1; + else + bEndCodeSeen = TRUE; + break; + case 4: + v += 8; + if (v >= nHeight) + v = 2; + break; + case 2: + case 6: + v += 4; + if (v >= nHeight) + v = 1; + break; + case 1: + case 3: + case 5: + case 7: + v += 2; + if (v >= nHeight) + bEndCodeSeen = TRUE; + break; + } + if (bEndCodeSeen) + break; // while (--nExplen >= 0) + } + else // non interlaced + { + if (++v == nHeight) + { + bEndCodeSeen = TRUE; + break; // while (--nExplen >= 0) + } + } + } + } + } + while (FALSE); + + nCurrCode = 0; + nBitsNeeded = nCurrCodesize; + } + } + } + + if (ReadGifByte(pBmp,&nBytecount)) + goto quit; + } + } + break; + + case '!': // extension block + { + INT i,nFunctionCode,nByteCount,nDummy; + + if (ReadGifByte(pBmp,&nFunctionCode)) goto quit; + if (ReadGifByte(pBmp,&nByteCount)) goto quit; + + // Graphic Control Label & correct Block Size + if (nFunctionCode == 0xF9 && nByteCount == 0x04) + { + INT nPackedFields,nColorIndex; + + // packed fields + if (ReadGifByte(pBmp,&nPackedFields)) goto quit; + + // delay time + if (ReadGifWord(pBmp,&nDummy)) goto quit; + + // transparent color index + if (ReadGifByte(pBmp,&nColorIndex)) goto quit; + + // transparent color flag set + if ((nPackedFields & 0x1) != 0) + { + if (pdwTransparentColor != NULL) + { + *pdwTransparentColor = RGB(sGlb.bmiColors[nColorIndex].rgbRed, + sGlb.bmiColors[nColorIndex].rgbGreen, + sGlb.bmiColors[nColorIndex].rgbBlue); + } + } + + // block terminator (0 byte) + if (!(!ReadGifByte(pBmp,&nDummy) && nDummy == 0)) goto quit; + } + else // other function + { + while (nByteCount != 0) + { + for (i = 0; i < nByteCount; ++i) + { + if (ReadGifByte(pBmp,&nDummy)) goto quit; + } + + if (ReadGifByte(pBmp,&nByteCount)) goto quit; + } + } + } + break; + + case ';': // terminator + bDecoding = FALSE; + break; + + default: goto quit; + } + } + + _ASSERT(bDecoding == FALSE); // decoding successful + + // normal decoding exit + if (hPalette == NULL) + { + hPalette = CreateBIPalette((PBITMAPINFOHEADER) &bmi); + // save old palette + hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE); + RealizePalette(hWindowDC); + } + +quit: + if (hBitmap != NULL && bDecoding) // creation failed + { + DeleteObject(hBitmap); // delete bitmap + hBitmap = NULL; + } + return hBitmap; +} + +static HBITMAP DecodePng(LPBMPFILE pBmp) +{ + // this implementation use the PNG image file decoder + // engine of Copyright (c) 2005-2018 Lode Vandevenne + + HBITMAP hBitmap; + + UINT uError,uWidth,uHeight; + INT nWidth,nHeight; + LONG lBytesPerLine; + + LPBYTE pbyImage; // PNG RGB image data + LPBYTE pbySrc; // source buffer pointer + LPBYTE pbyPixels; // BMP buffer + + BITMAPINFO bmi; + + hBitmap = NULL; + pbyImage = NULL; + + // decode PNG image + uError = lodepng_decode_memory(&pbyImage,&uWidth,&uHeight,pBmp->pbyFile,pBmp->dwFileSize,LCT_RGB,8); + if (uError) goto quit; + + ZeroMemory(&bmi,sizeof(bmi)); // init bitmap info + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = (LONG) uWidth; + bmi.bmiHeader.biHeight = (LONG) uHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; // create a true color DIB + bmi.bmiHeader.biCompression = BI_RGB; + + // bitmap dimensions + lBytesPerLine = WIDTHBYTES(bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount); + bmi.bmiHeader.biSizeImage = lBytesPerLine * bmi.bmiHeader.biHeight; + + // allocate buffer for pixels + VERIFY(hBitmap = CreateDIBSection(hWindowDC, + &bmi, + DIB_RGB_COLORS, + (VOID **)&pbyPixels, + NULL, + 0)); + if (hBitmap == NULL) goto quit; + + pbySrc = pbyImage; // init source loop pointer + + // fill bottom up DIB pixel buffer with color information + for (nHeight = bmi.bmiHeader.biHeight - 1; nHeight >= 0; --nHeight) + { + LPBYTE pbyLine = pbyPixels + nHeight * lBytesPerLine; + + for (nWidth = 0; nWidth < bmi.bmiHeader.biWidth; ++nWidth) + { + *pbyLine++ = pbySrc[2]; // blue + *pbyLine++ = pbySrc[1]; // green + *pbyLine++ = pbySrc[0]; // red + pbySrc += 3; + } + + _ASSERT((DWORD) (pbyLine - pbyPixels) <= bmi.bmiHeader.biSizeImage); + } + + if (hPalette == NULL) + { + hPalette = CreateBIPalette((PBITMAPINFOHEADER) &bmi); + // save old palette + hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE); + RealizePalette(hWindowDC); + } + +quit: + if (pbyImage != NULL) // buffer for PNG image allocated + { + free(pbyImage); // free PNG image data + } + + if (hBitmap != NULL && uError != 0) // creation failed + { + DeleteObject(hBitmap); // delete bitmap + hBitmap = NULL; + } + return hBitmap; +} + +HBITMAP LoadBitmapFile(LPCTSTR szFilename) +{ + HANDLE hFile; + HANDLE hMap; + BMPFILE Bmp; + HBITMAP hBitmap; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) return NULL; + Bmp.dwFileSize = GetFileSize(hFile, NULL); + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap == NULL) + { + CloseHandle(hFile); + return NULL; + } + Bmp.pbyFile = (LPBYTE) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if (Bmp.pbyFile == NULL) + { + CloseHandle(hMap); + CloseHandle(hFile); + return NULL; + } + + do + { + // check for bitmap file header "BM" + if (Bmp.dwFileSize >= 2 && *(WORD *) Bmp.pbyFile == 0x4D42) + { + hBitmap = DecodeBmp(&Bmp); + break; + } + + // check for GIF file header + if ( Bmp.dwFileSize >= 6 + && (memcmp(Bmp.pbyFile,"GIF87a",6) == 0 || memcmp(Bmp.pbyFile,"GIF89a",6) == 0)) + { + hBitmap = DecodeGif(&Bmp,&dwTColor); + break; + } + + // check for PNG file header + if (Bmp.dwFileSize >= 8 && memcmp(Bmp.pbyFile,"\x89PNG\r\n\x1a\n",8) == 0) + { + hBitmap = DecodePng(&Bmp); + break; + } + + // unknown file type + hBitmap = NULL; + } + while (FALSE); + + UnmapViewOfFile(Bmp.pbyFile); + CloseHandle(hMap); + CloseHandle(hFile); + return hBitmap; +} + +static BOOL AbsColorCmp(DWORD dwColor1,DWORD dwColor2,DWORD dwTol) +{ + DWORD dwDiff; + + dwDiff = (DWORD) abs((INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF)); + dwColor1 >>= 8; + dwColor2 >>= 8; + dwDiff += (DWORD) abs((INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF)); + dwColor1 >>= 8; + dwColor2 >>= 8; + dwDiff += (DWORD) abs((INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF)); + + return dwDiff > dwTol; // FALSE = colors match +} + +static BOOL LabColorCmp(DWORD dwColor1,DWORD dwColor2,DWORD dwTol) +{ + DWORD dwDiff; + INT nDiffCol; + + nDiffCol = (INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF); + dwDiff = (DWORD) (nDiffCol * nDiffCol); + dwColor1 >>= 8; + dwColor2 >>= 8; + nDiffCol = (INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF); + dwDiff += (DWORD) (nDiffCol * nDiffCol); + dwColor1 >>= 8; + dwColor2 >>= 8; + nDiffCol = (INT) (dwColor1 & 0xFF) - (INT) (dwColor2 & 0xFF); + dwDiff += (DWORD) (nDiffCol * nDiffCol); + dwTol *= dwTol; + + return dwDiff > dwTol; // FALSE = colors match +} + +static DWORD EncodeColorBits(DWORD dwColorVal,DWORD dwMask) +{ + #define MAXBIT 32 + UINT uLshift = MAXBIT; + UINT uRshift = 8; + DWORD dwBitMask = dwMask; + + dwColorVal &= 0xFF; // the color component using the lowest 8 bit + + // position of highest bit + while ((dwBitMask & (1<<(MAXBIT-1))) == 0 && uLshift > 0) + { + dwBitMask <<= 1; // next bit + --uLshift; // next position + } + + if (uLshift > 24) // avoid overflow on 32bit mask + { + uLshift -= uRshift; // normalize left shift + uRshift = 0; + } + + return ((dwColorVal << uLshift) >> uRshift) & dwMask; + #undef MAXBIT +} + +HRGN CreateRgnFromBitmap(HBITMAP hBmp,COLORREF color,DWORD dwTol) +{ + #define ADD_RECTS_COUNT 256 + + BOOL (*fnColorCmp)(DWORD dwColor1,DWORD dwColor2,DWORD dwTol); + + DWORD dwRed,dwGreen,dwBlue; + HRGN hRgn; + LPRGNDATA pRgnData; + LPBITMAPINFO bi; + LPBYTE pbyBits; + LPBYTE pbyColor; + DWORD dwAlignedWidthBytes; + DWORD dwBpp; + DWORD dwRectsCount; + LONG x,y,xleft; + BOOL bFoundLeft; + BOOL bIsMask; + + if (dwTol >= 1000) // use CIE L*a*b compare + { + fnColorCmp = LabColorCmp; + dwTol -= 1000; // remove L*a*b compare selector + } + else // use Abs summation compare + { + fnColorCmp = AbsColorCmp; + } + + // allocate memory for extended image information incl. RGBQUAD color table + bi = (LPBITMAPINFO) calloc(1,sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); + bi->bmiHeader.biSize = sizeof(bi->bmiHeader); + _ASSERT(bi->bmiHeader.biBitCount == 0); // for query without color table + + // get information about image + GetDIBits(hWindowDC,hBmp,0,0,NULL,bi,DIB_RGB_COLORS); + + // DWORD aligned bitmap width in BYTES + dwAlignedWidthBytes = WIDTHBYTES( bi->bmiHeader.biWidth + * bi->bmiHeader.biPlanes + * bi->bmiHeader.biBitCount); + + // biSizeImage is empty + if (bi->bmiHeader.biSizeImage == 0 && bi->bmiHeader.biCompression == BI_RGB) + { + bi->bmiHeader.biSizeImage = dwAlignedWidthBytes * bi->bmiHeader.biHeight; + } + + // allocate memory for image data (colors) + pbyBits = (LPBYTE) malloc(bi->bmiHeader.biSizeImage); + + // fill bits buffer + GetDIBits(hWindowDC,hBmp,0,bi->bmiHeader.biHeight,pbyBits,bi,DIB_RGB_COLORS); + + // convert color if current DC is 16-bit/32-bit bitfield coded + if (bi->bmiHeader.biCompression == BI_BITFIELDS) + { + dwRed = *(LPDWORD) &bi->bmiColors[0]; + dwGreen = *(LPDWORD) &bi->bmiColors[1]; + dwBlue = *(LPDWORD) &bi->bmiColors[2]; + } + else // RGB coded + { + // convert color if current DC is 16-bit RGB coded + if (bi->bmiHeader.biBitCount == 16) + { + // for 15 bit (5:5:5) + dwRed = 0x00007C00; + dwGreen = 0x000003E0; + dwBlue = 0x0000001F; + } + else + { + // convert COLORREF to RGBQUAD color + dwRed = 0x00FF0000; + dwGreen = 0x0000FF00; + dwBlue = 0x000000FF; + } + } + color = EncodeColorBits((color >> 16), dwBlue) + | EncodeColorBits((color >> 8), dwGreen) + | EncodeColorBits((color >> 0), dwRed); + + dwBpp = bi->bmiHeader.biBitCount >> 3; // bytes per pixel + + // DIB is bottom up image so we begin with the last scanline + pbyColor = pbyBits + (bi->bmiHeader.biHeight - 1) * dwAlignedWidthBytes; + + dwRectsCount = bi->bmiHeader.biHeight; // number of rects in allocated buffer + + bFoundLeft = FALSE; // set when mask has been found in current scan line + + // allocate memory for region data + pRgnData = (PRGNDATA) malloc(sizeof(RGNDATAHEADER) + dwRectsCount * sizeof(RECT)); + + // fill it by default + ZeroMemory(&pRgnData->rdh,sizeof(pRgnData->rdh)); + pRgnData->rdh.dwSize = sizeof(pRgnData->rdh); + pRgnData->rdh.iType = RDH_RECTANGLES; + SetRect(&pRgnData->rdh.rcBound,MAXLONG,MAXLONG,0,0); + + for (y = 0; y < bi->bmiHeader.biHeight; ++y) + { + LPBYTE pbyLineStart = pbyColor; + + for (x = 0; x < bi->bmiHeader.biWidth; ++x) + { + // get color + switch (bi->bmiHeader.biBitCount) + { + case 8: + bIsMask = fnColorCmp(*(LPDWORD)(&bi->bmiColors)[*pbyColor],color,dwTol); + break; + case 16: + // it makes no sense to allow a tolerance here + bIsMask = (*(LPWORD)pbyColor != (WORD) color); + break; + case 24: + bIsMask = fnColorCmp((*(LPDWORD)pbyColor & 0x00ffffff),color,dwTol); + break; + case 32: + bIsMask = fnColorCmp(*(LPDWORD)pbyColor,color,dwTol); + } + pbyColor += dwBpp; // shift pointer to next color + + if (!bFoundLeft && bIsMask) // non transparent color found + { + xleft = x; + bFoundLeft = TRUE; + } + + if (bFoundLeft) // found non transparent color in scanline + { + // transparent color or last column + if (!bIsMask || x + 1 == bi->bmiHeader.biWidth) + { + // non transparent color and last column + if (bIsMask && x + 1 == bi->bmiHeader.biWidth) + ++x; + + // save current RECT + ((LPRECT) pRgnData->Buffer)[pRgnData->rdh.nCount].left = xleft; + ((LPRECT) pRgnData->Buffer)[pRgnData->rdh.nCount].top = y; + ((LPRECT) pRgnData->Buffer)[pRgnData->rdh.nCount].right = x; + ((LPRECT) pRgnData->Buffer)[pRgnData->rdh.nCount].bottom = y + 1; + pRgnData->rdh.nCount++; + + if (xleft < pRgnData->rdh.rcBound.left) + pRgnData->rdh.rcBound.left = xleft; + + if (y < pRgnData->rdh.rcBound.top) + pRgnData->rdh.rcBound.top = y; + + if (x > pRgnData->rdh.rcBound.right) + pRgnData->rdh.rcBound.right = x; + + if (y + 1 > pRgnData->rdh.rcBound.bottom) + pRgnData->rdh.rcBound.bottom = y + 1; + + // if buffer full reallocate it with more room + if (pRgnData->rdh.nCount >= dwRectsCount) + { + dwRectsCount += ADD_RECTS_COUNT; + + pRgnData = (LPRGNDATA) realloc(pRgnData,sizeof(RGNDATAHEADER) + dwRectsCount * sizeof(RECT)); + } + + bFoundLeft = FALSE; + } + } + } + + // previous scanline + pbyColor = pbyLineStart - dwAlignedWidthBytes; + } + // release image data + free(pbyBits); + free(bi); + + // create region + hRgn = ExtCreateRegion(NULL,sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount * sizeof(RECT),pRgnData); + + free(pRgnData); + return hRgn; + #undef ADD_RECTS_COUNT +} diff --git a/app/src/main/cpp/I28F160.C b/app/src/main/cpp/I28F160.C new file mode 100644 index 0000000..b7175c9 --- /dev/null +++ b/app/src/main/cpp/I28F160.C @@ -0,0 +1,754 @@ +/* + * i28f160.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "i28f160.h" + +#define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) + +// Flash Command Set +#define READ_ARRAY 0xFF +#define READ_ID_CODES 0x90 +#define READ_QUERY 0x98 +#define READ_STATUS_REG 0x70 +#define CLEAR_STATUS_REG 0x50 +#define WRITE_BUFFER 0xE8 +#define WORD_BYTE_PROG1 0x40 +#define WORD_BYTE_PROG2 0x10 +#define BLOCK_ERASE 0x20 +#define BLOCK_ERASE_SUSPEND 0xB0 +#define BLOCK_ERASE_RESUME 0xD0 +#define STS_CONFIG 0xB8 +#define SET_CLR_BLOCK_LOCK 0x60 +#define FULL_CHIP_ERASE 0x30 + +#define CONFIRM 0xD0 + +// Status Register Definition +#define WSMS 0x80 // WRITE STATE MACHINE STATUS +#define ESS 0x40 // ERASE SUSPEND STATUS +#define ECLBS 0x20 // ERASE AND CLEAR LOCK-BIT STATUS +#define BWSLBS 0x10 // PROGRAM AND SET LOCK-BIT STATUS +#define VPPS 0x08 // Vpp STATUS +#define BWSS 0x04 // PROGRAM SUSPEND STATUS +#define DPS 0x02 // DEVICE PROTECT STATUS + +// Extended Status Register Definition +#define WBS 0x80 // WRITE BUFFER STATUS + +// write state defines +#define WRS_DATA 0 // idle state +#define WRS_WR_BUFFER_N 1 // write buffer no. of data +#define WRS_WR_BUFFER_D 2 // write buffer data +#define WRS_WR_BUFFER_C 3 // write buffer confirm +#define WRS_WR_BYTE 4 // write byte/word +#define WRS_BLOCK_ERASE 5 // block erase +#define WRS_CHIP_ERASE 6 // full chip erase +#define WRS_STS_PIN_CONFIG 7 // STS pin Configuration +#define WRS_LOCK_BITS 8 // Set/Clear Block Lock-Bits + +// read state defines +#define RDS_DATA 0 // data read +#define RDS_ID 1 // read identifier codes +#define RDS_QUERY 2 // read query +#define RDS_SR 3 // read status register +#define RDS_XSR 4 // read extended status register + +// global data +WSMSET WSMset; +BOOL bWP = FALSE; // WP# = low, locked blocks cannot be erased + +// function prototypes +// write function WSM state +static VOID WrStateIdle(BYTE a, DWORD d); +static VOID WrStateE8(DWORD d); +static VOID WrStateE8N(BYTE a, DWORD d); +static VOID WrStateE8D(BYTE a, DWORD d); +static VOID WrStateE8C(BYTE a, DWORD d); +static VOID WrState40(DWORD d); +static VOID WrState40D(BYTE a, DWORD d); +static VOID WrState20(DWORD d); +static VOID WrState20C(BYTE a, DWORD d); +static VOID WrState30(DWORD d); +static VOID WrState30C(BYTE a, DWORD d); +static VOID WrStateB8(DWORD d); +static VOID WrStateB8D(BYTE a, DWORD d); +static VOID WrState60(DWORD d); +static VOID WrState60D(BYTE a, DWORD d); + +static VOID (*CONST fnWrState[])(BYTE a, DWORD d) = +{ + WrStateIdle, + WrStateE8N, WrStateE8D, WrStateE8C, + WrState40D, + WrState20C, + WrState30C, + WrStateB8D, + WrState60D +}; + +// read function WSM state +static BYTE RdStateData(DWORD d); +static BYTE RdStateId(DWORD d); +static BYTE RdStateQuery(DWORD d); +static BYTE RdStateSR(DWORD d); +static BYTE RdStateXSR(DWORD d); + +static BYTE (*CONST fnRdState[])(DWORD d) = +{ + RdStateData, RdStateId, RdStateQuery, RdStateSR, RdStateXSR +}; + + +// read query table +// device address A16-A1, A0 unused +static CONST BYTE byQueryTab[] = +{ + // access with "Read Identifier Codes" command + // Identifier codes + 0xB0, // 00, Manufacturer Code + 0xD0, // 01, Device Code (16 Mbit) + 0x00, // 02, Block Lock Configuration + 0x02, // 03, ?? + + 0x00, // 04, Reserved for vendor-specific information + 0x00, // 05, " + 0x00, // 06, " + 0x00, // 07, " + 0x00, // 08, " + 0x00, // 09, " + 0x00, // 0A, " + 0x00, // 0B, " + 0x00, // 0C, " + 0x00, // 0D, " + 0x00, // 0E, " + 0x00, // 0F, " + + // access with "Read Query" command + // CFI query identification string + 0x51, // 10, Query-Unique ASCII string "Q" + 0x52, // 11, Query-Unique ASCII string "R" + 0x59, // 12, Query-Unique ASCII string "Y" + 0x01, // 13, Primary Vendor Command Set and Control Interface ID CODE + 0x00, // 14, " + 0x31, // 15, Address for Primary Algorithm Extended Query Table + 0x00, // 16, " + 0x00, // 17, Alternate Vendor Command Set and Control Interface ID Code + 0x00, // 18, " + 0x00, // 19, Address for Secondary Algorithm Extended Query Table + 0x00, // 1A, " + + // System interface information + 0x30, // 1B, Vcc Logic Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) + 0x55, // 1C, Vcc Logic Supply Maximum Program/Erase Voltage + 0x30, // 1D, Vpp [Programming] Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) + 0x55, // 1E, Vpp [Programming] Supply Maximum Program/Erase Voltage + 0x03, // 1F, Typical Time-Out per Single Byte/Word Program + 0x06, // 20, Typical Time-Out for Max. Buffer Write + 0x0A, // 21, Typical Time-Out per Individual Block Erase + 0x0F, // 22, Typical Time-Out for Full Chip Erase + 0x04, // 23, Maximum Time-Out for Byte/Word Program + 0x04, // 24, Maximum Time-Out for Buffer Write + 0x04, // 25, Maximum Time-Out per Individual Block Erase + 0x04, // 26, Maximum Time-Out for Full Chip Erase + 0x15, // 27, Device Size + 0x02, // 28, Flash Device Interface Description + 0x00, // 29, " + 0x05, // 2A, Maximum Number of Bytes in Write Buffer + 0x00, // 2B, " + 0x01, // 2C, Number of Erase Block Regions within Device + 0x1F, // 2D, Erase Block Region Information + 0x00, // 2E, " + 0x00, // 2F, " + 0x01, // 30, " + + // Intel-specific extended query table + 0x50, // 31, Primary Extended Query Table, Unique ASCII string "P" + 0x52, // 32, Primary Extended Query Table, Unique ASCII string "R" + 0x49, // 33, Primary Extended Query Table, Unique ASCII string "I" + 0x31, // 34, Major Version Number, ASCII + 0x30, // 35, Minor Version Number, ASCII + 0x0F, // 36, Optional Feature & Command Support + 0x00, // 37, " + 0x00, // 38, " + 0x00, // 39, " + 0x01, // 3A, Supported Functions after Suspend + 0x03, // 3B, Block Status Register Mask + 0x00, // 3C, " + 0x50, // 3D, Vcc Logic Supply Optimum Program/Erase voltage + 0x50, // 3E, Vpp [Programming] Supply Optimum Program/Erase voltage + 0x00 // 3F, ?? +}; + + +// +// ROM buffer access functions +// + +static __inline void WrDirtyPage(DWORD d) +{ + if (pbyRomDirtyPage) // using dirty ROM page table + { + DWORD dwPage = d / ROMPAGESIZE; // this is the page + + _ASSERT(dwPage < dwRomDirtyPageSize); + pbyRomDirtyPage[dwPage] = TRUE; // page is dirty + } + return; +} + +static __inline void EraseBlock(DWORD d,DWORD dwNibSize) +{ + LPBYTE pbyAddr = pbyRom + d; + + while (dwNibSize--) + { + WrDirtyPage(d++); // make page dirty + *pbyAddr++ = 0x0F; // clear address + } + return; +} + +static __inline void WriteByte(DWORD d,BYTE byData) +{ + WrDirtyPage(d); // make page dirty + + _ASSERT(d+1 < dwRomSize); // address valid? + *(pbyRom+d) &= (byData & 0x0F); // write LSB + *(pbyRom+d+1) &= (byData >> 4); // write MSB + return; +} + +static __inline BYTE ReadByte(DWORD d) +{ + _ASSERT(d+1 < dwRomSize); // address valid? + return *(pbyRom+d)|(*(pbyRom+d+1)<<4); // get byte +} + + +// +// write state functions +// + +static VOID WrStateIdle(BYTE a, DWORD d) +{ + WSMset.bRomArray = FALSE; // register access + + switch(a) + { + case READ_ARRAY: // read array mode, normal operation + WSMset.bRomArray = TRUE; // data array access + WSMset.uWrState = WRS_DATA; + WSMset.uRdState = RDS_DATA; + break; + case READ_ID_CODES: // read identifier codes register + WSMset.uRdState = RDS_ID; + break; + case READ_QUERY: // read query register + WSMset.uRdState = RDS_QUERY; + break; + case READ_STATUS_REG: // read status register + WSMset.uRdState = RDS_SR; + break; + case CLEAR_STATUS_REG: // clear status register + WSMset.byStatusReg = 0; + break; + case WRITE_BUFFER: // write to buffer + WrStateE8(d); + break; + case WORD_BYTE_PROG1: + case WORD_BYTE_PROG2: // byte/word program + WrState40(d); + break; + case BLOCK_ERASE: // block erase + WrState20(d); + break; + case BLOCK_ERASE_SUSPEND: // block erase, word/byte program suspend + WSMset.byStatusReg |= WSMS; // operation suspended + WSMset.byStatusReg &= ~ESS; // block erase completed (because no timing emulation) + WSMset.byStatusReg &= ~BWSS; // program completed (because no timing emulation) + WSMset.uRdState = RDS_SR; + break; + case BLOCK_ERASE_RESUME: // block erase, word/byte program resume + WSMset.byStatusReg &= ~WSMS; // operation in progress + WSMset.byStatusReg &= ~ESS; // block erase in progress + WSMset.byStatusReg &= ~BWSS; // program in progress + WSMset.byStatusReg |= WSMS; // operation completed (because no timing emulation) + WSMset.uRdState = RDS_SR; + break; + case STS_CONFIG: + WSMset.bRomArray = bFlashRomArray; // old access mode + WrStateB8(d); + break; + case SET_CLR_BLOCK_LOCK: // set/clear block lock-bits + WrState60(d); + break; + case FULL_CHIP_ERASE: // full chip erase + WrState30(d); + break; + default: // wrong command + WSMset.bRomArray = bFlashRomArray; // old access mode + break; + } + + if (bFlashRomArray != WSMset.bRomArray) // new access mode + { + bFlashRomArray = WSMset.bRomArray; // change register access + Map(0x00,0xFF); // update memory mapping + UpdatePatches(bFlashRomArray); // patch/unpatch ROM again + } + return; +} + +// write to buffer initial command +static VOID WrStateE8(DWORD d) +{ + // @todo add 2nd write buffer implementation + // @todo add program timing implementation + + WSMset.byExStatusReg = 0; // no write buffer + if (WSMset.byWrite1No == 0) // buffer1 available + { + WSMset.byWrite1No = 1; // buffer1 in use + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.byExStatusReg = WBS; // write buffer available + // fill write buffer + FillMemory(WSMset.pbyWrite1,ARRAYSIZEOF(WSMset.pbyWrite1),0xFF); + WSMset.uWrState = WRS_WR_BUFFER_N; // set state machine + WSMset.uRdState = RDS_XSR; + } + return; +} + +// write to buffer number of byte +static VOID WrStateE8N(BYTE a, DWORD d) +{ + if (a < (1 << byQueryTab[0x2A])) // byte is length information + { + WSMset.byWrite1No += a; // save no. of byte to program + WSMset.byWrite1Size = a; // save size to check write buffer boundaries + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_WR_BUFFER_D; + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + } + WSMset.uRdState = RDS_SR; + return; +} + +// write to buffer data +static VOID WrStateE8D(BYTE a, DWORD d) +{ + // first data byte + if (WSMset.byWrite1No == WSMset.byWrite1Size + 1) + { + DWORD dwBlockMask = ~(((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256 - 1); + + // same block + if ((WSMset.dwWrite1Addr & dwBlockMask) == (d & dwBlockMask)) + { + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.pbyWrite1[0] = a; // save byte + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; + } + } + else + { + // write address within buffer + if (d >= WSMset.dwWrite1Addr && d <= WSMset.dwWrite1Addr + WSMset.byWrite1Size) + { + // save byte in buffer + WSMset.pbyWrite1[d-WSMset.dwWrite1Addr] = a; + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; + } + } + + if (--WSMset.byWrite1No == 0) // last byte written + WSMset.uWrState = WRS_WR_BUFFER_C; // goto confirm state + return; +} + +// write to buffer confirm +static VOID WrStateE8C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // write buffer confirm? + { + BYTE byPos; + + d = WSMset.dwWrite1Addr << 1; // nibble start address + + for (byPos = 0; byPos <= WSMset.byWrite1Size; ++byPos) + { + a = WSMset.pbyWrite1[byPos]; // get char from buffer + + _ASSERT(d+1 < dwRomSize); // address valid? + // no error set in BWSLBS, because I could alway program a "0" + WriteByte(d,a); // write byte + d += 2; // next address + } + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; +} + +// byte/word program initial command +static VOID WrState40(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_WR_BYTE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// byte/word program data +static VOID WrState40D(BYTE a, DWORD d) +{ + // no error set in BWSLBS, because I could alway program a "0" + WriteByte(d << 1,a); // write byte + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; +} + +// block erase initial command +static VOID WrState20(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_BLOCK_ERASE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// block erase data & confirm +static VOID WrState20C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // block erase confirm? + { + // lock bit of block is set + if ((WSMset.dwLockCnfg & (1<<(d>>16))) != 0) + { + WSMset.byStatusReg |= ECLBS; // error in block erasure + WSMset.byStatusReg |= DPS; // lock bit detected + } + else + { + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; + + d &= ~(dwBlockSize-1); // start of block + dwBlockSize *= 2; // block size in nibbles + _ASSERT(d+dwBlockSize <= dwRomSize); // address valid? + EraseBlock(d << 1,dwBlockSize); // erase 128K nibble + } + } + else + { + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // block erased + WSMset.uWrState = WRS_DATA; + return; +} + +// full chip erase initial command +static VOID WrState30(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_CHIP_ERASE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// full chip erase confirm +static VOID WrState30C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // chip erase confirm? + { + UINT i; + + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; + + DWORD dwBlockAddr = 0; + + dwBlockSize *= 2; // block size in nibbles + + for (i = 0; i <= wNoOfBlocks; ++i) // check all blocks + { + _ASSERT((i+1)*dwBlockSize <= dwRomSize); + + // lock bit of block is set & WP# = low, locked blocks cannot be erased + if ((WSMset.dwLockCnfg & (1<>16)); // set block lock bit + else + WSMset.byStatusReg |= (BWSLBS | DPS); // device protect detect, WP# = low + break; + case CONFIRM: // clear block lock bits + if (bWP) // WP# = high, can change block lock status + { + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + + for (i = 0; i <= wNoOfBlocks; ++i) // clear all lock bits + { + WSMset.dwLockCnfg &= ~(1 << i); // clear block lock bit + } + } + else + { + WSMset.byStatusReg |= (ECLBS | DPS); // device protect detect, WP# = low + } + break; + default: // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // block lock-bit changed + WSMset.uWrState = WRS_DATA; + return; +} + + +// +// read state functions +// + +// read array +static BYTE RdStateData(DWORD d) +{ + return ReadByte(d << 1); // get byte +} + +// read identifier codes +static BYTE RdStateId(DWORD d) +{ + BYTE byData; + + d >>= 1; // A0 is not connected, ignore it + if ((d & 0x03) != 0x02) // id code request + { + d &= 0x03; // data repetition + byData = byQueryTab[d]; // get data from first 4 bytes id/query table + } + else // block lock table + { + // get data from block lock table + byData = (BYTE) ((WSMset.dwLockCnfg >> (d >> 15)) & 1); + + d &= 0x1F; // data repetition + if (d >= 4) byData |= 0x02; // set bit 1 on wrong ID adress + } + return byData; +} + +// read query +static BYTE RdStateQuery(DWORD d) +{ + BYTE byData; + + d >>= 1; // A0 is not connected, ignore it + if ((d & 0x7F) != 0x02) // query request + { + d &= 0x7F; // data repetition + + // get data from id/query table + byData = (d >= 0x40 && d < 0x50) ? 0 : byQueryTab[d&0x3F]; + } + else // block lock table + { + // get data from block lock table + byData = (BYTE) ((WSMset.dwLockCnfg >> (d >> 15)) & 1); + } + return byData; +} + +// read status register +static BYTE RdStateSR(DWORD d) +{ + return WSMset.byStatusReg; + UNREFERENCED_PARAMETER(d); +} + +// read extended status register +static BYTE RdStateXSR(DWORD d) +{ + return WSMset.byExStatusReg; + UNREFERENCED_PARAMETER(d); +} + + +// +// public functions +// + +VOID FlashInit(VOID) +{ + // check if locking bit table has more or equal than 32 bit + _ASSERT(sizeof(WSMset.dwLockCnfg) * 8 >= 32); + + ZeroMemory(&WSMset,sizeof(WSMset)); + strcpy((LPSTR) WSMset.byType,"WSM"); // Write State Machine header + WSMset.uSize = sizeof(WSMset); // size of this structure + WSMset.byVersion = WSMVER; // version of flash implementation structure + + // factory setting of locking bits + WSMset.dwLockCnfg = (1 << 0); // first 64KB block is locked + + WSMset.uWrState = WRS_DATA; + WSMset.uRdState = RDS_DATA; + + // data mode of ROM + WSMset.bRomArray = bFlashRomArray = TRUE; + return; +} + +VOID FlashRead(BYTE *a, DWORD d, UINT s) +{ + BYTE v; + + while (s) // each nibble + { + // output muliplexer + _ASSERT(WSMset.uRdState < ARRAYSIZEOF(fnRdState)); + v = fnRdState[WSMset.uRdState](d>>1); + + if ((d & 1) == 0) // even address + { + *a++ = v & 0xf; ++d; --s; + } + if (s && (d & 1)) // odd address + { + *a++ = v >> 4; ++d; --s; + } + } + return; +} + +VOID FlashWrite(BYTE *a, DWORD d, UINT s) +{ + BYTE v; + DWORD p; + + while (s) // each nibble + { + p = d >> 1; // byte address + if (s > 1 && (d & 1) == 0) // more than one byte on even address + { + v = *a++; // LSB + v |= *a++ << 4; // MSB + d += 2; s -= 2; + } + else + { + // get byte from output muliplexer + _ASSERT(WSMset.uRdState < ARRAYSIZEOF(fnRdState)); + v = fnRdState[WSMset.uRdState](p); + + if (d & 1) // odd address + v = (v & 0x0F) | (*a << 4); // replace MSB + else // even address + v = (v & 0xF0) | *a; // replace LSB + ++a; ++d; --s; + } + + _ASSERT(WSMset.uWrState < ARRAYSIZEOF(fnWrState)); + fnWrState[WSMset.uWrState](v,p); // WSM + } + return; +} diff --git a/app/src/main/cpp/I28F160.H b/app/src/main/cpp/I28F160.H new file mode 100644 index 0000000..a164678 --- /dev/null +++ b/app/src/main/cpp/I28F160.H @@ -0,0 +1,40 @@ +/* + * i28f160.h + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#define WSMVER 0 // version of flash implementation structure + +#define WSMSET WSMset_t +typedef struct +{ + BYTE byType[4]; // "WSM" + UINT uSize; // size of this structure + BYTE byVersion; // WSM version + + BOOL bRomArray; // copy of bFlashRomArray + DWORD dwLockCnfg; // block lock table (32 entries) + UINT uWrState; // state of write function WSM + UINT uRdState; // state of read function WSM + BYTE byStatusReg; // status register + BYTE byExStatusReg; // extended status register + BYTE byWrite1No; // no. of written data in write buffer1 + BYTE byWrite1Size; // no. of valid data in write buffer1 + DWORD dwWrite1Addr; // destination address of buffer1 + BYTE pbyWrite1[32]; // write buffer1 +// BYTE byWrite2No; // no. of written data in write buffer2 +// BYTE byWrite2Size; // no. of valid data in write buffer2 +// DWORD dwWrite2Addr; // destination address of buffer2 +// BYTE pbyWrite2[32]; // write buffer2 +} WSMset_t; + +// i28f160.h +extern WSMSET WSMset; +extern BOOL bWP; +extern VOID FlashInit(VOID); +extern VOID FlashRead(BYTE *a, DWORD d, UINT s); +extern VOID FlashWrite(BYTE *a, DWORD d, UINT s); diff --git a/app/src/main/cpp/IO.H b/app/src/main/cpp/IO.H new file mode 100644 index 0000000..b5a4bda --- /dev/null +++ b/app/src/main/cpp/IO.H @@ -0,0 +1,154 @@ +/* + * io.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +// I/O addresses without mapping offset +#define BITOFFSET 0x00 // Display bit offset and DON +#define CRC 0x04 // Crc (16 bit, LSB first) +#define LPD 0x08 // Low Power Detection +#define LPE 0x09 // Low Power detection Enable +#define ANNCTRL 0x0b // Annunciator Control (2 nibble) +#define BAUD 0x0d // Baudrate (Bit 2-0) +#define CARDCTL 0x0e // card control +#define CARDSTAT 0x0f // card status +#define IOC 0x10 // IO CONTROL +#define RCS 0x11 // RCS +#define TCS 0x12 // TCS +#define CRER 0x13 // CRER +#define RBR_LSB 0x14 // RBR low nibble +#define RBR_MSB 0x15 // RBR high nibble +#define TBR_LSB 0x16 // TBR low nibble +#define TBR_MSB 0x17 // TBR high nibble +#define SRQ1 0x18 // SRQ1 +#define SRQ2 0x19 // SRQ2 +#define IR_CTRL 0x1a // IR CONTROL +#define LCR 0x1c // Led Control Register +#define LBR 0x1d // Led Buffer Register +#define DISP1CTL 0x20 // Display Start Address +#define LINENIBS 0x25 // Display Line Offset +#define LINECOUNT 0x28 // Display Line Counter +#define TIMER1_CTRL 0x2e // Timer1 Control +#define TIMER2_CTRL 0x2f // Timer2 Control +#define DISP2CTL 0x30 // Display Secondary Start Address +#define TIMER1 0x37 // Timer1 (4 bit) +#define TIMER2 0x38 // Timer2 (32 bit, LSB first) + +// 0x00 Display bit offset and DON [DON OFF2 OFF1 OFF0] +#define DON 0x08 // Display On +#define OFF2 0x04 // Display OFFset Bit2 +#define OFF1 0x02 // Display OFFset Bit1 +#define OFF0 0x01 // Display OFFset Bit0 + +// 0x08 Low Power Detection [LB2 LB1 LB0 VLBI] +#define LB2 0x08 // Low Battery indicator memory port 2 +#define LB1 0x04 // Low Battery indicator memory port 1 +#define LB0 0x02 // Low Battery indicator system battery +#define VLBI 0x01 // Very Low Battery Indicator + +// 0x09 Low Power detection Enable [ELBI EVLBI GRAM RST] +#define ELBI 0x08 // Enable Low Battery Indicator +#define EVLBI 0x04 // Enable Very Low Battery Indicator +#define GRAM 0x02 // Glitch sensitive RAM +#define RST 0x01 // ReSeT + +// 0x0b Annunciator Control [AON XTRA LA6 LA5] [LA4 LA3 LA2 LA1] +#define AON 0x80 // Annunciators on +#define LXTRA 0x40 // does nothing +#define LA6 0x20 // LA6 - Transmitting +#define LA5 0x10 // LA5 - Busy +#define LA4 0x08 // LA4 - Alert +#define LA3 0x04 // LA3 - Alpha +#define LA2 0x02 // LA2 - ALT Shift +#define LA1 0x01 // LA1 - Shift + +// 0x0d SERIAL Baud Rate [UCK BD2 BD1 BD0] +#define UCK 0x08 // Uart ClocK +#define BD2 0x04 // BauDrate Bit2 +#define BD1 0x02 // BauDrate Bit1 +#define BD0 0x01 // BauDrate Bit0 + +// 0x0e Card Control [ECDT RCDT SMP SWINT] +#define ECDT 0x08 // Enable Card Detect +#define RCDT 0x04 // Run Card Detect +#define SMP 0x02 // Set module pulled +#define SWINT 0x01 // Software Interrupt + +// 0x0f Card Status [P2W P1W P2C P1C] +#define P2W 0x08 // High when Port2 writeable +#define P1W 0x04 // High when Port1 writeable +#define P2C 0x02 // High when Card in Port2 inserted +#define P1C 0x01 // High when Card in Port1 inserted + +// 0x10 Serial I/O Control [SON ETBE ERBF ERBZ] +#define SON 0x08 // Serial on +#define ETBE 0x04 // Interrupt on transmit buffer empty +#define ERBF 0x02 // Interrupt on receive buffer full +#define ERBZ 0x01 // Interrupt on receiver busy + +// 0x11 Serial Receive Control/Status [RX RER RBZ RBF] +#define RX 0x08 // Rx pin state (read-only) +#define RER 0x04 // Receiver error +#define RBZ 0x02 // Receiver busy +#define RBF 0x01 // Receive buffer full + +// 0x12 Serial Transmit Control/Status [BRK LPB TBZ TBF] +#define BRK 0x08 // Break +#define LPB 0x04 // Loopback +#define TBZ 0x02 // Transmitter busy +#define TBF 0x01 // Transmit buffer full + +// 0x18 Service Request Register 1 [ISRQ TSRQ USRQ VSRQ] +#define ISRQ 0x08 // IR receiver pulls NINT2 +#define TSRQ 0x04 // Timer pulls NINT2 +#define USRQ 0x02 // UART pulls NINT2 +#define VSRQ 0x01 // VLBI pulls NINT2 + +// 0x19 Service Request Register 2 [KDN NINT2 NINT LSRQ] +#define KDN 0x08 // Bit set when ON Key or Key Interrupt +#define NINT2 0x04 // State of NINT2 +#define NINT 0x02 // State of NINT +#define LSRQ 0x01 // LED driver pulls NINT2 + +// 0x1a IR Control Register [IRI EIRU EIRI IRE] +#define IRI 0x08 // IR input (read-only) +#define EIRU 0x04 // Enable IR UART mode +#define EIRI 0x02 // Enable IR interrupt +#define IRE 0x01 // IR event + +// 0x1c Led Control Register [LED ELBE LBZ LBF] +#define LED 0x08 // Turn on LED +#define ELBE 0x04 // Enable Interrupt on Led Buffer empty +#define LBZ 0x02 // Led Port Busy +#define LBF 0x01 // Led Buffer Full + +// 0x1d Led Buffer Register [0 0 0 LBO] (bits 1-3 read zero) +#define LBO 0x01 + +// 0x28 Display Line Counter LSB [LC3 LC2 LC1 LC0] +#define LC3 0x08 // LC3 - Line Counter Bit3 +#define LC2 0x04 // LC2 - Line Counter Bit2 +#define LC1 0x02 // LC1 - Line Counter Bit1 +#define LC0 0x01 // LC0 - Line Counter Bit0 + +// 0x29 Display Line Counter MSB [DA19 M32 LC5 LC4] +#define DA19 0x08 // Drive A[19] +#define M32 0x04 // Multiplex 32 way +#define LC5 0x02 // LC5 - Line Counter Bit5 +#define LC4 0x01 // LC4 - Line Counter Bit4 + +// 0x2e Timer1 Control [SRQ WKE INT XTRA] +#define SRQ 0x08 // Service request +#define WKE 0x04 // Wake up +#define INTR 0x02 // Interrupt +#define XTRA 0x01 // Extra function + +// 0x2f Timer2 Control [SRQ WKE INT RUN] +#define SRQ 0x08 // Service request +#define WKE 0x04 // Wake up +#define INTR 0x02 // Interrupt +#define RUN 0x01 // Timer run diff --git a/app/src/main/cpp/KEYBOARD.C b/app/src/main/cpp/KEYBOARD.C new file mode 100644 index 0000000..9072982 --- /dev/null +++ b/app/src/main/cpp/KEYBOARD.C @@ -0,0 +1,131 @@ +/* + * keyboard.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" // I/O definitions + +DWORD dwKeyMinDelay = 50; // minimum time for key hold + +static WORD Keyboard_GetIR(VOID) +{ + WORD r = 0; + + // OR[0:8] are wired on Clarke/Yorke chip + if (Chipset.out==0) return 0; + if (Chipset.out&0x001) r|=Chipset.Keyboard_Row[0]; + if (Chipset.out&0x002) r|=Chipset.Keyboard_Row[1]; + if (Chipset.out&0x004) r|=Chipset.Keyboard_Row[2]; + if (Chipset.out&0x008) r|=Chipset.Keyboard_Row[3]; + if (Chipset.out&0x010) r|=Chipset.Keyboard_Row[4]; + if (Chipset.out&0x020) r|=Chipset.Keyboard_Row[5]; + if (Chipset.out&0x040) r|=Chipset.Keyboard_Row[6]; + if (Chipset.out&0x080) r|=Chipset.Keyboard_Row[7]; + if (Chipset.out&0x100) r|=Chipset.Keyboard_Row[8]; + return r; +} + +VOID ScanKeyboard(BOOL bActive, BOOL bReset) +{ + // bActive = TRUE -> function called by direct read (A=IN, C=IN, RSI) + // FALSE -> function called by 1ms keyboard poll simulation + // bReset = TRUE -> Reset Chipset.in interrupt state register + // FALSE -> generate interrupt only for new pressed keys + + // keyboard read not active? + if (!( bActive || Chipset.Shutdn || Chipset.IR15X + || (Chipset.intk && (Chipset.IORam[TIMER2_CTRL]&RUN) != 0))) + { + EnterCriticalSection(&csKeyLock); + { + Chipset.in &= ~0x8000; // remove ON key + } + LeaveCriticalSection(&csKeyLock); + return; + } + + EnterCriticalSection(&csKeyLock); // synchronize + { + BOOL bKbdInt; + + WORD wOldIn = Chipset.in; // save old Chipset.in state + + UpdateKdnBit(); // update KDN bit + Chipset.dwKdnCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + + Chipset.in = Keyboard_GetIR(); // update Chipset.in register + Chipset.in |= Chipset.IR15X; // add ON key + + // interrupt for any new pressed keys? + bKbdInt = (Chipset.in && (wOldIn & 0x1FF) == 0) || Chipset.IR15X || bReset; + + // update keyboard interrupt pending flag when 1ms keyboard scan is disabled + Chipset.intd = Chipset.intd || (bKbdInt && !Chipset.intk); + + // keyboard interrupt enabled? + bKbdInt = bKbdInt && Chipset.intk; + + // interrupt at ON key pressed + bKbdInt = bKbdInt || Chipset.IR15X != 0; + + // no interrupt if still inside interrupt service routine + bKbdInt = bKbdInt && Chipset.inte; + + if (Chipset.in != 0) // any key pressed + { + if (bKbdInt) // interrupt enabled + { + Chipset.SoftInt = TRUE; // interrupt request + bInterrupt = TRUE; // exit emulation loop + } + + if (Chipset.Shutdn) // cpu sleeping + { + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + } + else + { + Chipset.intd = FALSE; // no keyboard interrupt pending + } + } + LeaveCriticalSection(&csKeyLock); + + return; +} + +VOID KeyboardEvent(BOOL bPress, UINT out, UINT in) +{ + if (nState != SM_RUN) // not in running state + return; // ignore key + + KeyMacroRecord(bPress,out,in); // save all keyboard events + + if (in == 0x8000) // ON key ? + { + Chipset.IR15X = bPress?0x8000:0x0000; // refresh special ON key flag + } + else + { + // "out" is outside Keyboard_Row + if (out >= ARRAYSIZEOF(Chipset.Keyboard_Row)) return; + + // in &= 0x1FF; // only IR[0:8] are wired on Clarke/Yorke chip + + _ASSERT(out < ARRAYSIZEOF(Chipset.Keyboard_Row)); + if (bPress) // key pressed + Chipset.Keyboard_Row[out] |= in; // set key marker in keyboard row + else + Chipset.Keyboard_Row[out] &= (~in); // clear key marker in keyboard row + } + AdjKeySpeed(); // adjust key repeat speed + ScanKeyboard(FALSE,FALSE); // update Chipset.in register by 1ms keyboard poll + Sleep(dwKeyMinDelay); // hold key state for a definite time + return; +} diff --git a/app/src/main/cpp/KEYMACRO.C b/app/src/main/cpp/KEYMACRO.C new file mode 100644 index 0000000..9d4d930 --- /dev/null +++ b/app/src/main/cpp/KEYMACRO.C @@ -0,0 +1,337 @@ +/* + * Keymacro.c + * + * This file is part of Emu48 + * + * Copyright (C) 2004 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "kml.h" + +#define KEYMACROHEAD "Emu-KeyMacro" // macro signature + +#define MIN_SPEED 0 +#define MAX_SPEED 500 + +typedef struct +{ + DWORD dwTime; // elapsed time + DWORD dwKeyEvent; // key code +} KeyData; + +INT nMacroState = MACRO_OFF; +INT nMacroTimeout = MIN_SPEED; +BOOL bMacroRealSpeed = TRUE; +DWORD dwMacroMinDelay = 0; // minimum macro play key hold time in ms + +static DWORD dwTimeRef; + +static HANDLE hMacroFile = INVALID_HANDLE_VALUE; +static HANDLE hEventPlay = NULL; +static HANDLE hThreadEv = NULL; + +static VOID InitializeOFN(LPOPENFILENAME ofn) +{ + ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME)); + ofn->lStructSize = sizeof(OPENFILENAME); + ofn->hwndOwner = hWnd; + ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY; + return; +} + +// +// thread playing keys +// +static DWORD WINAPI EventThread(LPVOID pParam) +{ + DWORD dwRead = 0; + DWORD dwData = 0,dwTime = 0; + + while (WaitForSingleObject(hEventPlay,dwTime) == WAIT_TIMEOUT) + { + if (dwRead != 0) // data read + { + UINT nIn = (dwData >> 0) & 0xFFFF; + UINT nOut = (dwData >> 16) & 0xFF; + BOOL bPress = (dwData >> 24) & 0xFF; + + PlayKey(nOut,nIn,bPress); + } + + dwTime = nMacroTimeout; // set default speed + + while (TRUE) + { + // read next data element + if ( !ReadFile(hMacroFile,&dwData,sizeof(dwData),&dwRead,NULL) + || dwRead != sizeof(dwData)) + { + // play record empty -> quit + PostMessage(hWnd,WM_COMMAND,ID_TOOL_MACRO_STOP,0); + return 0; // exit on file end + } + + if ((dwData & 0x80000000) != 0) // time information + { + if (bMacroRealSpeed) // realspeed from data + { + dwTime = dwData & 0x7FFFFFFF; + } + continue; + } + + // hold the key state the minimum macro play key hold time + if (dwTime < dwMacroMinDelay) dwTime = dwMacroMinDelay; + + dwTime -= dwKeyMinDelay; // remove the actual key hold time + // set negative number to zero + if ((dwTime & 0x80000000) != 0) dwTime = 0; + break; // got key information + } + } + return 0; // exit on stop + UNREFERENCED_PARAMETER(pParam); +} + +// +// callback function for recording keys +// +VOID KeyMacroRecord(BOOL bPress, UINT out, UINT in) +{ + if (nMacroState == MACRO_NEW) // save key event + { + KeyData Data; + DWORD dwWritten; + + dwWritten = GetTickCount(); // time reference + Data.dwTime = (dwWritten - dwTimeRef); + Data.dwTime |= 0x80000000; // set time marker + dwTimeRef = dwWritten; + + Data.dwKeyEvent = (bPress & 0xFF); + Data.dwKeyEvent = (Data.dwKeyEvent << 8) | (out & 0xFF); + Data.dwKeyEvent = (Data.dwKeyEvent << 16) | (in & 0xFFFF); + + // save key event in file + WriteFile(hMacroFile,&Data,sizeof(Data),&dwWritten,NULL); + _ASSERT(dwWritten == sizeof(Data)); + } + return; +} + +// +// message handler for save new keyboard macro +// +LRESULT OnToolMacroNew(VOID) +{ + TCHAR szMacroFile[MAX_PATH]; + OPENFILENAME ofn; + DWORD dwExtensionLength,dwWritten; + + // get filename for saving + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Keyboard Macro Files (*.MAC)\0*.MAC\0") + _T("All Files (*.*)\0*.*\0"); + ofn.lpstrDefExt = _T("MAC"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szMacroFile; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szMacroFile); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return 0; + + // open file for writing + hMacroFile = CreateFile(szMacroFile, + GENERIC_READ|GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hMacroFile == INVALID_HANDLE_VALUE) return 0; + + // write header + WriteFile(hMacroFile,KEYMACROHEAD,sizeof(KEYMACROHEAD) - 1,&dwWritten,NULL); + _ASSERT(dwWritten == (DWORD) strlen(KEYMACROHEAD)); + + // write extension length + dwExtensionLength = 0; // no extension + WriteFile(hMacroFile,&dwExtensionLength,sizeof(dwExtensionLength),&dwWritten,NULL); + _ASSERT(dwWritten == sizeof(dwExtensionLength)); + + nMacroState = MACRO_NEW; + + MessageBox(hWnd, + _T("Press OK to begin to record the Macro."), + _T("Macro Recorder"), + MB_OK|MB_ICONINFORMATION); + + dwTimeRef = GetTickCount(); // time reference + return 0; +} + +// +// message handler for play keyboard macro +// +LRESULT OnToolMacroPlay(VOID) +{ + BYTE byHeader[sizeof(KEYMACROHEAD)-1]; + TCHAR szMacroFile[MAX_PATH]; + OPENFILENAME ofn; + DWORD dwExtensionLength,dwRead,dwThreadId; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Keyboard Macro Files (*.MAC)\0*.MAC\0") + _T("All Files (*.*)\0*.*\0"); + ofn.lpstrDefExt = _T("MAC"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szMacroFile; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szMacroFile); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return 0; + + // open file for Reading + hMacroFile = CreateFile(szMacroFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hMacroFile == INVALID_HANDLE_VALUE) return 0; + + // read header + ReadFile(hMacroFile,byHeader,sizeof(byHeader),&dwRead,NULL); + if ( dwRead != sizeof(byHeader) + || memcmp(byHeader,KEYMACROHEAD,dwRead) != 0) + { + MessageBox(hWnd, + _T("Wrong keyboard macro file format."), + _T("Macro Recorder"), + MB_OK|MB_ICONSTOP); + CloseHandle(hMacroFile); + return 0; + } + + // read extension length + ReadFile(hMacroFile,&dwExtensionLength,sizeof(dwExtensionLength),&dwRead,NULL); + if (dwRead != sizeof(dwExtensionLength)) + { + CloseHandle(hMacroFile); + return 0; + } + + // read extension + while (dwExtensionLength-- > 0) + { + BYTE byData; + + ReadFile(hMacroFile,&byData,sizeof(byData),&dwRead,NULL); + if (dwRead != sizeof(byData)) + { + CloseHandle(hMacroFile); + return 0; + } + } + + // event for quit playing + hEventPlay = CreateEvent(NULL,FALSE,FALSE,NULL); + + nMacroState = MACRO_PLAY; + + // start playing thread + VERIFY(hThreadEv = CreateThread(NULL,0,&EventThread,NULL,0,&dwThreadId)); + return 0; +} + +// +// message handler for stop recording/playing +// +LRESULT OnToolMacroStop(VOID) +{ + if (nMacroState != MACRO_OFF) + { + if (hEventPlay) // playing keys + { + // stop playing thread + SetEvent(hEventPlay); // quit play loop + + WaitForSingleObject(hThreadEv,INFINITE); + CloseHandle(hThreadEv); + hThreadEv = NULL; + + CloseHandle(hEventPlay); // close playing keys event + hEventPlay = NULL; + } + + // macro file open + if (hMacroFile != INVALID_HANDLE_VALUE) CloseHandle(hMacroFile); + + nMacroState = MACRO_OFF; + } + return 0; +} + +// +// activate/deactivate slider +// +static VOID SliderEnable(HWND hDlg,BOOL bEnable) +{ + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_SLOW),bEnable); + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_FAST),bEnable); + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_SLIDER),bEnable); + return; +} + +// +// Macro settings dialog +// +static INT_PTR CALLBACK MacroProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + // set slider + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETRANGE,FALSE,MAKELONG(0,MAX_SPEED-MIN_SPEED)); + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETTICFREQ,MAX_SPEED/10,0); + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETPOS,TRUE,MAX_SPEED-nMacroTimeout); + + // set button + CheckDlgButton(hDlg,bMacroRealSpeed ? IDC_MACRO_REAL : IDC_MACRO_MANUAL,BST_CHECKED); + SliderEnable(hDlg,!bMacroRealSpeed); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_MACRO_REAL: + SliderEnable(hDlg,FALSE); + return TRUE; + case IDC_MACRO_MANUAL: + SliderEnable(hDlg,TRUE); + return TRUE; + case IDOK: + // get macro data + nMacroTimeout = MAX_SPEED - (INT) SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_GETPOS,0,0); + bMacroRealSpeed = IsDlgButtonChecked(hDlg,IDC_MACRO_REAL); + // no break + case IDCANCEL: + EndDialog(hDlg, LOWORD(wParam)); + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +LRESULT OnToolMacroSettings(VOID) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_MACROSET), hWnd, (DLGPROC)MacroProc) == -1) + AbortMessage(_T("Macro Dialog Box Creation Error !")); + return 0; +} diff --git a/app/src/main/cpp/KML.C b/app/src/main/cpp/KML.C new file mode 100644 index 0000000..a0fbdb3 --- /dev/null +++ b/app/src/main/cpp/KML.C @@ -0,0 +1,2660 @@ +/* + * kml.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "kml.h" + +static VOID InitLex(LPCTSTR szScript); +static VOID CleanLex(VOID); +static VOID SkipWhite(UINT nMode); +static TokenId ParseToken(UINT nMode); +static DWORD ParseInteger(VOID); +static LPTSTR ParseString(VOID); +static TokenId Lex(UINT nMode); +static KmlLine* ParseLine(TokenId eCommand); +static KmlLine* IncludeLines(BOOL bInclude, LPCTSTR szFilename); +static KmlLine* ParseLines(BOOL bInclude); +static KmlBlock* ParseBlock(BOOL bInclude, TokenId eBlock); +static KmlBlock* IncludeBlocks(BOOL bInclude, LPCTSTR szFilename); +static KmlBlock* ParseBlocks(BOOL bInclude, BOOL bEndTokenEn); +static VOID FreeLines(KmlLine* pLine); +static VOID PressButton(UINT nId); +static VOID ReleaseButton(UINT nId); +static VOID PressButtonById(UINT nId); +static VOID ReleaseButtonById(UINT nId); +static LPCTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand); +static KmlLine* If(KmlLine* pLine, BOOL bCondition); +static KmlLine* RunLine(KmlLine* pLine); +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename); + +KmlBlock* pKml = NULL; +static KmlBlock* pVKey[256]; +static BYTE byVKeyMap[256]; +static KmlButton pButton[256]; +static KmlAnnunciator pAnnunciator[6]; +static UINT nButtons = 0; +static UINT nScancodes = 0; +static UINT nAnnunciators = 0; +static BOOL bDebug = TRUE; +static WORD wKeybLocId = 0; +static BOOL bLocaleInc = FALSE; // no locale block content included +static UINT nLexLine; +static UINT nLexInteger; +static UINT nBlocksIncludeLevel; +static UINT nLinesIncludeLevel; +static DWORD nKMLFlags = 0; +static LPTSTR szLexString; +static LPCTSTR szText; +static LPCTSTR szLexDelim[] = +{ + _T(" \t\n\r"), // valid whitespaces for LEX_BLOCK + _T(" \t\n\r"), // valid whitespaces for LEX_COMMAND + _T(" \t\r") // valid whitespaces for LEX_PARAM +}; + +static CONST KmlToken pLexToken[] = +{ + {TOK_ANNUNCIATOR,000001,11,_T("Annunciator")}, + {TOK_BACKGROUND, 000000,10,_T("Background")}, + {TOK_IFPRESSED, 000001, 9,_T("IfPressed")}, + {TOK_RESETFLAG, 000001, 9,_T("ResetFlag")}, + {TOK_SCANCODE, 000001, 8,_T("Scancode")}, + {TOK_HARDWARE, 000002, 8,_T("Hardware")}, + {TOK_MENUITEM, 000001, 8,_T("MenuItem")}, + {TOK_SYSITEM, 000001, 7,_T("SysItem")}, + {TOK_SETFLAG, 000001, 7,_T("SetFlag")}, + {TOK_RELEASE, 000001, 7,_T("Release")}, + {TOK_VIRTUAL, 000000, 7,_T("Virtual")}, + {TOK_INCLUDE, 000002, 7,_T("Include")}, + {TOK_NOTFLAG, 000001, 7,_T("NotFlag")}, + {TOK_MENUBAR, 000001, 7,_T("Menubar")}, // for PPC compatibility reasons + {TOK_GLOBAL, 000000, 6,_T("Global")}, + {TOK_AUTHOR, 000002, 6,_T("Author")}, + {TOK_BITMAP, 000002, 6,_T("Bitmap")}, + {TOK_OFFSET, 000011, 6,_T("Offset")}, + {TOK_ZOOMXY, 000011, 6,_T("Zoomxy")}, + {TOK_BUTTON, 000001, 6,_T("Button")}, + {TOK_IFFLAG, 000001, 6,_T("IfFlag")}, + {TOK_ONDOWN, 000000, 6,_T("OnDown")}, + {TOK_NOHOLD, 000000, 6,_T("NoHold")}, + {TOK_LOCALE, 000001, 6,_T("Locale")}, + {TOK_TOPBAR, 000001, 6,_T("Topbar")}, // for PPC compatibility reasons + {TOK_TITLE, 000002, 5,_T("Title")}, + {TOK_OUTIN, 000011, 5,_T("OutIn")}, + {TOK_PATCH, 000002, 5,_T("Patch")}, + {TOK_PRINT, 000002, 5,_T("Print")}, + {TOK_DEBUG, 000001, 5,_T("Debug")}, + {TOK_COLOR, 001111, 5,_T("Color")}, + {TOK_MODEL, 000002, 5,_T("Model")}, + {TOK_CLASS, 000001, 5,_T("Class")}, + {TOK_PRESS, 000001, 5,_T("Press")}, + {TOK_IFMEM, 000111, 5,_T("IfMem")}, + {TOK_SCALE, 000011, 5,_T("Scale")}, + {TOK_TYPE, 000001, 4,_T("Type")}, + {TOK_SIZE, 000011, 4,_T("Size")}, + {TOK_ZOOM, 000001, 4,_T("Zoom")}, + {TOK_DOWN, 000011, 4,_T("Down")}, + {TOK_ELSE, 000000, 4,_T("Else")}, + {TOK_ONUP, 000000, 4,_T("OnUp")}, + {TOK_ICON, 000002, 4,_T("Icon")}, + {TOK_MAP, 000011, 3,_T("Map")}, + {TOK_ROM, 000002, 3,_T("Rom")}, + {TOK_VGA, 000001, 3,_T("Vga")}, // for PPC compatibility reasons + {TOK_LCD, 000000, 3,_T("Lcd")}, + {TOK_END, 000000, 3,_T("End")}, + {TOK_NONE, 000000, 0,_T("")} +}; + +static CONST TokenId eIsGlobalBlock[] = +{ + TOK_GLOBAL, + TOK_BACKGROUND, + TOK_LCD, + TOK_ANNUNCIATOR, + TOK_BUTTON, + TOK_SCANCODE, + TOK_LOCALE +}; + +static CONST TokenId eIsBlock[] = +{ + TOK_IFFLAG, + TOK_IFPRESSED, + TOK_IFMEM, + TOK_ONDOWN, + TOK_ONUP +}; + +static BOOL bClicking = FALSE; +static UINT uButtonClicked = 0; + +static BOOL bKeyPressed = FALSE; // no key pressed +static UINT uLastKeyPressed = 0; // var for last pressed key + +static INT nScaleMul = 0; // no scaling +static INT nScaleDiv = 0; + +//################ +//# +//# Compilation Result +//# +//################ + +static UINT nLogLength = 0; +static LPTSTR szLog = NULL; + +static VOID ClearLog() +{ + nLogLength = 0; + if (szLog != NULL) + { + free(szLog); + szLog = NULL; + } + return; +} + +static VOID AddToLog(LPCTSTR szString) +{ + UINT nLength = lstrlen(szString) + 2; // CR+LF + if (szLog == NULL) + { + nLogLength = nLength + 1; // \0 + szLog = (LPTSTR) malloc(nLogLength*sizeof(szLog[0])); + if (szLog==NULL) + { + nLogLength = 0; + return; + } + lstrcpy(szLog,szString); + } + else + { + LPTSTR szLogTmp = (LPTSTR) realloc(szLog,(nLogLength+nLength)*sizeof(szLog[0])); + if (szLogTmp == NULL) + { + ClearLog(); + return; + } + szLog = szLogTmp; + lstrcpy(&szLog[nLogLength-1],szString); + nLogLength += nLength; + } + szLog[nLogLength-3] = _T('\r'); + szLog[nLogLength-2] = _T('\n'); + szLog[nLogLength-1] = 0; + return; +} + +static VOID __cdecl PrintfToLog(LPCTSTR lpFormat, ...) +{ + TCHAR cOutput[1024]; + va_list arglist; + + va_start(arglist,lpFormat); + wvsprintf(cOutput,lpFormat,arglist); + AddToLog(cOutput); + va_end(arglist); + return; +} + +static INT_PTR CALLBACK KMLLogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + LPCTSTR szString; + + switch (message) + { + case WM_INITDIALOG: + // set OK + EnableWindow(GetDlgItem(hDlg,IDOK),(BOOL) lParam); + // set IDC_TITLE + szString = GetStringParam(pKml, TOK_GLOBAL, TOK_TITLE, 0); + if (szString == NULL) szString = _T("Untitled"); + SetDlgItemText(hDlg,IDC_TITLE,szString); + // set IDC_AUTHOR + szString = GetStringParam(pKml, TOK_GLOBAL, TOK_AUTHOR, 0); + if (szString == NULL) szString = _T(""); + SetDlgItemText(hDlg,IDC_AUTHOR,szString); + // set IDC_KMLLOG + szString = szLog; + if (szString == NULL) szString = _T("Memory Allocation Failure."); + SetDlgItemText(hDlg,IDC_KMLLOG,szString); + // set IDC_ALWAYSDISPLOG + CheckDlgButton(hDlg,IDC_ALWAYSDISPLOG,bAlwaysDisplayLog); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + if ((wParam==IDOK)||(wParam==IDCANCEL)) + { + bAlwaysDisplayLog = IsDlgButtonChecked(hDlg, IDC_ALWAYSDISPLOG); + EndDialog(hDlg, wParam); + return TRUE; + } + break; + } + return FALSE; +} + +BOOL DisplayKMLLog(BOOL bOkEnabled) +{ + return IDOK == DialogBoxParam(hApp, + MAKEINTRESOURCE(IDD_KMLLOG), + hWnd, + (DLGPROC)KMLLogProc, + bOkEnabled); +} + + + +//################ +//# +//# Choose Script +//# +//################ + +typedef struct _KmlScript +{ + LPTSTR szFilename; + LPTSTR szTitle; + DWORD nId; + struct _KmlScript* pNext; +} KmlScript; + +static KmlScript* pKmlList = NULL; +static CHAR cKmlType; + +static VOID DestroyKmlList(VOID) +{ + KmlScript* pList; + + while (pKmlList) + { + pList = pKmlList->pNext; + free(pKmlList->szFilename); + free(pKmlList->szTitle); + free(pKmlList); + pKmlList = pList; + } + return; +} + +static VOID CreateKmlList(VOID) +{ + HANDLE hFindFile; + WIN32_FIND_DATA pFindFileData; + UINT nKmlFiles; + + _ASSERT(pKmlList == NULL); // KML file list must be empty + SetCurrentDirectory(szEmuDirectory); + hFindFile = FindFirstFile(_T("*.KML"),&pFindFileData); + SetCurrentDirectory(szCurrentDirectory); + if (hFindFile == INVALID_HANDLE_VALUE) return; + nKmlFiles = 0; + do + { + KmlScript* pScript; + KmlBlock* pBlock; + LPCTSTR szTitle; + + pBlock = LoadKMLGlobal(pFindFileData.cFileName); + if (pBlock == NULL) continue; + // check for correct KML script platform + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_HARDWARE,0); + if (szTitle && lstrcmpi(_T(HARDWARE),szTitle) != 0) + { + FreeBlocks(pBlock); + continue; + } + // check for supported Model + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_MODEL,0); + // skip all scripts with invalid or different Model statement + if ( (szTitle == NULL) + || (cKmlType && szTitle[0] != cKmlType) + || !isModelValid(szTitle[0])) + { + FreeBlocks(pBlock); + continue; + } + VERIFY(pScript = (KmlScript*) malloc(sizeof(KmlScript))); + pScript->szFilename = DuplicateString(pFindFileData.cFileName); + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_TITLE,0); + if (szTitle == NULL) szTitle = pScript->szFilename; + pScript->szTitle = DuplicateString(szTitle); + FreeBlocks(pBlock); + pScript->nId = nKmlFiles; + pScript->pNext = pKmlList; + pKmlList = pScript; + nKmlFiles++; + } while (FindNextFile(hFindFile,&pFindFileData)); + FindClose(hFindFile); + return; +}; + +static INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + TCHAR szDir[MAX_PATH]; + + switch(uMsg) + { + case BFFM_INITIALIZED: + SendMessage(hwnd,BFFM_SETSELECTION,TRUE,pData); + break; + case BFFM_SELCHANGED: + // Set the status window to the currently selected path. + if (SHGetPathFromIDList((LPITEMIDLIST) lp,szDir)) + { + SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM) szDir); + } + break; + } + return 0; +} + +static VOID BrowseFolder(HWND hDlg) +{ + TCHAR szDir[MAX_PATH]; + BROWSEINFO bi; + LPITEMIDLIST pidl; + LPMALLOC pMalloc; + + // gets the shell's default allocator + if (SUCCEEDED(SHGetMalloc(&pMalloc))) + { + GetDlgItemText(hDlg,IDC_EMUDIR,szDir,ARRAYSIZEOF(szDir)); + + ZeroMemory(&bi,sizeof(bi)); + bi.hwndOwner = hDlg; + bi.pidlRoot = NULL; + bi.pszDisplayName = NULL; + bi.lpszTitle = _T("Choose a folder:"); + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT; + bi.lpfn = BrowseCallbackProc; + bi.lParam = (LPARAM) szDir; // current setting + + pidl = SHBrowseForFolder(&bi); + if (pidl) + { + if (SHGetPathFromIDList(pidl,szDir)) + { + SetDlgItemText(hDlg,IDC_EMUDIR,szDir); + } + // free the PIDL allocated by SHBrowseForFolder + #if defined __cplusplus + pMalloc->Free(pidl); + #else + pMalloc->lpVtbl->Free(pMalloc,pidl); + #endif + } + // release the shell's allocator + #if defined __cplusplus + pMalloc->Release(); + #else + pMalloc->lpVtbl->Release(pMalloc); + #endif + } + return; +} + +static VOID UpdateScriptList(HWND hDlg) +{ + HWND hList; + KmlScript* pList; + UINT nIndex,nEntries; + DWORD dwActId = (DWORD) CB_ERR; + + // add all script titles to combo box + hList = GetDlgItem(hDlg,IDC_KMLSCRIPT); + SendMessage(hList, CB_RESETCONTENT, 0, 0); + for (nEntries = 0, pList = pKmlList; pList; pList = pList->pNext) + { + nIndex = (UINT) SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)pList->szTitle); + SendMessage(hList, CB_SETITEMDATA, nIndex, (LPARAM) pList->nId); + + // this has the same filename like the actual KML script + if (lstrcmpi(szCurrentKml, pList->szFilename) == 0) + dwActId = pList->nId; + + nEntries++; + } + + while (--nEntries > 0) // scan all combo box items + { + // found ID of actual KML script + if ((DWORD) SendMessage(hList, CB_GETITEMDATA, nEntries, 0) == dwActId) + break; + } + SendMessage(hList, CB_SETCURSEL, nEntries, 0); + return; +} + +static INT_PTR CALLBACK ChooseKMLProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND hList; + KmlScript* pList; + UINT nIndex; + + switch (message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory); + UpdateScriptList(hDlg); // update combo box with script titles + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_EMUDIRSEL: + BrowseFolder(hDlg); // select new folder for IDC_EMUDIR + // fall into IDC_UPDATE to search for KML files in new folder + case IDC_UPDATE: + DestroyKmlList(); + GetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + CreateKmlList(); + UpdateScriptList(hDlg); // update combo box with script titles + return TRUE; + case IDOK: + GetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + hList = GetDlgItem(hDlg,IDC_KMLSCRIPT); + nIndex = (UINT) SendMessage(hList, CB_GETCURSEL, 0, 0); + nIndex = (UINT) SendMessage(hList, CB_GETITEMDATA, nIndex, 0); + for (pList = pKmlList; pList; pList = pList->pNext) + { + if (pList->nId == nIndex) + { + lstrcpy(szCurrentKml, pList->szFilename); + EndDialog(hDlg, IDOK); + break; + } + } + return TRUE; + case IDCANCEL: + EndDialog(hDlg, IDCANCEL); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +BOOL DisplayChooseKml(CHAR cType) +{ + INT_PTR nResult; + cKmlType = cType; + CreateKmlList(); + nResult = DialogBox(hApp, MAKEINTRESOURCE(IDD_CHOOSEKML), hWnd, (DLGPROC)ChooseKMLProc); + DestroyKmlList(); + return (nResult == IDOK); +} + + + +//################ +//# +//# KML File Mapping +//# +//################ + +static LPTSTR MapKMLFile(HANDLE hFile) +{ + DWORD lBytesRead; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPTSTR lpBuf = NULL; + + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { + AddToLog(_T("File is too large.")); + goto fail; + } + + lpBuf = (LPTSTR) malloc((dwFileSizeLow+1)*sizeof(lpBuf[0])); + if (lpBuf == NULL) + { + PrintfToLog(_T("Cannot allocate %i bytes."), (dwFileSizeLow+1)*sizeof(lpBuf[0])); + goto fail; + } + #if defined _UNICODE + { + LPSTR szTmp = (LPSTR) malloc(dwFileSizeLow+1); + if (szTmp == NULL) + { + free(lpBuf); + lpBuf = NULL; + PrintfToLog(_T("Cannot allocate %i bytes."), dwFileSizeLow+1); + goto fail; + } + ReadFile(hFile, szTmp, dwFileSizeLow, &lBytesRead, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTmp, lBytesRead, lpBuf, dwFileSizeLow+1); + free(szTmp); + } + #else + { + ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL); + } + #endif + lpBuf[dwFileSizeLow] = 0; + +fail: + CloseHandle(hFile); + return lpBuf; +} + + + +//################ +//# +//# Script Parsing +//# +//################ + +static VOID InitLex(LPCTSTR szScript) +{ + nLexLine = 1; + szText = szScript; + return; +} + +static VOID CleanLex(VOID) +{ + nLexLine = 0; + nLexInteger = 0; + szLexString = NULL; + szText = NULL; + return; +} + +static BOOL IsGlobalBlock(TokenId eId) +{ + UINT i; + + for (i = 0; i < ARRAYSIZEOF(eIsGlobalBlock); ++i) + { + if (eId == eIsGlobalBlock[i]) return TRUE; + } + return FALSE; +} + +static BOOL IsBlock(TokenId eId) +{ + UINT i; + + for (i = 0; i < ARRAYSIZEOF(eIsBlock); ++i) + { + if (eId == eIsBlock[i]) return TRUE; + } + return FALSE; +} + +static LPCTSTR GetStringOf(TokenId eId) +{ + UINT i; + + for (i = 0; pLexToken[i].nLen; ++i) + { + if (pLexToken[i].eId == eId) return pLexToken[i].szName; + } + return _T(""); +} + +static VOID SkipWhite(UINT nMode) +{ + LPCTSTR pcDelim; + + while (*szText) + { + // search for delimiter + if ((pcDelim = _tcschr(szLexDelim[nMode],*szText)) != NULL) + { + _ASSERT(*pcDelim != 0); // no EOS + if (*pcDelim == _T('\n')) nLexLine++; + szText++; + continue; + } + if (*szText == _T('#')) // start of remark + { + // skip until LF or EOS + do szText++; while (*szText != _T('\n') && *szText != 0); + if (nMode != LEX_PARAM) continue; + } + break; + } + return; +} + +static TokenId ParseToken(UINT nMode) +{ + UINT i,j; + + for (i = 0; szText[i]; i++) // search for delimeter + { + if (_tcschr(szLexDelim[nMode],szText[i]) != NULL) + break; + } + if (i == 0) return TOK_NONE; + + // token length longer or equal than current command + for (j = 0; pLexToken[j].nLen >= i; ++j) + { + if (pLexToken[j].nLen == i) // token length has command length + { + if (_tcsncmp(pLexToken[j].szName,szText,i) == 0) + { + szText += i; // remove command from text + return pLexToken[j].eId; // return token Id + } + } + } + if (bDebug) // token not found + { + // allocate target string memory with token length + LPTSTR szToken = (LPTSTR) malloc((i+1) * sizeof(szToken[0])); + lstrcpyn(szToken,szText,i+1); // copy token text and append EOS + PrintfToLog(_T("%i: Undefined token %s"),nLexLine,szToken); + free(szToken); + } + return TOK_NONE; +} + +static DWORD ParseInteger(VOID) +{ + DWORD nNum = 0; + while (_istdigit(*szText)) + { + nNum = nNum * 10 + ((*szText) - _T('0')); + szText++; + } + return nNum; +} + +static LPTSTR ParseString(VOID) +{ + LPTSTR lpszString; + UINT nLength; + UINT nBlock; + + szText++; // skip leading '"' + nLength = 0; + nBlock = 256; + lpszString = (LPTSTR) malloc(nBlock * sizeof(lpszString[0])); + while (*szText != _T('"')) + { + if (nLength == nBlock - 1) // ran out of buffer space + { + nBlock += 256; + lpszString = (LPTSTR) realloc(lpszString,nBlock * sizeof(lpszString[0])); + } + + if (*szText == _T('\\')) // escape char + { + // skip a '\' escape char before a quotation to + // decode the \" sequence as a quotation mark inside text + switch (szText[1]) + { + case _T('\"'): + case _T('\\'): + ++szText; // skip escape char '\' + break; + } + } + + if (*szText == 0) // EOS found inside string + { + lpszString[nLength] = 0; // set EOS + PrintfToLog(_T("%i: Invalid string %s."), nLexLine, lpszString); + free(lpszString); + return NULL; + } + lpszString[nLength++] = *szText++; // save char + } + szText++; // skip ending '"' + lpszString[nLength] = 0; // set EOS + + // release unnecessary allocated bytes + return (LPTSTR) realloc(lpszString,(nLength+1) * sizeof(lpszString[0])); +} + +static TokenId Lex(UINT nMode) +{ + _ASSERT(nMode >= LEX_BLOCK && nMode <= LEX_PARAM); + _ASSERT(nMode >= 0 && nMode < ARRAYSIZEOF(szLexDelim)); + + SkipWhite(nMode); + if (_istdigit(*szText)) + { + nLexInteger = ParseInteger(); + return TOK_INTEGER; + } + if (*szText == _T('"')) + { + szLexString = ParseString(); + return TOK_STRING; + } + if (nMode == LEX_PARAM) + { + if (*szText == _T('\n')) // end of line + { + nLexLine++; // next line + szText++; // skip LF + return TOK_EOL; + } + if (*szText == 0) // end of file + { + return TOK_EOL; + } + } + return ParseToken(nMode); +} + +static KmlLine* ParseLine(TokenId eCommand) +{ + UINT i, j; + DWORD nParams; + TokenId eToken; + KmlLine* pLine; + + for (i = 0; pLexToken[i].nLen; ++i) + { + if (pLexToken[i].eId == eCommand) break; + } + if (pLexToken[i].nLen == 0) return NULL; + + pLine = (KmlLine*) calloc(1,sizeof(KmlLine)); + pLine->eCommand = eCommand; + + for (j = 0, nParams = pLexToken[i].nParams; TRUE; nParams >>= 3) + { + // check for parameter overflow + _ASSERT(j < ARRAYSIZEOF(pLine->nParam)); + + eToken = Lex(LEX_PARAM); // decode argument token + if ((nParams & 7) == TYPE_NONE) + { + if (eToken != TOK_EOL) + { + PrintfToLog(_T("%i: Too many parameters for %s (%i expected)."), nLexLine, pLexToken[i].szName, j); + break; // free memory of arguments + } + return pLine; // normal exit -> parsed line + } + if ((nParams & 7) == TYPE_INTEGER) + { + if (eToken != TOK_INTEGER) + { + PrintfToLog(_T("%i: Parameter %i of %s must be an integer."), nLexLine, j+1, pLexToken[i].szName); + break; // free memory of arguments + } + pLine->nParam[j++] = nLexInteger; + continue; + } + if ((nParams & 7) == TYPE_STRING) + { + if (eToken != TOK_STRING) + { + PrintfToLog(_T("%i: Parameter %i of %s must be a string."), nLexLine, j+1, pLexToken[i].szName); + break; // free memory of arguments + } + pLine->nParam[j++] = (DWORD_PTR) szLexString; + szLexString = NULL; + continue; + } + _ASSERT(FALSE); // unknown parameter type + break; + } + + // if last argument was string, free it + if (eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + + nParams = pLexToken[i].nParams; // get argument types of command + for (i = 0; i < j; ++i) // handle all scanned arguments + { + if ((nParams & 7) == TYPE_STRING) // string type + { + free((LPVOID)pLine->nParam[i]); + } + nParams >>= 3; // next argument type + } + free(pLine); + return NULL; +} + +static KmlLine* IncludeLines(BOOL bInclude, LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPCTSTR szOldText; + KmlLine* pLine; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + PrintfToLog(_T("Error while opening include file %s."), szFilename); + return NULL; + } + if ((lpbyBuf = MapKMLFile(hFile)) == NULL) + { + return NULL; + } + + uOldLine = nLexLine; + szOldText = szText; + + nLinesIncludeLevel++; + PrintfToLog(_T("l%i:%s %s"), + nLinesIncludeLevel, + (bInclude) ? _T("Including") : _T("Parsing"), + szFilename); + InitLex(lpbyBuf); + pLine = ParseLines(bInclude); + CleanLex(); + nLinesIncludeLevel--; + + nLexLine = uOldLine; + szText = szOldText; + free(lpbyBuf); + + return pLine; +} + +static KmlLine* ParseLines(BOOL bInclude) +{ + KmlLine* pFirst = NULL; + KmlLine* pLine = NULL; + TokenId eToken; + UINT nLevel = 0; + + while ((eToken = Lex(LEX_COMMAND)) != TOK_NONE) + { + if (IsGlobalBlock(eToken)) // check for block command + { + PrintfToLog(_T("%i: Invalid Command %s."), nLexLine, GetStringOf(eToken)); + goto abort; + } + if (IsBlock(eToken)) nLevel++; + if (eToken == TOK_INCLUDE) + { + LPTSTR szFilename; + eToken = Lex(LEX_PARAM); // get include parameter in 'szLexString' + if (eToken != TOK_STRING) // not a string (token don't begin with ") + { + PrintfToLog(_T("%i: Include: string expected as parameter."), nLexLine); + goto abort; + } + szFilename = szLexString; // save pointer to allocated memory + szLexString = NULL; + eToken = Lex(LEX_PARAM); // decode argument + if (eToken != TOK_EOL) + { + free(szFilename); // free filename string + if (eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + PrintfToLog(_T("%i: Include: Too many parameters."), nLexLine); + goto abort; + } + if (pFirst) + { + pLine = pLine->pNext = IncludeLines(bInclude,szFilename); + } + else + { + pLine = pFirst = IncludeLines(bInclude,szFilename); + } + free(szFilename); // free filename string + if (pLine == NULL) // parsing error + goto abort; + while (pLine->pNext) pLine=pLine->pNext; + continue; + } + if (eToken == TOK_END) + { + if (nLevel) + { + nLevel--; + } + else + { + if (pFirst == NULL) // regular exit with empty block + { + // create an empty line + pLine = pFirst = (KmlLine*) calloc(1,sizeof(KmlLine)); + pLine->eCommand = TOK_NONE; + } + if (pLine) pLine->pNext = NULL; + _ASSERT(szLexString == NULL); + return pFirst; + } + } + if (pFirst) + { + pLine = pLine->pNext = ParseLine(eToken); + } + else + { + pLine = pFirst = ParseLine(eToken); + } + if (pLine == NULL) // parsing error + goto abort; + } + if (nLinesIncludeLevel) + { + if (pLine) pLine->pNext = NULL; + _ASSERT(szLexString == NULL); + return pFirst; + } +abort: + if (pFirst) FreeLines(pFirst); + _ASSERT(szLexString == NULL); + return NULL; +} + +static KmlBlock* ParseBlock(BOOL bInclude, TokenId eType) +{ + UINT i; + KmlBlock* pBlock; + TokenId eToken; + + nLinesIncludeLevel = 0; + + VERIFY(pBlock = (KmlBlock *) calloc(1,sizeof(KmlBlock))); + pBlock->eType = eType; + + for (i = 0; pLexToken[i].nLen; ++i) // search for token + { + if (pLexToken[i].eId == eType) break; + } + + if (pLexToken[i].nParams) // has block command arguments + { + // block command parser accept only one integer argument + _ASSERT(pLexToken[i].nParams == TYPE_INTEGER); + + eToken = Lex(LEX_PARAM); // decode argument + if (eToken != TOK_INTEGER) + { + if (eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + PrintfToLog(_T("%i: Block %s parameter must be an integer."), nLexLine, pLexToken[i].szName); + free(pBlock); + _ASSERT(szLexString == NULL); + return NULL; + } + + pBlock->nId = nLexInteger; // remember block no. + } + + eToken = Lex(LEX_PARAM); // decode argument + if (eToken != TOK_EOL) + { + if (eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + PrintfToLog(_T("%i: Too many parameters for block %s."), nLexLine, pLexToken[i].szName); + free(pBlock); + _ASSERT(szLexString == NULL); + return NULL; + } + + pBlock->pFirstLine = ParseLines(bInclude); + if (pBlock->pFirstLine == NULL) // break on ParseLines error + { + free(pBlock); + pBlock = NULL; + } + _ASSERT(szLexString == NULL); + return pBlock; +} + +static KmlBlock* IncludeBlocks(BOOL bInclude, LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPCTSTR szOldText; + KmlBlock* pFirst; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + PrintfToLog(_T("Error while opening include file %s."), szFilename); + return NULL; + } + if ((lpbyBuf = MapKMLFile(hFile)) == NULL) + { + return NULL; + } + + uOldLine = nLexLine; + szOldText = szText; + + nBlocksIncludeLevel++; + PrintfToLog(_T("b%i:%s %s"), + nBlocksIncludeLevel, + (bInclude) ? _T("Including") : _T("Parsing"), + szFilename); + InitLex(lpbyBuf); + pFirst = ParseBlocks(bInclude, FALSE); + CleanLex(); + nBlocksIncludeLevel--; + + nLexLine = uOldLine; + szText = szOldText; + free(lpbyBuf); + + return pFirst; +} + +static KmlBlock* ParseBlocks(BOOL bInclude, BOOL bEndTokenEn) +{ + TokenId eToken; + KmlBlock* pFirst = NULL; + KmlBlock* pBlock = NULL; + + while ((eToken = Lex(LEX_BLOCK)) != TOK_NONE) + { + // allow TOK_END token only as end of a "Locale" block + if (bEndTokenEn && eToken == TOK_END) + { + return pFirst; + } + if (eToken == TOK_INCLUDE) + { + LPTSTR szFilename; + eToken = Lex(LEX_PARAM); // get include parameter in 'szLexString' + if (eToken != TOK_STRING) // not a string (token don't begin with ") + { + AddToLog(_T("Include: string expected as parameter.")); + goto abort; + } + szFilename = szLexString; // save pointer to allocated memory + szLexString = NULL; + eToken = Lex(LEX_PARAM); // decode argument + if (eToken != TOK_EOL) + { + free(szFilename); // free filename string + PrintfToLog(_T("%i: Include: Too many parameters."), nLexLine); + goto abort; + } + if (pFirst) + pBlock = pBlock->pNext = IncludeBlocks(bInclude,szFilename); + else + pBlock = pFirst = IncludeBlocks(bInclude,szFilename); + free(szFilename); // free filename string + if (pBlock == NULL) // parsing error + goto abort; + while (pBlock->pNext) pBlock = pBlock->pNext; + continue; + } + if (eToken == TOK_LOCALE) + { + WORD wLocId,wKeybId; + KmlBlock* pData; + BOOL bIncludeId; + + eToken = Lex(LEX_PARAM); // get include parameter in 'nLexInteger' + if (eToken != TOK_INTEGER) + { + PrintfToLog(_T("%i: Locale parameter must be an integer."), nLexLine); + goto abort; + } + wLocId = nLexInteger; // requested keyboard locale id + eToken = Lex(LEX_PARAM); // decode argument + if (eToken != TOK_EOL) + { + PrintfToLog(_T("%i: Too many parameters for Locale."), nLexLine); + goto abort; + } + + wKeybId = wKeybLocId; // get current keyboard layout input locale + if (SUBLANGID(wLocId) == SUBLANG_NEUTRAL) + { + wKeybId = (PRIMARYLANGID(wLocId) != LANG_NEUTRAL) + ? PRIMARYLANGID(wKeybId) + : LANG_NEUTRAL; + } + + // check if block should be included or skipped + bIncludeId = bInclude && !bLocaleInc && (wKeybId == wLocId); + + PrintfToLog(_T("b%i:%s \"Locale %i\""), + nBlocksIncludeLevel, + (bIncludeId) ? _T("Including") : _T("Skipping"), + wLocId); + + pData = ParseBlocks(bIncludeId,TRUE); // parse block, allow "End" + if (pData == NULL) // parsing error + { + // don't blame the block twice + if (pFirst) FreeBlocks(pFirst); + return NULL; + } + + if (bIncludeId) // insert blocks to block list + { + if (pFirst) + pBlock = pBlock->pNext = pData; + else + pBlock = pFirst = pData; + + // goto end of insertion + while (pBlock->pNext) pBlock = pBlock->pNext; + bLocaleInc = TRUE; // locale block content included + } + else // skip block + { + if (pData) FreeBlocks(pData); + } + continue; + } + if (!IsGlobalBlock(eToken)) // check for valid block commands + { + PrintfToLog(_T("%i: Invalid Block %s."), nLexLine, GetStringOf(eToken)); + goto abort; + } + if (pFirst) + pBlock = pBlock->pNext = ParseBlock(bInclude,eToken); + else + pBlock = pFirst = ParseBlock(bInclude,eToken); + + if (pBlock == NULL) goto abort; + } + if (*szText != 0) // still KML text left + { + goto abort; + } + _ASSERT(szLexString == NULL); + return pFirst; + +abort: + PrintfToLog(_T("Fatal Error at line %i."), nLexLine); + if (szLexString && eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + if (pFirst) FreeBlocks(pFirst); + _ASSERT(szLexString == NULL); + return NULL; +} + + + +//################ +//# +//# Initialization Phase +//# +//################ + +static VOID InitGlobal(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_TITLE: + PrintfToLog(_T("Title: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_AUTHOR: + PrintfToLog(_T("Author: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_PRINT: + AddToLog((LPTSTR)pLine->nParam[0]); + break; + case TOK_HARDWARE: + PrintfToLog(_T("Hardware Platform: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_MODEL: + cCurrentRomType = ((BYTE *)pLine->nParam[0])[0]; + PrintfToLog(_T("Calculator Model : %c"), cCurrentRomType); + break; + case TOK_CLASS: + nCurrentClass = (UINT) pLine->nParam[0]; + PrintfToLog(_T("Calculator Class : %u"), nCurrentClass); + break; + case TOK_ICON: + if (!LoadIconFromFile((LPTSTR) pLine->nParam[0])) + { + PrintfToLog(_T("Cannot load Icon %s."), (LPTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Icon %s loaded."), (LPTSTR)pLine->nParam[0]); + break; + case TOK_DEBUG: + bDebug = (BOOL) pLine->nParam[0]&1; + PrintfToLog(_T("Debug %s"), bDebug?_T("On"):_T("Off")); + break; + case TOK_ROM: + if (pbyRom != NULL) + { + PrintfToLog(_T("Rom %s ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put only one Rom command in the Global block.")); + break; + } + if (!MapRom((LPTSTR)pLine->nParam[0])) + { + PrintfToLog(_T("Cannot open Rom %s."), (LPTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Rom %s loaded."), (LPTSTR)pLine->nParam[0]); + break; + case TOK_PATCH: + if (pbyRom == NULL) + { + PrintfToLog(_T("Patch %s ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put the Rom command before any Patch.")); + break; + } + if (PatchRom((LPTSTR)pLine->nParam[0]) == TRUE) + PrintfToLog(_T("Patch %s loaded."), (LPTSTR)pLine->nParam[0]); + else + PrintfToLog(_T("Patch %s is Wrong or Missing."), (LPTSTR)pLine->nParam[0]); + break; + case TOK_BITMAP: + if (hMainDC != NULL) + { + PrintfToLog(_T("Bitmap %s ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put only one Bitmap command in the Global block.")); + break; + } + if (!CreateMainBitmap((LPTSTR)pLine->nParam[0])) + { + PrintfToLog(_T("Cannot load Bitmap %s."), (LPTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Bitmap %s loaded."), (LPTSTR)pLine->nParam[0]); + break; + case TOK_COLOR: + dwTColorTol = (DWORD) pLine->nParam[0]; + dwTColor = RGB((BYTE) pLine->nParam[1],(BYTE) pLine->nParam[2],(BYTE) pLine->nParam[3]); + break; + case TOK_SCALE: + nScaleMul = (INT) pLine->nParam[0]; + nScaleDiv = (INT) pLine->nParam[1]; + break; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return; +} + +static KmlLine* InitBackground(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + nBackgroundX = (UINT) pLine->nParam[0]; + nBackgroundY = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + nBackgroundW = (UINT) pLine->nParam[0]; + nBackgroundH = (UINT) pLine->nParam[1]; + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static KmlLine* InitLcd(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + nLcdX = (UINT) pLine->nParam[0]; + nLcdY = (UINT) pLine->nParam[1]; + break; + case TOK_ZOOM: + if ((nGdiXZoom = (UINT) pLine->nParam[0]) == 0) + nGdiXZoom = 1; // default zoom + + // search for memory DC zoom (1-4) + for (nLcdZoom = 4; (nGdiXZoom % nLcdZoom) != 0; --nLcdZoom) { }; + _ASSERT(nLcdZoom > 0); // because (nGdiXZoom % 1) == 0 + nGdiXZoom /= nLcdZoom; // remainder is GDI zoom + nGdiYZoom = nGdiXZoom; + break; + case TOK_ZOOMXY: + if ((nGdiXZoom = (UINT) pLine->nParam[0]) == 0) + nGdiXZoom = 1; // default zoom + if ((nGdiYZoom = (UINT) pLine->nParam[1]) == 0) + nGdiYZoom = 1; // default zoom + + // search for memory DC zoom (1-4) + for (nLcdZoom = 4; ((nGdiXZoom % nLcdZoom) | (nGdiYZoom % nLcdZoom)) != 0 ; --nLcdZoom) { }; + _ASSERT(nLcdZoom > 0); // because (nGdiYZoom % 1) == 0 && (nGdiYZoom % 1) == 0 + nGdiXZoom /= nLcdZoom; // remainder is GDI zoom + nGdiYZoom /= nLcdZoom; + break; + case TOK_COLOR: + SetLcdColor((UINT) pLine->nParam[0],(UINT) pLine->nParam[1], + (UINT) pLine->nParam[2],(UINT) pLine->nParam[3]); + break; + case TOK_BITMAP: + if (hAnnunDC != NULL) + { + PrintfToLog(_T("Bitmap %s ignored."), (LPCTSTR)pLine->nParam[0]); + AddToLog(_T("Please put only one Bitmap command in the Lcd block.")); + break; + } + if (!CreateAnnunBitmap((LPCTSTR)pLine->nParam[0])) + { + PrintfToLog(_T("Cannot load Annunciator Bitmap %s."), (LPCTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Annunciator Bitmap %s loaded."), (LPCTSTR)pLine->nParam[0]); + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static KmlLine* InitAnnunciator(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + UINT nId = pBlock->nId-1; + if (nId >= ARRAYSIZEOF(pAnnunciator)) + { + PrintfToLog(_T("Wrong Annunciator Id %i"), nId); + return NULL; + } + nAnnunciators++; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + pAnnunciator[nId].nOx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nOy = (UINT) pLine->nParam[1]; + break; + case TOK_DOWN: + pAnnunciator[nId].nDx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nDy = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + pAnnunciator[nId].nCx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nCy = (UINT) pLine->nParam[1]; + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static VOID InitButton(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + UINT nLevel = 0; + + _ASSERT(ARRAYSIZEOF(pButton) == 256); // adjust warning message + if (nButtons >= ARRAYSIZEOF(pButton)) + { + AddToLog(_T("Only the first 256 buttons will be defined.")); + return; + } + pButton[nButtons].nId = pBlock->nId; + pButton[nButtons].bDown = FALSE; + pButton[nButtons].nType = 0; // default: user defined button + while (pLine) + { + if (nLevel) + { + if (IsBlock(pLine->eCommand)) nLevel++; + if (pLine->eCommand == TOK_END) nLevel--; + pLine = pLine->pNext; + continue; + } + if (IsBlock(pLine->eCommand)) nLevel++; + switch (pLine->eCommand) + { + case TOK_TYPE: + pButton[nButtons].nType = (UINT) pLine->nParam[0]; + break; + case TOK_OFFSET: + pButton[nButtons].nOx = (UINT) pLine->nParam[0]; + pButton[nButtons].nOy = (UINT) pLine->nParam[1]; + break; + case TOK_DOWN: + pButton[nButtons].nDx = (UINT) pLine->nParam[0]; + pButton[nButtons].nDy = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + pButton[nButtons].nCx = (UINT) pLine->nParam[0]; + pButton[nButtons].nCy = (UINT) pLine->nParam[1]; + break; + case TOK_OUTIN: + pButton[nButtons].nOut = (UINT) pLine->nParam[0]; + pButton[nButtons].nIn = (UINT) pLine->nParam[1]; + break; + case TOK_ONDOWN: + pButton[nButtons].pOnDown = pLine; + break; + case TOK_ONUP: + pButton[nButtons].pOnUp = pLine; + break; + case TOK_NOHOLD: + pButton[nButtons].dwFlags &= ~(BUTTON_VIRTUAL); + pButton[nButtons].dwFlags |= BUTTON_NOHOLD; + break; + case TOK_VIRTUAL: + pButton[nButtons].dwFlags &= ~(BUTTON_NOHOLD); + pButton[nButtons].dwFlags |= BUTTON_VIRTUAL; + break; + default: + PrintfToLog(_T("Command %s Ignored in Block %s %i"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType), pBlock->nId); + } + pLine = pLine->pNext; + } + if (nLevel) + PrintfToLog(_T("%i Open Block(s) in Block %s %i"), nLevel, GetStringOf(pBlock->eType), pBlock->nId); + nButtons++; + return; +} + + + +//################ +//# +//# Execution +//# +//################ + +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand) +{ + UINT nLevel = 0; + while (pLine) + { + if (IsBlock(pLine->eCommand)) nLevel++; + if (pLine->eCommand == eCommand) + { + // found token, return command behind token + if (nLevel == 0) return pLine->pNext; + } + if (pLine->eCommand == TOK_END) + { + if (nLevel) + nLevel--; + else + break; + } + pLine = pLine->pNext; + } + return pLine; +} + +static KmlLine* If(KmlLine* pLine, BOOL bCondition) +{ + pLine = pLine->pNext; + if (bCondition) + { + while (pLine) + { + if (pLine->eCommand == TOK_END) + { + pLine = pLine->pNext; + break; + } + if (pLine->eCommand == TOK_ELSE) + { + pLine = SkipLines(pLine, TOK_END); + break; + } + pLine = RunLine(pLine); + } + } + else + { + pLine = SkipLines(pLine, TOK_ELSE); + while (pLine) + { + if (pLine->eCommand == TOK_END) + { + pLine = pLine->pNext; + break; + } + pLine = RunLine(pLine); + } + } + return pLine; +} + +static KmlLine* RunLine(KmlLine* pLine) +{ + BYTE byVal; + + switch (pLine->eCommand) + { + case TOK_MAP: + if (byVKeyMap[pLine->nParam[0]&0xFF]&1) + PressButtonById((UINT) pLine->nParam[1]); + else + ReleaseButtonById((UINT) pLine->nParam[1]); + break; + case TOK_PRESS: + PressButtonById((UINT) pLine->nParam[0]); + break; + case TOK_RELEASE: + ReleaseButtonById((UINT) pLine->nParam[0]); + break; + case TOK_MENUITEM: + PostMessage(hWnd, WM_COMMAND, 0x19C40+(pLine->nParam[0]&0xFF), 0); + break; + case TOK_SYSITEM: + PostMessage(hWnd, WM_SYSCOMMAND, pLine->nParam[0], 0); + break; + case TOK_SETFLAG: + nKMLFlags |= 1<<(pLine->nParam[0]&0x1F); + break; + case TOK_RESETFLAG: + nKMLFlags &= ~(1<<(pLine->nParam[0]&0x1F)); + break; + case TOK_NOTFLAG: + nKMLFlags ^= 1<<(pLine->nParam[0]&0x1F); + break; + case TOK_IFPRESSED: + return If(pLine,byVKeyMap[pLine->nParam[0]&0xFF]); + case TOK_IFFLAG: + return If(pLine,(nKMLFlags>>(pLine->nParam[0]&0x1F))&1); + case TOK_IFMEM: + Npeek(&byVal,(DWORD) pLine->nParam[0],1); + return If(pLine,(byVal & pLine->nParam[1]) == pLine->nParam[2]); + default: + break; + } + return pLine->pNext; +} + + + +//################ +//# +//# Clean Up +//# +//################ + +static VOID FreeLines(KmlLine* pLine) +{ + while (pLine) + { + KmlLine* pThisLine = pLine; + UINT i = 0; + DWORD nParams; + while (pLexToken[i].nLen) // search in all token definitions + { + // break when token definition found + if (pLexToken[i].eId == pLine->eCommand) break; + i++; // next token definition + } + nParams = pLexToken[i].nParams; // get argument types of command + i = 0; // first parameter + while ((nParams&7)) // argument left + { + if ((nParams&7) == TYPE_STRING) // string type + { + free((LPVOID)pLine->nParam[i]); + } + i++; // incr. parameter buffer index + nParams >>= 3; // next argument type + } + pLine = pLine->pNext; // get next line + free(pThisLine); + } + return; +} + +VOID FreeBlocks(KmlBlock* pBlock) +{ + while (pBlock) + { + KmlBlock* pThisBlock = pBlock; + pBlock = pBlock->pNext; + FreeLines(pThisBlock->pFirstLine); + free(pThisBlock); + } + return; +} + +VOID KillKML(VOID) +{ + if ((nState==SM_RUN)||(nState==SM_SLEEP)) + { + AbortMessage(_T("FATAL: KillKML while emulator is running !!!")); + SwitchToState(SM_RETURN); + DestroyWindow(hWnd); + } + UnmapRom(); + DestroyLcdBitmap(); + DestroyAnnunBitmap(); + DestroyMainBitmap(); + if (hPalette) + { + if (hWindowDC) SelectPalette(hWindowDC, hOldPalette, FALSE); + VERIFY(DeleteObject(hPalette)); + hPalette = NULL; + } + if (hRgn != NULL) // region defined + { + if (hWnd != NULL) // window available + { + EnterCriticalSection(&csGDILock); + { + // deletes the region resource + SetWindowRgn(hWnd,NULL,FALSE); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + } + hRgn = NULL; + } + LoadIconDefault(); + bClicking = FALSE; + uButtonClicked = 0; + FreeBlocks(pKml); + pKml = NULL; + nButtons = 0; + nScancodes = 0; + nAnnunciators = 0; + bDebug = TRUE; + wKeybLocId = 0; + bLocaleInc = FALSE; + nKMLFlags = 0; + ZeroMemory(pButton, sizeof(pButton)); + ZeroMemory(pAnnunciator, sizeof(pAnnunciator)); + ZeroMemory(pVKey, sizeof(pVKey)); + ZeroMemory(byVKeyMap, sizeof(byVKeyMap)); + ClearLog(); + nBackgroundX = 0; + nBackgroundY = 0; + nBackgroundW = 256; + nBackgroundH = 0; + nLcdZoom = 1; + nGdiXZoom = 1; + nGdiYZoom = 1; + dwTColor = (DWORD) -1; + dwTColorTol = 0; + nScaleMul = 0; + nScaleDiv = 0; + cCurrentRomType = 0; + nCurrentClass = 0; + ResizeWindow(); + return; +} + + + +//################ +//# +//# Extract Keyword's Parameters +//# +//################ + +static LPCTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +{ + while (pBlock) + { + if (pBlock->eType == eBlock) + { + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + if (pLine->eCommand == eCommand) + { + return (LPCTSTR) pLine->nParam[nParam]; + } + pLine = pLine->pNext; + } + } + pBlock = pBlock->pNext; + } + return NULL; +} + +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +{ + while (pBlock) + { + if (pBlock->eType == eBlock) + { + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + if (pLine->eCommand == eCommand) + { + return (DWORD) pLine->nParam[nParam]; + } + pLine = pLine->pNext; + } + } + pBlock = pBlock->pNext; + } + return 0; +} + + + +//################ +//# +//# Buttons +//# +//################ + +static UINT iSqrt(UINT nNumber) // integer y=sqrt(x) function +{ + UINT b, t; + + b = t = nNumber; + + if (nNumber > 0) + { + do + { + b = t; + t = (t + nNumber / t) / 2; // Heron's method + } + while (t < b); + } + return b; +} + +static VOID AdjustPixel(LPBYTE pbyPixel, BYTE byOffset) +{ + INT i = 3; // BGR colors + + while (--i >= 0) + { + WORD wColor = (WORD) *pbyPixel + byOffset; + // jumpless saturation to 0xFF + wColor = -(wColor >> 8) | (BYTE) wColor; + *pbyPixel++ = (BYTE) wColor; + } + return; +} + +// draw transparent circle button type +static __inline VOID TransparentCircle(UINT nOx, UINT nOy, UINT nCx, UINT nCy) +{ + #define HIGHADJ 0x80 // color incr. at center + #define LOWADJ 0x10 // color incr. at border + + BITMAPINFO bmi; + HDC hMemDC; + HBITMAP hMemBitMap; + LPBYTE pbyPixels; // BMP data + + UINT x, y, cx, cy, r, rr, rrc; + + r = min(nCx,nCy) / 2; // radius + if (r < 2) return; // radius 2 pixel minimum + + // create memory copy of button rectangle + ZeroMemory(&bmi,sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = (LONG) nCx; + bmi.bmiHeader.biHeight = (LONG) nCy; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; // use 32 bit bitmap for easier buffer calculation + bmi.bmiHeader.biCompression = BI_RGB; + VERIFY(hMemBitMap = CreateDIBSection(hWindowDC, + &bmi, + DIB_RGB_COLORS, + (VOID **)&pbyPixels, + NULL, + 0)); + if (hMemBitMap == NULL) return; + + hMemDC = CreateCompatibleDC(hWindowDC); + hMemBitMap = (HBITMAP) SelectObject(hMemDC,hMemBitMap); + BitBlt(hMemDC, 0, 0, nCx, nCy, hWindowDC, nOx, nOy, SRCCOPY); + + cx = nCx / 2; // x-center coordinate + cy = nCy / 2; // y-center coordinate + + rr = r * r; // calculate r^2 + rrc = (r-1) * (r-1); // calculate (r-1)^2 for color steps + + // y-rows of circle + for (y = 0; y < r; ++y) + { + UINT yy = y * y; // calculate y^2 + + // x-columns of circle + UINT nXWidth = iSqrt(rr-yy); + + for (x = 0; x < nXWidth; ++x) + { + // color offset, sqrt(x*x+y*y) < r !!! + BYTE byOff = HIGHADJ - (BYTE) (iSqrt((x*x+yy) * (HIGHADJ-LOWADJ)*(HIGHADJ-LOWADJ) / rrc)); + + AdjustPixel(pbyPixels + (((cy+y) * bmi.bmiHeader.biWidth + (cx+x)) << 2), byOff); + + if (x != 0) + { + AdjustPixel(pbyPixels + (((cy+y) * bmi.bmiHeader.biWidth + (cx-x)) << 2), byOff); + } + + if (y != 0) + { + AdjustPixel(pbyPixels + (((cy-y) * bmi.bmiHeader.biWidth + (cx+x)) << 2), byOff); + } + + if (x != 0 && y != 0) + { + AdjustPixel(pbyPixels + (((cy-y) * bmi.bmiHeader.biWidth + (cx-x)) << 2), byOff); + } + } + } + + // update button area with modified data + BitBlt(hWindowDC, nOx, nOy, nCx, nCy, hMemDC, 0, 0, SRCCOPY); + + // delete memory bitmap + VERIFY(DeleteObject(SelectObject(hMemDC,hMemBitMap))); + DeleteDC(hMemDC); + return; + + #undef HIGHADJ + #undef LOWADJ +} + +static VOID DrawButton(UINT nId) +{ + UINT x0 = pButton[nId].nOx; + UINT y0 = pButton[nId].nOy; + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + switch (pButton[nId].nType) + { + case 0: // bitmap key + if (pButton[nId].bDown) + { + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, pButton[nId].nDx, pButton[nId].nDy, SRCCOPY); + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + case 1: // shift key to right down + if (pButton[nId].bDown) + { + UINT x1 = x0+pButton[nId].nCx-1; + UINT y1 = y0+pButton[nId].nCy-1; + BitBlt(hWindowDC, x0+3,y0+3,pButton[nId].nCx-5,pButton[nId].nCy-5,hMainDC,x0+2,y0+2,SRCCOPY); + SelectObject(hWindowDC, GetStockObject(BLACK_PEN)); + MoveToEx(hWindowDC, x0, y0, NULL); LineTo(hWindowDC, x1, y0); + MoveToEx(hWindowDC, x0, y0, NULL); LineTo(hWindowDC, x0, y1); + SelectObject(hWindowDC, GetStockObject(WHITE_PEN)); + MoveToEx(hWindowDC, x1, y0, NULL); LineTo(hWindowDC, x1, y1); + MoveToEx(hWindowDC, x0, y1, NULL); LineTo(hWindowDC, x1+1, y1); + } + else + { + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + case 2: // do nothing + break; + case 3: // invert key color, even in display + if (pButton[nId].bDown) + { + PatBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, DSTINVERT); + } + else + { + RECT Rect; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; + InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw + } + break; + case 4: // bitmap key, even in display + if (pButton[nId].bDown) + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + else + { + RECT Rect; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; + InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw + } + break; + case 5: // transparent circle + if (pButton[nId].bDown) + { + TransparentCircle(x0, y0, pButton[nId].nCx, pButton[nId].nCy); + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + default: // black key, default drawing on illegal types + if (pButton[nId].bDown) + { + PatBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, BLACKNESS); + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + } + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +static VOID PressButton(UINT nId) +{ + if (!pButton[nId].bDown) // button not pressed + { + pButton[nId].bDown = TRUE; + DrawButton(nId); + if (pButton[nId].nIn) + { + KeyboardEvent(TRUE,pButton[nId].nOut,pButton[nId].nIn); + } + else + { + KmlLine* pLine = pButton[nId].pOnDown; + while ((pLine)&&(pLine->eCommand!=TOK_END)) + { + pLine = RunLine(pLine); + } + } + } + return; +} + +static VOID ReleaseButton(UINT nId) +{ + if (pButton[nId].bDown) // button not released + { + pButton[nId].bDown = FALSE; + DrawButton(nId); + if (pButton[nId].nIn) + { + KeyboardEvent(FALSE,pButton[nId].nOut,pButton[nId].nIn); + } + else + { + KmlLine* pLine = pButton[nId].pOnUp; + while ((pLine)&&(pLine->eCommand!=TOK_END)) + { + pLine = RunLine(pLine); + } + } + } + return; +} + +static VOID PressButtonById(UINT nId) +{ + UINT i; + for (i=0; iright > (LONG) (pButton[i].nOx) + && rc->bottom > (LONG) (pButton[i].nOy) + && rc->left <= (LONG) (pButton[i].nOx + pButton[i].nCx) + && rc->top <= (LONG) (pButton[i].nOy + pButton[i].nCy)) + { + // on button type 3 and 5 clear complete key area before drawing + if (pButton[i].nType == 3 || pButton[i].nType == 5) + { + UINT x0 = pButton[i].nOx; + UINT y0 = pButton[i].nOy; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + BitBlt(hWindowDC, x0, y0, pButton[i].nCx, pButton[i].nCy, hMainDC, x0, y0, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + } + DrawButton(i); // redraw pressed button + } + } + return; +} + + +//################ +//# +//# Annunciators +//# +//################ + +VOID DrawAnnunciator(UINT nId, BOOL bOn) +{ + HDC hDC; + UINT nSx,nSy; + + --nId; // zero based ID + if (nId >= ARRAYSIZEOF(pAnnunciator)) return; + if (bOn) + { + hDC = hAnnunDC != NULL ? hAnnunDC : hMainDC; + + nSx = pAnnunciator[nId].nDx; // position of annunciator + nSy = pAnnunciator[nId].nDy; + } + else + { + hDC = hMainDC; + + nSx = pAnnunciator[nId].nOx; // position of background + nSy = pAnnunciator[nId].nOy; + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + BitBlt(hWindowDC, + pAnnunciator[nId].nOx, pAnnunciator[nId].nOy, + pAnnunciator[nId].nCx, pAnnunciator[nId].nCy, + hDC, + nSx, nSy, + SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + + + +//################ +//# +//# Mouse +//# +//################ + +static BOOL ClipButton(UINT x, UINT y, UINT nId) +{ + x += nBackgroundX; // source display offset + y += nBackgroundY; + + return (pButton[nId].nOx<=x) + && (pButton[nId].nOy<=y) + &&(x<(pButton[nId].nOx+pButton[nId].nCx)) + &&(y<(pButton[nId].nOy+pButton[nId].nCy)); +} + +BOOL MouseIsButton(DWORD x, DWORD y) +{ + UINT i; + for (i = 0; i < nButtons; i++) // scan all buttons + { + if (ClipButton(x,y,i)) // cursor over button? + { + return TRUE; + } + } + return FALSE; +} + +VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y) +{ + UINT i; + for (i=0; i hand cursor else normal arrow cursor + SetCursor(MouseIsButton(x,y) ? hCursorHand : hCursorArrow); + + if (!(nFlags&MK_LBUTTON)) return; // left mouse key not pressed -> quit + if (bKeyPressed && !ClipButton(x,y,uLastKeyPressed)) // not on last pressed key + ReleaseAllButtons(); // release all buttons + if (!bClicking) return; // normal emulation key -> quit + + if (pButton[uButtonClicked].dwFlags&BUTTON_NOHOLD) + { + if (ClipButton(x,y, uButtonClicked) != pButton[uButtonClicked].bDown) + { + pButton[uButtonClicked].bDown = !pButton[uButtonClicked].bDown; + DrawButton(uButtonClicked); + } + return; + } + if (pButton[uButtonClicked].dwFlags&BUTTON_VIRTUAL) + { + if (!ClipButton(x,y, uButtonClicked)) + { + ReleaseButton(uButtonClicked); + bClicking = FALSE; + uButtonClicked = 0; + } + return; + } + return; +} + + + +//################ +//# +//# Keyboard +//# +//################ + +VOID RunKey(BYTE nId, BOOL bPressed) +{ + if (pVKey[nId]) + { + KmlLine* pLine = pVKey[nId]->pFirstLine; + byVKeyMap[nId] = (BYTE) bPressed; + while (pLine) pLine = RunLine(pLine); + } + else + { + if (bDebug&&bPressed) + { + TCHAR szTemp[128]; + wsprintf(szTemp,_T("Scancode %i"),nId); + InfoMessage(szTemp); + } + } + return; +} + + + +//################ +//# +//# Macro player +//# +//################ + +VOID PlayKey(UINT nOut, UINT nIn, BOOL bPressed) +{ + // scan from last buttons because LCD buttons mostly defined first + INT i = nButtons; + while (--i >= 0) + { + if (pButton[i].nOut == nOut && pButton[i].nIn == nIn) + { + if (bPressed) + PressButton(i); + else + ReleaseButton(i); + return; + } + } + return; +} + + + +//################ +//# +//# Load and Initialize Script +//# +//################ + +static VOID ResizeMainBitmap(INT nMul, INT nDiv) +{ + if (nMul * nDiv > 0) // resize main picture + { + BITMAP Bitmap; + int nMode; + INT nWidth,nHeight; + UINT i; + + // update graphic + nBackgroundX = MulDiv(nBackgroundX,nMul,nDiv); + nBackgroundY = MulDiv(nBackgroundY,nMul,nDiv); + nBackgroundW = MulDiv(nBackgroundW,nMul,nDiv); + nBackgroundH = MulDiv(nBackgroundH,nMul,nDiv); + nLcdX = MulDiv(nLcdX,nMul,nDiv); + nLcdY = MulDiv(nLcdY,nMul,nDiv); + nGdiXZoom = MulDiv(nGdiXZoom * nLcdZoom,nMul,nDiv); + nGdiYZoom = MulDiv(nGdiYZoom * nLcdZoom,nMul,nDiv); + + // search for memory DC zoom (1-4) + for (nLcdZoom = 4; ((nGdiXZoom % nLcdZoom) | (nGdiYZoom % nLcdZoom)) != 0 ; --nLcdZoom) { }; + _ASSERT(nLcdZoom > 0); // because (nGdiYZoom % 1) == 0 && (nGdiYZoom % 1) == 0 + nGdiXZoom /= nLcdZoom; // remainder is GDI zoom + nGdiYZoom /= nLcdZoom; + + // update script coordinates (buttons) + for (i = 0; i < nButtons; ++i) + { + pButton[i].nOx = (UINT) (pButton[i].nOx * nMul / nDiv); + pButton[i].nOy = (UINT) (pButton[i].nOy * nMul / nDiv); + pButton[i].nDx = (UINT) (pButton[i].nDx * nMul / nDiv); + pButton[i].nDy = (UINT) (pButton[i].nDy * nMul / nDiv); + pButton[i].nCx = (UINT) (pButton[i].nCx * nMul / nDiv); + pButton[i].nCy = (UINT) (pButton[i].nCy * nMul / nDiv); + } + + // update script coordinates (annunciators) + for (i = 0; i < ARRAYSIZEOF(pAnnunciator); ++i) + { + // translate offset + pAnnunciator[i].nOx = (UINT) (pAnnunciator[i].nOx * nMul / nDiv); + pAnnunciator[i].nOy = (UINT) (pAnnunciator[i].nOy * nMul / nDiv); + + if (hAnnunDC == NULL) // no external annunciator bitmap + { + // translate down (with rounding) + pAnnunciator[i].nDx = (UINT) MulDiv(pAnnunciator[i].nDx,nMul,nDiv); + pAnnunciator[i].nDy = (UINT) MulDiv(pAnnunciator[i].nDy,nMul,nDiv); + // translate size (with rounding) + pAnnunciator[i].nCx = (UINT) MulDiv(pAnnunciator[i].nCx,nMul,nDiv); + pAnnunciator[i].nCy = (UINT) MulDiv(pAnnunciator[i].nCy,nMul,nDiv); + } + } + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // get bitmap size + GetObject(GetCurrentObject(hMainDC,OBJ_BITMAP),sizeof(Bitmap),&Bitmap); + + // resulting bitmap size + nWidth = MulDiv(Bitmap.bmWidth,nMul,nDiv); + nHeight = MulDiv(Bitmap.bmHeight,nMul,nDiv); + + VERIFY(nMode = SetStretchBltMode(hMainDC,HALFTONE)); + + if (nMul <= nDiv) // shrinking bitmap + { + VERIFY(StretchBlt( + hMainDC,0,0,nWidth,nHeight, + hMainDC,0,0,Bitmap.bmWidth,Bitmap.bmHeight, + SRCCOPY)); + } + else // expanding bitmap + { + // bitmap with new size + HDC hMemDC = CreateCompatibleDC(hMainDC); + HBITMAP hMainBitMap = CreateCompatibleBitmap(hMainDC,nWidth,nHeight); + HBITMAP hMemBitMap = (HBITMAP) SelectObject(hMemDC,SelectObject(hMainDC,hMainBitMap)); + + VERIFY(StretchBlt( + hMainDC,0,0,nWidth,nHeight, + hMemDC,0,0,Bitmap.bmWidth,Bitmap.bmHeight, + SRCCOPY)); + + // delete original bitmap + VERIFY(DeleteObject(SelectObject(hMemDC,hMemBitMap))); + DeleteDC(hMemDC); + } + + VERIFY(SetStretchBltMode(hMainDC,nMode)); + + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + ResizeWindow(); + } + return; +} + +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + TokenId eToken; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) return NULL; + if ((lpBuf = MapKMLFile(hFile)) == NULL) + return NULL; + + InitLex(lpBuf); + pBlock = NULL; + while ((eToken = Lex(LEX_BLOCK)) != TOK_NONE) + { + if (eToken == TOK_GLOBAL) + { + pBlock = ParseBlock(TRUE,eToken); + if (pBlock) pBlock->pNext = NULL; + break; + } + if (eToken == TOK_STRING) + { + free(szLexString); + szLexString = NULL; + } + } + CleanLex(); + ClearLog(); + free(lpBuf); + return pBlock; +} + +BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog) +{ + TCHAR szKLID[KL_NAMELENGTH]; + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + BOOL bOk = FALSE; + + KillKML(); + + // get current keyboard layout input locale + if (GetKeyboardLayoutName(szKLID)) + { + wKeybLocId = (WORD) _tcstoul(szKLID,NULL,16); + } + + nBlocksIncludeLevel = 0; + PrintfToLog(_T("Reading %s"), szFilename); + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + AddToLog(_T("Error while opening the file.")); + goto quit; + } + if ((lpBuf = MapKMLFile(hFile)) == NULL) + goto quit; + + InitLex(lpBuf); + pKml = ParseBlocks(TRUE, // include blocks + FALSE); // keyword "End" is invalid + CleanLex(); + + free(lpBuf); + if (pKml == NULL) goto quit; + + pBlock = pKml; + while (pBlock) + { + switch (pBlock->eType) + { + case TOK_BUTTON: + InitButton(pBlock); + break; + case TOK_SCANCODE: + nScancodes++; + pVKey[pBlock->nId] = pBlock; + break; + case TOK_ANNUNCIATOR: + InitAnnunciator(pBlock); + break; + case TOK_GLOBAL: + InitGlobal(pBlock); + break; + case TOK_LCD: + InitLcd(pBlock); + break; + case TOK_BACKGROUND: + InitBackground(pBlock); + break; + default: + PrintfToLog(_T("Block %s Ignored."), GetStringOf(pBlock->eType)); + pBlock = pBlock->pNext; + } + pBlock = pBlock->pNext; + } + + if (!isModelValid(cCurrentRomType)) + { + AddToLog(_T("This KML Script doesn't specify a valid model.")); + goto quit; + } + if (pbyRom == NULL) + { + AddToLog(_T("This KML Script doesn't specify the ROM to use, or the ROM could not be loaded.")); + goto quit; + } + if (hMainDC == NULL) + { + AddToLog(_T("This KML Script doesn't specify the background bitmap, or bitmap could not be loaded.")); + goto quit; + } + if (!CrcRom(&wRomCrc)) // build patched ROM fingerprint and check for unpacked data + { + AddToLog(_T("Error, packed ROM image detected.")); + UnmapRom(); // free memory + goto quit; + } + + ResizeMainBitmap(nScaleMul,nScaleDiv); // resize main picture + CreateLcdBitmap(); + + PrintfToLog(_T("%i Buttons Defined"), nButtons); + PrintfToLog(_T("%i Scancodes Defined"), nScancodes); + PrintfToLog(_T("%i Annunciators Defined"), nAnnunciators); + PrintfToLog(_T("Keyboard Locale: %i"), wKeybLocId); + + bOk = TRUE; + +quit: + if (bOk) + { + // HP38G/HP39G(+|s)/HP40G(s) have no object loading, ignore // CdB for HP: add apples + DragAcceptFiles(hWnd,cCurrentRomType != '6' && cCurrentRomType != 'A' && cCurrentRomType != 'E' && cCurrentRomType != 'P'); + + if (!bNoLog) + { + AddToLog(_T("Press Ok to Continue.")); + if (bAlwaysDisplayLog&&(!DisplayKMLLog(bOk))) + { + KillKML(); + return FALSE; + } + } + } + else + { + AddToLog(_T("Press Cancel to Abort.")); + if (!DisplayKMLLog(bOk)) + { + KillKML(); + return FALSE; + } + } + + ResizeWindow(); + ClearLog(); + return bOk; +} diff --git a/app/src/main/cpp/KML.H b/app/src/main/cpp/KML.H new file mode 100644 index 0000000..ced8be5 --- /dev/null +++ b/app/src/main/cpp/KML.H @@ -0,0 +1,133 @@ +/* + * kml.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +#define LEX_BLOCK 0 +#define LEX_COMMAND 1 +#define LEX_PARAM 2 + +typedef enum eTokenId +{ + TOK_NONE, //0 + TOK_ANNUNCIATOR, //1 + TOK_BACKGROUND, //2 + TOK_IFPRESSED, //3 + TOK_RESETFLAG, //4 + TOK_SCANCODE, //5 + TOK_HARDWARE, //6 + TOK_MENUITEM, //7 + TOK_SYSITEM, //8 + TOK_INTEGER, //9 + TOK_SETFLAG, //10 + TOK_RELEASE, //11 + TOK_VIRTUAL, //12 + TOK_INCLUDE, //13 + TOK_NOTFLAG, //14 + TOK_STRING, //15 + TOK_GLOBAL, //16 + TOK_AUTHOR, //17 + TOK_BITMAP, //18 + TOK_ZOOMXY, //19 + TOK_OFFSET, //20 + TOK_BUTTON, //21 + TOK_IFFLAG, //22 + TOK_ONDOWN, //23 + TOK_NOHOLD, //24 + TOK_LOCALE, //25 + TOK_TOPBAR, //26 + TOK_MENUBAR, //27 + TOK_TITLE, //28 + TOK_OUTIN, //29 + TOK_PATCH, //30 + TOK_PRINT, //31 + TOK_DEBUG, //32 + TOK_COLOR, //33 + TOK_MODEL, //34 + TOK_CLASS, //35 + TOK_PRESS, //36 + TOK_IFMEM, //37 + TOK_SCALE, //38 + TOK_TYPE, //39 + TOK_SIZE, //40 + TOK_DOWN, //41 + TOK_ZOOM, //42 + TOK_ELSE, //43 + TOK_ONUP, //44 + TOK_ICON, //45 + TOK_EOL, //46 + TOK_MAP, //47 + TOK_ROM, //48 + TOK_VGA, //49 + TOK_LCD, //50 + TOK_END //51 +} TokenId; + +#define TYPE_NONE 00 +#define TYPE_INTEGER 01 +#define TYPE_STRING 02 + +typedef struct KmlToken +{ + TokenId eId; + DWORD nParams; + DWORD nLen; + LPCTSTR szName; +} KmlToken; + +typedef struct KmlLine +{ + struct KmlLine* pNext; + TokenId eCommand; + DWORD_PTR nParam[6]; +} KmlLine; + +typedef struct KmlBlock +{ + TokenId eType; + DWORD nId; + struct KmlLine* pFirstLine; + struct KmlBlock* pNext; +} KmlBlock; + +#define BUTTON_NOHOLD 0x0001 +#define BUTTON_VIRTUAL 0x0002 +typedef struct KmlButton +{ + UINT nId; + BOOL bDown; + UINT nType; + DWORD dwFlags; + UINT nOx, nOy; + UINT nDx, nDy; + UINT nCx, nCy; + UINT nOut, nIn; + KmlLine* pOnDown; + KmlLine* pOnUp; +} KmlButton; + +typedef struct KmlAnnunciator +{ + UINT nOx, nOy; + UINT nDx, nDy; + UINT nCx, nCy; +} KmlAnnunciator; + +extern KmlBlock* pKml; +extern BOOL DisplayChooseKml(CHAR cType); +extern VOID FreeBlocks(KmlBlock* pBlock); +extern VOID DrawAnnunciator(UINT nId, BOOL bOn); +extern VOID ReloadButtons(BYTE *Keyboard_Row, UINT nSize); +extern VOID RefreshButtons(RECT *rc); +extern BOOL MouseIsButton(DWORD x, DWORD y); +extern VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y); +extern VOID MouseButtonUpAt(UINT nFlags, DWORD x, DWORD y); +extern VOID MouseMovesTo(UINT nFlags, DWORD x, DWORD y); +extern VOID RunKey(BYTE nId, BOOL bPressed); +extern VOID PlayKey(UINT nOut, UINT nIn, BOOL bPressed); +extern BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog); +extern VOID KillKML(VOID); diff --git a/app/src/main/cpp/LODEPNG.C b/app/src/main/cpp/LODEPNG.C new file mode 100644 index 0000000..d67b3eb --- /dev/null +++ b/app/src/main/cpp/LODEPNG.C @@ -0,0 +1,6300 @@ +/* +LodePNG version 20180611 + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" + +#include +#include +#include + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20180611"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) +{ +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif + return malloc(size); +} + +static void* lodepng_realloc(void* ptr, size_t new_size) +{ +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) +{ + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code)\ +{\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code)\ +{\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call)\ +{\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*dynamic vector of unsigned ints*/ +typedef struct uivector +{ + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) +{ + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_reserve(uivector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) +{ + if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; + p->size = size; + return 1; /*success*/ +} + +/*resize and give all new elements the value*/ +static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) +{ + size_t oldsize = p->size, i; + if(!uivector_resize(p, size)) return 0; + for(i = oldsize; i < size; ++i) p->data[i] = value; + return 1; +} + +static void uivector_init(uivector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) +{ + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector +{ + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_reserve(ucvector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) +{ + if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; + p->size = size; + return 1; /*success*/ +} + +#ifdef LODEPNG_COMPILE_PNG + +static void ucvector_cleanup(void* p) +{ + ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; + lodepng_free(((ucvector*)p)->data); + ((ucvector*)p)->data = NULL; +} + +static void ucvector_init(ucvector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*you can both convert from vector to buffer&size and vica versa. If you use +init_buffer to take over a buffer and size, it is not needed to use cleanup*/ +static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) +{ + p->data = buffer; + p->allocsize = p->size = size; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_push_back(ucvector* p, unsigned char c) +{ + if(!ucvector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned string_resize(char** out, size_t size) +{ + char* data = (char*)lodepng_realloc(*out, size + 1); + if(data) + { + data[size] = 0; /*null termination char*/ + *out = data; + } + return data != 0; +} + +/*init a {char*, size_t} pair for use as string*/ +static void string_init(char** out) +{ + *out = NULL; + string_resize(out, 0); +} + +/*free the above pair again*/ +static void string_cleanup(char** out) +{ + lodepng_free(*out); + *out = NULL; +} + +static void string_set(char** out, const char* in) +{ + size_t insize = strlen(in), i; + if(string_resize(out, insize)) + { + for(i = 0; i != insize; ++i) + { + (*out)[i] = in[i]; + } + } +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_read32bitInt(const unsigned char* buffer) +{ + return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); +} + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) +{ + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +#ifdef LODEPNG_COMPILE_ENCODER +static void lodepng_add32bitInt(ucvector* buffer, unsigned value) +{ + ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ + lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) +{ + FILE* file; + long size; + file = fopen(filename, "rb"); + if(!file) return -1; + + if(fseek(file, 0, SEEK_END) != 0) + { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + + fclose(file); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) +{ + FILE* file; + size_t readsize; + file = fopen(filename, "rb"); + if(!file) return 78; + + readsize = fread(out, 1, size, file); + fclose(file); + + if (readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + long size = lodepng_filesize(filename); + if (size < 0) return 78; + *outsize = (size_t)size; + + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ + + return lodepng_buffer_file(*out, (size_t)size, filename); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) +{ + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite((char*)buffer , 1 , buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*TODO: this ignores potential out of memory errors*/ +#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ +{\ + /*add a new byte at the end*/\ + if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ + (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ + ++(*bitpointer);\ +} + +static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); +} + +static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) + +static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0, i; + for(i = 0; i != nbits; ++i) + { + result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; + ++(*bitpointer); + } + return result; +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + +/*the order in which "code length alphabet code lengths" are stored, out of this +the huffman tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree +{ + unsigned* tree2d; + unsigned* tree1d; + unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ +} HuffmanTree; + +/*function used for debug purposes to draw the tree in ascii art with C++*/ +/* +static void HuffmanTree_draw(HuffmanTree* tree) +{ + std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; + for(size_t i = 0; i != tree->tree1d.size; ++i) + { + if(tree->lengths.data[i]) + std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; + } + std::cout << std::endl; +}*/ + +static void HuffmanTree_init(HuffmanTree* tree) +{ + tree->tree2d = 0; + tree->tree1d = 0; + tree->lengths = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) +{ + lodepng_free(tree->tree2d); + lodepng_free(tree->tree1d); + lodepng_free(tree->lengths); +} + +/*the tree representation used by the decoder. return value is error*/ +static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) +{ + unsigned nodefilled = 0; /*up to which node it is filled*/ + unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ + unsigned n, i; + + tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); + if(!tree->tree2d) return 83; /*alloc fail*/ + + /* + convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means + uninited, a value >= numcodes is an address to another bit, a value < numcodes + is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as + many columns as codes - 1. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + Here, the internal nodes are stored (what their 0 and 1 option point to). + There is only memory for such good tree currently, if there are more nodes + (due to too long length codes), error 55 will happen + */ + for(n = 0; n < tree->numcodes * 2; ++n) + { + tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + } + + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ + { + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ + { + unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; + if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if(i + 1 == tree->lengths[n]) /*last bit*/ + { + tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ + treepos = 0; + } + else + { + /*put address of the next step in here, first that address has to be found of course + (it's just nodefilled + 1)...*/ + ++nodefilled; + /*addresses encoded with numcodes added to it*/ + tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; + treepos = nodefilled; + } + } + else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; + } + } + + for(n = 0; n < tree->numcodes * 2; ++n) + { + if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) +{ + uivector blcount; + uivector nextcode; + unsigned error = 0; + unsigned bits, n; + + uivector_init(&blcount); + uivector_init(&nextcode); + + tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + if(!tree->tree1d) error = 83; /*alloc fail*/ + + if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) + || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) + error = 83; /*alloc fail*/ + + if(!error) + { + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) + { + nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) + { + if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; + } + } + + uivector_cleanup(&blcount); + uivector_cleanup(&nextcode); + + if(!error) return HuffmanTree_make2DTree(tree); + else return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) +{ + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode +{ + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists +{ + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) +{ + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +/*sort the leaves with stable mergesort*/ +static void bpmnode_sort(BPMNode* leaves, size_t num) +{ + BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); + size_t width, counter = 0; + for(width = 1; width < num; width *= 2) + { + BPMNode* a = (counter & 1) ? mem : leaves; + BPMNode* b = (counter & 1) ? leaves : mem; + size_t p; + for(p = 0; p < num; p += 2 * width) + { + size_t q = (p + width > num) ? num : (p + width); + size_t r = (p + 2 * width > num) ? num : (p + 2 * width); + size_t i = p, j = q, k; + for(k = p; k < r; k++) + { + if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; + else b[k] = a[j++]; + } + } + counter++; + } + if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); + lodepng_free(mem); +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) +{ + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) + { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else + { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) + { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) + { + if(frequencies[i] > 0) + { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + for(i = 0; i != numcodes; ++i) lengths[i] = 0; + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) + { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } + else if(numpresent == 1) + { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } + else + { + BPMLists lists; + BPMNode* node; + + bpmnode_sort(leaves, numpresent); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) + { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) + { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + /*initialize all lengths to 0*/ + memset(tree->lengths, 0, numcodes * sizeof(unsigned)); + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} + +static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) +{ + return tree->tree1d[index]; +} + +static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) +{ + return tree->lengths[index]; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code, or (unsigned)(-1) if error happened +inbitlength is the length of the complete buffer, in bits (so its byte length times 8) +*/ +static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, + const HuffmanTree* codetree, size_t inbitlength) +{ + unsigned treepos = 0, ct; + for(;;) + { + if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ + /* + decode the symbol from the tree. The "readBitFromStream" code is inlined in + the expression below because this is the biggest bottleneck while decoding + */ + ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; + ++(*bp); + if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ + else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ + + if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) +{ + /*TODO: check for out of memory errors*/ + generateFixedLitLenTree(tree_ll); + generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + const unsigned char* in, size_t* bp, size_t inlength) +{ + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + size_t inbitlength = inlength * 8; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBitsFromStream(bp, in, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBitsFromStream(bp, in, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBitsFromStream(bp, in, 4) + 4; + + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + + HuffmanTree_init(&tree_cl); + + while(!error) + { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); + + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) + { + if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); + else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) + { + unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); + if(code <= 15) /*a length code*/ + { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } + else if(code == 16) /*repeat previous*/ + { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } + else if(code == 17) /*repeat "0" 3-10 times*/ + { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else if(code == 18) /*repeat "0" 11-138 times*/ + { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + if(code == (unsigned)(-1)) + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inbitlength ? 10 : 11; + } + else error = 16; /*unexisting code, this can never happen*/ + break; + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength, unsigned btype) +{ + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + size_t inbitlength = inlength * 8; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); + else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ + { + /*code_ll is literal, length or end code*/ + unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); + if(code_ll <= 255) /*literal symbol*/ + { + /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ + if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[*pos] = (unsigned char)code_ll; + ++(*pos); + } + else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ + { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, forward, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + length += readBitsFromStream(bp, in, numextrabits_l); + + /*part 3: get distance code*/ + code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); + if(code_d > 29) + { + if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inlength * 8 ? 10 : 11; + } + else error = 18; /*error: invalid distance code (30-31 are never used)*/ + break; + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + distance += readBitsFromStream(bp, in, numextrabits_d); + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = (*pos); + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; + } + } + else if(code_ll == 256) + { + break; /*end code, break the loop*/ + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = ((*bp) > inlength * 8) ? 10 : 11; + break; + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) +{ + size_t p; + unsigned LEN, NLEN, n, error = 0; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); + p = (*bp) / 8; /*byte position*/ + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256u * in[p + 1]; p += 2; + NLEN = in[p] + 256u * in[p + 1]; p += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ + + if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; + + (*bp) = p * 8; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ + size_t bp = 0; + unsigned BFINAL = 0; + size_t pos = 0; /*byte position in the out buffer*/ + unsigned error = 0; + + (void)settings; + + while(!BFINAL) + { + unsigned BTYPE; + if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBitFromStream(&bp, in); + BTYPE = 1u * readBitFromStream(&bp, in); + BTYPE += 2u * readBitFromStream(&bp, in); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ + else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + + if(error) return error; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + if(settings->custom_inflate) + { + return settings->custom_inflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_inflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*bitlen is the size in bits of the code*/ +static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) +{ + addBitsToStreamReversed(bp, compressed, code, bitlen); +} + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) +{ + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) +{ + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); + uivector_push_back(values, extra_length); + uivector_push_back(values, dist_code); + uivector_push_back(values, extra_distance); +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash +{ + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) +{ + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) + { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) +{ + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) +{ + unsigned result = 0; + if(pos + 2 < size) + { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= (unsigned)(data[pos + 0] << 0u); + result ^= (unsigned)(data[pos + 1] << 4u); + result ^= (unsigned)(data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) +{ + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) +{ + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = (int)wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = (int)wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) +{ + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; + unsigned maxlazymatch = windowsize >= 8192 ? (unsigned)MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = (unsigned)MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) + { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) + { + if(chainlength++ >= maxchainlength) break; + current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) + { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) + { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ + { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) + { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) + { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } + else + { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) + { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) + { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) + { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) + { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ + { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else if(length < minmatch || (length == 3 && offset > 4096)) + { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) + { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) +{ + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) + { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); + ucvector_push_back(out, firstbyte); + + LEN = 65535; + if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); + + /*Decompressed data*/ + for(j = 0; j < 65535 && datapos < datasize; ++j) + { + ucvector_push_back(out, data[datapos++]); + } + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) +{ + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) + { + unsigned val = lz77_encoded->data[i]; + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); + if(val > 256) /*for a length code, 3 more things have to be added*/ + { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), + HuffmanTree_getLength(tree_d, distance_code)); + addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lenghts used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + uivector frequencies_ll; /*frequency of lit,len codes*/ + uivector frequencies_d; /*frequency of dist codes*/ + uivector frequencies_cl; /*frequency of code length codes*/ + uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ + uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ + /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl + (these are written as is in the file, it would be crazy to compress these using yet another huffman + tree that needs to be represented by yet another set of code lengths)*/ + uivector bitlen_cl; + size_t datasize = dataend - datapos; + + /* + Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t numcodes_ll, numcodes_d, i; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + uivector_init(&frequencies_ll); + uivector_init(&frequencies_d); + uivector_init(&frequencies_cl); + uivector_init(&bitlen_lld); + uivector_init(&bitlen_lld_e); + uivector_init(&bitlen_cl); + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) + { + if(settings->use_lz77) + { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } + else + { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); + if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) + { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll.data[symbol]; + if(symbol > 256) + { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d.data[dist]; + i += 3; + } + } + frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); + if(error) break; + + numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; + numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; + /*store the code lengths of both generated trees in bitlen_lld*/ + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) + { + unsigned j = 0; /*amount of repititions*/ + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; + + if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ + { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ + { + uivector_push_back(&bitlen_lld_e, 17); + uivector_push_back(&bitlen_lld_e, j - 3); + } + else /*repeat code 18 supports max 138 zeroes*/ + { + if(j > 138) j = 138; + uivector_push_back(&bitlen_lld_e, 18); + uivector_push_back(&bitlen_lld_e, j - 11); + } + i += (j - 1); + } + else if(j >= 3) /*repeat code for value other than zero*/ + { + size_t k; + unsigned num = j / 6, rest = j % 6; + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + for(k = 0; k < num; ++k) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, 6 - 3); + } + if(rest >= 3) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, rest - 3); + } + else j -= rest; + i += j; + } + else /*too short to benefit from repeat code*/ + { + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + + if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != bitlen_lld_e.size; ++i) + { + ++frequencies_cl.data[bitlen_lld_e.data[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e.data[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, + frequencies_cl.size, frequencies_cl.size, 7); + if(error) break; + + if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != tree_cl.numcodes; ++i) + { + /*lenghts of code length tree is in the order as specified by deflate*/ + bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); + } + while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) + { + /*remove zeros at the end, but minimum size must be 4*/ + if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); + } + if(error) break; + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ + addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)bitlen_cl.size - 4; + /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; + addBitsToStream(bp, out, HLIT, 5); + addBitsToStream(bp, out, HDIST, 5); + addBitsToStream(bp, out, HCLEN, 4); + + /*write the code lenghts of the code length alphabet*/ + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + + /*write the lenghts of the lit/len AND the dist alphabet*/ + for(i = 0; i != bitlen_lld_e.size; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), + HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); + /*extra bits of repeat codes*/ + if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); + else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); + else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); + + /*write the end code*/ + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + uivector_cleanup(&frequencies_ll); + uivector_cleanup(&frequencies_d); + uivector_cleanup(&frequencies_cl); + uivector_cleanup(&bitlen_lld_e); + uivector_cleanup(&bitlen_lld); + uivector_cleanup(&bitlen_cl); + + return error; +} + +static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + generateFixedLitLenTree(&tree_ll); + generateFixedDistanceTree(&tree_d); + + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 1); /*first bit of BTYPE*/ + addBitToStream(bp, out, 0); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ + { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } + else /*no LZ77, but still will be Huffman compressed*/ + { + for(i = datapos; i < dataend; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); + } + } + /*add END code*/ + if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + size_t bp = 0; /*the bit pointer*/ + Hash hash; + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ + { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8 + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + if(error) return error; + + for(i = 0; i != numdeflateblocks && !error; ++i) + { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + if(settings->custom_deflate) + { + return settings->custom_deflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) +{ + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; + + while(len > 0) + { + /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5552 ? 5552 : len; + len -= amount; + while(amount > 0) + { + s1 += (*data++); + s2 += s1; + --amount; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) +{ + return update_adler32(1L, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) + { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) + { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) + { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflate(out, outsize, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) + { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(*out, (unsigned)(*outsize)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_decompress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + /*initially, *out must be NULL and outsize 0, if you just give some random *out + that's pointing to a non allocated buffer, this'll crash*/ + ucvector outv; + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + /*ucvector-controlled version of the output buffer, for dynamic array*/ + ucvector_init_buffer(&outv, *out, *outsize); + + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + if(!error) + { + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); + lodepng_free(deflatedata); + lodepng_add32bitInt(&outv, ADLER32); + } + + *out = outv.data; + *outsize = outv.size; + + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) +{ + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) +{ + settings->ignore_adler32 = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) +{ + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) + { + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0; + size_t i; + for(i = 0 ; i < nbits; ++i) + { + result <<= 1; + result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); + } + return result; +} + +#ifdef LODEPNG_COMPILE_DECODER +static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream must be 0 for this to work*/ + if(bit) + { + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ + bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); + } + ++(*bitpointer); +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); + else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) +{ + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) +{ + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) +{ + if(strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) +{ + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) +{ + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) +{ + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) +{ + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) +{ + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) +{ + unsigned i; + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + unsigned char *chunk_start, *new_buffer; + size_t new_length = (*outlength) + total_chunk_length; + if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data) +{ + unsigned i; + unsigned char *chunk, *new_buffer; + size_t new_length = (*outlength) + length + 12; + if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk = &(*out)[(*outlength) - length - 12]; + + /*1: length*/ + lodepng_set32bitInt(chunk, (unsigned)length); + + /*2: chunk name (4 letters)*/ + chunk[4] = (unsigned char)type[0]; + chunk[5] = (unsigned char)type[1]; + chunk[6] = (unsigned char)type[2]; + chunk[7] = (unsigned char)type[3]; + + /*3: the data*/ + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a LodePNG error code*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ +{ + switch(colortype) + { + case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ + case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ + case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ + case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ + case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ + default: return 31; + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) +{ + switch(colortype) + { + case 0: return 1; /*grey*/ + case 2: return 3; /*RGB*/ + case 3: return 1; /*palette*/ + case 4: return 2; /*grey + alpha*/ + case 6: return 4; /*RGBA*/ + } + return 0; /*unexisting color type*/ +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) +{ + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) +{ + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) +{ + size_t i; + lodepng_color_mode_cleanup(dest); + *dest = *source; + if(source->palette) + { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; + } + return 0; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) +{ + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) + { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) +{ + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + unsigned char* data; + /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with + the max of 256 colors, it'll have the exact alloc size*/ + if(!info->palette) /*allocate palette if empty*/ + { + /*room for 256 colors with 4 bytes each*/ + data = (unsigned char*)lodepng_realloc(info->palette, 1024); + if(!data) return 83; /*alloc fail*/ + else info->palette = data; + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +unsigned lodepng_get_bpp(const LodePNGColorMode* info) +{ + /*calculate bits per pixel out of colortype and bitdepth*/ + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) +{ + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) +{ + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) +{ + size_t i; + for(i = 0; i != info->palettesize; ++i) + { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) +{ + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = (size_t)w * (size_t)h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); +} + + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_DECODER + +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, +and in addition has one extra byte per line: the filter byte. So this gives a larger +result than lodepng_get_raw_size. */ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + size_t bpp = lodepng_get_bpp(color); + /* + 1 for the filter byte, and possibly plus padding bits per line */ + size_t line = ((size_t)(w / 8) * bpp) + 1 + ((w & 7) * bpp + 7) / 8; + return (size_t)h * line; +} + +/* Safely check if multiplying two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_mulofl(size_t a, size_t b, size_t* result) +{ + *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ + return (a != 0 && *result / a != b); +} + +/* Safely check if adding two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_addofl(size_t a, size_t b, size_t* result) +{ + *result = a + b; /* Unsigned addition is well defined and safe in C90 */ + return *result < a; +} + +/*Safely checks whether size_t overflow can be caused due to amount of pixels. +This check is overcautious rather than precise. If this check indicates no overflow, +you can safely compute in a size_t (but not an unsigned): +-(size_t)w * (size_t)h * 8 +-amount of bytes in IDAT (including filter, padding and Adam7 bytes) +-amount of bytes in raw color model +Returns 1 if overflow possible, 0 if not. +*/ +static int lodepng_pixel_overflow(unsigned w, unsigned h, + const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) +{ + size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); + size_t numpixels, total; + size_t line; /* bytes per line in worst case */ + + if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; + if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ + + /* Bytes per scanline with the expression "(w / 8) * bpp) + ((w & 7) * bpp + 7) / 8" */ + if(lodepng_mulofl((size_t)(w / 8), bpp, &line)) return 1; + if(lodepng_addofl(line, ((w & 7) * bpp + 7) / 8, &line)) return 1; + + if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ + if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ + + return 0; /* no overflow */ +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) +{ + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) + { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) + { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) +{ + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->text_num; ++i) + { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->text_keys = 0; + dest->text_strings = 0; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +void lodepng_clear_text(LodePNGInfo* info) +{ + LodePNGText_cleanup(info); +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + if(!new_keys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->text_num; + info->text_keys = new_keys; + info->text_strings = new_strings; + + string_init(&info->text_keys[info->text_num - 1]); + string_set(&info->text_keys[info->text_num - 1], key); + + string_init(&info->text_strings[info->text_num - 1]); + string_set(&info->text_strings[info->text_num - 1], str); + + return 0; +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) +{ + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->itext_num; ++i) + { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->itext_keys = 0; + dest->itext_langtags = 0; + dest->itext_transkeys = 0; + dest->itext_strings = 0; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) +{ + LodePNGIText_cleanup(info); +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_langtags); + lodepng_free(new_transkeys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->itext_num; + info->itext_keys = new_keys; + info->itext_langtags = new_langtags; + info->itext_transkeys = new_transkeys; + info->itext_strings = new_strings; + + string_init(&info->itext_keys[info->itext_num - 1]); + string_set(&info->itext_keys[info->itext_num - 1], key); + + string_init(&info->itext_langtags[info->itext_num - 1]); + string_set(&info->itext_langtags[info->itext_num - 1], langtag); + + string_init(&info->itext_transkeys[info->itext_num - 1]); + string_set(&info->itext_transkeys[info->itext_num - 1], transkey); + + string_init(&info->itext_strings[info->itext_num - 1]); + string_set(&info->itext_strings[info->itext_num - 1], str); + + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) +{ + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) +{ + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + lodepng_info_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) +{ + LodePNGInfo temp = *a; + *a = *b; + *b = temp; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) +{ + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = (unsigned)index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8] = in; + else out[index * bits / 8] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree +{ + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) tree->children[i] = 0; + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) + { + if(tree->children[i]) + { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int bit = 0; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ +static void color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) +{ + int bit; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) + { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) out[i] = grey; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; + else + { + /*take the most significant bits of grey*/ + grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); + addColorBits(out, i, mode->bitdepth, grey); + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } + else + { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } + else if(mode->colortype == LCT_PALETTE) + { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) + { + out[i * 2 + 0] = grey; + out[i * 2 + 1] = a; + } + else if(mode->bitdepth == 16) + { + out[i * 4 + 0] = out[i * 4 + 1] = grey; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } + else + { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 2 + 0] = (grey >> 8) & 255; + out[i * 2 + 1] = grey & 255; + } + else if(mode->colortype == LCT_RGB) + { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 4 + 0] = (grey >> 8) & 255; + out[i * 4 + 1] = grey & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } + else if(mode->colortype == LCT_RGBA) + { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } + else if(mode->bitdepth == 16) + { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } + else + { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else + { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but common PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + *r = *g = *b = 0; + *a = 255; + } + else + { + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } + else + { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } + else + { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with +enough memory, if has_alpha is true the output is RGBA. mode has the color mode +of the input buffer.*/ +static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, + unsigned has_alpha, const unsigned char* in, + const LodePNGColorMode* mode) +{ + unsigned num_channels = has_alpha ? 4 : 3; + size_t i; + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i]; + if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; + } + } + else if(mode->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 3 + 0]; + buffer[1] = in[i * 3 + 1]; + buffer[2] = in[i * 3 + 2]; + if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r + && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + if(has_alpha) buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + if(mode->bitdepth == 8) index = in[i]; + else index = readBitsFromReversedStream(&j, in, mode->bitdepth); + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but most PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + buffer[0] = buffer[1] = buffer[2] = 0; + if(has_alpha) buffer[3] = 255; + } + else + { + buffer[0] = mode->palette[index * 4 + 0]; + buffer[1] = mode->palette[index * 4 + 1]; + buffer[2] = mode->palette[index * 4 + 2]; + if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; + } + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + if(has_alpha) buffer[3] = in[i * 2 + 1]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + if(has_alpha) buffer[3] = in[i * 4 + 2]; + } + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 4 + 0]; + buffer[1] = in[i * 4 + 1]; + buffer[2] = in[i * 4 + 2]; + if(has_alpha) buffer[3] = in[i * 4 + 3]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + if(has_alpha) buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_RGB) + { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } + else if(mode->colortype == LCT_RGBA) + { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) +{ + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + unsigned error = 0; + + if(lodepng_color_mode_equal(mode_out, mode_in)) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) + { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = (size_t)1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + /*if the input was also palette with same bitdepth, then the color types are also + equal, so copy literally. This to preserve the exact indices that were in the PNG + even in case there are duplicate colors in the palette.*/ + if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) + { + const unsigned char* p = &palette[i * 4]; + color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); + } + } + + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) + { + getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) + { + getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); + } + else + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if (error) break; + } + } + + if(mode_out->colortype == LCT_PALETTE) + { + color_tree_cleanup(&tree); + } + + return error; +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_profile_init(LodePNGColorProfile* profile) +{ + profile->colored = 0; + profile->key = 0; + profile->key_r = profile->key_g = profile->key_b = 0; + profile->alpha = 0; + profile->numcolors = 0; + profile->bits = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorProfile(LodePNGColorProfile* p) +{ + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) +{ + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*profile must already have been inited with mode. +It's ok to set some parameters of profile to done already.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) +{ + unsigned error = 0; + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + + unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode); + unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned maxnumcolors = 257; + unsigned sixteen = 0; + if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + + color_tree_init(&tree); + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode->bitdepth == 16) + { + unsigned short r, g, b, a; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ + { + sixteen = 1; + break; + } + } + } + + if(sixteen) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + profile->bits = 16; + bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 65535 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 65535 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + } + } + else /* < 16-bit */ + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + + if(!bits_done && profile->bits < 8) + { + /*only r is checked, < 8 bits is only relevant for greyscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > profile->bits) profile->bits = bits; + } + bits_done = (profile->bits >= bpp); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 255 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 255 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) + { + if(!color_tree_has(&tree, r, g, b, a)) + { + color_tree_add(&tree, r, g, b, a, profile->numcolors); + if(profile->numcolors < 256) + { + unsigned char* p = profile->palette; + unsigned n = profile->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++profile->numcolors; + numcolors_done = profile->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + } + + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); + } + + color_tree_cleanup(&tree); + return error; +} + +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, ... +Updates values of mode with a potentially smaller color model. mode_out should +contain the user chosen color model, but will be overwritten with the new chosen one.*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) +{ + LodePNGColorProfile prof; + unsigned error = 0; + unsigned palettebits, palette_ok; + size_t i, n; + size_t numpixels = (size_t)w * (size_t)h; + + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + mode_out->key_defined = 0; + + if(prof.key && numpixels <= 16) + { + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + prof.key = 0; + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + n = prof.numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && prof.bits <= 8; + if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + + if(palette_ok) + { + unsigned char* p = prof.palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != prof.numcolors; ++i) + { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) + { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } + else /*8-bit or 16-bit per channel*/ + { + mode_out->bitdepth = prof.bits; + mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof.colored ? LCT_RGB : LCT_GREY); + + if(prof.key) + { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ + mode_out->key_r = prof.key_r & mask; + mode_out->key_g = prof.key_g & mask; + mode_out->key_b = prof.key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predicter, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) +{ + short pa = abs(b - c); + short pb = abs(a - c); + short pc = abs(a + b - c - c); + + if(pc < pa && pc < pb) return (unsigned char)c; + else if(pb < pa) return (unsigned char)b; + else return (unsigned char)a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) +{ + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) + { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) + { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) +{ + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) + { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) + { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) + { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) + { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + *w = lodepng_read32bitInt(&in[16]); + *h = lodepng_read32bitInt(&in[20]); + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + + if(!state->decoder.ignore_crc) + { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) + { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) +{ + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) + { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) + { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } + else + { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); + } + break; + case 4: + if(precon) + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + for(i = bytewidth; i < length; ++i) + { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) + { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: unexisting filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + size_t linebytes = (w * bpp + 7) / 8; + + for(y = 0; y < h; ++y) + { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ + setBitOfReversedStream0(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) + { + size_t x; + for(x = 0; x < olinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) +{ + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) + { + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) + { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) + { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7) / 8) * 8, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned pos = 0, i; + if(color->palette) lodepng_free(color->palette); + color->palettesize = chunkLength / 3; + color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); + if(!color->palette && color->palettesize) + { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + if(color->palettesize > 256) return 38; /*error: palette too big*/ + + for(i = 0; i != color->palettesize; ++i) + { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned i; + if(color->colortype == LCT_PALETTE) + { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 38; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } + else if(color->colortype == LCT_GREY) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } + else if(color->colortype == LCT_RGB) + { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(info->color.colortype == LCT_PALETTE) + { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } + else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 44; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + /*error: this chunk must be 6 bytes for greyscale image*/ + if(chunkLength != 6) return 45; + + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + char *key = 0, *str = 0; + unsigned i; + + while(!error) /*not really a while loop, only used to break on error*/ + { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + str[length] = 0; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + char *key = 0; + ucvector decoded; + + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(error) break; + ucvector_push_back(&decoded, 0); + + error = lodepng_add_text(info, key, (char*)decoded.data); + + break; + } + + lodepng_free(key); + ucvector_cleanup(&decoded); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + ucvector decoded; + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + langtag[length] = 0; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + transkey[length] = 0; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; + + /*read the actual text*/ + begin += length + 1; + + length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; + + if(compressed) + { + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[begin]), + length, zlibsettings); + if(error) break; + if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; + ucvector_push_back(&decoded, 0); + } + else + { + if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); + + decoded.data[length] = 0; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; + } + + error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + ucvector_cleanup(&decoded); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + unsigned char IEND = 0; + const unsigned char* chunk; + size_t i; + ucvector idat; /*the data from idat chunks*/ + ucvector scanlines; + size_t predict; + size_t outsize = 0; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + /*provide some proper output values if error will happen*/ + *out = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) + { + CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ + } + + ucvector_init(&idat); + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) + { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 30); + } + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 63); + } + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) + { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat.size; + size_t newsize; + if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*IEND chunk*/ + else if(lodepng_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if(lodepng_chunk_type_equals(chunk, "PLTE")) + { + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*palette transparency chunk (tRNS)*/ + else if(lodepng_chunk_type_equals(chunk, "tRNS")) + { + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + else if(lodepng_chunk_type_equals(chunk, "bKGD")) + { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } + /*text chunk (tEXt)*/ + else if(lodepng_chunk_type_equals(chunk, "tEXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } + /*compressed text chunk (zTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "zTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + /*international text chunk (iTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "iTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + else if(lodepng_chunk_type_equals(chunk, "tIME")) + { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "pHYs")) + { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) + { + CERROR_BREAK(state->error, 69); + } + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) + { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ + { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk); + } + + ucvector_init(&scanlines); + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) + { + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color); + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color); + } + if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, + idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ + } + ucvector_cleanup(&idat); + + if(!state->error) + { + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) + { + for(i = 0; i < outsize; i++) (*out)[i] = 0; + state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); + } + ucvector_cleanup(&scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) + { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) + { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } + else + { + /*color conversion needed; sort of copy of the data*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) + { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) + { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer = 0; + size_t buffersize; + unsigned error; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) +{ + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + settings->ignore_critical = 0; + settings->ignore_end = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) +{ +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) +{ + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) +{ + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*chunkName must be string of 4 characters*/ +static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) +{ + CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); + out->allocsize = out->size; /*fix the allocsize again*/ + return 0; +} + +static void writeSignature(ucvector* out) +{ + /*8 bytes PNG signature, aka the magic bytes*/ + ucvector_push_back(out, 137); + ucvector_push_back(out, 80); + ucvector_push_back(out, 78); + ucvector_push_back(out, 71); + ucvector_push_back(out, 13); + ucvector_push_back(out, 10); + ucvector_push_back(out, 26); + ucvector_push_back(out, 10); +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) +{ + unsigned error = 0; + ucvector header; + ucvector_init(&header); + + lodepng_add32bitInt(&header, w); /*width*/ + lodepng_add32bitInt(&header, h); /*height*/ + ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ + ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ + ucvector_push_back(&header, 0); /*compression method*/ + ucvector_push_back(&header, 0); /*filter method*/ + ucvector_push_back(&header, interlace_method); /*interlace method*/ + + error = addChunk(out, "IHDR", header.data, header.size); + ucvector_cleanup(&header); + + return error; +} + +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector PLTE; + ucvector_init(&PLTE); + for(i = 0; i != info->palettesize * 4; ++i) + { + /*add all channels except alpha channel*/ + if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); + } + error = addChunk(out, "PLTE", PLTE.data, PLTE.size); + ucvector_cleanup(&PLTE); + + return error; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector tRNS; + ucvector_init(&tRNS); + if(info->colortype == LCT_PALETTE) + { + size_t amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) + { + if(info->palette[4 * (i - 1) + 3] == 255) --amount; + else break; + } + /*add only alpha channel*/ + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + } + else if(info->colortype == LCT_GREY) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + } + } + else if(info->colortype == LCT_RGB) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); + } + } + + error = addChunk(out, "tRNS", tRNS.data, tRNS.size); + ucvector_cleanup(&tRNS); + + return error; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) +{ + ucvector zlibdata; + unsigned error = 0; + + /*compress with the Zlib compressor*/ + ucvector_init(&zlibdata); + error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); + if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); + ucvector_cleanup(&zlibdata); + + return error; +} + +static unsigned addChunk_IEND(ucvector* out) +{ + unsigned error = 0; + error = addChunk(out, "IEND", 0, 0); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) +{ + unsigned error = 0; + size_t i; + ucvector text; + ucvector_init(&text); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&text, 0); /*0 termination char*/ + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); + error = addChunk(out, "tEXt", text.data, text.size); + ucvector_cleanup(&text); + + return error; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "zTXt", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*null termination char*/ + ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ + ucvector_push_back(&data, 0); /*compression method*/ + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + + if(compressed) + { + ucvector compressed_data; + ucvector_init(&compressed_data); + error = zlib_compress(&compressed_data.data, &compressed_data.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); + } + ucvector_cleanup(&compressed_data); + } + else /*not compressed*/ + { + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); + } + + if(!error) error = addChunk(out, "iTXt", data.data, data.size); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector bKGD; + ucvector_init(&bKGD); + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); + } + else if(info->color.colortype == LCT_PALETTE) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ + } + + error = addChunk(out, "bKGD", bKGD.data, bKGD.size); + ucvector_cleanup(&bKGD); + + return error; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) +{ + unsigned error = 0; + unsigned char* data = (unsigned char*)lodepng_malloc(7); + if(!data) return 83; /*alloc fail*/ + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); + data[2] = (unsigned char)time->month; + data[3] = (unsigned char)time->day; + data[4] = (unsigned char)time->hour; + data[5] = (unsigned char)time->minute; + data[6] = (unsigned char)time->second; + error = addChunk(out, "tIME", data, 7); + lodepng_free(data); + return error; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->phys_x); + lodepng_add32bitInt(&data, info->phys_y); + ucvector_push_back(&data, info->phys_unit); + + error = addChunk(out, "pHYs", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) +{ + size_t i; + switch(filterType) + { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) + { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } + else + { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) + { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) + { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*unexisting filter type given*/ + } +} + +/* log2 approximation. A slight bit faster than std::log. */ +static float flog2(float f) +{ + float result = 0; + while(f > 32) { result += 4; f /= 16; } + while(f > 2) { ++result; f /= 2; } + return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) +{ + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(info); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = (w * bpp + 7) / 8; + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy == LFS_ZERO) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = 0; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_MINSUM) + { + /*adaptive filtering*/ + size_t sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + if(!error) + { + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + sum[type] = 0; + if(type == 0) + { + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); + } + else + { + for(x = 0; x != linebytes; ++x) + { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum[type] += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_ENTROPY) + { + float sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + float smallest = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + sum[type] = 0; + for(x = 0; x != 256; ++x) + { + float p = count[x] / (float)(linebytes + 1); + sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_PREDEFINED) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_BRUTE_FORCE) + { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings = settings->zlibsettings; + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + for(y = 0; y != h; ++y) /*try the 5 filter types*/ + { + for(type = 0; type != 5; ++type) + { + unsigned testsize = (unsigned)linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) + { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) + { + size_t x; + for(x = 0; x < ilinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) +{ + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) + { + *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) + { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) + { + addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } + else + { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) + { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) + { + if(bpp < 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } + else + { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +/* +palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... +returns 0 if the palette is opaque, +returns 1 if the palette has a single color with alpha 0 ==> color key +returns 2 if the palette is semi-translucent. +*/ +static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) +{ + size_t i; + unsigned key = 0; + unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ + for(i = 0; i != palettesize; ++i) + { + if(!key && palette[4 * i + 3] == 0) + { + r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; + key = 1; + i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ + } + else if(palette[4 * i + 3] != 255) return 2; + /*when key, no opaque RGB may have key's RGB*/ + else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; + } + return key; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) +{ + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) + { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk); + } + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) +{ + LodePNGInfo info; + ucvector outv; + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + /*check input values validity*/ + if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) + { + CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ + } + if(state->encoder.zlibsettings.btype > 2) + { + CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ + } + if(state->info_png.interlace_method > 1) + { + CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ + } + state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + + /* color convert and compute scanline filter types */ + lodepng_info_init(&info); + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) + { + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + } + if (!state->error) + { + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + { + unsigned char* converted; + size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); + } + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + } + + /* output all PNG chunks */ + ucvector_init(&outv); + while(!state->error) /*while only executed once, to break on error*/ + { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + writeSignature(&outv); + /*IHDR*/ + addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) + { + addChunk_PLTE(&outv, &info.color); + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) + { + addChunk_PLTE(&outv, &info.color); + } + /*tRNS*/ + if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) + { + addChunk_tRNS(&outv, &info.color); + } + if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) + { + addChunk_tRNS(&outv, &info.color); + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) addChunk_bKGD(&outv, &info); + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) addChunk_pHYs(&outv, &info); + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) addChunk_tIME(&outv, &info.time); + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) + { + if(strlen(info.text_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.text_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + if(state->encoder.text_compression) + { + addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + } + else + { + addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) + { + unsigned alread_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) + { + if(!strcmp(info.text_keys[i], "LodePNG")) + { + alread_added_id_text = 1; + break; + } + } + if(alread_added_id_text == 0) + { + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) + { + if(strlen(info.itext_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.itext_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + addChunk_iTXt(&outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + addChunk_IEND(&outv); + + break; /*this isn't really a while loop; no error happened so break out now!*/ + } + + lodepng_info_cleanup(&info); + lodepng_free(data); + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) +{ + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) +{ + switch(code) + { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + case 16: return "unexisting code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too big"; /*more than 256 colors*/ + case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; + case 40: return "tRNS chunk has wrong size for greyscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for greyscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ + case 62: return "conversion from color to greyscale not supported"; + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "integer overflow due to too many pixels"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + case 95: return "integer overflow with combined idat chunk size"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector& buffer, const std::string& filename) +{ + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector& buffer, const std::string& filename) +{ + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings) +{ + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings) +{ + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() +{ + lodepng_state_init(this); +} + +State::State(const State& other) +{ + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() +{ + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) +{ + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) + { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) +{ + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) +{ + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) + { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in) +{ + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state) +{ + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/app/src/main/cpp/LODEPNG.H b/app/src/main/cpp/LODEPNG.H new file mode 100644 index 0000000..6c56c1d --- /dev/null +++ b/app/src/main/cpp/LODEPNG.H @@ -0,0 +1,1770 @@ +/* +LodePNG version 20180611 + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include /*for size_t*/ + +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw).*/ +typedef enum LodePNGColorType +{ + LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ + LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings +{ + /* Check LodePNGDecoderSettings for more ignorable errors */ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + + /*use custom zlib decoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ +{ + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode +{ + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use + lodepng_palette_clear, then for each color use lodepng_palette_add. + If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ + size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For greyscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/greyscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a greyscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime +{ + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo +{ + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + suggested background color chunk (bKGD) + This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. + + For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding + the encoder writes the red one. For palette PNGs: When decoding, the RGB value + will be stored, not a palette index. But when encoding, specify the index of + the palette in background_r, the other two are then ignored. + + The decoder does not use this background color to edit the color of pixels. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + A keyword is minimum 1 character and maximum 79 characters long. It's + discouraged to use a single line length longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + international text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys". + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + unknown chunks + There are 3 buffers, one for each position in the PNG where unknown chunks can appear + each buffer contains all unknown chunks for that position consecutively + The 3 buffers are the unknown chunks between certain critical chunks: + 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ + +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings +{ + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + /* Check LodePNGDecompressSettings for more ignorable errors */ + unsigned ignore_crc; /*ignore CRC checksums*/ + unsigned ignore_critical; /*ignore unknown critical chunks*/ + unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy +{ + /*every filter at zero*/ + LFS_ZERO, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorProfile +{ + unsigned colored; /*not greyscale*/ + unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ + unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ +} LodePNGColorProfile; + +void lodepng_color_profile_init(LodePNGColorProfile* profile); + +/*Get a LodePNGColorProfile of the image.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); +/*The function LodePNG uses internally to decide the PNG color with auto_convert. +Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings +{ + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState +{ +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +#ifdef LODEPNG_COMPILE_CPP + /* For the lodepng::State subclass. */ + virtual ~LodePNGState(){} +#endif +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the header chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +PNG standard chunk naming conventions: +First byte: uppercase = critical, lowercase = ancillary +Second byte: uppercase = public, lowercase = private +Third byte: must be uppercase +Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState +{ + public: + State(); + State(const State& other); + virtual ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) +[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported (generated/interpreted) by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not supported but treated as unknown chunks by LodePNG + cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT + Some of these are not supported on purpose: LodePNG wants to provide the RGB values + stored in the pixels, not values modified by system dependent gamma or color models. + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to greyscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG " to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, greyscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to greyscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: greyscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: greyscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +Non supported color conversions: +-color to greyscale: no error is thrown, but the result will look ugly because +only the red channel is taken +-anything to palette when that palette does not have that color in it: in this +case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any grey or grey+alpha, to grey or grey+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outlength. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distionction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards complient. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include + +int main(int argc, char *argv[]) +{ + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) +{ + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.ignore_critical: ignore unknown critical chunks +state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +*) 11 jun 2018: less restrictive check for pixel size integer overflow +*) 14 jan 2018: allow optionally ignoring a few more recoverable errors +*) 17 sep 2017: fix memory leak for some encoder input error cases +*) 27 nov 2016: grey+alpha auto color model detection bugfix +*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011 (!): changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2018 Lode Vandevenne +*/ diff --git a/app/src/main/cpp/LOWBAT.C b/app/src/main/cpp/LOWBAT.C new file mode 100644 index 0000000..adfc75d --- /dev/null +++ b/app/src/main/cpp/LOWBAT.C @@ -0,0 +1,122 @@ +/* + * lowbat.c + * + * This file is part of Emu48 + * + * Copyright (C) 2006 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" // I/O definitions + +// #define BAT_SIMULATION // switch low bat simulation + +#define BAT_FREQ (60*1000) // bat update time in ms (real machine = 60us, HP28C = 60s) + +BOOL bLowBatDisable = FALSE; + +static HANDLE hCThreadBat = NULL; +static HANDLE hEventBat; + +static DWORD WINAPI LowBatThread(LPVOID pParam) +{ + BOOL bLBI,bVLBI; + + do + { + GetBatteryState(&bLBI,&bVLBI); // get battery state + + // very low bat detection + bVLBI = bVLBI && (Chipset.IORam[LPE] & EVLBI) != 0; + + IOBit(LPD,VLBI,bVLBI); // set VLBI + IOBit(SRQ1,VSRQ,bVLBI); // and service bit + + if (bVLBI) // VLBI detected + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + + if (Chipset.Shutdn) // CPU shut down + { + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + } + } + while (WaitForSingleObject(hEventBat,BAT_FREQ) == WAIT_TIMEOUT); + + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +VOID StartBatMeasure(VOID) +{ + DWORD dwThreadId; + + if (hCThreadBat) // Bat measuring thread running + return; // -> quit + + // event to cancel Bat refresh loop + hEventBat = CreateEvent(NULL,FALSE,FALSE,NULL); + + VERIFY(hCThreadBat = CreateThread(NULL,0,&LowBatThread,NULL,0,&dwThreadId)); + return; +} + +VOID StopBatMeasure(VOID) +{ + if (hCThreadBat == NULL) // thread stopped + return; // -> quit + + SetEvent(hEventBat); // leave Bat update thread + WaitForSingleObject(hCThreadBat,INFINITE); + CloseHandle(hCThreadBat); + hCThreadBat = NULL; // set flag Bat update stopped + CloseHandle(hEventBat); // close Bat event + return; +} + +VOID GetBatteryState(BOOL *pbLBI, BOOL *pbVLBI) +{ +#if defined BAT_SIMULATION + switch (GetPrivateProfileInt(_T("LowBat"),_T("Level"),2,_T(".\\Lowbat.ini"))) + { + case 0: // empty + *pbLBI = TRUE; + *pbVLBI = TRUE; + break; + case 1: // low + *pbLBI = TRUE; + *pbVLBI = FALSE; + break; + default: // full + *pbLBI = FALSE; + *pbVLBI = FALSE; + break; + } +#else + SYSTEM_POWER_STATUS sSps; + + *pbLBI = FALSE; // no battery warning + *pbVLBI = FALSE; + + VERIFY(GetSystemPowerStatus(&sSps)); + + // low bat emulation enabled and battery powered + if (!bLowBatDisable && sSps.ACLineStatus == AC_LINE_OFFLINE) + { + // on critical battery state make sure that lowbat flag is also set + if ((sSps.BatteryFlag & BATTERY_FLAG_CRITICAL) != 0) + sSps.BatteryFlag |= BATTERY_FLAG_LOW; + + // low bat detection + *pbLBI = ((sSps.BatteryFlag & BATTERY_FLAG_LOW) != 0); + + // very low bat detection + *pbVLBI = ((sSps.BatteryFlag & BATTERY_FLAG_CRITICAL) != 0); + } +#endif + return; +} diff --git a/app/src/main/cpp/MOPS.C b/app/src/main/cpp/MOPS.C new file mode 100644 index 0000000..4cf0aee --- /dev/null +++ b/app/src/main/cpp/MOPS.C @@ -0,0 +1,1851 @@ +/* + * mops.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "opcodes.h" +#include "io.h" +#include "i28f160.h" // flash support + +// #define DEBUG_SERIAL // switch for SERIAL debug purpose +// #define DEBUG_IO // switch for I/O debug purpose +// #define DEBUG_FLASH // switch for FLASH MEMORY debug purpose + +// defines for reading an open data bus +#define READEVEN 0x0D +#define READODD 0x0E + +// on mapping boundary adjusted base addresses +#define P0MAPBASE ((BYTE)(Chipset.P0Base & ~Chipset.P0Size)) +#define P1MAPBASE ((BYTE)(Chipset.P1Base & ~Chipset.P1Size)) +#define P2MAPBASE ((BYTE)(Chipset.P2Base & ~Chipset.P2Size)) +#define BSMAPBASE ((BYTE)(Chipset.BSBase & ~Chipset.BSSize)) + +BOOL bFlashRomArray = TRUE; // flag ROM mode + +BYTE disp = 0; // flag for update display area + +static LPBYTE pbyRomView[2] = {NULL, NULL}; // HP49G ROM views + +// CRC calculation +static WORD crc_table[] = +{ + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F +}; +static __inline VOID UpCRC(BYTE nib) +{ + Chipset.crc = (WORD)((Chipset.crc>>4)^crc_table[(Chipset.crc^nib)&0xf]); +} + +static __inline UINT MIN(UINT a, UINT b) +{ + return (ab)?a:b; +} + +// generate UCK signal +static __inline BYTE UckBit(BYTE byBaudIndex) +{ + // table content = baudrate * 16 + const DWORD dwBaudrates[] = { 19200, 30720, 38400, 61440, 76800, 122880, 153600, 245760 }; + + LARGE_INTEGER lLC; + + _ASSERT(byBaudIndex < ARRAYSIZEOF(dwBaudrates)); + + if ((Chipset.IORam[IOC] & SON) == 0) // UART off + return UCK; // UCK bit always set + + QueryPerformanceCounter(&lLC); // get counter value + + // calculate UCK frequency + return (((BYTE)(((lLC.QuadPart - lAppStart.QuadPart) * dwBaudrates[byBaudIndex]) + / lFreq.QuadPart) & 0x1) << 3); +} + +// calculate nibble based linear flash address +DWORD FlashROMAddr(DWORD d) +{ + DWORD dwLinAddr; + + // 6 bit of latch (was A6-A1 of address bus) + dwLinAddr = (Chipset.Bank_FF >> 1) & 0x3f; + // decode A21-A18 + dwLinAddr = ((d & 0x40000) ? (dwLinAddr & 0xf) : (dwLinAddr >> 4)) << 18; + // decode A21-A18, A17-A0 + dwLinAddr |= d & 0x3FFFF; + return dwLinAddr; +} + +// update display +static __inline VOID UpdateDisplay(DWORD d, UINT s) +{ + BYTE p[16]; + DWORD u; + UINT c; + + // address in display main area? + if ((dChipset.start12)) + { + // write to display main area + u = d; // copy destination ptr + c = MIN(s,Chipset.end1-d); // number of nibbles to copy + + if (d < Chipset.start12) // first address is out of display area + { + u = Chipset.start12; // set destination ptr to start of display area + c -= Chipset.start12 - d; // - number of bytes that aren't in display area + } + + _ASSERT(c <= ARRAYSIZEOF(p)); + Npeek(p,u,c); // get source data + WriteToMainDisplay(p,u,c); + } + // address in display menu area? + if ((dChipset.start2)) + { + // write to display menu area + u = d; // copy destination ptr + c = MIN(s,Chipset.end2-d); // number of nibbles to copy + + if (d < Chipset.start2) // first address is out of display area + { + u = Chipset.start2; // set destination ptr to start of display area + c -= Chipset.start2 - d; // - number of bytes that are not in display area + } + + _ASSERT(c <= ARRAYSIZEOF(p)); + Npeek(p,u,c); // get source data + WriteToMenuDisplay(p,u,c); + } + return; +} + +// port mapping + +LPBYTE RMap[256] = {NULL,}; +LPBYTE WMap[256] = {NULL,}; + +static VOID MapP0(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + a = (BYTE)MAX(a,P0MAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.P0End); + m = (Chipset.Port0Size*2048)-1; + p = (a<<12)&m; // offset to begin of P0 in nibbles + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P0Base) & ~Chipset.P0Size) == 0) + { + RMap[i]=Port0 + p; + WMap[i]=Port0 + p; + } + p = (p+0x1000)&m; + } + return; +} + +static VOID MapBS(BYTE a, BYTE b) +{ + UINT i; + + a = (BYTE)MAX(a,BSMAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.BSEnd); + for (i=a;i<=b;i++) + { + // mapping area may have holes + if (((i ^ Chipset.BSBase) & ~Chipset.BSSize) == 0) + { + RMap[i]=NULL; // no read cycle, open data bus + WMap[i]=NULL; + } + } + return; +} + +static VOID MapP1(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + // clear mapping area if port1 is configured but not plugged + a = (BYTE)MAX(a,P1MAPBASE); // lowest address for use is P1Base + b = (BYTE)MIN(b,Chipset.P1End); // highest address for use is P1End + + // port1 not plugged + if (Port1 == NULL || !(Chipset.cards_status & PORT1_PRESENT)) + { + for (i=a; i<=b; i++) // scan each 2KB page + { + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=NULL; + WMap[i]=NULL; + } + } + return; + } + + m = (Chipset.Port1Size*2048)-1; // real size of module, address mask for mirroring + p = (a<<12)&m; // offset to begin of P1 in nibbles + + if (Chipset.cards_status & PORT1_WRITE) // port1 write enabled + { + for (i=a; i<=b; i++) // scan each 2KB page + { + // mapping area may have holes + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=Port1 + p; // save page address for read + WMap[i]=Port1 + p; // save page address for write + } + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + } + else // port1 read only + { + for (i=a; i<=b; i++) // scan each 2KB page + { + // mapping area may have holes + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=Port1 + p; // save page address for read + WMap[i]=NULL; // no writing + } + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + } + return; +} + +static VOID MapP2(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + LPBYTE pbyTemp; + + // clear mapping area if port2 is configured but not plugged + a = (BYTE)MAX(a,P2MAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.P2End); + + if (Chipset.Port2Size) // internal port2 + { + m = (Chipset.Port2Size*2048)-1; + p = (a<<12)&m; // offset to begin of P0 in nibbles + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=Port2 + p; + WMap[i]=Port2 + p; + } + p = (p+0x1000)&m; + } + return; + } + + // HP48SX / HP48GX + // only fill mapping table when CE2.2 is set + for (i=a; i<=b; i++) // fill mapping area with not configured + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=NULL; + WMap[i]=NULL; + } + } + + // port2 not plugged + if (pbyPort2 == NULL || !(Chipset.cards_status & PORT2_PRESENT)) + return; + + pbyTemp = pbyPort2; + if (cCurrentRomType != 'S') // bank switching only with GX + { + // Chipset.Port2_Bank is the saved port2 FF content + pbyTemp += (((Chipset.Bank_FF>>1)-1)&dwPort2Mask) << 18; + } + + // max. size per bank is 128KB + m = (dwPort2Size > 128) ? 128 : dwPort2Size; + + m = (m * 2048) - 1; // real size of module, address mask for mirroring + p = (a << 12) & m; // offset to begin of P2 in nibbles + + // SX: CE2.2 = CE2 + // GX: CE2.2 = BEN & /DA19 & /NCE3 + if (cCurrentRomType == 'S' || ((Chipset.IORam[0x29]&DA19) == 0 && (Chipset.Bank_FF&0x40))) + { + if (bPort2Writeable) + { + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=pbyTemp + p; + WMap[i]=pbyTemp + p; + } + p = (p+0x1000)&m; + } + } + else + { + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=pbyTemp + p; + } + p = (p+0x1000)&m; + } + } + } + return; +} + +static VOID MapROM(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + // HP39(+)/40G, HP49G/g+ HP48gII // CdB for HP: add apples memory + if (cCurrentRomType == 'E' || cCurrentRomType == 'X' || cCurrentRomType == 'P' || cCurrentRomType == '2' || cCurrentRomType == 'Q') + { + if (bFlashRomArray) // view flash ROM data + { + _ASSERT(pbyRomView[0]); // check ROM bank set + _ASSERT(pbyRomView[1]); + + m = (128*1024*2)-1; // mapped in 128KB pages + p = (a<<12)&m; // offset to the begin of ROM in nibbles + for (i=a; i<=b; i++) // scan each 2KB page + { + RMap[i]=pbyRomView[(i & 0x40)!=0] + p; + WMap[i]=NULL; // no writing + p = (p+0x1000)&m; + } + } + else // view flash ROM register + { + for (i=a; i<=b; i++) // scan each 2KB page + { + RMap[i]=NULL; // view flash register + WMap[i]=NULL; // no writing + } + } + return; + } + + // HP38G / HP48SX / HP48GX + m = dwRomSize - 1; // ROM address mask for mirroring + // when 512KB ROM and DA19=0 (ROM disabled) + if ((m & 0x80000) != 0 && (Chipset.IORam[0x29]&DA19) == 0) + m >>= 1; // mirror ROM at #80000 (AR18=0) + p = (a*0x1000)&m; // data offset in nibbles + for (i=a;i<=b;i++) // scan each 2KB page + { + RMap[i]=pbyRom + p; // save page address for read + WMap[i]=NULL; // no writing + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + return; +} + +VOID Map(BYTE a, BYTE b) // maps 2KB pages with priority +{ + // On HP39/40G and HP49G Chipset.cards_status must be 0xF + _ASSERT((cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='P' && cCurrentRomType!='2' && cCurrentRomType!='Q') || !Chipset.P1Cfig || Chipset.cards_status == 0xF); // CdB for HP: add apples + + // priority order is HDW, RAM, CE2, CE1, NCE3, ROM + MapROM(a,b); // ROM, lowest priority, always mapped + if (cCurrentRomType == 'S') // HP48SX + { + if (Chipset.BSCfig) MapBS(a,b); // NCE3, not used in S(X) + if (Chipset.P1Cfig) MapP1(a,b); // CE1, port1 (lower priority than CE2) + if (Chipset.P2Cfig) MapP2(a,b); // CE2, port2 (higher priority than CE1) + } + else // HP48GX / HP49G + { + if (Chipset.P2Cfig) // NCE3, port2 + { + // LED bit set on a HP49 + if ((cCurrentRomType=='X' || cCurrentRomType=='Q') && (Chipset.IORam[LCR]&LED)) // CdB for HP: add apples + MapROM(a,b); // NCE3, ROM + else + MapP2(a,b); // NCE3, port2 + } + if (Chipset.BSCfig) MapBS(a,b); // CE1, bank select (lower priority than CE2) + if (Chipset.P1Cfig) MapP1(a,b); // CE2, port1 (higher priority than CE1) + } + if (Chipset.P0Cfig) MapP0(a,b); // RAM, highest priority (execpt HDW) + // CdB for HP: add apples header + // @todo cg, bug if display header area is mapped to addr 0 + if (Chipset.d0address!=0) + { + RMap[Chipset.d0address]=&(Chipset.d0memory[0]); RMap[Chipset.d0address+1]=&(Chipset.d0memory[2048*2]); + WMap[Chipset.d0address]=&(Chipset.d0memory[0]); WMap[Chipset.d0address+1]=&(Chipset.d0memory[2048*2]); + } + return; +} + +VOID RomSwitch(DWORD adr) +{ + // only HP39/40G, HP49G + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='P' || cCurrentRomType=='2' || cCurrentRomType=='Q') // CdB for HP: add apples + { + Chipset.Bank_FF = adr; // save address line + adr = (adr >> 1) & 0x3f; // 6 bit of latch (was A6-A1 of address bus) + // lower 4 bit (16 banks) for 2nd ROM view + pbyRomView[1] = pbyRom + (((adr & 0xf) * 128 * 1024 * 2) & (dwRomSize - 1)); + // higher 2 bit (4 banks) for 1st ROM view + pbyRomView[0] = pbyRom + (((adr >> 4) * 128 * 1024 * 2) & (dwRomSize - 1)); + } + Map(0x00,0xFF); // update memory mapping + return; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Bus Commands +// +//////////////////////////////////////////////////////////////////////////////// + +VOID Config() // configure modules in fixed order +{ + DWORD d = Npack(Chipset.C,5); // decode size or address + BYTE b = (BYTE)(d>>12); // number of 2KB pages or page address + BYTE s = (BYTE)(b^0xFF); // size in pages-1, offset to last page + + // config order is HDW, RAM, CE1, CE2, NCE3 + if (!Chipset.IOCfig) // address of HDW, first module, ROM always configured + { + Chipset.IOCfig = TRUE; + Chipset.IOBase = d&0xFFFC0; // save HDW base on a 64 nib boundary + Map(b,b); + return; + } + if (!Chipset.P0Cfg2) // RAM size, port0 + { + Chipset.P0Cfg2 = TRUE; + Chipset.P0Size = s; // offset to last used page + return; + } + if (!Chipset.P0Cfig) // RAM address, port0 + { + Chipset.P0Cfig = TRUE; + Chipset.P0Base = b; // save first page address + b &= ~Chipset.P0Size; // adjust base to mapping boundary + Chipset.P0End = b+Chipset.P0Size; // save last page address + Map(b,Chipset.P0End); // refresh mapping + return; + } + if (cCurrentRomType=='S') // HP48SX + { + if (!Chipset.P1Cfg2) // CE1 size, port1 + { + Chipset.P1Cfg2 = TRUE; + Chipset.P1Size = s; + return; + } + if (!Chipset.P1Cfig) // CE1 address, port1 + { + Chipset.P1Cfig = TRUE; + Chipset.P1Base = b; + b &= ~Chipset.P1Size; // adjust base to mapping boundary + Chipset.P1End = b+Chipset.P1Size; + Map(b,Chipset.P1End); // refresh mapping + return; + } + if (!Chipset.P2Cfg2) // CE2 size, port2 + { + Chipset.P2Cfg2 = TRUE; + Chipset.P2Size = s; + return; + } + if (!Chipset.P2Cfig) // CE2 address, port2 + { + Chipset.P2Cfig = TRUE; + Chipset.P2Base = b; + b &= ~Chipset.P2Size; // adjust base to mapping boundary + Chipset.P2End = b+Chipset.P2Size; + Map(b,Chipset.P2End); // refresh mapping + return; + } + if (!Chipset.BSCfg2) // NCE3 size, not used in S(X) + { + Chipset.BSCfg2 = TRUE; + Chipset.BSSize = s; + return; + } + if (!Chipset.BSCfig) // NCE3 address, not used in S(X) + { + Chipset.BSCfig = TRUE; + Chipset.BSBase = b; + b &= ~Chipset.BSSize; // adjust base to mapping boundary + Chipset.BSEnd = b+Chipset.BSSize; + Map(b,Chipset.BSEnd); // refresh mapping + return; + } + } + else // HP48GX / HP49G + { + if (!Chipset.BSCfg2) // CE1 size, bank select + { + Chipset.BSCfg2 = TRUE; + Chipset.BSSize = s; + return; + } + if (!Chipset.BSCfig) // CE1 address, bank select + { + Chipset.BSCfig = TRUE; + Chipset.BSBase = b; + b &= ~Chipset.BSSize; // adjust base to mapping boundary + Chipset.BSEnd = b+Chipset.BSSize; + Map(b,Chipset.BSEnd); // refresh mapping + return; + } + if (!Chipset.P1Cfg2) // CE2 size, port1 + { + Chipset.P1Cfg2 = TRUE; + Chipset.P1Size = s; + return; + } + if (!Chipset.P1Cfig) // CE2 address, port1 + { + Chipset.P1Cfig = TRUE; + Chipset.P1Base = b; + b &= ~Chipset.P1Size; // adjust base to mapping boundary + Chipset.P1End = b+Chipset.P1Size; + Map(b,Chipset.P1End); // refresh mapping + return; + } + if (!Chipset.P2Cfg2) // NCE3 size, port2 + { + Chipset.P2Cfg2 = TRUE; + Chipset.P2Size = s; + return; + } + if (!Chipset.P2Cfig) // NCE3 address, port2 + { + Chipset.P2Cfig = TRUE; + Chipset.P2Base = b; + b &= ~Chipset.P2Size; // adjust base to mapping boundary + Chipset.P2End = b+Chipset.P2Size; + Map(b,Chipset.P2End); // refresh mapping + return; + } + } + return; +} + +VOID Uncnfg() +{ + DWORD d=Npack(Chipset.C,5); // decode address + BYTE b=(BYTE)(d>>12); // page address + + // unconfig order is HDW, RAM, CE2, CE1, NCE3 + if ((Chipset.IOCfig)&&((d&0xFFFC0)==Chipset.IOBase)) + {Chipset.IOCfig=FALSE;Map(b,b);return;} + if ((Chipset.P0Cfig)&&((b&~Chipset.P0Size)==P0MAPBASE)) + {Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Map(P0MAPBASE,Chipset.P0End);return;} + if (cCurrentRomType=='S') // HP48SX + { + if ((Chipset.P2Cfig)&&((b&~Chipset.P2Size)==P2MAPBASE)) + {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(P2MAPBASE,Chipset.P2End);return;} + if ((Chipset.P1Cfig)&&((b&~Chipset.P1Size)==P1MAPBASE)) + {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(P1MAPBASE,Chipset.P1End);return;} + if ((Chipset.BSCfig)&&((b&~Chipset.BSSize)==BSMAPBASE)) + {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(BSMAPBASE,Chipset.BSEnd);return;} + } + else // HP48GX / HP49G + { + if ((Chipset.P1Cfig)&&((b&~Chipset.P1Size)==P1MAPBASE)) + {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(P1MAPBASE,Chipset.P1End);return;} + if ((Chipset.BSCfig)&&((b&~Chipset.BSSize)==BSMAPBASE)) + {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(BSMAPBASE,Chipset.BSEnd);return;} + if ((Chipset.P2Cfig)&&((b&~Chipset.P2Size)==P2MAPBASE)) + {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(P2MAPBASE,Chipset.P2End);return;} + } + return; +} + +VOID Reset() +{ + Chipset.IOCfig=FALSE;Chipset.IOBase=0x100000; + Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Chipset.P0Base=0;Chipset.P0Size=0;Chipset.P0End=0; + Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Chipset.BSBase=0;Chipset.BSSize=0;Chipset.BSEnd=0; + Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Chipset.P1Base=0;Chipset.P1Size=0;Chipset.P1End=0; + Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Chipset.P2Base=0;Chipset.P2Size=0;Chipset.P2End=0; + Map(0x00,0xFF); // refresh mapping + return; +} + +VOID C_Eq_Id() +{ + // config order is HDW, RAM, CE1, CE2, NCE3 + if (!Chipset.IOCfig) {Nunpack(Chipset.C,(Chipset.IOBase) ^0x00019,5);return;} + if (!Chipset.P0Cfg2) {Nunpack(Chipset.C,(Chipset.P0Size*0x1000)^0xFF003,5);return;} + if (!Chipset.P0Cfig) {Nunpack(Chipset.C,(Chipset.P0Base*0x1000)^0x000F4,5);return;} + if (cCurrentRomType=='S') // HP48SX + { + if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF005,5);return;} + if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F6,5);return;} + if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF007,5);return;} + if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F8,5);return;} + if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF001,5);return;} + if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F2,5);return;} + } + else // HP48GX / HP49G + { + if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF005,5);return;} + if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F6,5);return;} + if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF007,5);return;} + if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F8,5);return;} + if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF001,5);return;} + if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F2,5);return;} + } + memset(Chipset.C,0,5); + return; +} + +enum MMUMAP MapData(DWORD d) // check MMU area +{ + BYTE u = (BYTE) (d>>12); + + if (Chipset.IOCfig && ((d&0xFFFC0)==Chipset.IOBase)) return M_IO; + if (Chipset.P0Cfig && (((u^Chipset.P0Base) & ~Chipset.P0Size) == 0)) return M_RAM; + if (cCurrentRomType == 'S') + { + if (Chipset.P2Cfig && (((u^Chipset.P2Base) & ~Chipset.P2Size) == 0)) return M_P2; + if (Chipset.P1Cfig && (((u^Chipset.P1Base) & ~Chipset.P1Size) == 0)) return M_P1; + if (Chipset.BSCfig && (((u^Chipset.BSBase) & ~Chipset.BSSize) == 0)) return M_BS; + } + else + { + if (Chipset.P1Cfig && (((u^Chipset.P1Base) & ~Chipset.P1Size) == 0)) return M_P1; + if (Chipset.BSCfig && (((u^Chipset.BSBase) & ~Chipset.BSSize) == 0)) return M_BS; + if (Chipset.P2Cfig && (((u^Chipset.P2Base) & ~Chipset.P2Size) == 0)) return M_P2; + } + return M_ROM; +} + +VOID CpuReset(VOID) // register setting after Cpu Reset +{ + StopTimers(); // stop timer, do here because function change Chipset.t2 + + Chipset.pc = 0; + Chipset.rstkp = 0; + ZeroMemory(Chipset.rstk,sizeof(Chipset.rstk)); + Chipset.HST = 0; + Chipset.SoftInt = FALSE; + Chipset.Shutdn = TRUE; + Chipset.inte = TRUE; // enable interrupts + Chipset.intk = TRUE; // INTON + Chipset.intd = FALSE; // no keyboard interrupts pending + Chipset.crc = 0; + Chipset.Bank_FF = 0; // state of bank switcher FF + Chipset.FlashRomState = 0; // WSM state of flash memory + ZeroMemory(Chipset.IORam,sizeof(Chipset.IORam)); + Chipset.IORam[LPE] = RST; // set ReSeT bit at hardware reset + Reset(); // reset MMU + Chipset.t1 = 0; // reset timer values + Chipset.t2 = 0; + Chipset.loffset = 0; // right margin + Chipset.boffset = 0; // left margin + Chipset.lcounter = 0; // number of main display lines + Chipset.contrast = 0; // contrast dark + + UpdateContrast(Chipset.contrast); // update contrast + // display update when changing to run state + CommSetBaud(); // new baudrate + CheckSerial(); // close serial port + + RomSwitch(Chipset.Bank_FF); // force new memory mapping + return; +} + +VOID Npeek(BYTE *a, DWORD d, UINT s) +{ + enum MMUMAP eMap; + DWORD u, v; + UINT c; + BYTE *p; + + do + { + eMap = MapData(d); // get active memory controller + if (M_IO == eMap) // I/O access + { + v = d&0x3F; + + do + { + if (v == LPE) + { + // don't read LPE content with the function ReadIO() + c = 1; + memcpy(a, Chipset.IORam+v, c); + break; + } + if (v >= RBR_LSB && v <= RBR_MSB) + { + // don't read RBR content with the function ReadIO() + c = MIN(s,RBR_MSB-v+1); + memcpy(a, Chipset.IORam+v, c); + break; + } + // all others registers + do + { + if (v < LPE) + { + c = MIN(s,LPE-v); + break; + } + if (v < RBR_LSB && (v+s) > RBR_LSB) + { + c = MIN(s,RBR_LSB-v); + break; + } + c = MIN(s,0x40-v); + } + while (0); + ReadIO(a,v,c,FALSE); + } + while (0); + } + else + { + u = d>>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // Flash memory Read access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + FlashRead(a, FlashROMAddr(d), c); + } + else + { + if ((p=RMap[u]) != NULL) // module mapped + { + memcpy(a, p+v, c); + } + else // open data bus + { + for (u=0; u>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // bank switcher access + if (cCurrentRomType!='S' && M_BS == eMap) + { + if (cCurrentRomType=='G') // HP48GX + { + Chipset.Bank_FF = v+c; // save FF value + Map(Chipset.P2Base,Chipset.P2End); + } + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') // HP39/40G, HP49G // CdB for HP: add apples + { + RomSwitch(v+c); + } + } + // Flash memory Read access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + DWORD dwLinAddr = FlashROMAddr(d); + + FlashRead(a, dwLinAddr, c); + + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: Flash Read : %.5x (%.6x),%u = "),Chipset.pc,d,dwLinAddr,c); + for (j = 0;j < c;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + } + else + { + if ((p=RMap[u]) != NULL) // module mapped + { + memcpy(a, p+v, c); + } + // simulate open data bus + else // open data bus + { + for (u=0; u>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // bank switcher access + if (cCurrentRomType!='S' && M_BS == eMap) + { + BOOL bWrite = FALSE; + + // write enabled + if (Chipset.cards_status & PORT2_WRITE) + { + Chipset.Bank_FF = v+c-1;// save FF value + bWrite = TRUE; // bank switched + } + else // write disabled, so latch last read cycle + { + if ((v & 1) != 0) // low address odd + { + Chipset.Bank_FF = v;// save FF value + bWrite = TRUE; // bank switched + } + + if (((v+c) & 1) != 0) // high address odd + { + Chipset.Bank_FF = v+c-1;// save FF value + bWrite = TRUE; // bank switched + } + } + + if (bWrite) // write cycle? + { + // HP48GX + if (cCurrentRomType=='G') Map(Chipset.P2Base,Chipset.P2End); + // HP39/40G, HP49G + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') RomSwitch(Chipset.Bank_FF); // CdB for HP: add apples + } + } + // Flash memory Write access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + DWORD dwLinAddr = FlashROMAddr(d); + + FlashWrite(a, dwLinAddr, c); + + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: Flash Write: %.5x (%.6x),%u = "),Chipset.pc,d,dwLinAddr,c); + for (j = 0;j < c;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + } + else + { + if ((p=WMap[u]) != NULL) memcpy(p+v, a, c); + } + } + if (!bGrayscale) UpdateDisplay(d, c); // update display + a+=c; + d=(d+c)&0xFFFFF; + } while (s-=c); + return; +} + +DWORD Read5(DWORD d) +{ + BYTE p[5]; + + Npeek(p,d,5); + return Npack(p,5); +} + +BYTE Read2(DWORD d) +{ + BYTE p[2]; + + Npeek(p,d,2); + return (BYTE)(p[0]|(p[1]<<4)); +} + +VOID Write5(DWORD d, DWORD n) +{ + BYTE p[5]; + + Nunpack(p,n,5); + Nwrite(p,d,5); + return; +} + +VOID Write2(DWORD d, BYTE n) +{ + BYTE p[2]; + + Nunpack(p,n,2); + Nwrite(p,d,2); + return; +} + +VOID IOBit(DWORD d, BYTE b, BOOL s) // set/clear bit in I/O section +{ + EnterCriticalSection(&csIOLock); + { + if (s) + Chipset.IORam[d] |= b; // set bit + else + Chipset.IORam[d] &= ~b; // clear bit + } + LeaveCriticalSection(&csIOLock); +} + +static DWORD ReadT2Acc(VOID) +{ + static DWORD dwCyc = 0; // CPU cycle counter at last timer2 read access + + DWORD dwCycDif; + + // CPU cycles since last call + dwCycDif = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCyc; + dwCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + + // maybe CPU speed measurement, slow down the next 10 CPU opcodes + if (dwCycDif < 150) + { + EnterCriticalSection(&csSlowLock); + { + InitAdjustSpeed(); // init variables if necessary + nOpcSlow = 10; // slow down next 10 opcodes + } + LeaveCriticalSection(&csSlowLock); + } + return ReadT2(); +} + +VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate) +{ + BOOL bNINT,bNINT2; + BOOL bLBI,bVLBI; + + BYTE c = 0xFF; // LINECOUNT not initialized + BOOL rbr_acc = FALSE; // flag to receive data + + #if defined DEBUG_IO + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IO read : %02x,%u\n"),Chipset.pc,d,s); + OutputDebugString(buffer); + } + #endif + + do + { + switch (d) + { + case 0x00: *a = (Chipset.IORam[d]&DON)|Chipset.boffset; break; + case 0x01: *a = Chipset.contrast&0xF; break; + case 0x02: *a = Chipset.contrast>>4; break; + case 0x03: *a = 0; + case 0x04: *a = (Chipset.crc )&0xF; break; + case 0x05: *a = (Chipset.crc>> 4)&0xF; break; + case 0x06: *a = (Chipset.crc>> 8)&0xF; break; + case 0x07: *a = (Chipset.crc>>12)&0xF; break; + case 0x08: // LPD + // LB2 and LB1 not emulated, must be 0 + _ASSERT((Chipset.IORam[LPD] & (LB2 | LB1)) == 0); + + GetBatteryState(&bLBI,&bVLBI); // get battery state + + // check if battery states enabled + bLBI = bLBI && ((Chipset.IORam[LPE] & ELBI) != 0); + bVLBI = bVLBI && ((Chipset.IORam[LPE] & EVLBI) != 0); + + // set IO bits + IOBit(LPD,LB0,bLBI); + IOBit(LPD,VLBI,bVLBI); + IOBit(SRQ1,VSRQ,bVLBI); + *a = Chipset.IORam[d]; + break; + case 0x09: // LPE + *a = Chipset.IORam[d]; + if (bUpdate) + { + Chipset.IORam[d] &= ~RST; // clear RST bit after reading + } + break; + case 0x0A: *a = 0; break; +// case 0x0B: *a = Chipset.IORam[d]; break; +// case 0x0C: *a = Chipset.IORam[d]; break; + case 0x0D: // BAUD + if (isModelApple(cCurrentRomType)) + { + *a = Chipset.IORam[d]; + #if defined DEBUG_SERIAL // return BAUD value + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: BAUD Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + } // Clarke / Yorke chip + else + { + *a = Chipset.IORam[d] & 0x7; + #if defined DEBUG_SERIAL // return BAUD value + if (bUpdate) + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: BAUD Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + *a |= UckBit(*a); // add UCK bit to BAUD rate register + } + break; + case 0x0E: + // SMP is !NINT and SWINT is always 0 + // clear SMP and SWINT bit + Chipset.IORam[d] &= (ECDT | RCDT); + // SMP is !NINT + if ((Chipset.IORam[SRQ2] & NINT) == 0) + Chipset.IORam[d] |= SMP; + *a = Chipset.IORam[d]; + break; + case 0x0F: + // card detection disabled + if ((Chipset.IORam[CARDCTL] & ECDT) == 0) + { + *a = 0; // no cards + } + else + { + // on a HP30/40G and HP49G Chipset.cards_status bust always be 0xF + _ASSERT((cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='P' && cCurrentRomType!='Q') || Chipset.cards_status == 0xF); // CdB for HP: add apples + *a = Chipset.cards_status; + } + break; + case 0x10: // IO CONTROL + *a = Chipset.IORam[d]; // return IO CONTROL value + #if defined DEBUG_SERIAL + if (bUpdate) + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IOC Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x11: // RCS + *a = Chipset.IORam[d] | RX; // return RCS value + #if defined DEBUG_SERIAL + if (bUpdate) + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RCS Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x12: // TCS + *a = Chipset.IORam[d]; // return TCS value + #if defined DEBUG_SERIAL + if (bUpdate) + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TCS Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x13: // CRER + *a = 0; + break; + case 0x14: // RBR LSB + case 0x15: // RBR MSB + if (bUpdate) + { + Chipset.IORam[RCS] &= ~RBF; + *a = Chipset.IORam[d]; // return RBR value + UpdateUSRQ(); // update USRQ + rbr_acc = TRUE; // search for new RBR value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RBR %s Read: %x\n"),Chipset.pc,(d==0x14) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + } + else + { + *a = Chipset.IORam[d]; // return RBR value + UpdateUSRQ(); // update USRQ + } + break; +// case 0x16: *a = Chipset.IORam[d]; break; // TBR LSB +// case 0x17: *a = Chipset.IORam[d]; break; // TBR MSB + case 0x19: // SREQ? MSB + UpdateKdnBit(); // update KDN bit + bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + } + IOBit(SRQ2,NINT2,bNINT2); + IOBit(SRQ2,NINT,bNINT); + // no break! + case 0x18: // SREQ? LSB + *a = Chipset.IORam[d]; // return SREQ value + #if defined DEBUG_SERIAL + if (bUpdate) + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: SEQ %s Read: %x\n"),Chipset.pc,(d==0x18) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x1A: // IR CONTROL + if (cCurrentRomType=='E') // HP39/40G + { + Chipset.IORam[d] = (nCurrentClass != 40) + ? (Chipset.IORam[d] & ~IRI) // HP39G + : (Chipset.IORam[d] | IRI); // HP40G + } + *a = Chipset.IORam[d]; // return IR CONTROL value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IRC Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x1B: *a = 0; break; + case 0x1C: // LED CONTROL + // put LBF and LBZ always to zero to indicate a free REDEYE buffer and formatter + *a = (Chipset.IORam[d] & (LED|ELBE)); + break; + case 0x1D: // LED BUFFER + *a = (Chipset.IORam[d] & LBO); + break; +// case 0x1E: *a = Chipset.IORam[d]; break; +// case 0x1F: *a = Chipset.IORam[d]; break; + case 0x20: *a = 3; break; + case 0x21: *a = 3; break; + case 0x22: *a = 3; break; + case 0x23: *a = 3; break; + case 0x24: *a = 3; break; + case 0x25: *a = 3; break; + case 0x26: *a = 3; break; + case 0x27: *a = 3; break; + case 0x28: // LINECOUNT LSB + case 0x29: // LINECOUNT MSB + DA19 M32 + if (Chipset.IORam[0x00]&DON) // display on + { + if (c == 0xFF) // no actual line information + { + c = GetLineCounter(); // get LCD update line + // save line information in IO registers + Chipset.IORam[0x28] = c & 0xF; + Chipset.IORam[0x29] = (Chipset.IORam[0x29] & (DA19|M32)) | (c >> 4); + } + } + *a = Chipset.IORam[d]; + + if (d==0x29) // address 0x29 is mirrored to 0x2A-0x2D + { + Chipset.IORam[0x2A] = *a; + Chipset.IORam[0x2B] = *a; + Chipset.IORam[0x2C] = *a; + Chipset.IORam[0x2D] = *a; + } + break; +// case 0x2A: *a = 0; break; +// case 0x2B: *a = 0; break; +// case 0x2C: *a = 0; break; +// case 0x2D: *a = 0; break; + case 0x2E: + ReadT1(); // dummy read for update timer1 control register + *a = Chipset.IORam[d]; + break; + case 0x2F: + ReadT2(); // dummy read for update timer2 control register + *a = Chipset.IORam[d]; + break; + case 0x30: *a = 3; break; + case 0x31: *a = 3; break; + case 0x32: *a = 3; break; + case 0x33: *a = 3; break; + case 0x34: *a = 3; break; + case 0x35: *a = 0; break; + case 0x36: *a = 0; break; + case 0x37: *a = ReadT1(); break; + case 0x38: Nunpack(a, ReadT2Acc() , s); return; + case 0x39: Nunpack(a, ReadT2Acc()>> 4, s); return; + case 0x3A: Nunpack(a, ReadT2Acc()>> 8, s); return; + case 0x3B: Nunpack(a, ReadT2Acc()>>12, s); return; + case 0x3C: Nunpack(a, ReadT2Acc()>>16, s); return; + case 0x3D: Nunpack(a, ReadT2Acc()>>20, s); return; + case 0x3E: Nunpack(a, ReadT2Acc()>>24, s); return; + case 0x3F: Nunpack(a, ReadT2Acc()>>28, s); return; + default: *a = Chipset.IORam[d]; + } + d++; a++; + } while (--s); + if (rbr_acc) CommReceive(); // look for new character + return; +} + +VOID WriteIO(BYTE *a, DWORD d, DWORD s) +{ + DWORD b; + BYTE c; + BOOL tbr_acc = FALSE; // flag to transmit data + BOOL bDISPADDR = FALSE; // flag addr 0x120-0x124 changed + BOOL bLINEOFFS = FALSE; // flag addr 0x125-0x127 changed + BOOL bMENUADDR = FALSE; // flag addr 0x130-0x134 changed + + #if defined DEBUG_IO + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: IO write: %02x,%u = "),Chipset.pc,d,s); + for (j = 0;j < s;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + + do + { + c = *a; + switch (d) + { +// 00100 = NS:DISPIO +// 00100 @ Display bit offset and DON [DON OFF2 OFF1 OFF0] +// 00100 @ 3 nibs for display offset (scrolling), DON=Display ON + case 0x00: + if ((c^Chipset.IORam[d])&DON) // DON bit changed + { + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + + // adjust VBL counter start/stop values + if ((c & DON) != 0) // set display on + { + Chipset.IORam[d] |= DON; // for StartDisplay(); + UpdateContrast(Chipset.contrast); + StartDisplay((BYTE) Chipset.lcounter); // start display update + } + else // display is off + { + Chipset.IORam[d] &= ~DON; + UpdateContrast(Chipset.contrast); + StopDisplay(); // stop display update + } + } + // OFF bits changed + if ((c^Chipset.IORam[d]) & (OFF2 | OFF1 | OFF0)) + { + Chipset.boffset = c & (OFF2 | OFF1 | OFF0); + disp |= (DISP_POINTER | DISP_MAIN); + } + Chipset.IORam[d] = c; + break; + +// 00101 = NS:CONTRLSB +// 00101 @ Contrast Control [CON3 CON2 CON1 CON0] +// 00101 @ Higher value = darker screen + case 0x01: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d]=c; + Chipset.contrast = (Chipset.contrast&0x10)|c; + UpdateContrast(Chipset.contrast); + disp |= (DISP_MAIN | DISP_MENUE); + } + break; + +// 00102 = NS:DISPTEST +// 00102 @ Display test [VDIG LID TRIM CON4] [LRT LRTD LRTC BIN] +// 00102 @ Normally zeros + case 0x02: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d]=c; + Chipset.contrast = (Chipset.contrast&0x0f)|((c&1)<<4); + UpdateContrast(Chipset.contrast); + disp |= (DISP_MAIN | DISP_MENUE); + } + break; + + case 0x03: Chipset.IORam[d]=c; break; + +// 00104 = HP:CRC +// 00104 @ 16 bit hardware CRC (104-107) (X^16+X^12+X^5+1) +// 00104 @ crc = ( crc >> 4 ) ^ ( ( ( crc ^ nib ) & 0x000F ) * 0x1081 ); + case 0x04: Chipset.crc = (Chipset.crc&0xfff0)|(c*0x0001); break; + case 0x05: Chipset.crc = (Chipset.crc&0xff0f)|(c*0x0010); break; + case 0x06: Chipset.crc = (Chipset.crc&0xf0ff)|(c*0x0100); break; + case 0x07: Chipset.crc = (Chipset.crc&0x0fff)|(c*0x1000); break; + +// 00108 = NS:POWERSTATUS +// 00108 @ Low power registers (108-109) +// 00108 @ [LB2 LB1 LB0 VLBI] (read only) +// 00108 @ LowBat(2) LowBat(1) LowBat(S) VeryLowBat + case 0x08: break; // read-only + +// 00109 = NS:POWERCTRL +// 00109 @ [ELBI EVLBI GRST RST] (read/write) + case 0x09: + Chipset.IORam[d]=c; + if (c & RST) + { + CpuReset(); // emulate NRES signal + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE | DISP_ANNUN); + bInterrupt = TRUE; // SHUTDN + } + break; + +// 0010A = NS:MODE +// 0010A @ Mode Register (read-only) + case 0x0A: break; // read-only + +// 0010B = HP:ANNCTRL +// 0010B @ Annunciator control [LA4 LA3 LA2 LA1] = [ alarm alpha -> <- ] + case 0x0B: + case 0x0C: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d] = c; + disp |= DISP_ANNUN; + } + break; + +// 0010D = NS:BAUD +// 0010D @ Serial baud rate [UCK BD2 BD1 BD0] (bit 3 is read-only) +// 0010D @ 3 bits = {1200 1920 2400 3840 4800 7680 9600 15360} + case 0x0D: + if (isModelApple(cCurrentRomType)) + { + Chipset.IORam[d] = c; + } + else // Clarke / Yorke chip + { + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); // bit 3 is read-only + } + CommSetBaud(); // set baudrate + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: BAUD write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 0010E = NS:CARDCTL +// 0010E @ [ECDT RCDT SMP SWINT] (read/write) +// 0010E @ Enable Card Det., Run Card Det., Set Module Pulled, Software interrupt + case 0x0E: + if (c & SWINT) // SWINT bit set + { + c &= (ECDT | RCDT | SMP); // clear SWINT bit + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + if ((c & SMP) == 0) // SMP bit cleared + { + BOOL bNINT = TRUE; // ack NINT interrupt -> NINT high + // card detect disabled and CDT1 low -> retrigger + if ((c & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + bNINT = (Chipset.cards_status & (P1W|P1C)) != P1C; + IOBit(SRQ2,NINT,bNINT); + } + // falling edge of Enable Card Detect bit and timer running + if ( ((c^Chipset.IORam[d]) & ECDT) != 0 && (c & ECDT) == 0 + && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + BOOL bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + // card in slot1 isn't Read Only + if ((Chipset.cards_status & (P1W|P1C)) != P1C) + { + // use random state of NINT line + bNINT = bNINT && (ReadT2() & 0x1) != 0; + } + IOBit(SRQ2,NINT,bNINT); + + Chipset.HST |= MP; // set Module Pulled + + // Port1 and Port2 plugged and writeable or NINT2/NINT interrupt + if ( Chipset.cards_status != (P2W|P1W|P2C|P1C) + || (Chipset.IORam[SRQ2] & NINT2) == 0 + || (Chipset.IORam[SRQ2] & NINT ) == 0 + ) + { + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + } + Chipset.IORam[d]=c; + break; + +// 0010F = NS:CARDSTATUS +// 0010F @ [P2W P1W P2C P1C] (read-only) Port 2 writable .. Port 1 inserted + case 0x0F: break; // read-only + +// 00110 = HP:IOC +// 00110 @ Serial I/O Control [SON ETBE ERBF ERBZ] +// 00110 @ Serial On, Interrupt On Recv.Buf.Empty, Full, Buzy + case 0x10: + Chipset.IORam[d]=c; + CheckSerial(); // handle UART on/off + if ((c & SON) == 0) // SON bit cleared + { + Chipset.IORam[IOC] = 0; // clear IOC + Chipset.IORam[RCS] = 0; // clear RCS + Chipset.IORam[TCS] = 0; // clear TCS + Chipset.IORam[RBR_LSB] = 0; // clear RBR + Chipset.IORam[RBR_MSB] = 0; + Chipset.IORam[TBR_LSB] = 0; // clear TBR + Chipset.IORam[TBR_MSB] = 0; + } + if (UpdateUSRQ()) INTERRUPT; // update USRQ bit + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IOC write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 00111 = HP:RCS +// 00111 Serial Receive Control/Status [RX RER RBZ RBF] (bit 3 is read-only) + case 0x11: + if (Chipset.IORam[IOC] & SON) + { + EnterCriticalSection(&csIOLock); + { + // critical section because of RER access + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + } + LeaveCriticalSection(&csIOLock); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RCS write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00112 = HP:TCS +// 00112 @ Serial Transmit Control/Status [BRK LPB TBZ TBF] + case 0x12: + if (Chipset.IORam[IOC] & SON) + { + Chipset.IORam[d]=c; + CommTxBRK(); // update BRK condition + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TCS write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00113 = HP:CRER +// 00113 @ Serial Clear RER (writing anything clears RER bit) + case 0x13: + IOBit(RCS,RER,FALSE); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: CRER write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 00114 = HP:RBR +// 00114 @ Serial Receive Buffer Register (Reading clears RBF bit) + case 0x14: break; // read-only + case 0x15: break; // read-only + +// 00116 = HP:TBR +// 00116 @ Serial Transmit Buffer Register (Writing sets TBF bit) + case 0x16: + case 0x17: + if (Chipset.IORam[IOC] & SON) + { + Chipset.IORam[d]=c; + tbr_acc = TRUE; // new TBR value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TBR %s write: %x\n"),Chipset.pc,(d==0x16) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00118 = NS:SRR +// 00118 @ Service Request Register (read-only) +// 00118 @ [ISRQ TSRQ USRQ VSRQ] [KDN NINT2 NINT LSRQ] + case 0x18: break; // read-only + case 0x19: break; // read-only + +// 0011A = HP:IRC +// 0011A @ IR Control Register [IRI EIRU EIRI IRE] (bit 3 is read-only) +// 0011A @ IR Input, Enable IR UART mode, Enable IR Interrupt, IR Event + case 0x1A: + // COM port open and EIRU bit changed + if (bCommInit && ((c^Chipset.IORam[d]) & EIRU) != 0) + { + // save new value for COM open + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + // reopen COM port with new setting + bCommInit = CommOpen(szSerialWire,szSerialIr); + } + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IRC write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 0011B = NS:BASENIBOFF +// 0011B @ Used as addressto get BASENIB from 11F to the 5th nibble + case 0x1B: break; + +// 0011C = NS:LCR +// 0011C @ Led Control Register [LED ELBE LBZ LBF] (Setting LED is draining) + case 0x1C: + // HP49G new mapping on LED bit change + if (cCurrentRomType=='X' && ((c^Chipset.IORam[d])&LED)) + { + Chipset.IORam[d]=c; // save new value for mapping + Map(Chipset.P2Base,Chipset.P2End); // new ROM mapping + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: NCE3: R%cM\n"),Chipset.pc,(c&LED) ? 'O' : 'A'); + OutputDebugString(buffer); + } + #endif + } + // Saturnator on apples has no ELBE bit simulation + if (cCurrentRomType!='Q' && cCurrentRomType!='2' && cCurrentRomType!='P') + { + if ((c^Chipset.IORam[d])&ELBE) // ELBE bit changed + { + // Led Service ReQuest on Led Buffer Empty enabled + BOOL bLSRQ = (c & (ELBE | LBF)) == ELBE; + + IOBit(SRQ2,LSRQ,bLSRQ); // update LSRQ bit + if (bLSRQ) // interrupt on Led Buffer Empty enabled + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + } + } + Chipset.IORam[d]=c; + break; + +// 0011D = NS:LBR +// 0011D @ Led Buffer Register [0 0 0 LBO] (bits 1-3 read zero) + case 0x1D: + IrPrinter((BYTE)(c&LBO)); + Chipset.IORam[d]=c&LBO; + break; + +// 0011E = NS:SCRATCHPAD +// 0011E @ Scratch pad + case 0x1E: Chipset.IORam[d]=c; break; + +// 0011F = NS:BASENIB +// 0011F @ 7 or 8 for base memory + case 0x1F: Chipset.IORam[d]=c; break; + +// 00120 = NS:DISPADDR +// 00120 @ Display Start Address (write only) +// 00120 @ bit 0 is ignored (display must start on byte boundary) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + Chipset.IORam[d]=c; + bDISPADDR = TRUE; // addr 0x120-0x124 changed + break; + +// 00125 = NS:LINEOFFS +// 00125 @ Display Line offset (write only) (no of bytes skipped after each line) +// 00125 @ MSG sign extended + case 0x25: + case 0x26: + case 0x27: + Chipset.IORam[d]=c; + bLINEOFFS = TRUE; // addr 0x125-0x127 changed + break; + +// 00128 = NS:LINECOUNT +// 00128 @ Display Line Counter and miscellaneous (28-29) +// 00128 @ [LC3 LC2 LC1 LC0] [DA19 M32 LC5 LC4] +// 00128 @ Line counter 6 bits -> max = 2^6-1 = 63 = disp height +// 00128 @ Normally has 55 -> Menu starts at display row 56 + case 0x28: + // LSB of LINECOUNT changed + if (c != (BYTE) (Chipset.lcounter & 0xf)) + { + Chipset.lcounter = (Chipset.lcounter & ~0xF) | c; + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + } + break; + + case 0x29: + // MSB of LINECOUNT changed + b = (c & 0x3) << 4; // upper two bits + if (b != (Chipset.lcounter & 0x30)) + { + Chipset.lcounter = (Chipset.lcounter & ~0x30) | b; + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + } + + if ((c^Chipset.IORam[d])&DA19) // DA19 changed + { + Chipset.IORam[d]^=DA19; // save new DA19 + Map(0x00,0xFF); // new ROM mapping + } + break; + + case 0x2A: break; + case 0x2B: break; + case 0x2C: break; + case 0x2D: break; + +// 0012E = NS:TIMER1CTRL +// 0012E @ TIMER1 Control [SRQ WKE INT XTRA] + case 0x2E: + Chipset.IORam[d]=c; // don't clear XTRA bit + ReadT1(); // dummy read for checking control bits + break; + +// 0012F = NS:TIMER2CTRL +// 0012F @ TIMER2 Control [SRQ WKE INT RUN] + case 0x2F: + Chipset.IORam[d]=c; + ReadT2(); // dummy read for checking control bits + if (c&1) + StartTimers(); + else + StopTimers(); + disp |= DISP_ANNUN; // update annunciators + break; + +// 00130 = NS:MENUADDR +// 00130 @ Display Secondary Start Address (write only) (30-34) +// 00130 @ Menu Display Address, no line offsets + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + Chipset.IORam[d]=c; + bMENUADDR = TRUE; // addr 0x130-0x134 changed + break; + + case 0x35: break; + case 0x36: break; + +// 00137 = HP:TIMER1 +// 00137 @ Decremented 16 times/s + case 0x37: + SetT1(c); // set new value + break; + +// 00138 = HP:TIMER2 +// 00138 @ hardware timer (38-3F), decremented 8192 times/s + // nothing - fall through to default + + default: + Chipset.IORam[d]=c; // write data + + if (d >= TIMER2) // timer2 update + { + Nunpack(Chipset.IORam+TIMER2,ReadT2(),8); + memcpy(Chipset.IORam+d,a,s); + SetT2(Npack(Chipset.IORam+TIMER2,8)); + goto finish; + } + } + a++; d++; + } while (--s); + +finish: + if (bDISPADDR) // 0x120-0x124 changed + { + b = Npack(Chipset.IORam+DISP1CTL,5)&0xFFFFE; + if (b != Chipset.start1) + { + Chipset.start1 = b; + disp |= (DISP_POINTER | DISP_MAIN); + } + } + if (bLINEOFFS) // addr 0x125-0x127 changed + { + signed short lo = (signed short)Npack(Chipset.IORam+LINENIBS,3); + if (lo&0x800) lo-=0x1000; + if (lo != Chipset.loffset) + { + Chipset.loffset = lo; + disp |= (DISP_POINTER | DISP_MAIN); + } + } + if (bMENUADDR) // addr 0x130-0x134 changed + { + b = Npack(Chipset.IORam+DISP2CTL,5)&0xFFFFE; + if (b != Chipset.start2) + { + Chipset.start2 = b; + disp |= (DISP_POINTER | DISP_MENUE); + } + } + + if (tbr_acc) // addr 0x116-0x117 changed + { + IOBit(TCS,TBF,TRUE); // set transmit buffer full bit + CommTransmit(); // transmit char + } + + if (disp & DISP_POINTER) + { + disp &= ~DISP_POINTER; // display pointer updated + UpdateDisplayPointers(); + } + if (disp & DISP_ANNUN) + { + disp &= ~DISP_ANNUN; // annunciators updated + UpdateAnnunciators(); + } + return; +} diff --git a/app/src/main/cpp/MRU.C b/app/src/main/cpp/MRU.C new file mode 100644 index 0000000..baf367c --- /dev/null +++ b/app/src/main/cpp/MRU.C @@ -0,0 +1,353 @@ +/* + * mru.c + * + * This file is part of Emu48 + * + * Copyright (C) 2007 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" + +static TCHAR szOriginal[MAX_PATH] = _T(""); + +static LPTSTR *ppszFiles = NULL; // pointer to MRU table +static UINT nEntry = 0; // no. of MRU entries + +static BOOL GetMenuPosForId(HMENU hMenu, UINT nItem, HMENU *phMruMenu, UINT *pnMruPos) +{ + HMENU hSubMenu; + UINT i,nID,nMaxID; + + nMaxID = GetMenuItemCount(hMenu); + for (i = 0; i < nMaxID; ++i) + { + nID = GetMenuItemID(hMenu,i); // get ID + + if (nID == 0) continue; // separator or invalid command + + if (nID == (UINT)-1) // possibly a popup menu + { + hSubMenu = GetSubMenu(hMenu,i); // try to get handle to popup menu + if (hSubMenu != NULL) // it's a popup menu + { + // recursive search + if (GetMenuPosForId(hSubMenu,nItem,phMruMenu,pnMruPos)) + return TRUE; + } + continue; + } + + if (nID == nItem) // found ID + { + *phMruMenu = hMenu; // remember menu and position + *pnMruPos = i; + return TRUE; + } + } + return FALSE; +} + +BOOL MruInit(UINT nNum) +{ + HMENU hMenu = GetMenu(hWnd); // main menu + if (hMenu == NULL) return FALSE; // failed, no menu bar + + _ASSERT(ppszFiles == NULL); // MRU already initialized + + // no. of files in MRU list + nEntry = ReadSettingsInt(_T("MRU"),_T("FileCount"),nNum); + + if (nEntry > 0) // allocate MRU table + { + // create MRU table + if ((ppszFiles = (LPTSTR *) malloc(nEntry * sizeof(*ppszFiles))) == NULL) + return FALSE; + + // fill each entry + for (nNum = 0; nNum < nEntry; ++nNum) + ppszFiles[nNum] = NULL; + + MruReadList(); // read actual MRU list + } + return TRUE; +} + +VOID MruCleanup(VOID) +{ + UINT i; + + MruWriteList(); // write actual MRU list + + if (ppszFiles != NULL) // table defined + { + for (i = 0; i < nEntry; ++i) // cleanup each entry + { + if (ppszFiles[i] != NULL) + free(ppszFiles[i]); // cleanup entry + } + + free(ppszFiles); // free table + ppszFiles = NULL; + } + return; +} + +VOID MruAdd(LPCTSTR lpszEntry) +{ + TCHAR szFilename[MAX_PATH]; + LPTSTR lpFilePart; + UINT i; + + if (ppszFiles != NULL) // MRU initialized + { + _ASSERT(nEntry > 0); // must have entries + + // get full path name + GetFullPathName(lpszEntry,ARRAYSIZEOF(szFilename),szFilename,&lpFilePart); + + // look if entry is already in table + for (i = 0; i < nEntry; ++i) + { + // already in table -> quit + if ( ppszFiles[i] != NULL + && lstrcmpi(ppszFiles[i],szFilename) == 0) + { + MruMoveTop(i); // move to top and update menu + return; + } + } + + i = nEntry - 1; // last index + if (ppszFiles[i] != NULL) + free(ppszFiles[i]); // free oldest entry + + for (; i > 0; --i) // move old entries 1 line down + { + ppszFiles[i] = ppszFiles[i-1]; + } + + // add new entry to top + ppszFiles[0] = DuplicateString(szFilename); + } + return; +} + +VOID MruRemove(UINT nIndex) +{ + // MRU initialized and index inside valid range + if (ppszFiles != NULL && nIndex < nEntry) + { + free(ppszFiles[nIndex]); // free entry + + for (; nIndex < nEntry - 1; ++nIndex) // move below entries 1 line up + { + ppszFiles[nIndex] = ppszFiles[nIndex+1]; + } + + ppszFiles[nIndex] = NULL; // clear last line + } + return; +} + +VOID MruMoveTop(UINT nIndex) +{ + // MRU initialized and index inside valid range + if (ppszFiles != NULL && nIndex < nEntry) + { + LPTSTR lpszEntry = ppszFiles[nIndex];// remember selected entry + + for (; nIndex > 0; --nIndex) // move above entries 1 line down + { + ppszFiles[nIndex] = ppszFiles[nIndex-1]; + } + + ppszFiles[0] = lpszEntry; // insert entry on top + } + return; +} + +UINT MruEntries(VOID) +{ + return nEntry; +} + +LPCTSTR MruFilename(UINT nIndex) +{ + LPCTSTR lpszName = _T(""); + + // MRU initialized and index inside valid range + if (ppszFiles != NULL && nIndex < nEntry) + { + lpszName = ppszFiles[nIndex]; + } + return lpszName; +} + +VOID MruUpdateMenu(HMENU hMenu) +{ + TCHAR szCurPath[MAX_PATH]; + BOOL bEmpty; + UINT i; + + if (hMenu != NULL) // have menu + { + HMENU hMruMenu; // menu handle for MRU list + UINT nMruPos; // insert position for MRU list + + _ASSERT(IsMenu(hMenu)); // validate menu handle + + // search for menu position of ID_FILE_MRU_FILE1 + if (GetMenuPosForId(hMenu,ID_FILE_MRU_FILE1,&hMruMenu,&nMruPos)) + { + if (*szOriginal == 0) // get orginal value of first MRU entry + { + VERIFY(GetMenuString(hMruMenu,nMruPos,szOriginal,ARRAYSIZEOF(szOriginal),MF_BYPOSITION)); + } + + if (nEntry == 0) // kill MRU menu entry + { + // delete MRU menu + DeleteMenu(hMruMenu,nMruPos,MF_BYPOSITION); + + // delete the following separator + _ASSERT((GetMenuState(hMruMenu,nMruPos,MF_BYPOSITION) & MF_SEPARATOR) != 0); + DeleteMenu(hMruMenu,nMruPos,MF_BYPOSITION); + } + + if (ppszFiles != NULL) // MRU initialized + { + _ASSERT(nEntry > 0); // must have entries + + // delete all menu entries + for (i = 0; DeleteMenu(hMenu,ID_FILE_MRU_FILE1+i,MF_BYCOMMAND) != FALSE; ++i) { } + + // check if MRU list is empty + for (bEmpty = TRUE, i = 0; bEmpty && i < nEntry; ++i) + { + bEmpty = (ppszFiles[i] == NULL); + } + + if (bEmpty) // MRU list is empty + { + // fill with orginal string + VERIFY(InsertMenu(hMruMenu,nMruPos,MF_STRING|MF_BYPOSITION|MF_GRAYED,ID_FILE_MRU_FILE1,szOriginal)); + return; + } + + // current directory + GetCurrentDirectory(ARRAYSIZEOF(szCurPath),szCurPath); + + for (i = 0; i < nEntry; ++i) // add menu entries + { + if (ppszFiles[i] != NULL) // valid entry + { + TCHAR szMenuname[2*MAX_PATH+3]; + TCHAR szFilename[MAX_PATH]; + LPTSTR lpFilePart,lpszPtr; + + // check if file path and current path is identical + if (GetFullPathName(ppszFiles[i],ARRAYSIZEOF(szFilename),szFilename,&lpFilePart)) + { + TCHAR szCutname[MAX_PATH]; + + *(lpFilePart-1) = 0; // devide path and name + + // name is current directory -> use only name + if (lstrcmpi(szCurPath,szFilename) == 0) + { + // short name view + } + else + { + // full name view + lpFilePart = ppszFiles[i]; + } + + // cut filename to fit into menu + GetCutPathName(lpFilePart,szCutname,ARRAYSIZEOF(szCutname),36); + lpFilePart = szCutname; + + // adding accelerator key + lpszPtr = szMenuname; + *lpszPtr++ = _T('&'); + *lpszPtr++ = _T('0') + ((i + 1) % 10); + *lpszPtr++ = _T(' '); + + // copy file to view buffer and expand & to && + while (*lpFilePart != 0) + { + if (*lpFilePart == _T('&')) + { + *lpszPtr++ = *lpFilePart; + } + *lpszPtr++ = *lpFilePart++; + } + *lpszPtr = 0; + + VERIFY(InsertMenu(hMruMenu,nMruPos+i, + MF_STRING|MF_BYPOSITION,ID_FILE_MRU_FILE1+i, + szMenuname)); + } + } + } + } + } + } + return; +} + +VOID MruWriteList(VOID) +{ + TCHAR szItemname[32]; + UINT i; + + if (nEntry > 0) + { + // no. of files in MRU list + WriteSettingsInt(_T("MRU"),_T("FileCount"),nEntry); + + for (i = 0; i < nEntry; ++i) // add menu entries + { + _ASSERT(ppszFiles != NULL); // MRU not initialized + wsprintf(szItemname,_T("File%d"),i+1); + if (ppszFiles[i] != NULL) + { + WriteSettingsString(_T("MRU"),szItemname,ppszFiles[i]); + } + else + { + DelSettingsKey(_T("MRU"),szItemname); + } + } + } + return; +} + +VOID MruReadList(VOID) +{ + TCHAR szFilename[MAX_PATH]; + TCHAR szItemname[32]; + UINT i; + + for (i = 0; i < nEntry; ++i) // add menu entries + { + _ASSERT(ppszFiles != NULL); // MRU not initialized + + wsprintf(szItemname,_T("File%d"),i+1); + ReadSettingsString(_T("MRU"),szItemname,_T(""),szFilename,ARRAYSIZEOF(szFilename)); + + if (ppszFiles[i] != NULL) // already filled + { + free(ppszFiles[i]); // free entry + ppszFiles[i] = NULL; // clear last line + } + + if (*szFilename) // read a valid entry + { + ppszFiles[i] = DuplicateString(szFilename); + } + } + return; +} diff --git a/app/src/main/cpp/OPCODES.C b/app/src/main/cpp/OPCODES.C new file mode 100644 index 0000000..c6959d1 --- /dev/null +++ b/app/src/main/cpp/OPCODES.C @@ -0,0 +1,2463 @@ +/* + * opcodes.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "apple.h" +#include "io.h" // I/O register definitions + +#define w Chipset +#define GOYES3 {if(w.carry) o_goyes3(I);else{w.pc+=2;return;}} +#define GOYES5 {if(w.carry) o_goyes5(I);else{w.pc+=2;return;}} + +#if !defined _BIGENDIAN +#define REG(t,r) (*((t*)&(r))) // little endian machine +#else +#define REG(t,r) (*((t*)((BYTE*)&(r)+sizeof(r)-sizeof(t)))) // big endian machine +#endif + +#pragma intrinsic(memset,memcpy) + +#include "Ops.h" + +// Fields start and length +UINT F_s[16] = {0/*P*/,0,2,0,15,3,0,0,0,0,0,0,0,0,0,0}; +UINT F_l[16] = {1,1/*P+1*/,1,3,1,12,2,16,0,0,0,0,0,0,0,5}; + +VOID o00(LPBYTE I) // RTNSXM +{ + w.cycles+=9; + w.pc = rstkpop(); + w.HST |= XM; + return; +} + +VOID o01(LPBYTE I) // RTN +{ + w.cycles+=9; + w.pc = rstkpop(); + return; +} + +VOID o02(LPBYTE I) // RTNSC +{ + w.cycles+=9; + w.pc = rstkpop(); + w.carry = TRUE; + return; +} + +VOID o03(LPBYTE I) // RTNCC +{ + w.cycles+=9; + w.pc = rstkpop(); + w.carry = FALSE; + return; +} + +VOID o04(LPBYTE I) // SETHEX +{ + w.cycles+=3; + w.pc+=2; + w.mode_dec = FALSE; + return; +} + +VOID o05(LPBYTE I) // SETDEC +{ + w.cycles+=3; + w.pc+=2; + w.mode_dec = TRUE; + return; +} + +VOID o06(LPBYTE I) // RSTK=C +{ + w.cycles+=8; + w.pc+=2; + rstkpush(Npack(w.C,5)); + return; +} + +VOID o07(LPBYTE I) // C=RSTK +{ + w.cycles+=8; + w.pc+=2; + Nunpack(w.C,rstkpop(),5); + return; +} + +VOID o08(LPBYTE I) // CLRST +{ + w.cycles+=5; + w.pc+=2; + memset(w.ST, 0, 3); + return; +} + +VOID o09(LPBYTE I) // C=ST +{ + w.cycles+=5; + w.pc+=2; + memcpy(w.C, w.ST, 3); + return; +} + +VOID o0A(LPBYTE I) // ST=C +{ + w.cycles+=5; + w.pc+=2; + memcpy(w.ST, w.C, 3); + return; +} + +VOID o0B(LPBYTE I) // CSTEX +{ + w.cycles+=5; + w.pc+=2; + Nxchg(w.C, w.ST, 3); + return; +} + +VOID o0C(LPBYTE I) // P=P+1 +{ + w.cycles+=3; + w.pc+=2; + if (w.P<15) + { + w.P++; + w.carry=FALSE; + } + else + { + w.P=0; + w.carry=TRUE; + } + PCHANGED; + return; +} + +VOID o0D(LPBYTE I) // P=P-1 +{ + w.cycles+=3; + w.pc+=2; + if (w.P) + { + w.P--; + w.carry=FALSE; + } + else + { + w.P=0xF; + w.carry=TRUE; + } + PCHANGED; + return; +} + +VOID o0Ef0(LPBYTE I) // A=A&B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.A,w.B,I[2]); + return; +} +VOID o0Ef1(LPBYTE I) // B=B&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.B,w.C,I[2]); + return; +} +VOID o0Ef2(LPBYTE I) // C=C&A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.A,I[2]); + return; +} +VOID o0Ef3(LPBYTE I) // D=D&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.D,w.C,I[2]); + return; +} +VOID o0Ef4(LPBYTE I) // B=B&A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.B,w.A,I[2]); + return; +} +VOID o0Ef5(LPBYTE I) // C=C&B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.B,I[2]); + return; +} +VOID o0Ef6(LPBYTE I) // A=A&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.A,w.C,I[2]); + return; +} +VOID o0Ef7(LPBYTE I) // C=C&D f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.D,I[2]); + return; +} + +VOID o0Ef8(LPBYTE I) // A=A!B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.A,w.B,I[2]); + return; +} +VOID o0Ef9(LPBYTE I) // B=B!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.B,w.C,I[2]); + return; +} +VOID o0EfA(LPBYTE I) // C=C!A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.A,I[2]); + return; +} +VOID o0EfB(LPBYTE I) // D=D!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.D,w.C,I[2]); + return; +} +VOID o0EfC(LPBYTE I) // B=B!A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.B,w.A,I[2]); + return; +} +VOID o0EfD(LPBYTE I) // C=C!B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.B,I[2]); + return; +} +VOID o0EfE(LPBYTE I) // A=A!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.A,w.C,I[2]); + return; +} +VOID o0EfF(LPBYTE I) // C=C!D f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.D,I[2]); + return; +} + +VOID o0F(LPBYTE I) // RTI +{ + w.cycles+=9; + w.pc = rstkpop(); + w.inte = TRUE; // enable interrupt + + if ((w.intd && w.intk) || w.IR15X) // keyboard interrupt pending + { + w.intd = FALSE; // no keyboard interrupt pending any more + INTERRUPT; // restart interrupt handler + } + + // low interrupt lines + { + BOOL bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + BOOL bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + } + + if (!bNINT2 || !bNINT) // NINT2 or NINT interrupt line low + INTERRUPT; // restart interrupt handler + } + + // restart interrupt handler when timer interrupt + if (w.IORam[TIMER1_CTRL]&INTR) // INT bit of timer1 is set + ReadT1(); // check for int + + if (w.IORam[TIMER2_CTRL]&INTR) // INT bit of timer2 is set + ReadT2(); // check for int + return; +} + +VOID o100(LPBYTE I) // R0=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R0, w.A, 16); + return; +} + +VOID o101(LPBYTE I) // R1=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R1, w.A, 16); + return; +} + +VOID o102(LPBYTE I) // R2=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R2, w.A, 16); + return; +} + +VOID o103(LPBYTE I) // R3=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R3, w.A, 16); + return; +} + +VOID o104(LPBYTE I) // R4=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R4, w.A, 16); + return; +} + +VOID o108(LPBYTE I) // R0=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R0, w.C, 16); + return; +} + +VOID o109(LPBYTE I) // R1=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R1, w.C, 16); + return; +} + +VOID o10A(LPBYTE I) // R2=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R2, w.C, 16); + return; +} + +VOID o10B(LPBYTE I) // R3=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R3, w.C, 16); + return; +} + +VOID o10C(LPBYTE I) // R4=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R4, w.C, 16); + return; +} + +VOID o110(LPBYTE I) // A=R0 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R0, 16); + return; +} + +VOID o111(LPBYTE I) // A=R1 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R1, 16); + return; +} + +VOID o112(LPBYTE I) // A=R2 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R2, 16); + return; +} + +VOID o113(LPBYTE I) // A=R3 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R3, 16); + return; +} + +VOID o114(LPBYTE I) // A=R4 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R4, 16); + return; +} + +VOID o118(LPBYTE I) // C=R0 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R0, 16); + return; +} + +VOID o119(LPBYTE I) // C=R1 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R1, 16); + return; +} + +VOID o11A(LPBYTE I) // C=R2 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R2, 16); + return; +} + +VOID o11B(LPBYTE I) // C=R3 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R3, 16); + return; +} + +VOID o11C(LPBYTE I) // C=R4 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R4, 16); + return; +} + +VOID o120(LPBYTE I) // AR0EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R0, 16); + return; +} + +VOID o121(LPBYTE I) // AR1EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R1, 16); + return; +} + +VOID o122(LPBYTE I) // AR2EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R2, 16); + return; +} + +VOID o123(LPBYTE I) // AR3EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R3, 16); + return; +} + +VOID o124(LPBYTE I) // AR4EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R4, 16); + return; +} + +VOID o128(LPBYTE I) // CR0EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R0, 16); + return; +} + +VOID o129(LPBYTE I) // CR1EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R1, 16); + return; +} + +VOID o12A(LPBYTE I) // CR2EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R2, 16); + return; +} + +VOID o12B(LPBYTE I) // CR3EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R3, 16); + return; +} + +VOID o12C(LPBYTE I) // CR4EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R4, 16); + return; +} + +VOID o130(LPBYTE I) // D0=A +{ + w.cycles+=8; + w.pc+=3; + w.d0=Npack(w.A,5); + return; +} + +VOID o131(LPBYTE I) // D1=A +{ + w.cycles+=8; + w.pc+=3; + w.d1=Npack(w.A,5); + return; +} + +VOID o132(LPBYTE I) // AD0EX +{ + DWORD d = w.d0; + w.d0=Npack(w.A,5); + Nunpack(w.A,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o133(LPBYTE I) // AD1EX +{ + DWORD d=w.d1; + w.d1=Npack(w.A,5); + Nunpack(w.A,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o134(LPBYTE I) // D0=C +{ + w.cycles+=8; + w.pc+=3; + w.d0=Npack(w.C,5); + return; +} + +VOID o135(LPBYTE I) // D1=C +{ + w.cycles+=8; + w.pc+=3; + w.d1=Npack(w.C,5); + return; +} + +VOID o136(LPBYTE I) // CD0EX +{ + DWORD d=w.d0; + w.d0=Npack(w.C,5); + Nunpack(w.C,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o137(LPBYTE I) // CD1EX +{ + DWORD d=w.d1; + w.d1=Npack(w.C,5); + Nunpack(w.C,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o138(LPBYTE I) // D0=AS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d0)=(WORD)Npack(w.A,4); + return; +} + +VOID o139(LPBYTE I) // D1=AS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d1)=(WORD)Npack(w.A,4); + return; +} + +VOID o13A(LPBYTE I) // AD0XS +{ + DWORD d=w.d0; + REG(WORD,w.d0)=(WORD)Npack(w.A,4); + Nunpack(w.A,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13B(LPBYTE I) // AD1XS +{ + DWORD d=w.d1; + REG(WORD,w.d1)=(WORD)Npack(w.A,4); + Nunpack(w.A,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13C(LPBYTE I) // D0=CS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d0)=(WORD)Npack(w.C,4); + return; +} + +VOID o13D(LPBYTE I) // D1=CS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d1)=(WORD)Npack(w.C,4); + return; +} + +VOID o13E(LPBYTE I) // CD0XS +{ + DWORD d=w.d0; + REG(WORD,w.d0)=(WORD)Npack(w.C,4); + Nunpack(w.C,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13F(LPBYTE I) // CD1XS +{ + DWORD d=w.d1; + REG(WORD,w.d1)=(WORD)Npack(w.C,4); + Nunpack(w.C,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o140(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.A, w.d0, 5); return; } // DAT0=A A +VOID o141(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.A, w.d1, 5); return; } // DAT1=A A +VOID o144(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.C, w.d0, 5); return; } // DAT0=C A +VOID o145(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.C, w.d1, 5); return; } // DAT1=C A +VOID o148(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.A, w.d0, 2); return; } // DAT0=A B +VOID o149(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.A, w.d1, 2); return; } // DAT1=A B +VOID o14C(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.C, w.d0, 2); return; } // DAT0=C B +VOID o14D(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.C, w.d1, 2); return; } // DAT1=C B + +VOID o142(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.A, w.d0, 5); return; } // A=DAT0 A +VOID o143(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.A, w.d1, 5); return; } // A=DAT1 A +VOID o146(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.C, w.d0, 5); return; } // C=DAT0 A +VOID o147(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.C, w.d1, 5); return; } // C=DAT1 A +VOID o14A(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.A, w.d0, 2); return; } // A=DAT0 B +VOID o14B(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.A, w.d1, 2); return; } // A=DAT1 B +VOID o14E(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.C, w.d0, 2); return; } // C=DAT0 B +VOID o14F(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.C, w.d1, 2); return; } // C=DAT0 B + +VOID o150a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.A, w.d0, I[3]); return; } // DAT0=A a +VOID o151a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.A, w.d1, I[3]); return; } // DAT1=A a +VOID o154a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.C, w.d0, I[3]); return; } // DAT0=C a +VOID o155a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.C, w.d1, I[3]); return; } // DAT1=C a +VOID o152a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.A, w.d0, I[3]); return; } // A=DAT0 a +VOID o153a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.A, w.d1, I[3]); return; } // A=DAT1 a +VOID o156a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.C, w.d0, I[3]); return; } // C=DAT0 a +VOID o157a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.C, w.d1, I[3]); return; } // C=DAT1 a + +VOID o158x(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.A, w.d0, I[3]+1); return; } // DAT0=A x +VOID o159x(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.A, w.d1, I[3]+1); return; } // DAT1=A x +VOID o15Cx(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.C, w.d0, I[3]+1); return; } // DAT0=C x +VOID o15Dx(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.C, w.d1, I[3]+1); return; } // DAT1=C x +VOID o15Ax(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.A, w.d0, I[3]+1); return; } // A=DAT0 x +VOID o15Bx(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.A, w.d1, I[3]+1); return; } // A=DAT1 x +VOID o15Ex(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.C, w.d0, I[3]+1); return; } // C=DAT0 x +VOID o15Fx(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.C, w.d1, I[3]+1); return; } // C=DAT1 x + +VOID o16x(LPBYTE I) // D0=D0+ (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d0+=I[2]+1; + if (w.d0>0xfffff) + { + w.d0&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o17x(LPBYTE I) // D1=D1+ (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d1+=I[2]+1; + if (w.d1>0xfffff) + { + w.d1&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o18x(LPBYTE I) // D0=D0- (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d0-=I[2]+1; + if (w.d0>0xfffff) + { + w.d0&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o19d2(LPBYTE I) // D0=(2) #dd +{ + w.cycles+=4; + w.pc+=4; + REG(BYTE,w.d0)=(BYTE)Npack(I+2,2); + return; +} + +VOID o1Ad4(LPBYTE I) // D0=(4) #dddd +{ + w.cycles+=6; + w.pc+=6; + REG(WORD,w.d0)=(WORD)Npack(I+2,4); + return; +} + +VOID o1Bd5(LPBYTE I) // D0=(5) #ddddd +{ + w.cycles+=7; + w.pc+=7; + w.d0=Npack(I+2,5); + return; +} + +VOID o1Cx(LPBYTE I) // D1=D1- (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d1-=I[2]+1; + if (w.d1>0xfffff) + { + w.d1&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o1Dd2(LPBYTE I) // D1=(2) #dd +{ + w.cycles+=4; + w.pc+=4; + REG(BYTE,w.d1)=(BYTE)Npack(I+2,2); + return; +} + +VOID o1Ed4(LPBYTE I) // D1=(4) #dddd +{ + w.cycles+=6; + w.pc+=6; + REG(WORD,w.d1)=(WORD)Npack(I+2,4); + return; +} + +VOID o1Fd5(LPBYTE I) // D1=(5) #ddddd +{ + w.cycles+=7; + w.pc+=7; + w.d1=Npack(I+2,5); + return; +} + +VOID o2n(LPBYTE I) // P= n +{ + w.cycles+=2; + w.pc+=2; + w.P=I[1]; + PCHANGED; + return; +} + +VOID o3X(LPBYTE I) // LCHEX +{ + UINT n=I[1]+1; + UINT d=16-w.P; + w.cycles+=3+I[1]; + w.pc+=2; + I+=2; // UNSAFE + if (n<=d) + { + memcpy(w.C+w.P,I,n); + } + else + { + memcpy(w.C+w.P,I,d); + memcpy(w.C,I+d,n-d); + } + w.pc+=n; + return; +} + +VOID o4d2(LPBYTE I) // GOC #dd +{ + if (!w.carry) + { + w.cycles+=3; + w.pc+=3; + } + else + { + signed char jmp=I[1]+(I[2]<<4); + if (jmp) + w.pc+=jmp+1; + else + w.pc=rstkpop(); + w.cycles+=10; + w.pc&=0xFFFFF; + } + return; +} + +VOID o5d2(LPBYTE I) // GONC +{ + if (w.carry) + { + w.cycles+=3; + w.pc+=3; + } + else + { + signed char jmp=I[1]+(I[2]<<4); + if (jmp) + w.pc+=jmp+1; + else + w.pc=rstkpop(); + w.cycles+=10; + w.pc&=0xFFFFF; + } + return; +} + +VOID o6d3(LPBYTE I) // GOTO +{ + DWORD d=Npack(I+1,3); + if (d&0x800) + w.pc-=0xFFF-d; + else + w.pc+=d+1; + w.cycles+=11; + w.pc&=0xFFFFF; + return; +} + +VOID o7d3(LPBYTE I) // GOSUB +{ + DWORD d=Npack(I+1,3); + rstkpush(w.pc+4); + if (d&0x800) w.pc-=0xFFC-d; else w.pc+=d+4; + w.cycles+=12; + w.pc&=0xFFFFF; + return; +} + +VOID o800(LPBYTE I) // OUT=CS +{ + w.cycles+=4; + w.pc+=3; + w.out = (w.out&0xff0) | w.C[0]; + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + return; +} + +VOID o801(LPBYTE I) // OUT=C +{ + WORD wOut; + w.cycles+=6; + w.pc+=3; + wOut = (WORD) Npack(w.C, 3); + if (((wOut ^ w.out) & 0x800) != 0) // beeper bit OR[11] changed + SoundOut(&w,wOut); + w.out = wOut; + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + return; +} + +VOID o802(LPBYTE I) // A=IN +{ + w.cycles+=7; + // emulate Clarke/Yorke bug + if ((w.pc & 1) == 0 || MapData(w.pc) == M_IO) + w.pc+=3; + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + IOBit(0x19,8,w.in != 0); // update KDN bit in the SRQ register + Nunpack(w.A, w.in, 4); + return; +} + +VOID o803(LPBYTE I) // C=IN +{ + w.cycles+=7; + // emulate Clarke/Yorke bug + if ((w.pc & 1) == 0 || MapData(w.pc) == M_IO) + w.pc+=3; + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + IOBit(0x19,8,w.in != 0); // update KDN bit in the SRQ register + Nunpack(w.C, w.in, 4); + return; +} + +VOID o804(LPBYTE I) // UNCNFG +{ + w.cycles+=12; + w.pc+=3; + Uncnfg(); + return; +} + +VOID o805(LPBYTE I) // CONFIG +{ + w.cycles+=11; + w.pc+=3; + Config(); + return; +} + +VOID o806(LPBYTE I) // C=ID +{ + w.cycles+=11; + w.pc+=3; + C_Eq_Id(); + return; +} + +VOID o807(LPBYTE I) // SHUTDN +{ + BOOL bShutdn = TRUE; // shut down + + // only shut down when no timer wake up + if (w.IORam[TIMER1_CTRL]&WKE) // WKE bit of timer1 is set + { + if (ReadT1()&0x08) // and MSB of timer1 is set + { + w.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.IORam[TIMER2_CTRL]&WKE) // WKE bit of timer2 is set + { + if (ReadT2()&0x80000000) // and MSB of timer2 is set + { + w.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + // out register going low during shutdown, so normal keys produce a rising + // edge trigger when out register going high again. Because the ON key is not + // connected to the out register, the rising edge trigger must be done manually. + if ((w.in & 0x7FFF) == 0 && bShutdn) // shut down only when enabled + { + w.Shutdn = TRUE; // set mode before exit emulation loop + bInterrupt = TRUE; + + // emulation of BS reset circuit in deep sleep + // HP39/40G, HP48GX, HP49G, display off, card control off or in slow mode + if ( (cCurrentRomType=='E' || cCurrentRomType=='G' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') // CdB for HP: add apples + && (w.IORam[BITOFFSET]&DON) == 0 + && ((w.IORam[CARDCTL]&(ECDT|RCDT)) != (ECDT|RCDT))) + { + // on HP48GX ROM must be selected (DA19=1) and + // the NOT MA18 address line must be low (test for high) + // else we get power on VCO from the ROM chip + // (MA18 input pin security diode to VCC pin) + if ( cCurrentRomType!='G' + || ((w.IORam[LINECOUNT+1]&DA19) && (w.pc & 0x80000))) + { + w.Bank_FF = 0; // reset bank switcher FF + RomSwitch(w.Bank_FF); // force new mapping + } + } + } + w.cycles+=6; + w.pc+=3; + return; +} + +VOID o8080(LPBYTE I) // INTON +{ + w.cycles+=5; + w.pc+=4; + w.intk = TRUE; + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + if (w.intd || w.IR15X) // keyboard interrupt pending + { + w.intd = FALSE; // no keyboard interrupt pending any more + INTERRUPT; // restart interrupt handler + } + return; +} + +VOID o80810(LPBYTE I) // RSI +{ + w.cycles+=6; + w.pc+=5; + ScanKeyboard(TRUE,TRUE); // one input bit high (direct)? + + // enable KDN update + w.dwKdnCycles = (DWORD) (w.cycles & 0xFFFFFFFF) - (DWORD) T2CYCLES * 16; + + if (w.in && w.inte == FALSE) // key interrupt pending + w.intd = TRUE; // keyboard interrupt pending + return; +} + +VOID o8082X(LPBYTE I) // LA +{ + UINT n=I[4]+1; + UINT d=16-w.P; + w.cycles+=6+I[4]; + w.pc+=5+n; + I+=5; // UNSAFE + if (n<=d) + { + memcpy(w.A+w.P,I,n); + } + else + { + memcpy(w.A+w.P,I,d); + memcpy(w.A,I+d,n-d); + } + return; +} + +VOID o8083(LPBYTE I) // BUSCB +{ + w.cycles+=7; + w.pc+=4; + // emulated as NOP + // InfoMessage(_T("BUSCB instruction executed.")); + return; +} + +VOID o8084n(LPBYTE I) // ABIT=0 n +{ + w.cycles+=6; + w.pc+=5; + Nbit0(w.A, I[4]); + return; +} + +VOID o8085n(LPBYTE I) // ABIT=1 n +{ + w.cycles+=6; + w.pc+=5; + Nbit1(w.A, I[4]); + return; +} + +VOID o8086n(LPBYTE I) // ?ABIT=0 n +{ + w.cycles+=9; + w.pc+=5; + Tbit0(w.A, I[4]); + GOYES5; +} + +VOID o8087n(LPBYTE I) // ?ABIT=1 n +{ + w.cycles+=9; + w.pc+=5; + Tbit1(w.A, I[4]); + GOYES5; +} + +VOID o8088n(LPBYTE I) // CBIT=0 n +{ + w.cycles+=6; + w.pc+=5; + Nbit0(w.C, I[4]); + return; +} + +VOID o8089n(LPBYTE I) // CBIT=1 n +{ + w.cycles+=6; + w.pc+=5; + Nbit1(w.C, I[4]); + return; +} + +VOID o808An(LPBYTE I) // ?CBIT=0 n +{ + w.cycles+=9; + w.pc+=5; + Tbit0(w.C, I[4]); + GOYES5; +} + +VOID o808Bn(LPBYTE I) // ?CBIT=1 n +{ + w.cycles+=9; + w.pc+=5; + Tbit1(w.C, I[4]); + GOYES5; +} + +VOID o808C(LPBYTE I) // PC=(A) +{ + BYTE p[5]; + w.cycles+=23; + Nread(p,Npack(w.A,5),5); // read (A) and update CRC + w.pc=Npack(p,5); + return; +} + +VOID o808D(LPBYTE I) // BUSCD +{ + w.cycles+=7; + w.pc+=4; + // emulated as NOP + // InfoMessage(_T("BUSCD instruction executed.")); + return; +} + +VOID o808E(LPBYTE I) // PC=(C) +{ + BYTE p[5]; + w.cycles+=23; + Nread(p,Npack(w.C,5),5); // read (C) and update CRC + w.pc=Npack(p,5); + return; +} + +VOID o808F(LPBYTE I) // INTOFF +{ + w.cycles+=5; + w.pc+=4; + UpdateKdnBit(); // update KDN bit + w.intk = FALSE; + return; +} + +VOID o809(LPBYTE I) // C+P+1 - HEX MODE +{ + w.cycles+=8; + w.pc+=3; + w.C[0]+=w.P; Nincx(w.C,5); + return; +} + +VOID o80A(LPBYTE I) // RESET +{ + w.cycles+=6; + w.pc+=3; + Reset(); + return; +} + +VOID o80B(LPBYTE I) // BUSCC +{ + w.cycles+=6; + w.pc+=3; + // emulated as NOP + // InfoMessage(_T("BUSCC instruction executed.")); + if (cCurrentRomType=='Q' || cCurrentRomType=='2' || cCurrentRomType=='P') + { + o80BExt(I); // Saturnator extentions + } + return; +} + +VOID o80Cn(LPBYTE I) // C=P n +{ + w.cycles+=6; + w.pc+=4; + w.C[I[3]] = w.P; + return; +} + +VOID o80Dn(LPBYTE I) // P=C n +{ + w.cycles+=6; + w.pc+=4; + w.P = w.C[I[3]]; + PCHANGED; + return; +} + +VOID o80E(LPBYTE I) // SREQ? +{ + w.cycles+=7; + w.pc+=3; + w.C[0]=0; // no device + return; +} + +VOID o80Fn(LPBYTE I) // CPEX n +{ + BYTE n = w.P; + w.P = w.C[I[3]]; + w.C[I[3]] = n; + PCHANGED; + w.cycles+=6; + w.pc+=4; + return; +} + +VOID o810(LPBYTE I) // ASLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.A, 16); + return; +} + +VOID o811(LPBYTE I) // BSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.B, 16); + return; +} + +VOID o812(LPBYTE I) // CSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.C, 16); + return; +} + +VOID o813(LPBYTE I) // DSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.D, 16); + return; +} + +VOID o814(LPBYTE I) // ASRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.A, 16); + return; +} + +VOID o815(LPBYTE I) // BSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.B, 16); + return; +} + +VOID o816(LPBYTE I) // CSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.C, 16); + return; +} + +VOID o817(LPBYTE I) // DSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.D, 16); + return; +} + +VOID o818f0x(LPBYTE I) // A=A+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.A[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.A,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f1x(LPBYTE I) // B=B+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.B[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.B,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f2x(LPBYTE I) // C=C+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.C[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.C,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f3x(LPBYTE I) // D=D+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.D[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.D,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f8x(LPBYTE I) // A=A-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.A[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.A,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818f9x(LPBYTE I) // B=B-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.B[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.B,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818fAx(LPBYTE I) // C=C-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.C[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.C,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818fBx(LPBYTE I) // D=D-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.D[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.D,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o819f0(LPBYTE I) // ASRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.A, I[3]); + return; +} + +VOID o819f1(LPBYTE I) // BSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.B, I[3]); + return; +} + +VOID o819f2(LPBYTE I) // CSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.C, I[3]); + return; +} + +VOID o819f3(LPBYTE I) // DSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.D, I[3]); + return; +} + +VOID o81Af00(LPBYTE I) // R0=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R0, w.A, I[3]); + return; +} + +VOID o81Af01(LPBYTE I) // R1=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R1, w.A, I[3]); + return; +} + +VOID o81Af02(LPBYTE I) // R2=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R2, w.A, I[3]); + return; +} + +VOID o81Af03(LPBYTE I) // R3=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R3, w.A, I[3]); + return; +} + +VOID o81Af04(LPBYTE I) // R4=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R4, w.A, I[3]); + return; +} + +VOID o81Af08(LPBYTE I) // R0=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R0, w.C, I[3]); + return; +} + +VOID o81Af09(LPBYTE I) // R1=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R1, w.C, I[3]); + return; +} + +VOID o81Af0A(LPBYTE I) // R2=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R2, w.C, I[3]); + return; +} + +VOID o81Af0B(LPBYTE I) // R3=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R3, w.C, I[3]); + return; +} + +VOID o81Af0C(LPBYTE I) // R4=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R4, w.C, I[3]); + return; +} + +VOID o81Af10(LPBYTE I) // A=R0.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R0, I[3]); + return; +} + +VOID o81Af11(LPBYTE I) // A=R1.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R1, I[3]); + return; +} + +VOID o81Af12(LPBYTE I) // A=R2.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R2, I[3]); + return; +} + +VOID o81Af13(LPBYTE I) // A=R3.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R3, I[3]); + return; +} + +VOID o81Af14(LPBYTE I) // A=R4.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R4, I[3]); + return; +} + +VOID o81Af18(LPBYTE I) // C=R0.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R0, I[3]); + return; +} + +VOID o81Af19(LPBYTE I) // C=R1.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R1, I[3]); + return; +} + +VOID o81Af1A(LPBYTE I) // C=R2.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R2, I[3]); + return; +} + +VOID o81Af1B(LPBYTE I) // C=R3.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R3, I[3]); + return; +} + +VOID o81Af1C(LPBYTE I) // C=R4.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R4, I[3]); + return; +} + +VOID o81Af20(LPBYTE I) // AR0EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R0, I[3]); + return; +} + +VOID o81Af21(LPBYTE I) // AR1EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R1, I[3]); + return; +} + +VOID o81Af22(LPBYTE I) // AR2EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R2, I[3]); + return; +} + +VOID o81Af23(LPBYTE I) // AR3EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R3, I[3]); + return; +} + +VOID o81Af24(LPBYTE I) // AR4EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R4, I[3]); + return; +} + +VOID o81Af28(LPBYTE I) // CR0EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R0, I[3]); + return; +} + +VOID o81Af29(LPBYTE I) // CR1EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R1, I[3]); + return; +} + +VOID o81Af2A(LPBYTE I) // CR2EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R2, I[3]); + return; +} + +VOID o81Af2B(LPBYTE I) // CR3EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R3, I[3]); + return; +} + +VOID o81Af2C(LPBYTE I) // CR4EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R4, I[3]); + return; +} + +VOID o81B2(LPBYTE I) // PC=A +{ + w.cycles+=16; + w.pc = Npack(w.A,5); + return; +} + +VOID o81B3(LPBYTE I) // PC=C +{ + w.cycles+=16; + w.pc = Npack(w.C,5); + return; +} + +VOID o81B4(LPBYTE I) // A=PC +{ + w.cycles+=9; + w.pc+=4; + Nunpack(w.A,w.pc,5); + return; +} + +VOID o81B5(LPBYTE I) // C=PC +{ + w.cycles+=9; + w.pc+=4; + Nunpack(w.C,w.pc,5); + return; +} + +VOID o81B6(LPBYTE I) // APCEX +{ + DWORD d=w.pc+4; + w.cycles+=16; + w.pc=Npack(w.A,5); + Nunpack(w.A,d,5); + return; +} + +VOID o81B7(LPBYTE I) // CPCEX +{ + DWORD d=w.pc+4; + w.cycles+=16; + w.pc=Npack(w.C,5); + Nunpack(w.C,d,5); + return; +} + +VOID o81C(LPBYTE I) // ASRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.A, 16); + return; +} + +VOID o81D(LPBYTE I) // BSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.B, 16); + return; +} + +VOID o81E(LPBYTE I) // CSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.C, 16); + return; +} + +VOID o81F(LPBYTE I) // DSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.D, 16); + return; +} + +VOID o82n(LPBYTE I) // HST=0 m +{ + w.cycles+=3; + w.pc+=3; + w.HST&=~I[2]; + return; +} + +VOID o83n(LPBYTE I) // ?HST=0 m +{ + w.cycles+=6; + w.pc+=3; + w.carry=((w.HST&I[2])==0); + GOYES3; +} + +VOID o84n(LPBYTE I) // ST=0 n +{ + w.cycles+=4; + w.pc+=3; + Nbit0(w.ST, I[2]); + return; +} + +VOID o85n(LPBYTE I) // ST=1 n +{ + w.cycles+=4; + w.pc+=3; + Nbit1(w.ST, I[2]); + return; +} + +VOID o86n(LPBYTE I) // ?ST=0 n +{ + w.cycles+=7; + w.pc+=3; + Tbit0(w.ST, I[2]); + GOYES3; +} + +VOID o87n(LPBYTE I) // ?ST=1 n +{ + w.cycles+=7; + w.pc+=3; + Tbit1(w.ST, I[2]); + GOYES3; +} + +VOID o88n(LPBYTE I) // ?P# n +{ + w.cycles+=6; + w.pc+=3; + w.carry=(w.P!=I[2]); + GOYES3; +} + +VOID o89n(LPBYTE I) // ?P= n +{ + w.cycles+=6; + w.pc+=3; + w.carry=(w.P==I[2]); + GOYES3; +} + +VOID o8A0(LPBYTE I) // ?A=B A +{ + w.cycles+=11; + w.pc+=3; + Te(w.A, w.B, 5); + GOYES3; +} + +VOID o8A1(LPBYTE I) // ?B=C A +{ + w.cycles+=11; + w.pc+=3; + Te(w.B, w.C, 5); + GOYES3; +} + +VOID o8A2(LPBYTE I) // ?C=A A +{ + w.cycles+=11; + w.pc+=3; + Te(w.C, w.A, 5); + GOYES3; +} + +VOID o8A3(LPBYTE I) // ?D=C A +{ + w.cycles+=11; + w.pc+=3; + Te(w.D, w.C, 5); + GOYES3; +} + +VOID o8A4(LPBYTE I) // ?A#B A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.A, w.B, 5); + GOYES3; +} + +VOID o8A5(LPBYTE I) // ?B#C A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.B, w.C, 5); + GOYES3; +} + +VOID o8A6(LPBYTE I) // ?C#A A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.C, w.A, 5); + GOYES3; +} + +VOID o8A7(LPBYTE I) // ?D#C A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.D, w.C, 5); + GOYES3; +} + +VOID o8A8(LPBYTE I) // ?A=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.A, 5); + GOYES3; +} + +VOID o8A9(LPBYTE I) // ?B=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.B, 5); + GOYES3; +} + +VOID o8AA(LPBYTE I) // ?C=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.C, 5); + GOYES3; +} + +VOID o8AB(LPBYTE I) // ?D=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.D, 5); + GOYES3; +} + +VOID o8AC(LPBYTE I) // ?A#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.A, 5); + GOYES3; +} + +VOID o8AD(LPBYTE I) // ?B#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.B, 5); + GOYES3; +} + +VOID o8AE(LPBYTE I) // ?C#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.C, 5); + GOYES3; +} + +VOID o8AF(LPBYTE I) // ?D#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.D, 5); + GOYES3; +} + +VOID o8B0(LPBYTE I) // ?A>B A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.A, w.B, 5); + GOYES3; +} + +VOID o8B1(LPBYTE I) // ?B>C A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.B, w.C, 5); + GOYES3; +} + +VOID o8B2(LPBYTE I) // ?C>A A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.C, w.A, 5); + GOYES3; +} + +VOID o8B3(LPBYTE I) // ?D>C A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.D, w.C, 5); + GOYES3; +} + +VOID o8B4(LPBYTE I) // ?A=B A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.A, w.B, 5); + GOYES3; +} + +VOID o8B9(LPBYTE I) // ?B>=C A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.B, w.C, 5); + GOYES3; +} + +VOID o8BA(LPBYTE I) // ?C>=A A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.C, w.A, 5); + GOYES3; +} + +VOID o8BB(LPBYTE I) // ?D>=C A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.D, w.C, 5); + GOYES3; +} + +VOID o8BC(LPBYTE I) // ?A<=B A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.A, w.B, 5); + GOYES3; +} + +VOID o8BD(LPBYTE I) // ?B<=C A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.B, w.C, 5); + GOYES3; +} + +VOID o8BE(LPBYTE I) // ?C<=A A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.C, w.A, 5); + GOYES3; +} + +VOID o8BF(LPBYTE I) // ?D<=C A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.D, w.C, 5); + GOYES3; +} + +VOID o8Cd4(LPBYTE I) // GOLONG #dddd +{ + DWORD d=Npack(I+2, 4); + if (d&0x8000) w.pc -= 0xfffe - d; else w.pc+= d + 2; + w.cycles+=14; + w.pc&=0xFFFFF; + return; +} + +VOID o8Dd5(LPBYTE I) // GOVLNG #ddddd +{ + w.cycles+=14; + w.pc = Npack(I+2, 5); + return; +} + +VOID o8Ed4(LPBYTE I) // GOSUBL #dddd +{ + DWORD d=Npack(I+2,4); + rstkpush(w.pc+6); + if (d&0x8000) w.pc -= 0xfffa - d; else w.pc += d + 6; + w.cycles+=14; + w.pc&=0xFFFFF; + return; +} + +VOID o8Fd5(LPBYTE I) // GOSBVL #ddddd +{ + w.cycles+=15; + rstkpush(w.pc+7); + w.pc=Npack(I+2, 5); + return; +} + + // ?r=s f +VOID o9a0(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.A, w.B, I[1]); GOYES3; } +VOID o9a1(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.B, w.C, I[1]); GOYES3; } +VOID o9a2(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.C, w.A, I[1]); GOYES3; } +VOID o9a3(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.D, w.C, I[1]); GOYES3; } + + // ?r#s f +VOID o9a4(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.A, w.B, I[1]); GOYES3; } +VOID o9a5(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.B, w.C, I[1]); GOYES3; } +VOID o9a6(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.C, w.A, I[1]); GOYES3; } +VOID o9a7(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.D, w.C, I[1]); GOYES3; } + + // ?r=0 f +VOID o9a8(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.A, I[1]); GOYES3; } +VOID o9a9(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.B, I[1]); GOYES3; } +VOID o9aA(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.C, I[1]); GOYES3; } +VOID o9aB(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.D, I[1]); GOYES3; } + + // ?r#0 f +VOID o9aC(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.A, I[1]); GOYES3; } +VOID o9aD(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.B, I[1]); GOYES3; } +VOID o9aE(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.C, I[1]); GOYES3; } +VOID o9aF(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.D, I[1]); GOYES3; } + + // ?s>r f +VOID o9b0(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.A, w.B, I[1]&7); GOYES3; } +VOID o9b1(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.B, w.C, I[1]&7); GOYES3; } +VOID o9b2(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.C, w.A, I[1]&7); GOYES3; } +VOID o9b3(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.D, w.C, I[1]&7); GOYES3; } + + // ?r=s f +VOID o9b8(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.A, w.B, I[1]&7); GOYES3; } +VOID o9b9(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.B, w.C, I[1]&7); GOYES3; } +VOID o9bA(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.C, w.A, I[1]&7); GOYES3; } +VOID o9bB(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.D, w.C, I[1]&7); GOYES3; } + + // ?r<=s f +VOID o9bC(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.A, w.B, I[1]&7); GOYES3; } +VOID o9bD(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.B, w.C, I[1]&7); GOYES3; } +VOID o9bE(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.C, w.A, I[1]&7); GOYES3; } +VOID o9bF(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.D, w.C, I[1]&7); GOYES3; } + + // r=r+s f +VOID oAa0(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.B, I[1]); return; } +VOID oAa1(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.C, I[1]); return; } +VOID oAa2(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.A, I[1]); return; } +VOID oAa3(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.D, w.C, I[1]); return; } + + // r=r+r f +VOID oAa4(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.A, I[1]); return; } +VOID oAa5(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.B, I[1]); return; } +VOID oAa6(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.C, I[1]); return; } +VOID oAa7(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.D, w.D, I[1]); return; } + + // s=s+r f +VOID oAa8(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.A, I[1]); return; } +VOID oAa9(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.B, I[1]); return; } +VOID oAaA(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.C, I[1]); return; } +VOID oAaB(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.D, I[1]); return; } + + // r=r-1 f +VOID oAaC(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.A, I[1]); return; } +VOID oAaD(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.B, I[1]); return; } +VOID oAaE(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.C, I[1]); return; } +VOID oAaF(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.D, I[1]); return; } + + // r=0 f +VOID oAb0(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.A, I[1]&7); return; } +VOID oAb1(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.B, I[1]&7); return; } +VOID oAb2(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.C, I[1]&7); return; } +VOID oAb3(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.D, I[1]&7); return; } + + // r=s f +VOID oAb4(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.A, w.B, I[1]&7); return; } +VOID oAb5(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.B, w.C, I[1]&7); return; } +VOID oAb6(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.A, I[1]&7); return; } +VOID oAb7(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.D, w.C, I[1]&7); return; } + + // s=r f +VOID oAb8(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.B, w.A, I[1]&7); return; } +VOID oAb9(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.B, I[1]&7); return; } +VOID oAbA(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.A, w.C, I[1]&7); return; } +VOID oAbB(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.D, I[1]&7); return; } + + // rsEX f +VOID oAbC(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.A, w.B, I[1]&7); return; } +VOID oAbD(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.B, w.C, I[1]&7); return; } +VOID oAbE(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.C, w.A, I[1]&7); return; } +VOID oAbF(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.D, w.C, I[1]&7); return; } + + // r=r-s f +VOID oBa0(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.A, w.B, I[1]); return; } +VOID oBa1(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.B, w.C, I[1]); return; } +VOID oBa2(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.A, I[1]); return; } +VOID oBa3(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.D, w.C, I[1]); return; } + + // r=r+1 f +VOID oBa4(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.A, I[1]); return; } +VOID oBa5(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.B, I[1]); return; } +VOID oBa6(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.C, I[1]); return; } +VOID oBa7(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.D, I[1]); return; } + + // s=s-r f +VOID oBa8(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.B, w.A, I[1]); return; } +VOID oBa9(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.B, I[1]); return; } +VOID oBaA(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.A, w.C, I[1]); return; } +VOID oBaB(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.D, I[1]); return; } + + // r=s-r f +VOID oBaC(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.A, w.B, I[1]); return; } +VOID oBaD(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.B, w.C, I[1]); return; } +VOID oBaE(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.C, w.A, I[1]); return; } +VOID oBaF(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.D, w.C, I[1]); return; } + + // rSL f +VOID oBb0(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.A, I[1]&7); return; } +VOID oBb1(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.B, I[1]&7); return; } +VOID oBb2(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.C, I[1]&7); return; } +VOID oBb3(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.D, I[1]&7); return; } + + // rSR f +VOID oBb4(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.A, I[1]&7); return; } +VOID oBb5(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.B, I[1]&7); return; } +VOID oBb6(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.C, I[1]&7); return; } +VOID oBb7(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.D, I[1]&7); return; } + + // r=-r f +VOID oBb8(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.A, I[1]&7); return; } +VOID oBb9(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.B, I[1]&7); return; } +VOID oBbA(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.C, I[1]&7); return; } +VOID oBbB(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.D, I[1]&7); return; } + + // r=-r-1 f +VOID oBbC(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.A, I[1]&7); return; } +VOID oBbD(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.B, I[1]&7); return; } +VOID oBbE(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.C, I[1]&7); return; } +VOID oBbF(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.D, I[1]&7); return; } + + // r=r+s A +VOID oC0(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.B, 5); return; } +VOID oC1(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.C, 5); return; } +VOID oC2(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.A, 5); return; } +VOID oC3(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.D, w.C, 5); return; } + + // r=r+r A +VOID oC4(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.A, 5); return; } +VOID oC5(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.B, 5); return; } +VOID oC6(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.C, 5); return; } +VOID oC7(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.D, w.D, 5); return; } + + // s=s+r A +VOID oC8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.A, 5); return; } +VOID oC9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.B, 5); return; } +VOID oCA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.C, 5); return; } +VOID oCB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.D, 5); return; } + + // r=r-1 A +VOID oCC(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.A, 5, 0); return; } +VOID oCD(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.B, 5, 0); return; } +VOID oCE(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.C, 5, 0); return; } +VOID oCF(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.D, 5, 0); return; } + + // r=0 A +VOID oD0(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.A, 0, 5); return; } +VOID oD1(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.B, 0, 5); return; } +VOID oD2(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.C, 0, 5); return; } +VOID oD3(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.D, 0, 5); return; } + + // r=s A +VOID oD4(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.A, w.B, 5); return; } +VOID oD5(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.B, w.C, 5); return; } +VOID oD6(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.A, 5); return; } +VOID oD7(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.D, w.C, 5); return; } + + // s=r A +VOID oD8(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.B, w.A, 5); return; } +VOID oD9(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.B, 5); return; } +VOID oDA(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.A, w.C, 5); return; } +VOID oDB(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.D, 5); return; } + + // rsEX +VOID oDC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.A, w.B, 5); return; } +VOID oDD(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.B, w.C, 5); return; } +VOID oDE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.C, w.A, 5); return; } +VOID oDF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.D, w.C, 5); return; } + + // r=r-s A +VOID oE0(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.A, w.B, 5); return; } +VOID oE1(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.B, w.C, 5); return; } +VOID oE2(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.A, 5); return; } +VOID oE3(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.D, w.C, 5); return; } + + // r=r+1 A +VOID oE4(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.A, 5, 0); return; } +VOID oE5(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.B, 5, 0); return; } +VOID oE6(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.C, 5, 0); return; } +VOID oE7(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.D, 5, 0); return; } + + // s=s-r A +VOID oE8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.B, w.A, 5); return; } +VOID oE9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.B, 5); return; } +VOID oEA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.A, w.C, 5); return; } +VOID oEB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.D, 5); return; } + + // r=s-r A +VOID oEC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.A, w.B, 5); return; } +VOID oED(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.B, w.C, 5); return; } +VOID oEE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.C, w.A, 5); return; } +VOID oEF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.D, w.C, 5); return; } + + // rSL A +VOID oF0(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.A, 5); return; } +VOID oF1(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.B, 5); return; } +VOID oF2(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.C, 5); return; } +VOID oF3(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.D, 5); return; } + + // rSR A +VOID oF4(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.A, 5); return; } +VOID oF5(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.B, 5); return; } +VOID oF6(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.C, 5); return; } +VOID oF7(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.D, 5); return; } + + // r=-r A +VOID oF8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.A, 5); return; } +VOID oF9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.B, 5); return; } +VOID oFA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.C, 5); return; } +VOID oFB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.D, 5); return; } + + // r=-r-1 A +VOID oFC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.A, 5); return; } +VOID oFD(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.B, 5); return; } +VOID oFE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.C, 5); return; } +VOID oFF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.D, 5); return; } + +// length is guessed, just skip +VOID o_invalid3(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 3 nibbles + w.pc+=3; + return; +} + +VOID o_invalid4(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 4 nibbles + w.pc+=4; + return; +} + +VOID o_invalid5(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 5 nibbles + w.pc+=5; + return; +} + +VOID o_invalid6(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 6 nibbles + w.pc+=6; + return; +} + +VOID o_goyes3(LPBYTE I) +{ + signed char jmp = I[3]+(I[4]<<4); + w.cycles+=7; + if (jmp) + w.pc=(w.pc+jmp)&0xFFFFF; + else + w.pc=rstkpop(); + return; +} + +VOID o_goyes5(LPBYTE I) +{ + signed char jmp = I[5]+(I[6]<<4); + w.cycles+=7; + if (jmp) + w.pc=(w.pc+jmp)&0xFFFFF; + else + w.pc=rstkpop(); + return; +} + +//////// EXTENSIONS //////// +VOID o81B1(LPBYTE I) // beep patch +{ + if (cCurrentRomType=='Q' || cCurrentRomType=='2' || cCurrentRomType=='P') + { + // The ARM-based calculators use this previously-unused opcode to return to RPL + w.cycles+=48; + o80B00(); // LOOP2 + } + else + { + // Emu48 borrows this opcode for the beep patch on the non-ARM-based calculators + External(&w); // beep patch + PCHANGED; // update field select table + } + return; +} +//////////////////////////// diff --git a/app/src/main/cpp/OPCODES.H b/app/src/main/cpp/OPCODES.H new file mode 100644 index 0000000..949419d --- /dev/null +++ b/app/src/main/cpp/OPCODES.H @@ -0,0 +1,445 @@ +/* + * opcodes.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +#define PCHANGED ((void)(F_s[0]=Chipset.P,F_l[1]=Chipset.P+1)) +#define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE)) + +extern UINT F_s[16]; +extern UINT F_l[16]; + +extern VOID o00(LPBYTE I); // RTNSXM +extern VOID o01(LPBYTE I); // RTN +extern VOID o02(LPBYTE I); // RTNSC +extern VOID o03(LPBYTE I); // RTNCC +extern VOID o04(LPBYTE I); // SETHEX +extern VOID o05(LPBYTE I); // SETDEC +extern VOID o06(LPBYTE I); // RSTK=C +extern VOID o07(LPBYTE I); // C=RSTK +extern VOID o08(LPBYTE I); // CLRST +extern VOID o09(LPBYTE I); // C=ST +extern VOID o0A(LPBYTE I); // ST=C +extern VOID o0B(LPBYTE I); // CSTEX +extern VOID o0C(LPBYTE I); // P=P+1 +extern VOID o0D(LPBYTE I); // P=P-1 +extern VOID o0Ef0(LPBYTE I); // A=A&B f +extern VOID o0Ef1(LPBYTE I); // B=B&C f +extern VOID o0Ef2(LPBYTE I); // C=C&A f +extern VOID o0Ef3(LPBYTE I); // D=D&C f +extern VOID o0Ef4(LPBYTE I); // B=B&A f +extern VOID o0Ef5(LPBYTE I); // C=C&B f +extern VOID o0Ef6(LPBYTE I); // A=A&C f +extern VOID o0Ef7(LPBYTE I); // C=C&D f +extern VOID o0Ef8(LPBYTE I); // A=A!B f +extern VOID o0Ef9(LPBYTE I); // B=B!C f +extern VOID o0EfA(LPBYTE I); // C=C!A f +extern VOID o0EfB(LPBYTE I); // D=D!C f +extern VOID o0EfC(LPBYTE I); // B=B!A f +extern VOID o0EfD(LPBYTE I); // C=C!B f +extern VOID o0EfE(LPBYTE I); // A=A!C f +extern VOID o0EfF(LPBYTE I); // C=C!D f +extern VOID o0F(LPBYTE I); // RTI +extern VOID o100(LPBYTE I); // R0=A W +extern VOID o101(LPBYTE I); // R1=A W +extern VOID o102(LPBYTE I); // R2=A W +extern VOID o103(LPBYTE I); // R3=A W +extern VOID o104(LPBYTE I); // R4=A W +extern VOID o108(LPBYTE I); // R0=C W +extern VOID o109(LPBYTE I); // R1=C W +extern VOID o10A(LPBYTE I); // R2=C W +extern VOID o10B(LPBYTE I); // R3=C W +extern VOID o10C(LPBYTE I); // R4=C W +extern VOID o110(LPBYTE I); // A=R0 W +extern VOID o111(LPBYTE I); // A=R1 W +extern VOID o112(LPBYTE I); // A=R2 W +extern VOID o113(LPBYTE I); // A=R3 W +extern VOID o114(LPBYTE I); // A=R4 W +extern VOID o118(LPBYTE I); // C=R0 W +extern VOID o119(LPBYTE I); // C=R1 W +extern VOID o11A(LPBYTE I); // C=R2 W +extern VOID o11B(LPBYTE I); // C=R3 W +extern VOID o11C(LPBYTE I); // C=R4 W +extern VOID o120(LPBYTE I); // AR0EX W +extern VOID o121(LPBYTE I); // AR1EX W +extern VOID o122(LPBYTE I); // AR2EX W +extern VOID o123(LPBYTE I); // AR3EX W +extern VOID o124(LPBYTE I); // AR4EX W +extern VOID o128(LPBYTE I); // CR0EX W +extern VOID o129(LPBYTE I); // CR1EX W +extern VOID o12A(LPBYTE I); // CR2EX W +extern VOID o12B(LPBYTE I); // CR3EX W +extern VOID o12C(LPBYTE I); // CR4EX W +extern VOID o130(LPBYTE I); // D0=A +extern VOID o131(LPBYTE I); // D1=A +extern VOID o132(LPBYTE I); // AD0EX +extern VOID o133(LPBYTE I); // AD1EX +extern VOID o134(LPBYTE I); // D0=C +extern VOID o135(LPBYTE I); // D1=C +extern VOID o136(LPBYTE I); // CD0EX +extern VOID o137(LPBYTE I); // CD1EX +extern VOID o138(LPBYTE I); // D0=AS +extern VOID o139(LPBYTE I); // D1=AS +extern VOID o13A(LPBYTE I); // AD0XS +extern VOID o13B(LPBYTE I); // AD1XS +extern VOID o13C(LPBYTE I); // D0=CS +extern VOID o13D(LPBYTE I); // D1=CS +extern VOID o13E(LPBYTE I); // CD0XS +extern VOID o13F(LPBYTE I); // CD1XS +extern VOID o140(LPBYTE I); // DAT0=A A +extern VOID o141(LPBYTE I); // DAT0=A A +extern VOID o144(LPBYTE I); // DAT0=C A +extern VOID o145(LPBYTE I); // DAT1=C A +extern VOID o148(LPBYTE I); // DAT0=A B +extern VOID o149(LPBYTE I); // DAT1=A B +extern VOID o14C(LPBYTE I); // DAT0=C B +extern VOID o14D(LPBYTE I); // DAT1=C B +extern VOID o142(LPBYTE I); // A=DAT0 A +extern VOID o143(LPBYTE I); // A=DAT1 A +extern VOID o146(LPBYTE I); // C=DAT0 A +extern VOID o147(LPBYTE I); // C=DAT1 A +extern VOID o14A(LPBYTE I); // A=DAT0 B +extern VOID o14B(LPBYTE I); // A=DAT1 B +extern VOID o14E(LPBYTE I); // C=DAT0 B +extern VOID o14F(LPBYTE I); // C=DAT0 B +extern VOID o150a(LPBYTE I); // DAT0=A a +extern VOID o151a(LPBYTE I); // DAT1=A a +extern VOID o154a(LPBYTE I); // DAT0=C a +extern VOID o155a(LPBYTE I); // DAT1=C a +extern VOID o152a(LPBYTE I); // A=DAT0 a +extern VOID o153a(LPBYTE I); // A=DAT1 a +extern VOID o156a(LPBYTE I); // C=DAT0 a +extern VOID o157a(LPBYTE I); // C=DAT1 a +extern VOID o158x(LPBYTE I); // DAT0=A x +extern VOID o159x(LPBYTE I); // DAT1=A x +extern VOID o15Cx(LPBYTE I); // DAT0=C x +extern VOID o15Dx(LPBYTE I); // DAT1=C x +extern VOID o15Ax(LPBYTE I); // A=DAT0 x +extern VOID o15Bx(LPBYTE I); // A=DAT1 x +extern VOID o15Ex(LPBYTE I); // C=DAT0 x +extern VOID o15Fx(LPBYTE I); // C=DAT1 x +extern VOID o16x(LPBYTE I); // D0=D0+ (n+1) +extern VOID o17x(LPBYTE I); // D1=D1+ (n+1) +extern VOID o18x(LPBYTE I); // D0=D0- (n+1) +extern VOID o19d2(LPBYTE I); // D0=(2) #dd +extern VOID o1Ad4(LPBYTE I); // D0=(4) #dddd +extern VOID o1Bd5(LPBYTE I); // D0=(5) #ddddd +extern VOID o1Cx(LPBYTE I); // D1=D1- (n+1) +extern VOID o1Dd2(LPBYTE I); // D1=(2) #dd +extern VOID o1Ed4(LPBYTE I); // D1=(4) #dddd +extern VOID o1Fd5(LPBYTE I); // D1=(5) #ddddd +extern VOID o2n(LPBYTE I); // P= n +extern VOID o3X(LPBYTE I); // LCHEX +extern VOID o4d2(LPBYTE I); // GOC #dd +extern VOID o5d2(LPBYTE I); // GONC +extern VOID o6d3(LPBYTE I); // GOTO +extern VOID o7d3(LPBYTE I); // GOSUB +extern VOID o800(LPBYTE I); // OUT=CS +extern VOID o801(LPBYTE I); // OUT=C +extern VOID o802(LPBYTE I); // A=IN +extern VOID o803(LPBYTE I); // C=IN +extern VOID o804(LPBYTE I); // UNCNFG +extern VOID o805(LPBYTE I); // CONFIG +extern VOID o806(LPBYTE I); // C=ID +extern VOID o807(LPBYTE I); // SHUTDN +extern VOID o8080(LPBYTE I); // INTON +extern VOID o80810(LPBYTE I); // RSI +extern VOID o8082X(LPBYTE I); // LA +extern VOID o8083(LPBYTE I); // BUSCB +extern VOID o8084n(LPBYTE I); // ABIT=0 n +extern VOID o8085n(LPBYTE I); // ABIT=1 n +extern VOID o8086n(LPBYTE I); // ?ABIT=0 n +extern VOID o8087n(LPBYTE I); // ?ABIT=1 n +extern VOID o8088n(LPBYTE I); // CBIT=0 n +extern VOID o8089n(LPBYTE I); // CBIT=1 n +extern VOID o808An(LPBYTE I); // ?CBIT=0 n +extern VOID o808Bn(LPBYTE I); // ?CBIT=1 n +extern VOID o808C(LPBYTE I); // PC=(A) +extern VOID o808D(LPBYTE I); // BUSCD +extern VOID o808E(LPBYTE I); // PC=(C) +extern VOID o808F(LPBYTE I); // INTOFF +extern VOID o809(LPBYTE I); // C+P+1 - HEX MODE +extern VOID o80A(LPBYTE I); // RESET +extern VOID o80B(LPBYTE I); // BUSCC +extern VOID o80Cn(LPBYTE I); // C=P n +extern VOID o80Dn(LPBYTE I); // P=C n +extern VOID o80E(LPBYTE I); // SREQ? +extern VOID o80Fn(LPBYTE I); // CPEX n +extern VOID o810(LPBYTE I); // ASLC +extern VOID o811(LPBYTE I); // BSLC +extern VOID o812(LPBYTE I); // CSLC +extern VOID o813(LPBYTE I); // DSLC +extern VOID o814(LPBYTE I); // ASRC +extern VOID o815(LPBYTE I); // BSRC +extern VOID o816(LPBYTE I); // CSRC +extern VOID o817(LPBYTE I); // DSRC +extern VOID o818f0x(LPBYTE I); // A=A+x+1 f +extern VOID o818f1x(LPBYTE I); // B=B+x+1 f +extern VOID o818f2x(LPBYTE I); // C=C+x+1 f +extern VOID o818f3x(LPBYTE I); // D=D+x+1 f +extern VOID o818f8x(LPBYTE I); // A=A-x-1 f +extern VOID o818f9x(LPBYTE I); // B=B-x-1 f +extern VOID o818fAx(LPBYTE I); // C=C-x-1 f +extern VOID o818fBx(LPBYTE I); // D=D-x-1 f +extern VOID o819f0(LPBYTE I); // ASRB.F +extern VOID o819f1(LPBYTE I); // BSRB.F +extern VOID o819f2(LPBYTE I); // CSRB.F +extern VOID o819f3(LPBYTE I); // DSRB.F +extern VOID o81Af00(LPBYTE I); // R0=A.F f +extern VOID o81Af01(LPBYTE I); // R1=A.F f +extern VOID o81Af02(LPBYTE I); // R2=A.F f +extern VOID o81Af03(LPBYTE I); // R3=A.F f +extern VOID o81Af04(LPBYTE I); // R4=A.F f +extern VOID o81Af08(LPBYTE I); // R0=C.F f +extern VOID o81Af09(LPBYTE I); // R1=C.F f +extern VOID o81Af0A(LPBYTE I); // R2=C.F f +extern VOID o81Af0B(LPBYTE I); // R3=C.F f +extern VOID o81Af0C(LPBYTE I); // R4=C.F f +extern VOID o81Af10(LPBYTE I); // A=R0.F f +extern VOID o81Af11(LPBYTE I); // A=R1.F f +extern VOID o81Af12(LPBYTE I); // A=R2.F f +extern VOID o81Af13(LPBYTE I); // A=R3.F f +extern VOID o81Af14(LPBYTE I); // A=R4.F f +extern VOID o81Af18(LPBYTE I); // C=R0.F f +extern VOID o81Af19(LPBYTE I); // C=R1.F f +extern VOID o81Af1A(LPBYTE I); // C=R2.F f +extern VOID o81Af1B(LPBYTE I); // C=R3.F f +extern VOID o81Af1C(LPBYTE I); // C=R4.F f +extern VOID o81Af20(LPBYTE I); // AR0EX.F f +extern VOID o81Af21(LPBYTE I); // AR1EX.F f +extern VOID o81Af22(LPBYTE I); // AR2EX.F f +extern VOID o81Af23(LPBYTE I); // AR3EX.F f +extern VOID o81Af24(LPBYTE I); // AR4EX.F f +extern VOID o81Af28(LPBYTE I); // CR0EX.F f +extern VOID o81Af29(LPBYTE I); // CR1EX.F f +extern VOID o81Af2A(LPBYTE I); // CR2EX.F f +extern VOID o81Af2B(LPBYTE I); // CR3EX.F f +extern VOID o81Af2C(LPBYTE I); // CR4EX.F f +extern VOID o81B2(LPBYTE I); // PC=A +extern VOID o81B3(LPBYTE I); // PC=C +extern VOID o81B4(LPBYTE I); // A=PC +extern VOID o81B5(LPBYTE I); // C=PC +extern VOID o81B6(LPBYTE I); // APCEX +extern VOID o81B7(LPBYTE I); // CPCEX +extern VOID o81C(LPBYTE I); // ASRB +extern VOID o81D(LPBYTE I); // BSRB +extern VOID o81E(LPBYTE I); // CSRB +extern VOID o81F(LPBYTE I); // DSRB +extern VOID o82n(LPBYTE I); // HST=0 m +extern VOID o83n(LPBYTE I); // ?HST=0 m +extern VOID o84n(LPBYTE I); // ST=0 n +extern VOID o85n(LPBYTE I); // ST=1 n +extern VOID o86n(LPBYTE I); // ?ST=0 n +extern VOID o87n(LPBYTE I); // ?ST=1 n +extern VOID o88n(LPBYTE I); // ?P# n +extern VOID o89n(LPBYTE I); // ?P= n +extern VOID o8A0(LPBYTE I); // ?A=B A +extern VOID o8A1(LPBYTE I); // ?B=C A +extern VOID o8A2(LPBYTE I); // ?C=A A +extern VOID o8A3(LPBYTE I); // ?D=C A +extern VOID o8A4(LPBYTE I); // ?A#B A +extern VOID o8A5(LPBYTE I); // ?B#C A +extern VOID o8A6(LPBYTE I); // ?C#A A +extern VOID o8A7(LPBYTE I); // ?D#C A +extern VOID o8A8(LPBYTE I); // ?A=0 A +extern VOID o8A9(LPBYTE I); // ?B=0 A +extern VOID o8AA(LPBYTE I); // ?C=0 A +extern VOID o8AB(LPBYTE I); // ?D=0 A +extern VOID o8AC(LPBYTE I); // ?A#0 A +extern VOID o8AD(LPBYTE I); // ?B#0 A +extern VOID o8AE(LPBYTE I); // ?C#0 A +extern VOID o8AF(LPBYTE I); // ?D#0 A +extern VOID o8B0(LPBYTE I); // ?A>B A +extern VOID o8B1(LPBYTE I); // ?B>C A +extern VOID o8B2(LPBYTE I); // ?C>A A +extern VOID o8B3(LPBYTE I); // ?D>C A +extern VOID o8B4(LPBYTE I); // ?A=B A +extern VOID o8B9(LPBYTE I); // ?B>=C A +extern VOID o8BA(LPBYTE I); // ?C>=A A +extern VOID o8BB(LPBYTE I); // ?D>=C A +extern VOID o8BC(LPBYTE I); // ?A<=B A +extern VOID o8BD(LPBYTE I); // ?B<=C A +extern VOID o8BE(LPBYTE I); // ?C<=A A +extern VOID o8BF(LPBYTE I); // ?D<=C A +extern VOID o8Cd4(LPBYTE I); // GOLONG #dddd +extern VOID o8Dd5(LPBYTE I); // GOVLNG #ddddd +extern VOID o8Ed4(LPBYTE I); // GOSUBL #dddd +extern VOID o8Fd5(LPBYTE I); // GOSBVL #ddddd +extern VOID o9a0(LPBYTE I); // ?A=B f +extern VOID o9a1(LPBYTE I); // ?B=C f +extern VOID o9a2(LPBYTE I); // ?C=A f +extern VOID o9a3(LPBYTE I); // ?D=C f +extern VOID o9a4(LPBYTE I); // ?A#B f +extern VOID o9a5(LPBYTE I); // ?B#C f +extern VOID o9a6(LPBYTE I); // ?C#A f +extern VOID o9a7(LPBYTE I); // ?D#C f +extern VOID o9a8(LPBYTE I); // ?A=0 f +extern VOID o9a9(LPBYTE I); // ?B=0 f +extern VOID o9aA(LPBYTE I); // ?C=0 f +extern VOID o9aB(LPBYTE I); // ?D=0 f +extern VOID o9aC(LPBYTE I); // ?A#0 f +extern VOID o9aD(LPBYTE I); // ?B#0 f +extern VOID o9aE(LPBYTE I); // ?C#0 f +extern VOID o9aF(LPBYTE I); // ?D#0 f +extern VOID o9b0(LPBYTE I); // ?A>B f +extern VOID o9b1(LPBYTE I); // ?B>C f +extern VOID o9b2(LPBYTE I); // ?C>A f +extern VOID o9b3(LPBYTE I); // ?D>C f +extern VOID o9b4(LPBYTE I); // ?A=B f +extern VOID o9b9(LPBYTE I); // ?B>=C f +extern VOID o9bA(LPBYTE I); // ?C>=A f +extern VOID o9bB(LPBYTE I); // ?D>=C f +extern VOID o9bC(LPBYTE I); // ?A<=B f +extern VOID o9bD(LPBYTE I); // ?B<=C f +extern VOID o9bE(LPBYTE I); // ?C<=A f +extern VOID o9bF(LPBYTE I); // ?D<=C f +extern VOID oAa0(LPBYTE I); // A=A+B f +extern VOID oAa1(LPBYTE I); // B=B+C f +extern VOID oAa2(LPBYTE I); // C=C+A f +extern VOID oAa3(LPBYTE I); // D=D+C f +extern VOID oAa4(LPBYTE I); // A=A+A f +extern VOID oAa5(LPBYTE I); // B=B+B f +extern VOID oAa6(LPBYTE I); // C=C+C f +extern VOID oAa7(LPBYTE I); // D=D+D f +extern VOID oAa8(LPBYTE I); // B=B+A f +extern VOID oAa9(LPBYTE I); // C=C+B f +extern VOID oAaA(LPBYTE I); // A=A+C f +extern VOID oAaB(LPBYTE I); // C=C+D f +extern VOID oAaC(LPBYTE I); // A=A-1 f +extern VOID oAaD(LPBYTE I); // B=B-1 f +extern VOID oAaE(LPBYTE I); // C=C-1 f +extern VOID oAaF(LPBYTE I); // D=D-1 f +extern VOID oAb0(LPBYTE I); // A=0 f +extern VOID oAb1(LPBYTE I); // B=0 f +extern VOID oAb2(LPBYTE I); // C=0 f +extern VOID oAb3(LPBYTE I); // D=0 f +extern VOID oAb4(LPBYTE I); // A=B f +extern VOID oAb5(LPBYTE I); // B=C f +extern VOID oAb6(LPBYTE I); // C=A f +extern VOID oAb7(LPBYTE I); // D=C f +extern VOID oAb8(LPBYTE I); // B=A f +extern VOID oAb9(LPBYTE I); // C=B f +extern VOID oAbA(LPBYTE I); // A=C f +extern VOID oAbB(LPBYTE I); // C=D f +extern VOID oAbC(LPBYTE I); // ABEX f +extern VOID oAbD(LPBYTE I); // BCEX f +extern VOID oAbE(LPBYTE I); // CAEX f +extern VOID oAbF(LPBYTE I); // DCEX f +extern VOID oBa0(LPBYTE I); // A=A-B f +extern VOID oBa1(LPBYTE I); // B=B-C f +extern VOID oBa2(LPBYTE I); // C=C-A f +extern VOID oBa3(LPBYTE I); // D=D-C f +extern VOID oBa4(LPBYTE I); // A=A+1 f +extern VOID oBa5(LPBYTE I); // B=B+1 f +extern VOID oBa6(LPBYTE I); // C=C+1 f +extern VOID oBa7(LPBYTE I); // D=D+1 f +extern VOID oBa8(LPBYTE I); // B=B-A f +extern VOID oBa9(LPBYTE I); // C=C-B f +extern VOID oBaA(LPBYTE I); // A=A-C f +extern VOID oBaB(LPBYTE I); // C=C-D f +extern VOID oBaC(LPBYTE I); // A=B-A f +extern VOID oBaD(LPBYTE I); // B=C-B f +extern VOID oBaE(LPBYTE I); // C=A-C f +extern VOID oBaF(LPBYTE I); // D=C-D f +extern VOID oBb0(LPBYTE I); // ASL f +extern VOID oBb1(LPBYTE I); // BSL f +extern VOID oBb2(LPBYTE I); // CSL f +extern VOID oBb3(LPBYTE I); // DSL f +extern VOID oBb4(LPBYTE I); // ASR f +extern VOID oBb5(LPBYTE I); // BSR f +extern VOID oBb6(LPBYTE I); // CSR f +extern VOID oBb7(LPBYTE I); // DSR f +extern VOID oBb8(LPBYTE I); // A=-A f +extern VOID oBb9(LPBYTE I); // B=-B f +extern VOID oBbA(LPBYTE I); // C=-C f +extern VOID oBbB(LPBYTE I); // D=-D f +extern VOID oBbC(LPBYTE I); // A=-A-1 f +extern VOID oBbD(LPBYTE I); // B=-B-1 f +extern VOID oBbE(LPBYTE I); // C=-C-1 f +extern VOID oBbF(LPBYTE I); // D=-D-1 f +extern VOID oC0(LPBYTE I); // A=A+B A +extern VOID oC1(LPBYTE I); // B=B+C A +extern VOID oC2(LPBYTE I); // C=C+A A +extern VOID oC3(LPBYTE I); // D=D+C A +extern VOID oC4(LPBYTE I); // A=A+A A +extern VOID oC5(LPBYTE I); // B=B+B A +extern VOID oC6(LPBYTE I); // C=C+C A +extern VOID oC7(LPBYTE I); // D=D+D A +extern VOID oC8(LPBYTE I); // B=B+A A +extern VOID oC9(LPBYTE I); // C=C+B A +extern VOID oCA(LPBYTE I); // A=A+C A +extern VOID oCB(LPBYTE I); // C=C+D A +extern VOID oCC(LPBYTE I); // A=A-1 A +extern VOID oCD(LPBYTE I); // B=B-1 A +extern VOID oCE(LPBYTE I); // C=C-1 A +extern VOID oCF(LPBYTE I); // D=D-1 A +extern VOID oD0(LPBYTE I); // A=0 A +extern VOID oD1(LPBYTE I); // B=0 A +extern VOID oD2(LPBYTE I); // C=0 A +extern VOID oD3(LPBYTE I); // D=0 A +extern VOID oD4(LPBYTE I); // A=B A +extern VOID oD5(LPBYTE I); // B=C A +extern VOID oD6(LPBYTE I); // C=A A +extern VOID oD7(LPBYTE I); // D=C A +extern VOID oD8(LPBYTE I); // B=A A +extern VOID oD9(LPBYTE I); // C=B A +extern VOID oDA(LPBYTE I); // A=C A +extern VOID oDB(LPBYTE I); // C=D A +extern VOID oDC(LPBYTE I); // ABEX +extern VOID oDD(LPBYTE I); // BCEX +extern VOID oDE(LPBYTE I); // CAEX +extern VOID oDF(LPBYTE I); // DCEX +extern VOID oE0(LPBYTE I); // A=A-B A +extern VOID oE1(LPBYTE I); // B=B-C A +extern VOID oE2(LPBYTE I); // C=C-A A +extern VOID oE3(LPBYTE I); // D=D-C A +extern VOID oE4(LPBYTE I); // A=A+1 A +extern VOID oE5(LPBYTE I); // B=B+1 A +extern VOID oE6(LPBYTE I); // C=C+1 A +extern VOID oE7(LPBYTE I); // D=D+1 A +extern VOID oE8(LPBYTE I); // B=B-A A +extern VOID oE9(LPBYTE I); // C=C-B A +extern VOID oEA(LPBYTE I); // A=A-C A +extern VOID oEB(LPBYTE I); // C=C-D A +extern VOID oEC(LPBYTE I); // A=B-A A +extern VOID oED(LPBYTE I); // B=C-B A +extern VOID oEE(LPBYTE I); // C=A-C A +extern VOID oEF(LPBYTE I); // D=C-D A +extern VOID oF0(LPBYTE I); // ASL A +extern VOID oF1(LPBYTE I); // BSL A +extern VOID oF2(LPBYTE I); // CSL A +extern VOID oF3(LPBYTE I); // DSL A +extern VOID oF4(LPBYTE I); // ASR A +extern VOID oF5(LPBYTE I); // BSR A +extern VOID oF6(LPBYTE I); // CSR A +extern VOID oF7(LPBYTE I); // DSR A +extern VOID oF8(LPBYTE I); // A=-A A +extern VOID oF9(LPBYTE I); // B=-B A +extern VOID oFA(LPBYTE I); // C=-C A +extern VOID oFB(LPBYTE I); // D=-D A +extern VOID oFC(LPBYTE I); // A=-A-1 A +extern VOID oFD(LPBYTE I); // B=-B-1 A +extern VOID oFE(LPBYTE I); // C=-C-1 A +extern VOID oFF(LPBYTE I); // D=-D-1 A + +extern VOID o_invalid3(LPBYTE I); +extern VOID o_invalid4(LPBYTE I); +extern VOID o_invalid5(LPBYTE I); +extern VOID o_invalid6(LPBYTE I); + +extern VOID o_goyes3(LPBYTE I); +extern VOID o_goyes5(LPBYTE I); + +extern VOID o81B1(LPBYTE I); // beep patch diff --git a/app/src/main/cpp/OPS.H b/app/src/main/cpp/OPS.H new file mode 100644 index 0000000..95c9c36 --- /dev/null +++ b/app/src/main/cpp/OPS.H @@ -0,0 +1,476 @@ +/* + * ops.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +#define NFunpack(a, b, f) Nunpack((a)+F_s[f], b, F_l[f]) +#define NFread(a, b, f) Nread((a)+F_s[f], b, F_l[f]) +#define NFwrite(a, b, f) Nwrite((a)+F_s[f], b, F_l[f]) +#define NFcopy(a, b, f) memcpy((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFxchg(a, b, f) Nxchg((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFadd(a, b, f) Nadd((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFsub(a, b, f) Nsub((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFrsub(a, b, f) Nrsub((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFand(a, b, f) Nand((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFor(a, b, f) Nor((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFzero(a,f) memset((a)+F_s[f], 0, F_l[f]) +#define NFpack(a, f) Npack((a)+F_s[f], F_l[f]) +#define NFinc(a, f) Ninc(a, F_l[f], F_s[f]) +#define NFdec(a, f) Ndec(a, F_l[f], F_s[f]) +#define NFnot(a, f) Nnot((a)+F_s[f], F_l[f]) +#define NFneg(a, f) Nneg((a)+F_s[f], F_l[f]) +#define NFsl(a, f) Nsl((a)+F_s[f], F_l[f]) +#define NFsr(a, f) Nsr((a)+F_s[f], F_l[f]) +#define NFsrb(a, f) Nsrb((a)+F_s[f], F_l[f]) +#define TFe(a, b, f) Te((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFa(a, b, f) Ta((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFb(a, b, f) Tb((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFz(a, f) Tz((a)+F_s[f], F_l[f]) +#define TFne(a, b, f) Tne((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFae(a, b, f) Tae((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFbe(a, b, f) Tbe((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFnz(a, f) Tnz((a)+F_s[f], F_l[f]) + +static __inline LPBYTE FASTPTR(DWORD d) +{ + static BYTE pbyNULL[21]; + LPBYTE lpbyPage; + DWORD u, v; + + d &= 0xFFFFF; // handle address overflows + + u = d >> 12; // page + v = d & 0xFFF; // offset + + if ( !(Chipset.IOCfig && ((d & 0xFFFC0) == Chipset.IOBase)) + && RMap[u] != NULL // page valid + && ( v < 0x1000 - ARRAYSIZEOF(pbyNULL) // complete opcode inside page + // or next page continue linear addressing + || (RMap[u] + 0x1000 == RMap[(u+1) & (ARRAYSIZEOF(RMap)-1)]) + ) + ) + { + lpbyPage = RMap[u] + v; // full address + } + else + { + lpbyPage = pbyNULL; // memory allocation + Npeek(lpbyPage, d, ARRAYSIZEOF(pbyNULL)); // fill with data (LAHEX + 16 digits = longest opcode) + } + return lpbyPage; +} + +static __inline void rstkpush(DWORD d) +{ + Chipset.rstk[Chipset.rstkp] = d; + Chipset.rstkp=(Chipset.rstkp+1)&7; +} + +static __inline DWORD rstkpop(VOID) +{ + DWORD r; + + Chipset.rstkp=(Chipset.rstkp-1)&7; + r = Chipset.rstk[Chipset.rstkp]; + Chipset.rstk[Chipset.rstkp] = 0; + return r; +} + +static __inline DWORD Npack(BYTE *a, UINT s) +{ + DWORD r = 0; + + while (s--) r = (r<<4)|a[s]; + return r; +} + +static __inline VOID Nunpack(BYTE *a, DWORD b, UINT s) +{ + for (; s>0; --s) { *a++ = (BYTE)(b&0xf); b>>=4; } +} + +static __inline QWORD Npack64(BYTE *a, UINT s) +{ + QWORD r = 0; + + while (s--) r = (r<<4)|a[s]; + return r; +} + +static __inline VOID Nunpack64(BYTE *a, QWORD b, UINT s) +{ + UINT i; + for (i=0; i>=4; } +} + +static __inline void Nxchg(BYTE *a, BYTE *b, UINT s) +{ + BYTE X[16]; + + memcpy(X, b, s); + memcpy(b, a, s); + memcpy(a, X, s); +} + +static __inline void Ninc(BYTE *a, UINT s, UINT d) +{ + UINT i; + + if (Chipset.mode_dec) + { + BYTE c = 1; + for (i=d; iA)); + + // illegal number in dec mode + if (a[i] >= 10) a[i] &= 0x7; + + a[i] += c; + c = (a[i] >= 10); + if (c) a[i] -= 10; + } + Chipset.carry = (c==1); + } + else + { + for (i=d; iA)); + + a[i]++; + if (a[i] < 16) + { + Chipset.carry = FALSE; + return; + } + a[i] -= 16; + } + Chipset.carry = TRUE; + } +} + +static __inline void Ninc16(BYTE *a, UINT s, UINT d) +{ + UINT i; + + for (i=d; iA)); + + a[i]--; + if ((a[i] & 0xF0) == 0) // check overflow + { + Chipset.carry = FALSE; + return; + } + a[i] += cBase; + } + Chipset.carry = TRUE; +} + +static __inline void Ndec16(BYTE *a, UINT s, UINT d) +{ + UINT i; + + for (i=d; i= cBase) a[i] &= 0x7; + + a[i] += b[i] + c; + if (a[i] >= cBase) + { + a[i] -= cBase; + c = 1; + } + else + c = 0; + } + Chipset.carry = (c==1); +} + +static __inline void Nsub(BYTE *a, BYTE *b, UINT s) +{ + UINT i; + BYTE c = 0; + BYTE cBase = Chipset.mode_dec ? 10 : 16; + + for (i=0; i 0); // check for non-zero digit + for (--a[i]; i>= 1; + *a |= ((a[1] & 1) << 3); + a++; + } + *a >>= 1; +} + +static __inline void Nbit0(BYTE *a, UINT b) +{ + a[b>>2] &= ~(1<<(b&3)); +} + +static __inline void Nbit1(BYTE *a, UINT b) +{ + a[b>>2] |= 1<<(b&3); +} + +static __inline void Tbit0(BYTE *a, UINT b) +{ + Chipset.carry = ((a[b>>2] & (1<<(b&3))) == 0); +} + +static __inline void Tbit1(BYTE *a, UINT b) +{ + Chipset.carry = ((a[b>>2] & (1<<(b&3))) != 0); +} + +static __inline void Te(BYTE *a, BYTE *b, UINT s) +{ + while (s--) + { + if (a[s]!=b[s]) + { + Chipset.carry = FALSE; + return; + } + } + Chipset.carry = TRUE; +} + +static __inline void Tne(BYTE *a, BYTE *b, UINT s) +{ + while (s--) + { + if (a[s]!=b[s]) + { + Chipset.carry = TRUE; + return; + } + } + Chipset.carry = FALSE; +} + +static __inline void Tz(BYTE *a, UINT s) +{ + while (s--) + { + if (a[s]!=0) + { + Chipset.carry = FALSE; + return; + } + } + Chipset.carry = TRUE; +} + +static __inline void Tnz(BYTE *a, UINT s) +{ + while (s--) + { + if (a[s]!=0) + { + Chipset.carry = TRUE; + return; + } + } + Chipset.carry = FALSE; +} + +static __inline void Ta(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + Chipset.carry = (a[s]>b[s]); +} + +static __inline void Tb(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + Chipset.carry = (a[s]=b[s]); +} + +static __inline void Tbe(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + Chipset.carry = (a[s]<=b[s]); +} diff --git a/app/src/main/cpp/PCH.C b/app/src/main/cpp/PCH.C new file mode 100644 index 0000000..c20bfab --- /dev/null +++ b/app/src/main/cpp/PCH.C @@ -0,0 +1,5 @@ +// +// PCH.C +// + +#include "pch.h" diff --git a/app/src/main/cpp/PCH.H b/app/src/main/cpp/PCH.H new file mode 100644 index 0000000..31cb02a --- /dev/null +++ b/app/src/main/cpp/PCH.H @@ -0,0 +1,93 @@ +// +// PCH.H +// + +#define _WIN32_IE 0x0200 +#define _CRT_SECURE_NO_DEPRECATE +#define _CRTDBG_MAP_ALLOC +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined VERIFY +#if defined _DEBUG +#define VERIFY(f) _ASSERT(f) +#else // _DEBUG +#define VERIFY(f) ((VOID)(f)) +#endif // _DEBUG +#endif // _VERIFY + +#if !defined INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +#if !defined INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + +#if !defined GWLP_USERDATA +#define GWLP_USERDATA GWL_USERDATA +#endif + +#if !defined GCLP_HCURSOR +#define GCLP_HCURSOR GCL_HCURSOR +#endif + +#if !defined IDC_HAND // Win2k specific definition +#define IDC_HAND MAKEINTRESOURCE(32649) +#endif + +#if _MSC_VER <= 1200 // missing type definition in the MSVC6.0 SDK and earlier +#define SetWindowLongPtr SetWindowLong +#define GetWindowLongPtr GetWindowLong +#define SetClassLongPtr SetClassLong +#define GetClassLongPtr GetClassLong +typedef SIZE_T DWORD_PTR, *PDWORD_PTR; +typedef ULONG ULONG_PTR, *PULONG_PTR; +typedef LONG LONG_PTR, *PLONG_PTR; +#endif + +#if _MSC_VER >= 1400 // valid for VS2005 and later +#if defined _M_IX86 +#pragma comment(linker,"/manifestdependency:\" \ + type='win32' \ + name='Microsoft.Windows.Common-Controls' \ + version='6.0.0.0' processorArchitecture='x86' \ + publicKeyToken='6595b64144ccf1df' \ + language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker,"/manifestdependency:\" \ + type='win32' \ + name='Microsoft.Windows.Common-Controls' \ + version='6.0.0.0' processorArchitecture='ia64' \ + publicKeyToken='6595b64144ccf1df' \ + language='*'\"") +#elif defined _M_X64 +#pragma comment(linker,"/manifestdependency:\" \ + type='win32' \ + name='Microsoft.Windows.Common-Controls' \ + version='6.0.0.0' processorArchitecture='amd64' \ + publicKeyToken='6595b64144ccf1df' \ + language='*'\"") +#else +#pragma comment(linker,"/manifestdependency:\" \ + type='win32' \ + name='Microsoft.Windows.Common-Controls' \ + version='6.0.0.0' processorArchitecture='*' \ + publicKeyToken='6595b64144ccf1df' \ + language='*'\"") +#endif +#endif diff --git a/app/src/main/cpp/REDEYE.C b/app/src/main/cpp/REDEYE.C new file mode 100644 index 0000000..2c47163 --- /dev/null +++ b/app/src/main/cpp/REDEYE.C @@ -0,0 +1,177 @@ +/* + * redeye.c + * + * This file is part of Emu48 + * + * Copyright (C) 2011 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +#define ERR_CHAR 127 // character for transfer error + +#define H1 0x78 +#define H2 0xE6 +#define H3 0xD5 +#define H4 0x8B + +// HP redeye correction masks +static CONST BYTE byEmask[] = { H1, H2, H3, H4 }; + +static __inline UINT MAX(UINT a, UINT b) +{ + return (a>b)?a:b; +} + +static __inline BYTE Parity(BYTE b) +{ + b ^= (b >> 4); + b ^= (b >> 2); + b ^= (b >> 1); + return b & 1; +} + +static __inline BYTE CreateCorrectionBits(BYTE b) +{ + UINT i; + BYTE byVal = 0; + + for (i = 0; i < ARRAYSIZEOF(byEmask);++i) + { + byVal <<= 1; + byVal |= Parity((BYTE) (b & byEmask[i])); + } + return byVal; +} + +static __inline WORD CorrectData(WORD wData,WORD wMissed) +{ + while ((wMissed & 0xFF) != 0) // clear every missed bit in data area + { + BYTE byBitMask; + + // detect valid H(i) mask + WORD wMi = 0x800; // first M(i) bit + INT i = 0; // index to first H(i) mask + + while (TRUE) + { + if ((wMissed & wMi) == 0) // possible valid mask + { + _ASSERT(i < ARRAYSIZEOF(byEmask)); + + // select bit to correct + byBitMask = wMissed & byEmask[i]; + + if (Parity(byBitMask)) // only one bit set (parity odd) + break; // -> valid H(i) mask + } + + wMi >>= 1; // next M(i) bit + i++; // next H(i) mask + } + + // correct bit with H(i) mask + wMissed ^= byBitMask; // clear this missed bit + + // parity odd -> wrong data value + if (Parity((BYTE) ((wData & byEmask[i]) ^ ((wData & wMi) >> 8)))) + wData ^= byBitMask; // correct value + } + return wData & 0xFF; // only data byte is correct +} + +VOID IrPrinter(BYTE c) +{ + static INT nFrame = 0; // frame counter + static DWORD dwData = 0; // half bit data container + static INT nStart = 0; // frame counter disabled + + BOOL bLSRQ; + + dwData = (dwData << 1) | (c & LBO); // grab the last 32 bit send through IR + + // Led Service ReQuest on Led Buffer Empty enabled + bLSRQ = (Chipset.IORam[LCR] & ELBE) != 0; + + IOBit(SRQ2,LSRQ,bLSRQ); // update LSRQ bit + if (bLSRQ) // interrupt on Led Buffer Empty enabled + { + Chipset.SoftInt = TRUE; // execute interrupt + bInterrupt = TRUE; + } + + // HP40G and HP49G have no IR transmitter Led + if ((cCurrentRomType == 'E' && nCurrentClass == 40) || cCurrentRomType == 'X') + return; + + if (nFrame == 0) // waiting for start bit condition + { + if ((dwData & 0x3F) == 0x07) // start bit condition (000111 pattern) + { + nStart = 1; // enable frame counter + } + } + + if (nFrame == 24) // 24 half bit received + { + INT i; + + WORD wData = 0; // data container + WORD wMissed = 0; // missed bit container + INT nCount = 0; // no. of missed bits + + nFrame = 0; // reset for next character + nStart = 0; // disable frame counter + + // separate to data and missed bits + for (i = 0; i < 12; ++i) // 12 bit frames + { + BYTE b = (BYTE) (dwData & 3); // last 2 half bits + + if (b == 0x0 || b == 0x3) // illegal half bit combination + { + wMissed |= (1 << i); // this is a missed bit + ++nCount; // incr. number of missed bits + } + else // valid data bit + { + wData |= ((b >> 1) << i); // add data bit + } + dwData >>= 2; // next 2 half bits + } + + if (nCount <= 2) // error can be fixed + { + BYTE byOrgParity,byNewParity; + + byOrgParity = wData >> 8; // the original parity information with missed bits + byNewParity = ~(wMissed >> 8); // missed bit mask for recalculated parity + + if (nCount > 0) // error correction + { + wData = CorrectData(wData,wMissed); + } + + wData &= 0xFF; // remove parity information + + // recalculate parity data + byNewParity &= CreateCorrectionBits((BYTE) wData); + + // wrong parity + if (byOrgParity != byNewParity) + wData = ERR_CHAR; // character for transfer error + } + else + { + wData = ERR_CHAR; // character for transfer error + } + + SendByteUdp((BYTE) wData); // send data byte + return; + } + nFrame += nStart; // next frame + return; +} diff --git a/app/src/main/cpp/RESOURCE.H b/app/src/main/cpp/RESOURCE.H new file mode 100644 index 0000000..474771f --- /dev/null +++ b/app/src/main/cpp/RESOURCE.H @@ -0,0 +1,253 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Emu48.rc +// +#define IDM_DEBUG_SETTINGS 0x0010 +#define IDI_EMU48 100 +#define IDR_MENU 101 +#define IDM_MENU 102 +#define IDR_DEBUG 103 +#define IDR_DEBUG_TOOLBAR 104 +#define IDR_DEBUG_CODE 105 +#define IDR_DEBUG_MEM 106 +#define IDR_DEBUG_STACK 107 +#define IDB_CHECKBOX 108 +#define IDD_ABOUT 109 +#define IDD_SET_GENERAL 110 +#define IDD_SET_MEMORY 111 +#define IDD_SET_PERIPHERAL 112 +#define IDD_CHOOSEKML 113 +#define IDD_KMLLOG 114 +#define IDD_DISASM 115 +#define IDD_DEBUG 116 +#define IDD_NEWVALUE 117 +#define IDD_ENTERADR 118 +#define IDD_BREAKEDIT 119 +#define IDD_ENTERBREAK 120 +#define IDD_INSTRUCTIONS 121 +#define IDD_WRITEONLYREG 122 +#define IDD_FIND 123 +#define IDD_PROFILE 124 +#define IDD_RPLVIEW 125 +#define IDD_MACROSET 126 +#define IDD_DEBUG_MEMSAVE 127 +#define IDD_DEBUG_MEMLOAD 128 +#define IDD_DEBUG_SETTINGS 129 +#define IDC_REALSPEED 1000 +#define IDC_GRAYSCALE 1001 +#define IDC_ALWAYSONTOP 1002 +#define IDC_ACTFOLLOWSMOUSE 1003 +#define IDC_SINGLEINSTANCE 1004 +#define IDC_AUTOSAVE 1005 +#define IDC_AUTOSAVEONEXIT 1006 +#define IDC_OBJECTLOADWARNING 1007 +#define IDC_SHOWTITLE 1008 +#define IDC_SHOWMENU 1009 +#define IDC_ALWAYSDISPLOG 1010 +#define IDC_PORT1EN 1011 +#define IDC_PORT1WR 1012 +#define IDC_PORT2ISSHARED 1013 +#define IDC_PORT2WR 1014 +#define IDC_PORT2 1015 +#define IDC_PORT2LOAD 1016 +#define IDC_IR_ADDR 1017 +#define IDC_IR_PORT 1018 +#define IDC_WIRE 1019 +#define IDC_IR 1020 +#define IDC_EMUDIR 1021 +#define IDC_EMUDIRSEL 1022 +#define IDC_UPDATE 1023 +#define IDC_KMLSCRIPT 1024 +#define IDC_AUTHOR 1025 +#define IDC_TITLE 1026 +#define IDC_KMLLOG 1027 +#define IDC_VERSION 1028 +#define IDC_LICENSE 1029 +#define IDC_DISASM_WIN 1030 +#define IDC_DISASM_MODE_TEXT 1031 +#define IDC_DISASM_MODE 1032 +#define IDC_DISASM_MODULE 1033 +#define IDC_DISASM_HP 1034 +#define IDC_DISASM_CLASS 1035 +#define IDC_ADDRESS 1036 +#define IDC_DISASM_ADR 1037 +#define IDC_DISASM_NEXT 1038 +#define IDC_DISASM_COPY 1039 +#define IDC_DEBUG_CODE 1040 +#define IDC_STATIC_CODE 1041 +#define IDC_STATIC_REGISTERS 1042 +#define IDC_STATIC_MEMORY 1043 +#define IDC_STATIC_STACK 1044 +#define IDC_REG_A 1045 +#define IDC_REG_B 1046 +#define IDC_REG_C 1047 +#define IDC_REG_D 1048 +#define IDC_REG_R0 1049 +#define IDC_REG_R1 1050 +#define IDC_REG_R2 1051 +#define IDC_REG_R3 1052 +#define IDC_REG_R4 1053 +#define IDC_REG_D0 1054 +#define IDC_REG_D1 1055 +#define IDC_REG_P 1056 +#define IDC_REG_PC 1057 +#define IDC_REG_OUT 1058 +#define IDC_REG_IN 1059 +#define IDC_REG_ST 1060 +#define IDC_REG_CY 1061 +#define IDC_REG_MODE 1062 +#define IDC_REG_MP 1063 +#define IDC_REG_SR 1064 +#define IDC_REG_SB 1065 +#define IDC_REG_XM 1066 +#define IDC_MISC_INT 1067 +#define IDC_MISC_KEY 1068 +#define IDC_MISC_BS 1069 +#define IDC_NEWVALUE 1070 +#define IDC_ENTERADR 1071 +#define IDC_DEBUG_MEM 1072 +#define IDC_DEBUG_MEM_ADDR 1073 +#define IDC_DEBUG_MEM_COL0 1074 +#define IDC_DEBUG_MEM_COL1 1075 +#define IDC_DEBUG_MEM_COL2 1076 +#define IDC_DEBUG_MEM_COL3 1077 +#define IDC_DEBUG_MEM_COL4 1078 +#define IDC_DEBUG_MEM_COL5 1079 +#define IDC_DEBUG_MEM_COL6 1080 +#define IDC_DEBUG_MEM_COL7 1081 +#define IDC_DEBUG_MEM_TEXT 1082 +#define IDC_DEBUG_DATA_FILE 1083 +#define IDC_DEBUG_DATA_BUT 1084 +#define IDC_DEBUG_DATA_STARTADDR 1085 +#define IDC_DEBUG_DATA_ENDADDR 1086 +#define IDC_DEBUG_SET_SYMB 1087 +#define IDC_DEBUG_SET_MODEL 1088 +#define IDC_DEBUG_SET_FILE 1089 +#define IDC_DEBUG_SET_BROWSE 1090 +#define IDC_DEBUG_STACK 1091 +#define IDC_STATIC_BREAKPOINT 1092 +#define IDC_BREAKEDIT_ADD 1093 +#define IDC_BREAKEDIT_DELETE 1094 +#define IDC_BREAKEDIT_WND 1095 +#define IDC_STATIC_MMU 1096 +#define IDC_MMU_IO_A 1097 +#define IDC_MMU_NCE2_A 1098 +#define IDC_MMU_CE1_A 1099 +#define IDC_MMU_CE2_A 1100 +#define IDC_MMU_NCE3_A 1101 +#define IDC_MMU_IO_S 1102 +#define IDC_MMU_CE1_S 1103 +#define IDC_MMU_CE2_S 1104 +#define IDC_MMU_NCE2_S 1105 +#define IDC_MMU_NCE3_S 1106 +#define IDC_STATIC_MISC 1107 +#define IDC_MISC_BS_TXT 1108 +#define IDC_INSTR_TEXT 1109 +#define IDC_INSTR_CODE 1110 +#define IDC_INSTR_COPY 1111 +#define IDC_INSTR_CLEAR 1112 +#define IDC_PROFILE_LASTCYCLES 1113 +#define IDC_PROFILE_LASTTIME 1114 +#define IDC_BPCODE 1115 +#define IDC_BPRPL 1116 +#define IDC_BPACCESS 1117 +#define IDC_BPREAD 1118 +#define IDC_BPWRITE 1119 +#define IDC_FIND_DATA 1120 +#define IDC_FIND_PREV 1121 +#define IDC_FIND_NEXT 1122 +#define IDC_FIND_ASCII 1123 +#define IDC_ADDR20_24 1124 +#define IDC_ADDR25_27 1125 +#define IDC_ADDR28_29 1126 +#define IDC_ADDR30_34 1127 +#define IDC_RPLVIEW_DATA 1128 +#define IDC_MACRO_SLOW 1129 +#define IDC_MACRO_FAST 1130 +#define IDC_MACRO_SLIDER 1131 +#define IDC_MACRO_REAL 1132 +#define IDC_MACRO_MANUAL 1133 +#define IDC_SOUND_SLIDER 1134 +#define IDC_SOUND_DEVICE 1135 +#define ID_FILE_NEW 40001 +#define ID_FILE_OPEN 40002 +#define ID_FILE_SAVE 40003 +#define ID_FILE_SAVEAS 40004 +#define ID_FILE_EXIT 40005 +#define ID_VIEW_COPY 40006 +#define ID_VIEW_SETTINGS 40007 +#define ID_VIEW_RESET 40008 +#define ID_OBJECT_LOAD 40009 +#define ID_OBJECT_SAVE 40010 +#define ID_ABOUT 40011 +#define ID_HELP_TOPICS 40012 +#define ID_FILE_CLOSE 40013 +#define ID_BACKUP_SAVE 40014 +#define ID_BACKUP_RESTORE 40015 +#define ID_BACKUP_DELETE 40016 +#define ID_VIEW_SCRIPT 40017 +#define ID_STACK_COPY 40019 +#define ID_STACK_PASTE 40020 +#define ID_TOOL_DISASM 40021 +#define ID_TOOL_DEBUG 40022 +#define ID_TOOL_MACRO_RECORD 40023 +#define ID_TOOL_MACRO_PLAY 40024 +#define ID_TOOL_MACRO_STOP 40025 +#define ID_TOOL_MACRO_SETTINGS 40026 +#define ID_DEBUG_RUN 40027 +#define ID_DEBUG_RUNCURSOR 40028 +#define ID_DEBUG_STEP 40029 +#define ID_DEBUG_STEPOVER 40030 +#define ID_DEBUG_BREAK 40031 +#define ID_DEBUG_STEPOUT 40032 +#define ID_DEBUG_CANCEL 40033 +#define ID_BREAKPOINTS_SETBREAK 40034 +#define ID_BREAKPOINTS_CODEEDIT 40035 +#define ID_BREAKPOINTS_CLEARALL 40036 +#define ID_BREAKPOINTS_NOP3 40037 +#define ID_BREAKPOINTS_DOCODE 40038 +#define ID_BREAKPOINTS_RPL 40039 +#define ID_DEBUG_CODE_GOADR 40040 +#define ID_DEBUG_CODE_GOPC 40041 +#define ID_DEBUG_CODE_SETPCTOSELECT 40042 +#define ID_DEBUG_CODE_PREVPCO 40043 +#define ID_DEBUG_CODE_NEXTPCO 40044 +#define ID_DEBUG_MEM_GOADR 40045 +#define ID_DEBUG_MEM_GOPC 40046 +#define ID_DEBUG_MEM_GOD0 40047 +#define ID_DEBUG_MEM_GOD1 40048 +#define ID_DEBUG_MEM_GOSTACK 40049 +#define ID_DEBUG_MEM_FNONE 40050 +#define ID_DEBUG_MEM_FADDR 40051 +#define ID_DEBUG_MEM_FPC 40052 +#define ID_DEBUG_MEM_FD0 40053 +#define ID_DEBUG_MEM_FD1 40054 +#define ID_DEBUG_MEM_FIND 40055 +#define ID_DEBUG_MEM_MAP 40056 +#define ID_DEBUG_MEM_NCE1 40057 +#define ID_DEBUG_MEM_NCE2 40058 +#define ID_DEBUG_MEM_CE1 40059 +#define ID_DEBUG_MEM_CE2 40060 +#define ID_DEBUG_MEM_NCE3 40061 +#define ID_DEBUG_MEM_SAVE 40062 +#define ID_DEBUG_MEM_LOAD 40063 +#define ID_DEBUG_MEM_RPLVIEW 40064 +#define ID_DEBUG_STACK_PUSH 40065 +#define ID_DEBUG_STACK_POP 40066 +#define ID_DEBUG_STACK_MODIFY 40067 +#define ID_INTR_STEPOVERINT 40068 +#define ID_INFO_LASTINSTRUCTIONS 40069 +#define ID_INFO_PROFILE 40070 +#define ID_INFO_WRITEONLYREG 40071 +#define ID_FILE_MRU_FILE1 40100 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 130 +#define _APS_NEXT_COMMAND_VALUE 40072 +#define _APS_NEXT_CONTROL_VALUE 1136 +#define _APS_NEXT_SYMED_VALUE 109 +#endif +#endif diff --git a/app/src/main/cpp/RPL.C b/app/src/main/cpp/RPL.C new file mode 100644 index 0000000..2f90efe --- /dev/null +++ b/app/src/main/cpp/RPL.C @@ -0,0 +1,459 @@ +/* + * rpl.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" + +//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name +//#F0688 #806E9 #806E9 #7056A #806E9 #806E9 =TEMPOB +//#F068D #806EE #806EE #7056F #806EE #806EE =TEMPTOP +//#F0692 #806F3 #806F3 #70574 #806F3 #806F3 =RSKTOP (B) +//#F0697 #806F8 #806F8 #70579 #806F8 #806F8 =DSKTOP (D1) +//#F069C #806FD #806FD #7057E #806FD #806FD =EDITLINE +//#F0DEA #80E9B #80E9B #7066E #807ED #80E9B =AVMEM (D) +//#F0705 #8076B #8076B #705B0 #8072F #8076B =INTRPPTR (D0) +//#F0E42 #80F02 #80F02 #706C5 #80843 #80F02 =SystemFlags + +#define TEMPOB ((cCurrentRomType=='S')?0x7056A:0x806E9) +#define TEMPTOP ((cCurrentRomType=='S')?0x7056F:0x806EE) +#define RSKTOP ((cCurrentRomType=='S')?0x70574:0x806F3) +#define DSKTOP ((cCurrentRomType=='S')?0x70579:0x806F8) +#define EDITLINE ((cCurrentRomType=='S')?0x7057E:0x806FD) +// CdB for HP: add apples +#define AVMEM ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x7066E:0x807ED):0x80E9B) +#define INTRPPTR ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x705B0:0x8072F):0x8076B) +#define SYSTEMFLAGS ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x706C5:0x80843):0x80F02) + +#define DOINT 0x02614 // Precision Integer (HP49G) +#define DOLNGREAL 0x0263A // Precision Real (HP49G) +#define DOLNGCMP 0x02660 // Precision Complex (HP49G) +#define DOMATRIX 0x02686 // Symbolic matrix (HP49G) +#define DOFLASHP 0x026AC // Flash PTR (HP49G) +#define DOAPLET 0x026D5 // Aplet (HP49G) +#define DOMINIFONT 0x026FE // Mini Font (HP49G) +#define DOBINT 0x02911 // System Binary +#define DOREAL 0x02933 // Real +#define DOEREL 0x02955 // Long Real +#define DOCMP 0x02977 // Complex +#define DOECMP 0x0299D // Long Complex +#define DOCHAR 0x029BF // Character +#define DOARRY 0x029E8 // Array +#define DOLNKARRY 0x02A0A // Linked Array +#define DOCSTR 0x02A2C // String +#define DOHSTR 0x02A4E // Binary Integer +#define DOLIST 0x02A74 // List +#define DORRP 0x02A96 // Directory +#define DOSYMB 0x02AB8 // Algebraic +#define DOEXT 0x02ADA // Unit +#define DOTAG 0x02AFC // Tagged +#define DOGROB 0x02B1E // Graphic +#define DOLIB 0x02B40 // Library +#define DOBAK 0x02B62 // Backup +#define DOEXT0 0x02B88 // Library Data +#define DOEXT1 0x02BAA // Reserved 1, ACcess PoinTeR (HP48GX and later) +#define DOEXT2 0x02BCC // Reserved 2, Font (HP49G) +#define DOEXT3 0x02BEE // Reserved 3 +#define DOEXT4 0x02C10 // Reserved 4 +#define DOCOL 0x02D9D // Program +#define DOCODE 0x02DCC // Code +#define DOIDNT 0x02E48 // Global Name +#define DOLAM 0x02E6D // Local Name +#define DOROMP 0x02E92 // XLIB Name +#define SEMI 0x0312B // ; + +#define GARBAGECOL 0x0613E // =GARBAGECOL entry for HP48S/G and HP49G + +// check for Metakernel version +#define METAKERNEL Metakernel() + +// search for "MDGKER:MK2.30" or "MDGKER:PREVIE" in port1 of a HP48GX +static BOOL Metakernel(VOID) +{ + BOOL bMkDetect = FALSE; + + // card in slot1 of a HP48GX enabled + if (cCurrentRomType=='G' && Port1 && Chipset.cards_status & PORT1_PRESENT) + { + // check for Metakernel string "MDGKER:" + if (!strncmp((LPCSTR) &Port1[12],"\xD\x4\x4\x4\x7\x4\xB\x4\x5\x4\x2\x5\xA\x3",14)) + { + bMkDetect = TRUE; // Metakernel detected + // check for "MK" + if (!strncmp((LPCSTR) &Port1[26],"\xD\x4\xB\x4",4)) + { + // get version number + WORD wVersion = ((Port1[30] * 10) + Port1[34]) * 10 + + Port1[36]; + + // version newer then V2.30, then compatible with HP OS + bMkDetect = (wVersion <= 230); + } + } + } + return bMkDetect; +} + +static DWORD RPL_GarbageCol(VOID) // RPL variables must be in system RAM +{ + CHIPSET OrgChipset; + DWORD dwAVMEM; + + // only for HP48SX, HP48GX, HP49G, HP48gII and HP49g+ + _ASSERT( cCurrentRomType == 'S' || cCurrentRomType == 'G' || cCurrentRomType == 'X' + || cCurrentRomType == '2' || cCurrentRomType == 'Q'); + + OrgChipset = Chipset; // save original chipset + + // entry for =GARBAGECOL + Chipset.P = 0; // P=0 + Chipset.mode_dec = FALSE; // hex mode + Chipset.pc = GARBAGECOL; // =GARBAGECOL entry + rstkpush(0xFFFFF); // return address for stopping + + while (Chipset.pc != 0xFFFFF) // wait for stop address + { + EvalOpcode(FASTPTR(Chipset.pc)); // execute opcode + } + + dwAVMEM = Npack(Chipset.C,5); // available AVMEM + Chipset = OrgChipset; // restore original chipset + return dwAVMEM; +} + +BOOL RPL_GetSystemFlag(INT nFlag) +{ + DWORD dwAddr; + BYTE byMask,byFlag; + + _ASSERT(nFlag > 0); // first flag is 1 + + // calculate memory address and bit mask + dwAddr = SYSTEMFLAGS + (nFlag - 1) / 4; + byMask = 1 << ((nFlag - 1) & 0x3); + + Npeek(&byFlag,dwAddr,sizeof(byFlag)); + return (byFlag & byMask) != 0; +} + +DWORD RPL_SkipOb(DWORD d) +{ + BYTE X[8]; + DWORD n, l; + + Npeek(X,d,5); + n = Npack(X, 5); // read prolog + switch (n) + { + case DOFLASHP: l = (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') ? 5 : 12; break; // Flash PTR (HP49G) // CdB for HP: add apples + case DOBINT: l = 10; break; // System Binary + case DOREAL: l = 21; break; // Real + case DOEREL: l = 26; break; // Long Real + case DOCMP: l = 37; break; // Complex + case DOECMP: l = 47; break; // Long Complex + case DOCHAR: l = 7; break; // Character + case DOROMP: l = 11; break; // XLIB Name + case DOMATRIX: // Symbolic matrix (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOLIST: // List + case DOSYMB: // Algebraic + case DOEXT: // Unit + case DOCOL: // Program + n=d+5; + do + { + d=n; n=RPL_SkipOb(d); + } while (d!=n); + return n+5; + case SEMI: return d; // SEMI + case DOIDNT: // Global Name + case DOLAM: // Local Name + case DOTAG: // Tagged + Npeek(X,d+5,2); n = 7 + Npack(X,2)*2; + return RPL_SkipOb(d+n); + case DORRP: // Directory + d+=8; + n = Read5(d); + if (n==0) + { + return d+5; + } + else + { + d+=n; + Npeek(X,d,2); + n = Npack(X,2)*2 + 4; + return RPL_SkipOb(d+n); + } + case DOINT: // Precision Integer (HP49G) + case DOAPLET: // Aplet (HP49G) + case DOMINIFONT: // Mini Font (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOARRY: // Array + case DOLNKARRY: // Linked Array + case DOCSTR: // String + case DOHSTR: // Binary Integer + case DOGROB: // Graphic + case DOLIB: // Library + case DOBAK: // Backup + case DOEXT0: // Library Data + case DOEXT1: // Reserved 1 + if (n == DOEXT1 && cCurrentRomType != 'S') + { + // on HP48G series and later interpreted as DOACPTR + l = 15; break; // ACcess PoinTeR + break; + } + case DOEXT2: // Reserved 2, Font (HP49G) + case DOEXT3: // Reserved 3 + case DOEXT4: // Reserved 4 + case DOCODE: // Code + l = 5+Read5(d+5); + break; + case DOLNGREAL: // Precision Real (HP49G) + l = 5; + if (cCurrentRomType=='X') + { + l += Read5(d+l); + l += Read5(d+l); + } + break; + case DOLNGCMP: // Precision Complex (HP49G) + l = 5; + if (cCurrentRomType=='X') + { + l += Read5(d+l); + l += Read5(d+l); + l += Read5(d+l); + l += Read5(d+l); + } + break; + default: return d+5; + } + return d+l; +} + +DWORD RPL_ObjectSize(BYTE *o,DWORD s) +{ + #define SERIES49 (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') + + DWORD n, l = 0; + + if (s < 5) return BAD_OB; // size too small for prolog + n = Npack(o,5); // read prolog + switch (n) + { + case DOFLASHP: l = (SERIES49) ? 12 : 5; break; // Flash PTR (HP49G) + case DOBINT: l = 10; break; // System Binary + case DOREAL: l = 21; break; // Real + case DOEREL: l = 26; break; // Long Real + case DOCMP: l = 37; break; // Complex + case DOECMP: l = 47; break; // Long Complex + case DOCHAR: l = 7; break; // Character + case DOROMP: l = 11; break; // XLIB Name + case DOMATRIX: // Symbolic matrix (HP49G) + if (!SERIES49) + { + l = 5; + break; + } + case DOLIST: // List + case DOSYMB: // Algebraic + case DOEXT: // Unit + case DOCOL: // Program + n = 5; // prolog length + do + { + l += n; + if (l > s) return BAD_OB; // prevent negative size argument + n = RPL_ObjectSize(o+l,s-l); // get new object + if (n == BAD_OB) return BAD_OB; // buffer overflow + } + while (n); + l += 5; + break; + case SEMI: l = 0; break; // SEMI + case DOIDNT: // Global Name + case DOLAM: // Local Name + case DOTAG: // Tagged + if (s < 5 + 2) return BAD_OB; + l = 7 + Npack(o+5,2) * 2; // prolog + name length + if (l > s) return BAD_OB; // prevent negative size argument + n = RPL_ObjectSize(o+l,s-l); // get new object + if (n == BAD_OB) return BAD_OB; // buffer overflow + l += n; + break; + case DORRP: // Directory + if (s < 8 + 5) return BAD_OB; + n = Npack(o+8,5); + if (n == 0) // empty dir + { + l = 13; + } + else + { + l = 8 + n; + if (s < l + 2) return BAD_OB; + n = Npack(o+l,2) * 2 + 4; + l += n; + if (l > s) return BAD_OB; // prevent negative size argument + n = RPL_ObjectSize(o+l,s-l); // next rrp + if (n == BAD_OB) return BAD_OB; // buffer overflow + l += n; + } + break; + case DOINT: // Precision Integer (HP49G) + case DOAPLET: // Aplet (HP49G) + case DOMINIFONT: // Mini Font (HP49G) + if (!SERIES49) + { + l = 5; + break; + } + case DOARRY: // Array + case DOLNKARRY: // Linked Array + case DOCSTR: // String + case DOHSTR: // Binary Integer + case DOGROB: // Graphic + case DOLIB: // Library + case DOBAK: // Backup + case DOEXT0: // Library Data + case DOEXT1: // Reserved 1 + if (n == DOEXT1 && cCurrentRomType != 'S') + { + // on HP48G series and later interpreted as DOACPTR + l = 15; break; // ACcess PoinTeR + break; + } + case DOEXT2: // Reserved 2, Font (HP49G) + case DOEXT3: // Reserved 3 + case DOEXT4: // Reserved 4 + case DOCODE: // Code + if (s < 5 + 5) return BAD_OB; + l = 5 + Npack(o+5,5); + break; + case DOLNGREAL: // Precision Real (HP49G) + l = 5; + if (SERIES49) + { + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + } + break; + case DOLNGCMP: // Precision Complex (HP49G) + l = 5; + if (SERIES49) + { + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + if (s < l + 5) return BAD_OB; + l += Npack(o+l,5); + } + break; + default: l = 5; + } + return (s >= l) ? l : BAD_OB; + + #undef SERIES49 +} + +DWORD RPL_CreateTemp(DWORD l,BOOL bGarbageCol) +{ + DWORD a, b, c; + BYTE *p; + + l += 6; // memory for link field (5) + marker (1) and end + b = Read5(RSKTOP); // tail address of rtn stack + c = Read5(DSKTOP); // top of data stack + if ((b+l)>c && bGarbageCol) // there's not enough memory to move DSKTOP + { + RPL_GarbageCol(); // do a garbage collection + b = Read5(RSKTOP); // reload tail address of rtn stack + c = Read5(DSKTOP); // reload top of data stack + } + if ((b+l)>c) return 0; // check if now there's enough memory to move DSKTOP + a = Read5(TEMPTOP); // tail address of top object + Write5(TEMPTOP, a+l); // adjust new end of top object + Write5(RSKTOP, b+l); // adjust new end of rtn stack + Write5(AVMEM, (c-b-l)/5); // calculate free memory (*5 nibbles) + p = (LPBYTE) malloc(b-a); // move down rtn stack + Npeek(p,a,b-a); + Nwrite(p,a+l,b-a); + free(p); + Write5(a+l-5,l); // set object length field + return (a+1); // return base address of new object +} + +UINT RPL_Depth(VOID) +{ + return (Read5(EDITLINE) - Read5(DSKTOP)) / 5 - 1; +} + +DWORD RPL_Pick(UINT l) +{ + DWORD stkp; + + _ASSERT(l > 0); // first stack element is one + if (l == 0) return 0; + if (METAKERNEL) ++l; // Metakernel support + if (RPL_Depth() < l) return 0; // not enough elements on stack + stkp = Read5(DSKTOP) + (l-1)*5; + return Read5(stkp); // return object address +} + +VOID RPL_Replace(DWORD n) +{ + DWORD stkp; + + stkp = Read5(DSKTOP); + if (METAKERNEL) stkp+=5; // Metakernel support + Write5(stkp,n); + return; +} + +VOID RPL_Push(UINT l,DWORD n) +{ + UINT i; + DWORD stkp, avmem; + + if (l > RPL_Depth() + 1) return; // invalid stack level + + avmem = Read5(AVMEM); // amount of free memory + if (avmem == 0) return; // no memory free + avmem--; // fetch memory + Write5(AVMEM,avmem); // save new amount of free memory + + if (METAKERNEL) ++l; // Metakernel, save MK object on stack level 1 + + stkp = Read5(DSKTOP) - 5; // get pointer of new stack level 1 + Write5(DSKTOP,stkp); // save it + + for (i = 1; i < l; ++i) // move down stack level entries before insert pos + { + Write5(stkp,Read5(stkp+5)); // move down stack level entry + stkp += 5; // next stack entry + } + + Write5(stkp,n); // save pointer of new object on given stack level + return; +} diff --git a/app/src/main/cpp/SERIAL.C b/app/src/main/cpp/SERIAL.C new file mode 100644 index 0000000..ecdde4a --- /dev/null +++ b/app/src/main/cpp/SERIAL.C @@ -0,0 +1,388 @@ +/* + * Serial.c + * + * This file is part of Emu48 + * + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +#define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE)) + +// state of USRQ +#define NINT2ERBZ ((Chipset.IORam[IOC] & (SON | ERBZ)) == (SON | ERBZ) && (Chipset.IORam[RCS] & RBZ) != 0) +#define NINT2ERBF ((Chipset.IORam[IOC] & (SON | ERBF)) == (SON | ERBF) && (Chipset.IORam[RCS] & RBF) != 0) +#define NINT2ETBE ((Chipset.IORam[IOC] & (SON | ETBE)) == (SON | ETBE) && (Chipset.IORam[TCS] & TBF) == 0) + +#define NINT2USRQ (NINT2ERBZ || NINT2ERBF || NINT2ETBE) + +static HANDLE hComm = NULL; + +static HANDLE hCThreadTxd; +static HANDLE hCThreadEv; + +static HANDLE hEventTxd; +static BOOL bWriting; +static BYTE tbr; + +static BOOL bReading; +static BYTE cBuffer[32]; +static WORD nRp; +static DWORD dwBytesRead; + +static DWORD WINAPI TransmitThread(LPVOID pParam) +{ + OVERLAPPED osWr = { 0 }; + + osWr.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + while (bWriting) + { + WaitForSingleObject(hEventTxd,INFINITE); + if (bWriting) + { + DWORD dwWritten; + if (!WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&osWr)) + if (GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(hComm,&osWr,&dwWritten,TRUE); + } + } + + CloseHandle(osWr.hEvent); // close write event handle + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +static DWORD WINAPI EventThread(LPVOID pParam) +{ + DWORD dwEvent; + + bReading = TRUE; // flag for SerialThread started + while (bReading) + { + _ASSERT(hComm != NULL); + WaitCommEvent(hComm,&dwEvent,NULL); // wait for serial event + if (dwEvent & EV_RXCHAR) // signal char received + { + CommReceive(); // get data + // interrupt request and emulation thread down + if (Chipset.SoftInt && Chipset.Shutdn) + { + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + } + if (dwEvent & EV_TXEMPTY) // signal transmit buffer empty + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + CommTransmit(); // check for new char to transmit + } + if (dwEvent & EV_ERR) // signal error received + { + DWORD dwError; + + ClearCommError(hComm,&dwError,NULL); + if (dwError & (CE_FRAME | CE_OVERRUN | CE_BREAK)) + IOBit(RCS,RER,TRUE); // receiver error + } + } + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort) +{ + COMMTIMEOUTS CommTimeouts = { MAXDWORD, 0L, 0L, 0L, 0L }; + + LPCTSTR strPort = (Chipset.IORam[IR_CTRL] & EIRU) ? strIrPort : strWirePort; + + _ASSERT(Chipset.IORam[IOC] & SON); // UART on + CommClose(); // close port if already open + + if (lstrcmp(strPort, _T(NO_SERIAL))) // port defined + { + TCHAR szDevice[256] = _T("\\\\.\\"); + + // check if device buffer is big enough + _ASSERT(lstrlen(szDevice) + lstrlen(strPort) < (INT) ARRAYSIZEOF(szDevice)); + if (lstrlen(szDevice) + lstrlen(strPort) >= (INT) ARRAYSIZEOF(szDevice)) + return hComm != NULL; + + _tcscat(szDevice,strPort); // device name + hComm = CreateFile(szDevice, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if (hComm != INVALID_HANDLE_VALUE) + { + DWORD dwThreadId; + + nRp = 0; // reset receiver state + dwBytesRead = 0L; + + SetCommTimeouts(hComm,&CommTimeouts); + CommSetBaud(); + + CommTxBRK(); // update BRK condition + + // event to transmit character + hEventTxd = CreateEvent(NULL,FALSE,FALSE,NULL); + + // create char transmit handler + bWriting = TRUE; + hCThreadTxd = CreateThread(NULL,0,&TransmitThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadTxd); + SetThreadPriority(hCThreadTxd,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadTxd); // start thread + + // create Comm event handler + bReading = FALSE; + SetCommMask(hComm,EV_RXCHAR | EV_TXEMPTY | EV_ERR); // event on RX, TX, error + hCThreadEv = CreateThread(NULL,0,&EventThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadEv); + SetThreadPriority(hCThreadEv,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadEv); // start thread + while (!bReading) Sleep(0); // wait for SerialThread started + } + else + hComm = NULL; + } + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("COM port %s.\n"),hComm ? _T("opened"): _T("open error")); + OutputDebugString(buffer); + } + #endif + return hComm != NULL; +} + +VOID CommClose(VOID) +{ + if (hComm != NULL) // port open + { + // workaround to fix problems with some Kermit server programs + // reason: on one hand we have the character transmitting time base on the + // selected baudrate, on the other hand the time between sending the last + // character and closing the port. The last time is much longer on the real + // calculator than on the emulator running at full speed, therefore the + // slow down time on the emulator + Sleep(25); // slow down time + + bReading = FALSE; // kill event thread + SetCommMask(hComm,0L); // clear all events and force WaitCommEvent to return + WaitForSingleObject(hCThreadEv,INFINITE); + CloseHandle(hCThreadEv); + + bWriting = FALSE; // kill write thread + SetEvent(hEventTxd); // continue write thread + WaitForSingleObject(hCThreadTxd,INFINITE); + CloseHandle(hCThreadTxd); + + CloseHandle(hEventTxd); // close Txd event + CloseHandle(hComm); // close port + hComm = NULL; + #if defined DEBUG_SERIAL + OutputDebugString(_T("COM port closed.\n")); + #endif + } + return; +} + +VOID CommSetBaud(VOID) +{ + if (hComm != NULL) + { + const DWORD dwBaudrates[] = { 1200, 1920, 2400, 3840, 4800, 7680, 9600, 15360, + 14400, 19200, 38400, 57600, 115200, 115200, 115200, 115200 }; + + DCB dcb; + UINT uBaudIndex; + + uBaudIndex = isModelApple(cCurrentRomType) + ? Chipset.IORam[BAUD] + : Chipset.IORam[BAUD] & 0x7; + + ZeroMemory(&dcb,sizeof(dcb)); + dcb.DCBlength = sizeof(dcb); + dcb.BaudRate = dwBaudrates[uBaudIndex]; + dcb.fBinary = TRUE; + dcb.fParity = TRUE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fOutX = FALSE; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.fAbortOnError = FALSE; // may changed in further implementations + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; // no parity check, emulated by software + dcb.StopBits = TWOSTOPBITS; + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("CommsetBaud: %ld\n"),dcb.BaudRate); + OutputDebugString(buffer); + } + #endif + + SetCommState(hComm,&dcb); + } + return; +} + +BOOL UpdateUSRQ(VOID) // USRQ handling +{ + BOOL bUSRQ = NINT2USRQ; + IOBit(SRQ1,USRQ,bUSRQ); // update USRQ bit + return bUSRQ; +} + +VOID CommTxBRK(VOID) +{ + if (Chipset.IORam[TCS] & BRK) // BRK condition + { + if (hComm != NULL) // com port open + { + // abort data transfer + PurgeComm(hComm,PURGE_TXABORT | PURGE_TXCLEAR); + SetCommBreak(hComm); // set into BRK state + } + + // TBF and TBZ bits of TCS are undefined + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + dwBytesRead = nRp = 0; // clear receive buffer + cBuffer[dwBytesRead++] = 0; // save character in receive buffer + + CommReceive(); // receive available byte + IOBit(RCS,RER,TRUE); // receiver error (no stop bit) + } + } + else + { + if (hComm != NULL) // com port open + { + ClearCommBreak(hComm); // clear BRK state + } + } + return; +} + +VOID CommTransmit(VOID) +{ + BOOL bTxChar = FALSE; + + EnterCriticalSection(&csTxdLock); + if ( (Chipset.IORam[TCS] & TBZ) == 0 // transmitter not busy + && (Chipset.IORam[TCS] & TBF) != 0) // transmit buffer full + { + tbr = (Chipset.IORam[TBR_MSB] << 4) | Chipset.IORam[TBR_LSB]; + + IOBit(TCS,TBF,FALSE); // clear transmit buffer full bit + IOBit(TCS,TBZ,TRUE); // set transmitter busy bit + + bTxChar = TRUE; + } + LeaveCriticalSection(&csTxdLock); + + if (bTxChar) // character to transmit + { + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + if (isprint(tbr)) + wsprintf(buffer,_T("-> '%c'\n"),tbr); + else + wsprintf(buffer,_T("-> %02X\n"),tbr); + OutputDebugString(buffer); + } + #endif + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + if (dwBytesRead == 0) nRp = 0; // no character received, reset read pointer + cBuffer[nRp+dwBytesRead] = tbr; // save character in receive buffer + ++dwBytesRead; + + CommReceive(); // receive available byte + } + + if (hComm != NULL) // com port open + { + SetEvent(hEventTxd); // write TBR byte + } + else + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + } + } + if (UpdateUSRQ()) // update USRQ bit + INTERRUPT; + return; +} + +VOID CommReceive(VOID) +{ + OVERLAPPED os = { 0 }; + + if (!(Chipset.IORam[IOC] & SON)) // UART off + { + dwBytesRead = 0L; // no bytes received + return; + } + + EnterCriticalSection(&csRecvLock); + do + { + if (Chipset.IORam[RCS] & RBF) // receive buffer full + break; + + // reject reading if com port is closed and not whole operation + if (hComm && dwBytesRead == 0L) // com port open and buffer empty + { + if (ReadFile(hComm,&cBuffer,sizeof(cBuffer),&dwBytesRead,&os) == FALSE) + dwBytesRead = 0L; + else // bytes received + nRp = 0; // reset read pointer + } + + if (dwBytesRead == 0L) // receive buffer empty + break; + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + if (isprint(cBuffer[nRp])) + wsprintf(buffer,_T("<- '%c'\n"),cBuffer[nRp]); + else + wsprintf(buffer,_T("<- %02X\n"),cBuffer[nRp]); + OutputDebugString(buffer); + } + #endif + + Chipset.IORam[RBR_MSB] = (cBuffer[nRp] >> 4); + Chipset.IORam[RBR_LSB] = (cBuffer[nRp] & 0x0f); + ++nRp; + --dwBytesRead; + + Chipset.IORam[RCS] |= RBF; // receive buffer full + if (UpdateUSRQ()) // update USRQ bit + INTERRUPT; + } + while (FALSE); + LeaveCriticalSection(&csRecvLock); + return; +} diff --git a/app/src/main/cpp/SETTINGS.C b/app/src/main/cpp/SETTINGS.C new file mode 100644 index 0000000..6d5d539 --- /dev/null +++ b/app/src/main/cpp/SETTINGS.C @@ -0,0 +1,313 @@ +/* + * settings.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "i28f160.h" + +// #define REGISTRY // use registry instead of *.ini file + +//################ +//# +//# Low level subroutines +//# +//################ + +#if !defined REGISTRY + +// INI-file handling + +#if !defined EMU48_INI + #define EMU48_INI "Emu48.ini" +#endif + +#define ReadString(sec,key,dv,v,sv) GetPrivateProfileString(sec,key,dv,v,sv,_T(EMU48_INI)) +#define ReadInt(sec,key,dv) GetPrivateProfileInt(sec,key,dv,_T(EMU48_INI)); +#define WriteString(sec,key,v) WritePrivateProfileString(sec,key,v,_T(EMU48_INI)) +#define WriteInt(sec,key,v) WritePrivateProfileInt(sec,key,v,_T(EMU48_INI)) +#define DelKey(sec,key) WritePrivateProfileString(sec,key,NULL,_T(EMU48_INI)) + +static BOOL WritePrivateProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue, LPCTSTR lpszFilename) +{ + TCHAR s[16]; + wsprintf(s,_T("%i"),nValue); + return WritePrivateProfileString(lpszSection, lpszEntry, s, lpszFilename); +} + +#else + +// registry handling + +#if !defined REGISTRYKEY + #define REGISTRYKEY "Software\\Emu48" +#endif + +#define ReadString(sec,key,dv,v,sv) GetRegistryString(sec,key,dv,v,sv) +#define ReadInt(sec,key,dv) GetRegistryInt(sec,key,dv) +#define WriteString(sec,key,v) WriteReg(sec,key,REG_SZ,(BYTE *) v,(lstrlen(v)+1) * sizeof(*v)) +#define WriteInt(sec,key,v) WriteReg(sec,key,REG_DWORD,(BYTE *) &v,sizeof(int)) +#define DelKey(sec,key) DelReg(sec,key) + +static VOID ReadReg(LPCTSTR lpSubKey, LPCTSTR lpValueName, LPBYTE lpData, DWORD *pdwSize) +{ + TCHAR lpKey[256] = _T(REGISTRYKEY) _T("\\"); + + DWORD retCode,dwType; + HKEY hKey; + + lstrcat(lpKey, lpSubKey); // full registry key + + retCode = RegOpenKeyEx(HKEY_CURRENT_USER, + lpKey, + 0, + KEY_QUERY_VALUE, + &hKey); + if (retCode == ERROR_SUCCESS) + { + retCode = RegQueryValueEx(hKey,lpValueName,NULL,&dwType,lpData,pdwSize); + RegCloseKey(hKey); + } + + if (retCode != ERROR_SUCCESS) // registry entry not found + *pdwSize = 0; // return zero size + return; +} + +static BOOL WriteReg(LPCTSTR lpSubKey, LPCTSTR lpValueName, DWORD dwType, CONST BYTE *lpData, DWORD cbData) +{ + TCHAR lpKey[256] = _T(REGISTRYKEY) _T("\\"); + + DWORD retCode; + HKEY hKey; + DWORD dwDisposition; + + lstrcat(lpKey, lpSubKey); // full registry key + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, + lpKey, + 0,_T(""), + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, + &hKey, + &dwDisposition); + _ASSERT(retCode == ERROR_SUCCESS); + + RegSetValueEx(hKey,lpValueName,0,dwType,lpData,cbData); + RegCloseKey(hKey); + return retCode == ERROR_SUCCESS; +} + +static BOOL DelReg(LPCTSTR lpSubKey, LPCTSTR lpValueName) +{ + TCHAR lpKey[256] = _T(REGISTRYKEY) _T("\\"); + + DWORD retCode; + HKEY hKey; + + lstrcat(lpKey, lpSubKey); // full registry key + + retCode = RegOpenKeyEx(HKEY_CURRENT_USER, + lpKey, + 0, + KEY_SET_VALUE, + &hKey); + if (retCode == ERROR_SUCCESS) + { + retCode = RegDeleteValue(hKey,lpValueName); + RegCloseKey(hKey); + } + return retCode == ERROR_SUCCESS; +} + +static DWORD GetRegistryString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpDefault, LPTSTR lpData, DWORD dwSize) +{ + // buffer size in bytes + DWORD dwBufSize = dwSize * sizeof(*lpData); + + ReadReg(lpszSection,lpszEntry,(LPBYTE) lpData,&dwBufSize); + if (dwBufSize == 0) + { + lstrcpyn(lpData,lpDefault,dwSize); + dwSize = lstrlen(lpData); + } + else + { + dwSize = (dwBufSize / sizeof(*lpData)) - 1; + } + return dwSize; +} + +static UINT GetRegistryInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nDefault) +{ + UINT nValue; + DWORD dwSize = sizeof(nValue); + + ReadReg(lpszSection,lpszEntry,(LPBYTE) &nValue,&dwSize); + return dwSize ? nValue : nDefault; +} + +#endif + + +//################ +//# +//# Public functions +//# +//################ + +VOID ReadSettings(VOID) +{ + // Files + ReadString(_T("Files"),_T("Emu48Directory"),szCurrentDirectory,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + ReadString(_T("Files"),_T("RomDirectory"),_T(""),szRomDirectory,ARRAYSIZEOF(szRomDirectory)); + bAutoSave = ReadInt(_T("Files"),_T("AutoSave"),bAutoSave); + bAutoSaveOnExit = ReadInt(_T("Files"),_T("AutoSaveOnExit"),bAutoSaveOnExit); + bSaveDefConfirm = ReadInt(_T("Files"),_T("SaveDefaultConfirm"),bSaveDefConfirm); + bStartupBackup = ReadInt(_T("Files"),_T("StartupBackup"),bStartupBackup); + bLoadObjectWarning = ReadInt(_T("Files"),_T("LoadObjectWarning"),bLoadObjectWarning); + // Port2 + bPort2IsShared = ReadInt(_T("Port2"),_T("IsShared"),bPort2IsShared); + ReadString(_T("Port2"),_T("Filename"),_T("SHARED.BIN"),szPort2Filename,ARRAYSIZEOF(szPort2Filename)); + // KML + bAlwaysDisplayLog = ReadInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); + // Debugger + wInstrSize = ReadInt(_T("Debugger"),_T("LastInstrBufSize"),wInstrSize); + // Disassembler + disassembler_mode = ReadInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); + disassembler_symb = ReadInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb); + // Emulator + bShowTitle = ReadInt(_T("Emulator"),_T("ShowTitle"),bShowTitle); + bShowMenu = ReadInt(_T("Emulator"),_T("ShowMenu"),bShowMenu); + bAlwaysOnTop = ReadInt(_T("Emulator"),_T("AlwaysOnTop"),bAlwaysOnTop); + bActFollowsMouse = ReadInt(_T("Emulator"),_T("ActivationFollowsMouse"),bActFollowsMouse); + bClientWinMove = ReadInt(_T("Emulator"),_T("ClientWinMove"),bClientWinMove); + bSingleInstance = ReadInt(_T("Emulator"),_T("SingleInstance"),bSingleInstance); + bRealSpeed = ReadInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed); + dwSXCycles = ReadInt(_T("Emulator"),_T("SXCycles"),dwSXCycles); + dwGXCycles = ReadInt(_T("Emulator"),_T("GXCycles"),dwGXCycles); + dwGPCycles = ReadInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples + dwG2Cycles = ReadInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples + dwKeyMinDelay = ReadInt(_T("Emulator"),_T("KeyMinDelay"),dwKeyMinDelay); + dwWakeupDelay = ReadInt(_T("Emulator"),_T("WakeupDelay"),dwWakeupDelay); + bGrayscale = ReadInt(_T("Emulator"),_T("Grayscale"),bGrayscale); + uWaveDevId = ReadInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId); + dwWaveVol = ReadInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol); + dwWaveTime = ReadInt(_T("Emulator"),_T("WaveTime"),dwWaveTime); + // LowBat + bLowBatDisable = ReadInt(_T("LowBat"),_T("Disable"),bLowBatDisable); + // Macro + bMacroRealSpeed = ReadInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed); + nMacroTimeout = ReadInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout); + dwMacroMinDelay = ReadInt(_T("Macro"),_T("KeyMinDelay"),dwMacroMinDelay); + // IrPrinter + ReadString(_T("IrPrinter"),_T("Address"),szUdpServer,szUdpServer,ARRAYSIZEOF(szUdpServer)); + wUdpPort = ReadInt(_T("IrPrinter"),_T("Port"),wUdpPort); + // Serial + ReadString(_T("Serial"),_T("Wire"),_T(NO_SERIAL),szSerialWire,ARRAYSIZEOF(szSerialWire)); + ReadString(_T("Serial"),_T("Ir"),_T(NO_SERIAL),szSerialIr,ARRAYSIZEOF(szSerialIr)); + // ROM + bRomWriteable = ReadInt(_T("ROM"),_T("Writeable"),bRomWriteable); + bWP = ReadInt(_T("ROM"),_T("WP#"),bWP); + return; +} + +VOID WriteSettings(VOID) +{ + // Files + WriteString(_T("Files"),_T("Emu48Directory"),szEmuDirectory); + WriteInt(_T("Files"),_T("AutoSave"),bAutoSave); + WriteInt(_T("Files"),_T("AutoSaveOnExit"),bAutoSaveOnExit); + WriteInt(_T("Files"),_T("SaveDefaultConfirm"),bSaveDefConfirm); + WriteInt(_T("Files"),_T("StartupBackup"),bStartupBackup); + WriteInt(_T("Files"),_T("LoadObjectWarning"),bLoadObjectWarning); + // Port2 + WriteInt(_T("Port2"),_T("IsShared"),bPort2IsShared); + WriteString(_T("Port2"),_T("Filename"),szPort2Filename); + // KML + WriteInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); + // Debugger + WriteInt(_T("Debugger"),_T("LastInstrBufSize"),wInstrSize); + // Disassembler + WriteInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); + WriteInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb); + // Emulator + WriteInt(_T("Emulator"),_T("ShowTitle"),bShowTitle); + WriteInt(_T("Emulator"),_T("ShowMenu"),bShowMenu); + WriteInt(_T("Emulator"),_T("AlwaysOnTop"),bAlwaysOnTop); + WriteInt(_T("Emulator"),_T("ActivationFollowsMouse"),bActFollowsMouse); + WriteInt(_T("Emulator"),_T("ClientWinMove"),bClientWinMove); + WriteInt(_T("Emulator"),_T("SingleInstance"),bSingleInstance); + WriteInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed); + WriteInt(_T("Emulator"),_T("SXCycles"),dwSXCycles); + WriteInt(_T("Emulator"),_T("GXCycles"),dwGXCycles); + WriteInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples + WriteInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples + WriteInt(_T("Emulator"),_T("KeyMinDelay"),dwKeyMinDelay); + WriteInt(_T("Emulator"),_T("WakeupDelay"),dwWakeupDelay); + WriteInt(_T("Emulator"),_T("Grayscale"),bGrayscale); + WriteInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId); + WriteInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol); + WriteInt(_T("Emulator"),_T("WaveTime"),dwWaveTime); + // LowBat + WriteInt(_T("LowBat"),_T("Disable"),bLowBatDisable); + // Macro + WriteInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed); + WriteInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout); + WriteInt(_T("Macro"),_T("KeyMinDelay"),dwMacroMinDelay); + // IrPrinter + WriteString(_T("IrPrinter"),_T("Address"),szUdpServer); + WriteInt(_T("IrPrinter"),_T("Port"),wUdpPort); + // Serial + WriteString(_T("Serial"),_T("Wire"),szSerialWire); + WriteString(_T("Serial"),_T("Ir"),szSerialIr); + // ROM + WriteInt(_T("ROM"),_T("Writeable"),bRomWriteable); + return; +} + +VOID ReadLastDocument(LPTSTR szFilename, DWORD nSize) +{ + ReadString(_T("Files"),_T("LastDocument"),_T(""),szFilename,nSize); + return; +} + +VOID WriteLastDocument(LPCTSTR szFilename) +{ + WriteString(_T("Files"),_T("LastDocument"),szFilename); + return; +} + +VOID ReadSettingsString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpDefault, LPTSTR lpData, DWORD dwSize) +{ + ReadString(lpszSection,lpszEntry,lpDefault,lpData,dwSize); + return; +} + +VOID WriteSettingsString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPTSTR lpData) +{ + WriteString(lpszSection,lpszEntry,lpData); + return; +} + +INT ReadSettingsInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nDefault) +{ + return ReadInt(lpszSection,lpszEntry,nDefault); +} + +VOID WriteSettingsInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nValue) +{ + WriteInt(lpszSection,lpszEntry,nValue); + return; +} + +VOID DelSettingsKey(LPCTSTR lpszSection, LPCTSTR lpszEntry) +{ + DelKey(lpszSection,lpszEntry); + return; +} diff --git a/app/src/main/cpp/SNDDEF.H b/app/src/main/cpp/SNDDEF.H new file mode 100644 index 0000000..500232d --- /dev/null +++ b/app/src/main/cpp/SNDDEF.H @@ -0,0 +1,102 @@ +/* + * snddef.h + * + * This file is part of Emu48 + * + * Copyright (C) 2015 Christoph Gießelink + * + */ + +#include + +#if _MSC_VER >= 1600 // valid for VS2010 and later + +#include +#include + +#else // create the necessary definitions manually + +// +// IKsPropertySet +// + +#ifndef _IKsPropertySet_ +#define _IKsPropertySet_ + +#ifdef __cplusplus +struct IKsPropertySet; +#endif // __cplusplus + +typedef struct IKsPropertySet *LPKSPROPERTYSET; + +DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); + +#undef INTERFACE +#define INTERFACE IKsPropertySet + +DECLARE_INTERFACE_(IKsPropertySet, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IKsPropertySet methods + STDMETHOD(Get) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength, + LPVOID pPropertyData, ULONG ulDataLength, PULONG pulBytesReturned) PURE; + STDMETHOD(Set) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength, + LPVOID pPropertyData, ULONG ulDataLength) PURE; + STDMETHOD(QuerySupport) (THIS_ REFGUID rguidPropSet, ULONG ulId, PULONG pulTypeSupport) PURE; +}; + +#endif // _IKsPropertySet_ + +// DirectSound Configuration Component GUID {11AB3EC0-25EC-11d1-A4D8-00C04FC28ACA} +DEFINE_GUID(CLSID_DirectSoundPrivate, 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca); + +// DirectSound Device Properties {84624F82-25EC-11d1-A4D8-00C04FC28ACA} +DEFINE_GUID(DSPROPSETID_DirectSoundDevice, 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca); + +typedef enum +{ + DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_A = 1, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_1 = 2, + DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_1 = 3, + DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_W = 4, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_A = 5, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W = 6, + DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_A = 7, + DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W = 8, +} DSPROPERTY_DIRECTSOUNDDEVICE; + +#ifdef UNICODE +#define DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W +#else // UNICODE +#define DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_A +#endif // UNICODE + +typedef enum +{ + DIRECTSOUNDDEVICE_TYPE_EMULATED, + DIRECTSOUNDDEVICE_TYPE_VXD, + DIRECTSOUNDDEVICE_TYPE_WDM +} DIRECTSOUNDDEVICE_TYPE; + +typedef enum +{ + DIRECTSOUNDDEVICE_DATAFLOW_RENDER, + DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE +} DIRECTSOUNDDEVICE_DATAFLOW; + +typedef struct _DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA +{ + DIRECTSOUNDDEVICE_TYPE Type; // Device type + DIRECTSOUNDDEVICE_DATAFLOW DataFlow; // Device dataflow + GUID DeviceId; // DirectSound device id + LPTSTR Description; // Device description + LPTSTR Module; // Device driver module + LPTSTR Interface; // Device interface + ULONG WaveDeviceId; // Wave device id +} DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA, *PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA; + +#endif diff --git a/app/src/main/cpp/SNDENUM.C b/app/src/main/cpp/SNDENUM.C new file mode 100644 index 0000000..eb9adda --- /dev/null +++ b/app/src/main/cpp/SNDENUM.C @@ -0,0 +1,256 @@ +/* + * SndEnum.c + * + * This file is part of Emu48 + * + * Copyright (C) 2015 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "snddef.h" + +typedef HRESULT (WINAPI *LPFNDLLGETCLASSOBJECT)(REFCLSID,REFIID,LPVOID *); +static LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL; + +// +// create a IKsPropertySet interface +// +static __inline HRESULT DirectSoundPrivateCreate(LPKSPROPERTYSET *ppKsPropertySet) +{ + LPCLASSFACTORY pClassFactory = NULL; + HRESULT hr; + + // create a class factory object + #if defined __cplusplus + hr = pfnDllGetClassObject(CLSID_DirectSoundPrivate,IID_IClassFactory,(LPVOID *) &pClassFactory); + #else + hr = pfnDllGetClassObject(&CLSID_DirectSoundPrivate,&IID_IClassFactory,(LPVOID *) &pClassFactory); + #endif + + // create the DirectSoundPrivate object and query for an IKsPropertySet interface + if (SUCCEEDED(hr)) + { + #if defined __cplusplus + hr = pClassFactory->CreateInstance(NULL,IID_IKsPropertySet,(LPVOID *) ppKsPropertySet); + #else + hr = pClassFactory->lpVtbl->CreateInstance(pClassFactory,NULL,&IID_IKsPropertySet,(LPVOID *) ppKsPropertySet); + #endif + } + + if (pClassFactory) // release the class factory object + { + #if defined __cplusplus + pClassFactory->Release(); + #else + pClassFactory->lpVtbl->Release(pClassFactory); + #endif + } + + if (FAILED(hr) && *ppKsPropertySet) // handle failure + { + #if defined __cplusplus + (*ppKsPropertySet)->Release(); + #else + (*ppKsPropertySet)->lpVtbl->Release(*ppKsPropertySet); + #endif + } + return hr; +} + +// +// get the device information about a DirectSound GUID. +// +static BOOL GetInfoFromDSoundGUID(CONST GUID *lpGUID, UINT *puWaveDeviceID) +{ + LPKSPROPERTYSET pKsPropertySet = NULL; + HRESULT hr; + BOOL bSuccess = FALSE; + + hr = DirectSoundPrivateCreate(&pKsPropertySet); + if (SUCCEEDED(hr)) + { + ULONG ulBytesReturned = 0; + + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription; + + ZeroMemory(&sDirectSoundDeviceDescription,sizeof(sDirectSoundDeviceDescription)); + sDirectSoundDeviceDescription.DeviceId = *lpGUID; + + // get the size of the direct sound device description + #if defined __cplusplus + hr = pKsPropertySet->Get( + DSPROPSETID_DirectSoundDevice, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, + NULL, + 0, + &sDirectSoundDeviceDescription, + sizeof(sDirectSoundDeviceDescription), + &ulBytesReturned + ); + #else + hr = pKsPropertySet->lpVtbl->Get(pKsPropertySet, + &DSPROPSETID_DirectSoundDevice, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, + NULL, + 0, + &sDirectSoundDeviceDescription, + sizeof(sDirectSoundDeviceDescription), + &ulBytesReturned + ); + #endif + + if (SUCCEEDED(hr) && ulBytesReturned) + { + PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL; + + // fetch the direct sound device description + psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA) malloc(ulBytesReturned); + if (psDirectSoundDeviceDescription != NULL) + { + // init structure with data from length request + *psDirectSoundDeviceDescription = sDirectSoundDeviceDescription; + + #if defined __cplusplus + hr = pKsPropertySet->Get( + DSPROPSETID_DirectSoundDevice, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, + NULL, + 0, + psDirectSoundDeviceDescription, + ulBytesReturned, + &ulBytesReturned + ); + #else + hr = pKsPropertySet->lpVtbl->Get(pKsPropertySet, + &DSPROPSETID_DirectSoundDevice, + DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, + NULL, + 0, + psDirectSoundDeviceDescription, + ulBytesReturned, + &ulBytesReturned + ); + #endif + + if ((bSuccess = SUCCEEDED(hr))) + { + // the requested device ID + *puWaveDeviceID = psDirectSoundDeviceDescription->WaveDeviceId; + } + free(psDirectSoundDeviceDescription); + } + } + + #if defined __cplusplus + pKsPropertySet->Release(); + #else + pKsPropertySet->lpVtbl->Release(pKsPropertySet); + #endif + } + return bSuccess; +} + +// +// callback function for DirectSoundEnumerate() +// +static BOOL CALLBACK DSEnumProc(LPGUID lpGUID,LPCTSTR lpszDesc,LPCTSTR lpszDrvName,LPVOID lpContext) +{ + HWND hWnd = (HWND) lpContext; // window handle of the combo box + + if (lpGUID != NULL) // NULL only for "Primary Sound Driver" + { + UINT uDevID; + + if (GetInfoFromDSoundGUID(lpGUID,&uDevID)) + { + WAVEOUTCAPS woc; + + // has device the necessary capabilities? + if ( waveOutGetDevCaps(uDevID,&woc,sizeof(woc)) == MMSYSERR_NOERROR + && (woc.dwFormats & WAVE_FORMAT_4M08) != 0) + { + // copy product name and wave device ID to combo box + LONG i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) lpszDesc); + SendMessage(hWnd,CB_SETITEMDATA,i,uDevID); + } + } + } + return TRUE; + UNREFERENCED_PARAMETER(lpszDrvName); +} + +// set listfield for sound device combo box +VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID) +{ + typedef BOOL (CALLBACK *LPDSENUMCALLBACK)(LPGUID, LPCTSTR, LPCTSTR, LPVOID); + typedef HRESULT (WINAPI *LPFN_SDE)(LPDSENUMCALLBACK lpDSEnumCallback,LPVOID lpContext); + LPFN_SDE pfnDirectSoundEnumerate = NULL; + + UINT uSelectDevice,uDevID,uDevNo; + + HMODULE hDSound = LoadLibrary(_T("dsound.dll")); + + if (hDSound != NULL) // direct sound dll found + { + #if defined _UNICODE + pfnDirectSoundEnumerate = (LPFN_SDE) GetProcAddress(hDSound,"DirectSoundEnumerateW"); + #else + pfnDirectSoundEnumerate = (LPFN_SDE) GetProcAddress(hDSound,"DirectSoundEnumerateA"); + #endif + pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT) GetProcAddress(hDSound,"DllGetClassObject"); + } + + SendMessage(hWnd,CB_RESETCONTENT,0,0); + + // preset selector + uSelectDevice = (UINT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) _T("Standard Audio")); + SendMessage(hWnd,CB_SETITEMDATA,uSelectDevice,WAVE_MAPPER); + + // check for direct sound interface functions + if (pfnDirectSoundEnumerate != NULL && pfnDllGetClassObject != NULL) + { + // copy product name and wave device ID to combo box + if (SUCCEEDED(pfnDirectSoundEnumerate((LPDSENUMCALLBACK) DSEnumProc,hWnd))) + { + UINT i; + + uDevNo = (UINT) SendMessage(hWnd,CB_GETCOUNT,0,0); + for (i = 0; i < uDevNo; ++i) + { + // translate device ID to combo box position + uDevID = (UINT) SendMessage(hWnd,CB_GETITEMDATA,i,0); + + if (uDevID == uDeviceID) uSelectDevice = i; + } + } + } + else // direct sound not available, detect over wave capabilities + { + WAVEOUTCAPS woc; + + uDevNo = waveOutGetNumDevs(); + for (uDevID = 0; uDevID < uDevNo; ++uDevID) + { + if ( waveOutGetDevCaps(uDevID,&woc,sizeof(woc)) == MMSYSERR_NOERROR + && (woc.dwFormats & WAVE_FORMAT_4M08) != 0) + { + // copy product name and wave device ID to combo box + LONG i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) woc.szPname); + SendMessage(hWnd,CB_SETITEMDATA,i,uDevID); + + if (uDevID == uDeviceID) uSelectDevice = i; + } + } + } + + // activate last selected combo box item + SendMessage(hWnd,CB_SETCURSEL,uSelectDevice,0L); + + if (hDSound != NULL) // direct sound dll loaded + { + pfnDllGetClassObject = NULL; + VERIFY(FreeLibrary(hDSound)); + } + return; +} diff --git a/app/src/main/cpp/SOUND.C b/app/src/main/cpp/SOUND.C new file mode 100644 index 0000000..caeead0 --- /dev/null +++ b/app/src/main/cpp/SOUND.C @@ -0,0 +1,557 @@ +/* + * sound.c + * + * This file is part of Emu48 + * + * Copyright (C) 2013 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +// #define DEBUG_SOUND // switch for sound debug purpose +// #define SINE_APPROX // sine signal approximation + +#define SAMPLES_PER_SEC 44100 // sound sampling rate +#define MILLISEC_PER_BUFFER 20 // time period of each sound buffer + +#define NO_OF_BUFFERS 3 // number of reserve buffers before playing + +typedef struct _MSAMPLE +{ + LPBYTE pbyData; + DWORD dwBufferLength; + DWORD dwPosition; + // buffer admin part + DWORD dwIndex; // index to count no. of sample buffers + struct _MSAMPLE* pNext; // pointer to next sample buffer +} MSAMPLE, *PMSAMPLE; + +DWORD dwWaveVol = 64; // wave sound volume +DWORD dwWaveTime = MILLISEC_PER_BUFFER; // time period (in ms) of each sound buffer + +static HWAVEOUT hWaveDevice = NULL; // handle to the waveform-audio output device +static HANDLE hThreadWave = NULL; // thread handle of sound message handler +static DWORD dwThreadWaveId = 0; // thread id of sound message handler +static UINT uHeaders = 0; // no. of sending wave headers +static PMSAMPLE psHead = NULL; // head of sound samples +static PMSAMPLE psTail = NULL; // tail of sound samples + +static CRITICAL_SECTION csSoundLock; // critical section for sound emulation +static DWORD dwSoundBufferLength; // sound buffer length for the given time period + +static VOID FlushSample(VOID); + +// +// sound message handler thread +// +static DWORD WINAPI SoundWndProc(LPVOID pParam) +{ + MSG msg; + + while (GetMessage(&msg, NULL, 0, 0)) + { + if (msg.message == MM_WOM_DONE) + { + HWAVEOUT hwo = (HWAVEOUT) msg.wParam; + PWAVEHDR pwh = (PWAVEHDR) msg.lParam; + + VERIFY(waveOutUnprepareHeader(hwo,pwh,sizeof(*pwh)) == MMSYSERR_NOERROR); + free(pwh->lpData); // free waveform data + free(pwh); // free wavefom header + + _ASSERT(uHeaders > 0); + --uHeaders; // finished header + + FlushSample(); // check for new sample + + if (uHeaders == 0) // no wave headers in transmission + { + bSoundSlow = FALSE; // no sound slow down + bEnableSlow = TRUE; // reenable CPU slow down possibility + } + } + } + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +// +// create sound message handler thread +// +static BOOL CreateWaveThread(VOID) +{ + _ASSERT(hThreadWave == NULL); + + // create sound message handler thread + hThreadWave = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&SoundWndProc,NULL,0,&dwThreadWaveId); + return hThreadWave != NULL; +} + +// +// destroy sound message handler thread +// +static VOID DestroyWaveThread(VOID) +{ + if (hThreadWave != NULL) // sound message handler thread running + { + // shut down thread + while (!PostThreadMessage(dwThreadWaveId,WM_QUIT,0,0)) + Sleep(0); + WaitForSingleObject(hThreadWave,INFINITE); + CloseHandle(hThreadWave); + hThreadWave = NULL; + } + return; +} + +// +// add sample buffer to tail of sample job list +// +static __inline VOID AddSoundBuf(PMSAMPLE psData) +{ + _ASSERT(psData != NULL); // there must be a sample + psData->pNext = NULL; // last sample in job list + + // add sample to list + EnterCriticalSection(&csSoundLock); + { + if (psTail == NULL) // root + { + psData->dwIndex = 0; // this is the root index + + _ASSERT(psHead == NULL); + psHead = psTail = psData; // add sample at head + } + else // add at tail + { + // use next index + psData->dwIndex = psTail->dwIndex + 1; + + psTail->pNext = psData; // add sample at tail + psTail = psData; + } + } + LeaveCriticalSection(&csSoundLock); + return; +} + +// +// remove sample buffer from head of sample job list +// +static __inline BOOL GetSoundBuf(PMSAMPLE *ppsData) +{ + BOOL bSucc; + + EnterCriticalSection(&csSoundLock); + { + if ((bSucc = (psHead != NULL))) // data in head + { + *ppsData = psHead; // get sample + psHead = psHead->pNext; // and remove it from head + if (psHead == NULL) // was last one in head + { + psTail = NULL; // so tail is also the last one + } + } + } + LeaveCriticalSection(&csSoundLock); + return bSucc; +} + +// +// number of sample buffers in sample job list +// +static DWORD GetSoundBufSize(VOID) +{ + DWORD dwNoSamples; + + EnterCriticalSection(&csSoundLock); + { + // no. of samples in buffer + dwNoSamples = (psTail == NULL) + ? 0 + : (psTail->dwIndex - psHead->dwIndex) + 1; + } + LeaveCriticalSection(&csSoundLock); + return dwNoSamples; +} + +// +// allocate new sample buffer and add the +// buffer to the tail of the sample job list +// +static __inline BOOL AllocSample(PMSAMPLE *ppsData) +{ + // alloc new sample buffer + *ppsData = (PMSAMPLE) malloc(sizeof(**ppsData)); + + if (*ppsData != NULL) + { + (*ppsData)->dwPosition = 0; // begin of buffer + (*ppsData)->dwBufferLength = dwSoundBufferLength; + (*ppsData)->pbyData = (LPBYTE) malloc((*ppsData)->dwBufferLength); + if ((*ppsData)->pbyData != NULL) + { + // buffers allocated + _ASSERT(*ppsData != NULL && (*ppsData)->pbyData != NULL); + + AddSoundBuf(*ppsData); // add sample buffer to list + } + else + { + free(*ppsData); // data alloc failed, delete sample buffer + *ppsData = NULL; + } + } + return *ppsData != NULL; +} + +// +// write samples to sample buffer +// +static BOOL AddSamples(DWORD dwSamples, BYTE byLevel) +{ + PMSAMPLE psData; + DWORD dwBufSamples; + #if defined SINE_APPROX + INT w,s,ss,x,y; + #endif + + BOOL bSucc = TRUE; + + if (dwSamples == 0) return TRUE; // nothing to add + + #if defined SINE_APPROX + // calculate constants + w = (INT) (byLevel - 0x80); // max. wave level + s = (INT) dwSamples; // interval length (pi) + ss = s * s; // interval length ^ 2 + x = 1; // sample no. + #endif + + EnterCriticalSection(&csSoundLock); + { + psData = psTail; // get last sample buffer + + do + { + // number of free sound samples in current buffer + dwBufSamples = (psData != NULL) + ? (psData->dwBufferLength - psData->dwPosition) + : 0; + + if (dwBufSamples == 0) // sample buffer is full + { + // alloc new sample buffer + VERIFY(bSucc = AllocSample(&psData)); + if (!bSucc) break; + + _ASSERT( psData != NULL + && psData->pbyData != NULL + && psData->dwPosition == 0); + dwBufSamples = psData->dwBufferLength; + } + + if (dwSamples < dwBufSamples) // free sample buffer is larger then needed + dwBufSamples = dwSamples; // fill only the necessary no. of samples + + dwSamples -= dwBufSamples; // remaining samples after buffer fill + + // fill buffer with level for beep + #if defined SINE_APPROX + for (; dwBufSamples > 0; --dwBufSamples) + { + // sine approximation function + y = w - w * (4 * x * (x - s) + ss ) / ss; + ++x; // next sample + + psData->pbyData[psData->dwPosition++] = (BYTE) (y + 0x80); + } + #else + FillMemory(&psData->pbyData[psData->dwPosition],dwBufSamples,byLevel); + psData->dwPosition += dwBufSamples; + #endif + } + while (dwSamples > 0); + } + LeaveCriticalSection(&csSoundLock); + return bSucc; +} + +// +// write sample buffer from head of sample job list +// to waveform-audio output device and delete the +// sample buffer control from head of sample job list +// +static VOID FlushSample(VOID) +{ + PMSAMPLE psData; + + _ASSERT(hWaveDevice != NULL); + + if (GetSoundBuf(&psData) == TRUE) // fetch sample buffer + { + PWAVEHDR pwh; + + // allocate new wave header + if ((pwh = (PWAVEHDR) malloc(sizeof(*pwh))) != NULL) + { + pwh->lpData = (LPSTR) psData->pbyData; + pwh->dwBufferLength = psData->dwPosition; + pwh->dwBytesRecorded = 0; + pwh->dwUser = 0; + pwh->dwFlags = 0; + pwh->dwLoops = 0; + + ++uHeaders; // add header + + // prepare sample + VERIFY(waveOutPrepareHeader(hWaveDevice,pwh,sizeof(*pwh)) == MMSYSERR_NOERROR); + + // send sample + VERIFY(waveOutWrite(hWaveDevice,pwh,sizeof(*pwh)) == MMSYSERR_NOERROR); + } + free(psData); // delete sample buffer + } + return; +} + +// +// 44.1 kHz, mono, 8-bit waveform-audio output device available +// +BOOL SoundAvailable(UINT uDeviceID) +{ + WAVEOUTCAPS woc; + return waveOutGetDevCaps(uDeviceID,&woc,sizeof(woc)) == MMSYSERR_NOERROR + && (woc.dwFormats & WAVE_FORMAT_4M08) != 0; +} + +// +// get the device ID of the current waveform-audio output device +// +BOOL SoundGetDeviceID(UINT *puDeviceID) +{ + BOOL bSucc = FALSE; + + if (hWaveDevice) // have sound device + { + bSucc = (waveOutGetID(hWaveDevice,puDeviceID) == MMSYSERR_NOERROR); + } + return bSucc; +} + +// +// open waveform-audio output device +// +BOOL SoundOpen(UINT uDeviceID) +{ + // check if sound device is already open + if (hWaveDevice == NULL && SoundAvailable(uDeviceID)) + { + WAVEFORMATEX wf; + BOOL bSucc; + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 1; + wf.nSamplesPerSec = SAMPLES_PER_SEC; + wf.wBitsPerSample = 8; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.cbSize = 0; + + InitializeCriticalSection(&csSoundLock); + + // sound buffer length for the given time period + dwSoundBufferLength = SAMPLES_PER_SEC * dwWaveTime / 1000; + + if ((bSucc = CreateWaveThread())) // create sound message handler + { + // create a sound device, use the CALLBACK_THREAD flag because with the + // CALLBACK_FUNCTION flag unfortunately the called callback function + // can only call a specific set of Windows functions. Attempting to + // call other functions at the wrong time will result in a deadlock. + bSucc = (waveOutOpen(&hWaveDevice,uDeviceID,&wf,dwThreadWaveId,0,CALLBACK_THREAD) == MMSYSERR_NOERROR); + } + if (!bSucc) + { + DestroyWaveThread(); // shut down message thread + DeleteCriticalSection(&csSoundLock); + hWaveDevice = NULL; + } + } + return hWaveDevice != NULL; +} + +// +// close waveform-audio output device +// +VOID SoundClose(VOID) +{ + if (hWaveDevice != NULL) + { + EnterCriticalSection(&csSoundLock); + { + while (psHead != NULL) // cleanup remaining sample buffers + { + PMSAMPLE psNext = psHead->pNext; + + free(psHead->pbyData); + free(psHead); + psHead = psNext; + } + psTail = NULL; + } + LeaveCriticalSection(&csSoundLock); + + // abandon all pending wave headers + VERIFY(waveOutReset(hWaveDevice) == MMSYSERR_NOERROR); + + DestroyWaveThread(); // shut down message thread + + VERIFY(waveOutClose(hWaveDevice) == MMSYSERR_NOERROR); + DeleteCriticalSection(&csSoundLock); + hWaveDevice = NULL; + } + uHeaders = 0; // no wave headers in transmission + bSoundSlow = FALSE; // no sound slow down + bEnableSlow = TRUE; // reenable CPU slow down possibility + return; +} + +// +// calculate the wave level from the beeper bit state +// +static BYTE WaveLevel(WORD wOut) +{ + wOut >>= 11; // mask out beeper bit OR[11] + return (BYTE) (wOut & 0x01) + 1; // return 1 or 2 +} + +// +// decode change of beeper OUT bits +// +VOID SoundOut(CHIPSET* w, WORD wOut) +{ + static DWORD dwLastCyc; // last timer value at beeper bit change + + DWORD dwCycles,dwDiffSatCycles,dwDiffCycles,dwCpuFreq,dwSamples; + BYTE byWaveLevel; + + // sound device not opened or waveform-audio output device not available + if (hWaveDevice == NULL) + return; + + // actual timestamp + dwCycles = (DWORD) (w->cycles & 0xFFFFFFFF); + + dwDiffSatCycles = dwCycles - dwLastCyc; // time difference from syncpoint in original Saturn cycles + + // theoretical CPU frequency from given T2CYCLES + dwCpuFreq = T2CYCLES * 16384; + + if (dwDiffSatCycles > dwCpuFreq / 2) // frequency < 1 Hz + { + dwLastCyc = dwCycles; // initial call for start beeping + return; + } + + // estimated CPU cycles for Clarke/Yorke chip + dwDiffCycles = (cCurrentRomType == 'S') + ? (dwDiffSatCycles * 26) / 25 // Clarke * 1.04 + : (dwDiffSatCycles * 11) / 10; // Yorke * 1.10 + + // adjust original CPU cycles + w->cycles += (dwDiffCycles - dwDiffSatCycles); + dwLastCyc = (DWORD) (w->cycles & 0xFFFFFFFF); // new syncpoint + + // calculate no. of sound samples from CPU cycles, !! intermediate result maybe > 32bit !! + dwSamples = (DWORD) ((2 * (QWORD) dwDiffCycles + 1) * SAMPLES_PER_SEC / 2 / dwCpuFreq); + + if (dwSamples == 0) // frequency too high -> play nothing + return; + + #if defined DEBUG_SOUND + { + TCHAR buffer[256]; + + // calculate rounded time in us + QWORD lDuration = 1000000 * (2 * (QWORD) dwDiffCycles + 1) / (2 * dwCpuFreq); + + wsprintf(buffer,_T("State %u: Time = %I64u us f = %u Hz, Time = %I64u us f = %u Hz\n"), + wOut >> 11,lDuration,(DWORD) (1000000 / 2 / lDuration), + (QWORD) dwSamples * 1000000 / SAMPLES_PER_SEC,SAMPLES_PER_SEC / 2 / dwSamples); + OutputDebugString(buffer); + } + #endif + + // begin of beep + if (uHeaders == 0 && GetSoundBufSize() == 0) + { + // use silence buffers to start output engine + AddSamples(dwSoundBufferLength * NO_OF_BUFFERS,0x80); + } + + // offset for wave level + byWaveLevel = 0x80 + (BYTE) (dwWaveVol * (WaveLevel(wOut) - WaveLevel(w->out)) / 2); + + AddSamples(dwSamples,byWaveLevel); // add samples to latest wave sample buffer + + if (GetSoundBufSize() > NO_OF_BUFFERS) // have more than 3 wave sample buffers + { + FlushSample(); // send 2 of them + FlushSample(); + } + + // ran out of buffers -> disable CPU slow down + EnterCriticalSection(&csSlowLock); + { + InitAdjustSpeed(); // init variables if necessary + bEnableSlow = (GetSoundBufSize() > 1); + } + LeaveCriticalSection(&csSlowLock); + + if (bSoundSlow == FALSE) + { + EnterCriticalSection(&csSlowLock); + { + InitAdjustSpeed(); // init variables if necessary + bSoundSlow = TRUE; // CPU slow down + } + LeaveCriticalSection(&csSlowLock); + } + return; +} + +// +// beep with frequency (Hz) and duration (ms) +// +VOID SoundBeep(DWORD dwFrequency, DWORD dwDuration) +{ + QWORD lPeriods; + DWORD dwSamples; + BYTE byLevel; + + // waveform-audio output device opened and have frequency + if (hWaveDevice && dwFrequency > 0) + { + // samples for 1/2 of time period + dwSamples = SAMPLES_PER_SEC / 2 / dwFrequency; + + // overall half periods + lPeriods = (QWORD) dwFrequency * dwDuration / 500; + + while (lPeriods-- > 0) // create sample buffers + { + // signal level + byLevel = 0x80 + (BYTE) ((((DWORD) lPeriods & 1) * 2 - 1) * (dwWaveVol / 2)); + + AddSamples(dwSamples,byLevel); // add half period sample + } + + while (GetSoundBufSize() > 0) // samples in job list + FlushSample(); // send sample buffer + } + Sleep(dwDuration); + return; +} diff --git a/app/src/main/cpp/STACK.C b/app/src/main/cpp/STACK.C new file mode 100644 index 0000000..570dcbc --- /dev/null +++ b/app/src/main/cpp/STACK.C @@ -0,0 +1,834 @@ +/* + * stack.c + * + * This file is part of Emu48 + * + * Copyright (C) 2005 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +#define fnRadix 51 // fraction mark +#define fnApprox 105 // exact / approx. mode (HP49G) + +#define DOINT 0x02614 // Precision Integer (HP49G) +#define DOREAL 0x02933 // Real +#define DOCMP 0x02977 // Complex +#define DOCSTR 0x02A2C // String + +BOOL bDetectClpObject = TRUE; // try to detect clipboard object + +//################ +//# +//# Low level subroutines +//# +//################ + +static LPTSTR Trim(LPCTSTR cp) +{ + LPCTSTR pcWs = _T(" \t\n\r"); // valid whitespace characters + + LPTSTR pc; + DWORD dwFirst,dwLast; + + dwLast = lstrlen(cp); // last position in string (EOS) + + // trim leading and tailing whitespace characters + dwFirst = (DWORD) _tcsspn(cp,pcWs); // position of 1st non whitespace character + + // search for position behind last non whitespace character + while (dwLast > dwFirst && _tcschr(pcWs,cp[dwLast-1]) != NULL) + --dwLast; + + dwLast = 1 + dwLast - dwFirst; // calculate buffer length + + if ((pc = (LPTSTR) malloc(dwLast * sizeof(*pc))) != NULL) + { + lstrcpyn(pc,&cp[dwFirst],dwLast); // copy relevant data + EOS + } + return pc; +} + +static INT RPL_GetZInt(BYTE CONST *pbyNum,INT nIntLen,LPTSTR cp,INT nSize) +{ + INT i = 0; // character counter + + _ASSERT(nSize > 0); // target buffer size + + if (nIntLen > 1) // has sign nibble + { + --nIntLen; // remove sign from digit length + + // check for valid sign + _ASSERT(pbyNum[nIntLen] == 0 || pbyNum[nIntLen] == 9); + if (pbyNum[nIntLen] == 9) // negative number + { + *cp++ = _T('-'); // add sign + --nSize; // dec dest buffer size + ++i; // wrote one character + } + } + + if (nIntLen >= nSize) return 0; // dest buffer overflow + i += nIntLen; // adjust character counter + + while (nIntLen-- > 0) // write all digits + { + // check for valid digit + _ASSERT(pbyNum[nIntLen] >= 0 && pbyNum[nIntLen] <= 9); + *cp++ = _T('0') + pbyNum[nIntLen]; // and write + } + *cp = 0; // set EOS + return i; +} + +static __inline INT SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize) +{ + BYTE bySign; + INT nStrLen,nNumSize; + + _ASSERT(nSize > 0); // target buffer size + + nStrLen = lstrlen(cp); // source string length + + if ( nStrLen == 0 // empty string + // precisition integer contain only these numbers + || _tcsspn(cp,_T("0123456789+-")) != (SIZE_T) nStrLen) + return 0; + + bySign = (*cp != _T('-')) ? 0 : 9; // set sign nibble + if (*cp == _T('-') || *cp == _T('+')) // skip sign character + { + ++cp; + --nStrLen; + } + + if (nStrLen == 1 && *cp == _T('0')) // special code for zero + { + *pbyNum = 0; // zero data + return 1; // finish + } + + // nStrLen = no. of digits without sign + if (nStrLen >= nSize) // destination buffer too small + return 0; + + nNumSize = nStrLen + 1; // no. of written data + + while (--nStrLen >= 0) // eval all digits + { + TCHAR c = cp[nStrLen]; + + // only '0' .. '9' are valid here + if (!((c >= _T('0')) || (c <= _T('9')))) + return 0; + + c -= _T('0'); + *pbyNum++ = (BYTE) c; + } + *pbyNum = bySign; // add sign + + return nNumSize; +} + +static INT RPL_SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize) +{ + LPTSTR pszData; + + INT s = 0; + + if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string + { + s = SetZInt(pszData,pbyNum,nSize); + free(pszData); + } + return s; +} + +static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize) +{ + BYTE byNib; + LONG v,lExp; + BOOL bPflag,bExpflag; + INT i; + + lExp = 0; + for (v = 1; nExpLen--; v *= 10) // fetch exponent + { + lExp += (LONG) *pbyNum++ * v; // calc. exponent + } + + if (lExp > v / 2) lExp -= v; // negative exponent + + lExp -= nMantLen - 1; // set decimal point to end of mantissa + + i = 0; // first character + bPflag = FALSE; // show no decimal point + bExpflag = FALSE; // show no exponent + + // scan mantissa + for (v = (LONG) nMantLen - 1; v >= 0 || bPflag; v--) + { + if (v >= 0L) // still mantissa digits left + byNib = *pbyNum++; + else + byNib = 0; // zero for negativ exponent + + if (!i) // still delete zeros at end + { + if (byNib == 0 && lExp && v > 0) // delete zeros + { + lExp++; // adjust exponent + continue; + } + + // TRUE at x.E + bExpflag = v + lExp >= nMantLen || lExp < -nMantLen; + bPflag = !bExpflag && v < -lExp; // decimal point flag at neg. exponent + } + + // set decimal point + if ((bExpflag && v == 0) || (!lExp && i)) + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = cDec; // write decimal point + if (v < 0) // no mantissa digits any more + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = _T('0'); // write heading zero + } + bPflag = FALSE; // finished with negativ exponents + } + + if (v >= 0 || bPflag) + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = (TCHAR) byNib + _T('0'); // write character + } + + lExp++; // next position + } + + if (*pbyNum == 9) // negative number + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = _T('-'); // write sign + } + + if (i >= nSize) return 0; // dest buffer overflow + cp[i] = 0; // set EOS + + for (v = 0; v < (i / 2); v++) // reverse string + { + TCHAR cNib = cp[v]; // swap chars + cp[v] = cp[i-v-1]; + cp[i-v-1] = cNib; + } + + // write number with exponent + if (bExpflag) + { + if (i + 5 >= nSize) return 0; // dest buffer overflow + i += wsprintf(&cp[i],_T("E%d"),lExp-1); + } + return i; +} + +static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) +{ + TCHAR cVc[] = _T(".0123456789eE+-"); + + BYTE byNum[80]; + INT i,nIp,nDp,nMaxExp; + LONG lExp; + + cVc[0] = cDec; // replace decimal char + + if ( nMantLen + nExpLen >= nSize // destination buffer too small + || !*cp // empty string + || _tcsspn(cp,cVc) != (SIZE_T) lstrlen(cp) // real contain only these numbers + || (SIZE_T) lstrlen(cp) >= ARRAYSIZEOF(byNum)) // ignore too long reals + return 0; + + byNum[0] = (*cp != _T('-')) ? 0 : 9; // set sign nibble + if (*cp == _T('-') || *cp == _T('+')) // skip sign character + cp++; + + // only '.', '0' .. '9' are valid here + if (!((*cp == cDec) || (*cp >= _T('0')) || (*cp <= _T('9')))) + return 0; + + nIp = 0; // length of integer part + if (*cp != cDec) // no decimal point + { + // count integer part + while (*cp >= _T('0') && *cp <= _T('9')) + byNum[++nIp] = *cp++ - _T('0'); + if (!nIp) return 0; + } + + // only '.', 'E', 'e' or end are valid here + if (!(!*cp || (*cp == cDec) || (*cp == _T('E')) || (*cp == _T('e')))) + return 0; + + nDp = 0; // length of decimal part + if (*cp == cDec) // decimal point + { + cp++; // skip '.' + + // count decimal part + while (*cp >= _T('0') && *cp <= _T('9')) + byNum[nIp + ++nDp] = *cp++ - _T('0'); + } + + // count number of heading zeros in mantissa + for (i = 0; byNum[i+1] == 0 && i + 1 < nIp + nDp; ++i) { } + + if (i > 0) // have to normalize + { + INT j; + + nIp -= i; // for later ajust of exponent + for (j = 1; j <= nIp + nDp; ++j) // normalize mantissa + byNum[j] = byNum[j + i]; + } + + if (byNum[1] == 0) // number is 0 + { + ZeroMemory(pbyNum,nMantLen + nExpLen + 1); + return nMantLen + nExpLen + 1; + } + + for (i = nIp + nDp; i < nMantLen;) // fill rest of mantissa with 0 + byNum[++i] = 0; + + // must be 'E', 'e' or end + if (!(!*cp || (*cp == _T('E')) || (*cp == _T('e')))) + return 0; + + lExp = 0; + if (*cp == _T('E') || *cp == _T('e')) + { + cp++; // skip 'E' + + i = FALSE; // positive exponent + if (*cp == _T('-') || *cp == _T('+')) + { + i = (*cp++ == _T('-')); // adjust exponent sign + } + + // exponent symbol must be followed by number + if (*cp < _T('0') || *cp > _T('9')) return 0; + + while (*cp >= _T('0') && *cp <= _T('9')) + lExp = lExp * 10 + *cp++ - _T('0'); + + if (i) lExp = -lExp; + } + + if (*cp != 0) return 0; + + // adjust exponent value with exponent from normalized mantissa + lExp += nIp - 1; + + // calculate max. posive exponent + for (nMaxExp = 5, i = 1; i < nExpLen; ++i) + nMaxExp *= 10; + + // check range of exponent + if ((lExp < 0 && -lExp >= nMaxExp) || (lExp >= nMaxExp)) + return 0; + + if (lExp < 0) lExp += 2 * nMaxExp; // adjust negative offset + + for (i = nExpLen; i > 0; --i) // convert number into digits + { + byNum[nMantLen + i] = (BYTE) (lExp % 10); + lExp /= 10; + } + + // copy to target in reversed order + for (i = nMantLen + nExpLen; i >= 0; --i) + *pbyNum++ = byNum[i]; + + return nMantLen + nExpLen + 1; +} + +static INT RPL_SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) +{ + LPTSTR pszData; + + INT s = 0; + + if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string + { + s = SetBcd(pszData,nMantLen,nExpLen,cDec,pbyNum,nSize); + free(pszData); + } + return s; +} + +static INT RPL_GetComplex(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize) +{ + INT nLen,nPos; + TCHAR cSep; + + cSep = (cDec == _T('.')) // current separator + ? _T(',') // radix mark '.' -> ',' separator + : _T(';'); // radix mark ',' -> ';' separator + + nPos = 0; // write buffer position + + if (nSize < 5) return 0; // target buffer to small + nSize -= 4; // reserved room for (,)\0 + + cp[nPos++] = _T('('); // start of complex number + + // real part + nLen = RPL_GetBcd(pbyNum,nMantLen,nExpLen,cDec,&cp[1],nSize); + if (nLen == 0) return 0; // target buffer to small + + _ASSERT(nLen <= nSize); + + nPos += nLen; // actual buffer postion + nSize -= nLen; // remainder target buffer size + + cp[nPos++] = cSep; // write of complex number seperator + + // imaginary part + nLen = RPL_GetBcd(&pbyNum[16],nMantLen,nExpLen,cDec,&cp[nPos],nSize); + if (nLen == 0) return 0; // target buffer to small + + nPos += nLen; // actual buffer postion + + cp[nPos++] = _T(')'); // end of complex number + cp[nPos] = 0; // EOS + + _ASSERT(lstrlen(cp) == nPos); + + return nPos; +} + +static INT RPL_SetComplex(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) +{ + LPTSTR pcSep,pszData; + INT nLen; + TCHAR cSep; + + nLen = 0; // read data length + + cSep = (cDec == _T('.')) // current separator + ? _T(',') // radix mark '.' -> ',' separator + : _T(';'); // radix mark ',' -> ';' separator + + if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string + { + INT nStrLength = lstrlen(pszData); // string length + + // complex number with brackets around + if ( nStrLength > 0 + && pszData[0] == _T('(') + && pszData[nStrLength - 1] == _T(')')) + { + pszData[--nStrLength] = 0; // replace ')' with EOS + + // search for number separator + if ((pcSep = _tcschr(pszData+1,cSep)) != NULL) + { + INT nLen1st; + + *pcSep = 0; // set EOS for 1st substring + + // decode 1st substring + nLen1st = RPL_SetBcd(pszData+1,nMantLen,nExpLen,cDec,&pbyNum[0],nSize); + if (nLen1st > 0) + { + // decode 2nd substring + nLen = RPL_SetBcd(pcSep+1,nMantLen,nExpLen,cDec,&pbyNum[nMantLen+nExpLen+1],nSize-nLen1st); + if (nLen > 0) + { + nLen += nLen1st; // complete Bcd length + } + } + } + } + free(pszData); + } + return nLen; +} + + +//################ +//# +//# Object subroutines +//# +//################ + +static TCHAR GetRadix(VOID) +{ + // get locale decimal point + // GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,&cDecimal,1); + + return RPL_GetSystemFlag(fnRadix) ? _T(',') : _T('.'); +} + +static INT DoInt(DWORD dwAddress,LPTSTR cp,INT nSize) +{ + LPBYTE lpbyData; + INT nLength,nIntLen; + + nIntLen = Read5(dwAddress) - 5; // no. of digits + if (nIntLen <= 0) return 0; // error in calculator object + + nLength = 0; + if ((lpbyData = (LPBYTE) malloc(nIntLen))) + { + // get precisition integer object content and decode it + Npeek(lpbyData,dwAddress+5,nIntLen); + nLength = RPL_GetZInt(lpbyData,nIntLen,cp,nSize); + free(lpbyData); + } + return nLength; +} + +static INT DoReal(DWORD dwAddress,LPTSTR cp,INT nSize) +{ + BYTE byNumber[16]; + + // get real object content and decode it + Npeek(byNumber,dwAddress,ARRAYSIZEOF(byNumber)); + return RPL_GetBcd(byNumber,12,3,GetRadix(),cp,nSize); +} + +static INT DoComplex(DWORD dwAddress,LPTSTR cp,INT nSize) +{ + BYTE byNumber[32]; + + // get complex object content and decode it + Npeek(byNumber,dwAddress,ARRAYSIZEOF(byNumber)); + return RPL_GetComplex(byNumber,12,3,GetRadix(),cp,nSize); +} + + +//################ +//# +//# Stack routines +//# +//################ + +// +// ID_STACK_COPY +// +LRESULT OnStackCopy(VOID) // copy data from stack +{ + TCHAR cBuffer[128]; + HANDLE hClipObj; + LPBYTE lpbyData; + DWORD dwAddress,dwObject,dwSize; + UINT uClipboardFormat; + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + return 0; + } + + _ASSERT(nState == SM_SLEEP); + + if ((dwAddress = RPL_Pick(1)) == 0) // pick address of level1 object + { + MessageBeep(MB_OK); // error beep + goto error; + } + + switch (dwObject = Read5(dwAddress)) // select object + { + case DOINT: // Precision Integer (HP49G) + case DOREAL: // real object + case DOCMP: // complex object + dwAddress += 5; // object content + + switch (dwObject) + { + case DOINT: // Precision Integer (HP49G) + // get precision integer object content and decode it + dwSize = DoInt(dwAddress,cBuffer,ARRAYSIZEOF(cBuffer)); + break; + case DOREAL: // real object + // get real object content and decode it + dwSize = DoReal(dwAddress,cBuffer,ARRAYSIZEOF(cBuffer)); + break; + case DOCMP: // complex object + // get complex object content and decode it + dwSize = DoComplex(dwAddress,cBuffer,ARRAYSIZEOF(cBuffer)); + break; + } + + // calculate buffer size + dwSize = (dwSize + 1) * sizeof(*cBuffer); + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,dwSize)) == NULL) + goto error; + + if ((lpbyData = (LPBYTE) GlobalLock(hClipObj))) + { + // copy data to memory + CopyMemory(lpbyData,cBuffer,dwSize); + GlobalUnlock(hClipObj); // unlock memory + } + + #if defined _UNICODE + uClipboardFormat = CF_UNICODETEXT; + #else + uClipboardFormat = CF_TEXT; + #endif + break; + case DOCSTR: // string + dwAddress += 5; // address of string length + dwSize = (Read5(dwAddress) - 5) / 2; // length of string + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,dwSize + 1)) == NULL) + goto error; + + if ((lpbyData = (LPBYTE) GlobalLock(hClipObj))) // lock memory + { + // copy data into clipboard buffer + for (dwAddress += 5;dwSize-- > 0;dwAddress += 2,++lpbyData) + *lpbyData = Read2(dwAddress); + *lpbyData = 0; // set EOS + + GlobalUnlock(hClipObj); // unlock memory + } + uClipboardFormat = CF_TEXT; // always text + break; + default: + MessageBeep(MB_OK); // error beep + goto error; + } + + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + SetClipboardData(uClipboardFormat,hClipObj); + else + GlobalFree(hClipObj); + CloseClipboard(); + } + else // clipboard open failed + { + GlobalFree(hClipObj); + } + +error: + SwitchToState(SM_RUN); + return 0; +} + +// +// ID_STACK_PASTE +// +LRESULT OnStackPaste(VOID) // paste data to stack +{ + #if defined _UNICODE + #define CF_TEXTFORMAT CF_UNICODETEXT + #else + #define CF_TEXTFORMAT CF_TEXT + #endif + + HANDLE hClipObj; + + BOOL bSuccess = FALSE; + + // check if clipboard format is available + if (!IsClipboardFormatAvailable(CF_TEXTFORMAT)) + { + MessageBeep(MB_OK); // error beep + return 0; + } + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while (Chipset.Shutdn == FALSE) Sleep(0); + } + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + if (OpenClipboard(hWnd)) + { + if ((hClipObj = GetClipboardData(CF_TEXTFORMAT))) + { + LPCTSTR lpstrClipdata; + LPBYTE lpbyData; + + if ((lpstrClipdata = (LPCTSTR) GlobalLock(hClipObj))) + { + BYTE byNumber[128]; + DWORD dwAddress; + INT s; + + do + { + if (bDetectClpObject) // autodetect clipboard object enabled + { + // HP49G or HP49G+ in exact mode + if ( (cCurrentRomType == 'X' || cCurrentRomType == 'Q') + && !RPL_GetSystemFlag(fnApprox)) + { + // try to convert string to HP49 precision integer + s = RPL_SetZInt(lpstrClipdata,byNumber,sizeof(byNumber)); + + if (s > 0) // is a real number for exact mode + { + // get TEMPOB memory for HP49 precision integer object + dwAddress = RPL_CreateTemp(s+5+5,TRUE); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOINT); // prolog + Write5(dwAddress+5,s+5); // size + Nwrite(byNumber,dwAddress+10,s); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + break; + } + } + + // try to convert string to real format + _ASSERT(16 <= ARRAYSIZEOF(byNumber)); + s = RPL_SetBcd(lpstrClipdata,12,3,GetRadix(),byNumber,sizeof(byNumber)); + + if (s > 0) // is a real number + { + _ASSERT(s == 16); // length of real number BCD coded + + // get TEMPOB memory for real object + dwAddress = RPL_CreateTemp(16+5,TRUE); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOREAL); // prolog + Nwrite(byNumber,dwAddress+5,s); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + break; + } + + // try to convert string to complex format + _ASSERT(32 <= ARRAYSIZEOF(byNumber)); + s = RPL_SetComplex(lpstrClipdata,12,3,GetRadix(),byNumber,sizeof(byNumber)); + + if (s > 0) // is a real complex + { + _ASSERT(s == 32); // length of complex number BCD coded + + // get TEMPOB memory for complex object + dwAddress = RPL_CreateTemp(16+16+5,TRUE); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOCMP); // prolog + Nwrite(byNumber,dwAddress+5,s); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + break; + } + } + + // any other format + { + DWORD dwSize = lstrlen(lpstrClipdata); + if ((lpbyData = (LPBYTE) malloc(dwSize * 2))) + { + LPBYTE lpbySrc,lpbyDest; + DWORD dwLoop; + + #if defined _UNICODE + // copy data UNICODE -> ASCII + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + lpstrClipdata, dwSize, + (LPSTR) lpbyData+dwSize, dwSize, NULL, NULL); + #else + // copy data + memcpy(lpbyData+dwSize,lpstrClipdata,dwSize); + #endif + + // unpack data + lpbySrc = lpbyData+dwSize; + lpbyDest = lpbyData; + dwLoop = dwSize; + while (dwLoop-- > 0) + { + BYTE byTwoNibs = *lpbySrc++; + *lpbyDest++ = (BYTE) (byTwoNibs & 0xF); + *lpbyDest++ = (BYTE) (byTwoNibs >> 4); + } + + dwSize *= 2; // size in nibbles + + // get TEMPOB memory for string object + dwAddress = RPL_CreateTemp(dwSize+10,TRUE); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOCSTR); // String + Write5(dwAddress+5,dwSize+5); // length of String + Nwrite(lpbyData,dwAddress+10,dwSize); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + free(lpbyData); + } + } + } + while (FALSE); + + GlobalUnlock(hClipObj); + } + } + CloseClipboard(); + } + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + + if (bSuccess == FALSE) // data not copied + goto cancel; + + KeyboardEvent(TRUE,0,0x8000); + Sleep(dwWakeupDelay); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while (Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; + #undef CF_TEXTFORMAT +} diff --git a/app/src/main/cpp/SYMBFILE.C b/app/src/main/cpp/SYMBFILE.C new file mode 100644 index 0000000..16e7658 --- /dev/null +++ b/app/src/main/cpp/SYMBFILE.C @@ -0,0 +1,257 @@ +/* + * symbfile.c + * + * This file is part of Emu48 + * + * Copyright (C) 2008 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +//################ +//# +//# Saturn Object File Reading +//# +//################ + +#define RECORD_BLOCK 256 // block size +#define OS_RESOLVED 0x8000 // resolved symbol +#define OS_RELOCATABLE 0x4000 // relocatable symbol + +#define SAT_ID "Saturn3" // saturn block header +#define SYMB_ID "Symb" // symbol block header + +#define HASHENTRIES 199 // size of hash table + +typedef struct _REFDATA +{ + LPTSTR lpszName; // symbol name + DWORD dwAddr; // resolved address + struct _REFDATA* pNext; +} REFDATA, *PREFDATA; + +static PREFDATA ppsBase[HASHENTRIES]; // base of symbol references (initialized with NULL) + +static __inline DWORD GetHash(DWORD dwVal) +{ + return dwVal % HASHENTRIES; // hash function +} + +static DWORD GetBigEndian(LPBYTE pbyData, INT nSize) +{ + DWORD dwVal = 0; + + while (nSize-- > 0) + { + dwVal <<= 8; + dwVal += *pbyData++; + } + return dwVal; +} + +// +// check if entry table is empty +// +BOOL RplTableEmpty(VOID) +{ + DWORD i; + + BOOL bEmpty = TRUE; + + // check if hash table is empty + for (i = 0; bEmpty && i < ARRAYSIZEOF(ppsBase); ++i) + { + bEmpty = (ppsBase[i] == NULL); // check if empty + } + return bEmpty; +} + +// +// load entry table +// +BOOL RplLoadTable(LPCTSTR lpszFilename) +{ + BYTE byPage[RECORD_BLOCK]; // record page size + HANDLE hFile; + DWORD dwFileLength,dwCodeLength,dwNoSymbols,dwNoReferences; + DWORD dwFilePos,dwBytesRead,dwSymb,dwPageIndex,dwResolvedSymb; + BOOL bSymbol,bSucc; + + bSucc = FALSE; + + hFile = CreateFile(lpszFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + dwResolvedSymb = 0; // no resolved symbols added + bSymbol = TRUE; // next set is a symbol + + // read first page + ReadFile(hFile,byPage,sizeof(byPage),&dwBytesRead,NULL); + if (dwBytesRead == sizeof(byPage) && memcmp(byPage,SAT_ID,7) == 0) + { + // file length in bytes + dwFileLength = GetBigEndian(byPage+7,sizeof(WORD)) * sizeof(byPage); + + // code area in nibbles + dwCodeLength = GetBigEndian(byPage+9,sizeof(DWORD)); + + // no. of symbols & references + dwNoSymbols = GetBigEndian(byPage+13,sizeof(WORD)); + + // no. of references + dwNoReferences = GetBigEndian(byPage+15,sizeof(WORD)); + + // convert code area length into no. of pages + dwPageIndex = (dwCodeLength + (2 * sizeof(byPage) - 1)) / (2 * sizeof(byPage)); + + // calculate no. of code pages + dwFilePos = dwPageIndex * sizeof(byPage); + + // jump to begin of symbols by skipping no. of code pages + bSucc = SetFilePointer(hFile,dwFilePos,NULL,FILE_CURRENT) != INVALID_SET_FILE_POINTER; + + dwFilePos += sizeof(byPage); // actual file position + } + + // read all symbol pages + for (dwPageIndex = 256, dwSymb = 0; bSucc && dwSymb < dwNoSymbols; dwPageIndex += 42) + { + if (dwPageIndex >= 256) // read complete page + { + // read new symbol page + ReadFile(hFile,byPage,sizeof(byPage),&dwBytesRead,NULL); + dwFilePos += dwBytesRead; // update file position + if ( dwFilePos > dwFileLength + || dwBytesRead != sizeof(byPage) + || memcmp(byPage,SYMB_ID,4) != 0) + { + bSucc = FALSE; + break; + } + + dwPageIndex = 4; // begin of new symbol + } + + if (bSymbol) // this is the 42 byte symbol set + { + WORD wSymbolType = (WORD) GetBigEndian(byPage+dwPageIndex+36,sizeof(WORD)); + + // check if it's a resolved or relocatable symbol + bSymbol = (wSymbolType & OS_RESOLVED) != 0; + + if (bSymbol) ++dwResolvedSymb; // added resolved symbol + + if (wSymbolType == OS_RESOLVED) // resolved symbol type + { + TCHAR szSymbolName[36+1],*pcPtr; + PREFDATA pData; + DWORD dwHash; + + #if defined _UNICODE + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,(LPCSTR)byPage+dwPageIndex,36, + szSymbolName,ARRAYSIZEOF(szSymbolName)); + szSymbolName[36] = 0; // set EOS + } + #else + { + lstrcpyn(szSymbolName,(LPCSTR)byPage+dwPageIndex,ARRAYSIZEOF(szSymbolName)); + } + #endif + + // cut symbol name at first space character + if ((pcPtr = _tcschr(szSymbolName,_T(' '))) != NULL) + *pcPtr = 0; // set EOS + + // allocate symbol memory + VERIFY(pData = (PREFDATA) malloc(sizeof(*pData))); + pData->lpszName = DuplicateString(szSymbolName); + pData->dwAddr = GetBigEndian(byPage+dwPageIndex+38,sizeof(DWORD)); + + // add to hash table + dwHash = GetHash(pData->dwAddr); + pData->pNext = ppsBase[dwHash]; + ppsBase[dwHash] = pData; + } + + ++dwSymb; // got symbol + } + else // 42 byte fill reference + { + bSymbol = TRUE; // nothing to do, next is a symbol set + } + } + + bSucc = bSucc && (dwFilePos <= dwFileLength) + && (dwNoSymbols == (dwResolvedSymb + dwNoReferences)); + + CloseHandle(hFile); + } + + if (!bSucc) RplDeleteTable(); // delete current table + return bSucc; +} + +// +// delete entry table +// +VOID RplDeleteTable(VOID) +{ + PREFDATA pData; + DWORD i; + + // clear hash entries + for (i = 0; i < ARRAYSIZEOF(ppsBase); ++i) + { + while (ppsBase[i] != NULL) // walk through all datasets + { + pData = ppsBase[i]->pNext; + free(ppsBase[i]->lpszName); + free(ppsBase[i]); + ppsBase[i] = pData; + } + } + return; +} + +// +// return name for given entry address +// +LPCTSTR RplGetName(DWORD dwAddr) +{ + PREFDATA pData = ppsBase[GetHash(dwAddr)]; + + // walk through all datasets of hash entry + for (; pData != NULL; pData = pData->pNext) + { + if (pData->dwAddr == dwAddr) // found address + return pData->lpszName; // return symbol name + } + return NULL; +} + +// +// return entry address for given name +// +BOOL RplGetAddr(LPCTSTR lpszName, DWORD *pdwAddr) +{ + PREFDATA pData; + DWORD i; + + // check for every dataset in hash table + for (i = 0; i < ARRAYSIZEOF(ppsBase); ++i) + { + // walk through all datasets of hash entry + for (pData = ppsBase[i]; pData != NULL; pData = pData->pNext) + { + // found symbol name + if (lstrcmp(lpszName,pData->lpszName) == 0) + { + *pdwAddr = pData->dwAddr; // return address + return FALSE; // found + } + } + } + return TRUE; // not found +} diff --git a/app/src/main/cpp/TIMER.C b/app/src/main/cpp/TIMER.C new file mode 100644 index 0000000..ee34d51 --- /dev/null +++ b/app/src/main/cpp/TIMER.C @@ -0,0 +1,435 @@ +/* + * timer.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" // I/O definitions + +#define AUTO_OFF 10 // Time in minutes for 'auto off' + +// Ticks for 'auto off' +#define OFF_TIME ((ULONGLONG) (AUTO_OFF * 60) << 13) + +// memory address for clock and auto off +// S(X) = 0x70052-0x70070, G(X) = 0x80058-0x80076, 49G = 0x80058-0x80076 +#define RPLTIME ((cCurrentRomType=='S')?0x52:0x58) + +#define T1_FREQ 62 // Timer1 1/frequency in ms +#define T2_FREQ 8192 // Timer2 frequency + +static BOOL bStarted = FALSE; +static BOOL bOutRange = FALSE; // flag if timer value out of range +static UINT uT1TimerId = 0; +static UINT uT2TimerId = 0; + +static BOOL bNINT2T1 = FALSE; // state of NINT2 affected from timer1 +static BOOL bNINT2T2 = FALSE; // state of NINT2 affected from timer2 + +static BOOL bAccurateTimer; // flag if accurate timer is used +static LARGE_INTEGER lT2Ref; // counter value at timer2 start +static TIMECAPS tc; // timer information +static UINT uT2MaxTicks; // max. timer2 ticks handled by one timer event + +static DWORD dwT2Ref; // timer2 value at last timer2 access +static DWORD dwT2Cyc; // cpu cycle counter at last timer2 access + +static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); + +static DWORD CalcT2(VOID) // calculate timer2 value +{ + DWORD dwT2 = Chipset.t2; // get value from chipset + if (bStarted) // timer2 running + { + LARGE_INTEGER lT2Act; + DWORD dwT2Dif; + + // timer should run a little bit faster (10%) than maschine in authentic speed mode + DWORD dwCycPerTick = (9 * T2CYCLES) / 5; + + QueryPerformanceCounter(&lT2Act); // actual time + // calculate realtime timer2 ticks since reference point + dwT2 -= (DWORD) + (((lT2Act.QuadPart - lT2Ref.QuadPart) * T2_FREQ) + / lFreq.QuadPart); + + dwT2Dif = dwT2Ref - dwT2; // timer2 ticks since last request + + // checking if the MSB of dwT2Dif can be used as sign flag + _ASSERT((DWORD) tc.wPeriodMax < ((1<<(sizeof(dwT2Dif)*8-1))/8192)*1000); + + // 2nd timer call in a 32ms time frame or elapsed time is negative (Win2k bug) + if (!Chipset.Shutdn && ((dwT2Dif > 0x01 && dwT2Dif <= 0x100) || (dwT2Dif & 0x80000000) != 0)) + { + DWORD dwT2Ticks = ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwT2Cyc) / dwCycPerTick; + + // estimated < real elapsed timer2 ticks or negative time + if (dwT2Ticks < dwT2Dif || (dwT2Dif & 0x80000000) != 0) + { + // real time too long or got negative time elapsed + dwT2 = dwT2Ref - dwT2Ticks; // estimated timer2 value from CPU cycles + dwT2Cyc += dwT2Ticks * dwCycPerTick; // estimated CPU cycles for the timer2 ticks + } + else + { + // reached actual time -> new synchronizing + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + } + else + { + // valid actual time -> new synchronizing + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + + // check if timer2 interrupt is active -> no timer2 value below 0xFFFFFFFF + if ( Chipset.inte + && (dwT2 & 0x80000000) != 0 + && (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE)) + && (Chipset.IORam[TIMER2_CTRL]&INTR) + ) + { + dwT2 = 0xFFFFFFFF; + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + + dwT2Ref = dwT2; // new reference time + } + return dwT2; +} + +static VOID CheckT1(BYTE nT1) +{ + // implementation of TSRQ + bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (nT1&8) != 0; + IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2); + + if ((nT1&8) == 0) // timer1 MSB not set + { + Chipset.IORam[TIMER1_CTRL] &= ~SRQ; // clear SRQ bit + return; + } + + _ASSERT((nT1&8) != 0); // timer1 MSB set + + // timer MSB and INT or WAKE bit is set + if ((Chipset.IORam[TIMER1_CTRL]&(WKE|INTR)) != 0) + Chipset.IORam[TIMER1_CTRL] |= SRQ; // set SRQ + // cpu not sleeping and T1 -> Interrupt + if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER1_CTRL]&WKE)) + && (Chipset.IORam[TIMER1_CTRL]&INTR)) + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + // cpu sleeping and T1 -> Wake Up + if (Chipset.Shutdn && (Chipset.IORam[TIMER1_CTRL]&WKE)) + { + Chipset.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE bit + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + return; +} + +static VOID CheckT2(DWORD dwT2) +{ + // implementation of TSRQ + bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (dwT2&0x80000000) != 0; + IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2); + + if ((dwT2&0x80000000) == 0) // timer2 MSB not set + { + Chipset.IORam[TIMER2_CTRL] &= ~SRQ; // clear SRQ bit + return; + } + + _ASSERT((dwT2&0x80000000) != 0); // timer2 MSB set + + // timer MSB and INT or WAKE bit is set + if ((Chipset.IORam[TIMER2_CTRL]&(WKE|INTR)) != 0) + Chipset.IORam[TIMER2_CTRL] |= SRQ; // set SRQ + // cpu not sleeping and T2 -> Interrupt + if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE)) + && (Chipset.IORam[TIMER2_CTRL]&INTR)) + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + // cpu sleeping and T2 -> Wake Up + if (Chipset.Shutdn && (Chipset.IORam[TIMER2_CTRL]&WKE)) + { + Chipset.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE bit + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + return; +} + +static VOID RescheduleT2(BOOL bRefPoint) +{ + UINT uDelay; + _ASSERT(uT2TimerId == 0); // timer2 must stopped + if (bRefPoint) // save reference time + { + dwT2Ref = Chipset.t2; // timer2 value at last timer2 access + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); // cpu cycle counter at last timer2 access + QueryPerformanceCounter(&lT2Ref); // time of corresponding Chipset.t2 value + uDelay = Chipset.t2; // timer value for delay + } + else // called without new refpoint, restart t2 with actual value + { + uDelay = CalcT2(); // actual timer value for delay + } + if ((bOutRange = uDelay > uT2MaxTicks)) // delay greater maximum delay + uDelay = uT2MaxTicks; // wait maximum delay time + uDelay = (uDelay * 125 + 1023) / 1024; // timer delay in ms (1000/8192 = 125/1024) + uDelay = __max(tc.wPeriodMin,uDelay); // wait minimum delay of timer + _ASSERT(uDelay <= tc.wPeriodMax); // inside maximum event delay + // start timer2; schedule event, when Chipset.t2 will be zero + VERIFY(uT2TimerId = timeSetEvent(uDelay,0,&TimeProc,2,TIME_ONESHOT)); + return; +} + +static VOID AbortT2(VOID) +{ + _ASSERT(uT2TimerId); + timeKillEvent(uT2TimerId); // kill event + uT2TimerId = 0; // then reset var + return; +} + +static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +{ + if (uEventId == 0) return; // illegal EventId + + if (uEventId == uT1TimerId) // called from timer1 event (default period 16 Hz) + { + EnterCriticalSection(&csT1Lock); + { + Chipset.t1 = (Chipset.t1-1)&0xF;// decrement timer value + CheckT1(Chipset.t1); // test timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + return; + } + if (uEventId == uT2TimerId) // called from timer2 event, Chipset.t2 should be zero + { + EnterCriticalSection(&csT2Lock); + { + uT2TimerId = 0; // single shot timer timer2 stopped + if (!bOutRange) // timer event elapsed + { + // timer2 overrun, test timer2 control bits else restart timer2 + Chipset.t2 = CalcT2(); // calculate new timer2 value + CheckT2(Chipset.t2); // test timer2 control bits + } + RescheduleT2(!bOutRange); // restart timer2 + } + LeaveCriticalSection(&csT2Lock); + return; + } + return; + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwUser); + UNREFERENCED_PARAMETER(dw1); + UNREFERENCED_PARAMETER(dw2); +} + +VOID SetHP48Time(VOID) // set date and time +{ + SYSTEMTIME ts; + ULONGLONG ticks, time; + DWORD dw; + WORD crc, i; + BYTE p[4]; + + _ASSERT(sizeof(ULONGLONG) == 8); // check size of datatype + + GetLocalTime(&ts); // local time, _ftime() cause memory/resource leaks + + // calculate days until 01.01.0000 (Erlang BIF localtime/0) + dw = (DWORD) ts.wMonth; + if (dw > 2) + dw -= 3L; + else + { + dw += 9L; + --ts.wYear; + } + dw = (DWORD) ts.wDay + (153L * dw + 2L) / 5L; + dw += (146097L * (((DWORD) ts.wYear) / 100L)) / 4L; + dw += (1461L * (((DWORD) ts.wYear) % 100L)) / 4L; + dw += (-719469L + 719528L); // days from year 0 + + ticks = (ULONGLONG) dw; // convert to 64 bit + + // convert into seconds and add time + ticks = ticks * 24L + (ULONGLONG) ts.wHour; + ticks = ticks * 60L + (ULONGLONG) ts.wMinute; + ticks = ticks * 60L + (ULONGLONG) ts.wSecond; + + // create timerticks = (s + ms) * 8192 + ticks = (ticks << 13) | (((ULONGLONG) ts.wMilliseconds << 10) / 125); + + ticks += Chipset.t2; // add actual timer2 value + + time = ticks; // save for calc. timeout + time += OFF_TIME; // add 10 min for auto off + + dw = RPLTIME; // HP addresses for clock in port0 + + crc = 0x0; // reset crc value + for (i = 0; i < 13; ++i, ++dw) // write date and time + { + *p = (BYTE) ticks & 0xf; + crc = (crc >> 4) ^ (((crc ^ ((WORD) *p)) & 0xf) * 0x1081); + Port0[dw] = *p; // always store in port0 + ticks >>= 4; + } + + Nunpack(p,crc,4); // write crc + memcpy(Port0+dw,p,4); // always store in port0 + + dw += 4; // HP addresses for timeout + + for (i = 0; i < 13; ++i, ++dw) // write time for auto off + { + Port0[dw] = (BYTE) time & 0xf; // always store in port0 + time >>= 4; + } + + Port0[dw] = 0xf; // always store in port0 + return; +} + +VOID StartTimers(VOID) +{ + if (bStarted) // timer running + return; // -> quit + if (Chipset.IORam[TIMER2_CTRL]&RUN) // start timer1 and timer2 ? + { + bStarted = TRUE; // flag timer running + // initialisation of NINT2 lines + bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (Chipset.t1 & 8) != 0; + bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (Chipset.t2 & 0x80000000) != 0; + timeGetDevCaps(&tc,sizeof(tc)); // get timer resolution + + // max. timer2 ticks that can be handled by one timer event + uT2MaxTicks = __min((0xFFFFFFFF / 1024),tc.wPeriodMax); + uT2MaxTicks = __min((0xFFFFFFFF - 1023) / 125,uT2MaxTicks * 1024 / 125); + + CheckT1(Chipset.t1); // check for timer1 interrupts + CheckT2(Chipset.t2); // check for timer2 interrupts + // set timer resolution to greatest possible one + bAccurateTimer = (timeBeginPeriod(tc.wPeriodMin) == TIMERR_NOERROR); + // set timer1 with given period + VERIFY(uT1TimerId = timeSetEvent(T1_FREQ,0,&TimeProc,1,TIME_PERIODIC)); + RescheduleT2(TRUE); // start timer2 + } + return; +} + +VOID StopTimers(VOID) +{ + if (!bStarted) // timer stopped + return; // -> quit + if (uT1TimerId != 0) // timer1 running + { + // Critical Section handler may cause a dead lock + timeKillEvent(uT1TimerId); // stop timer1 + uT1TimerId = 0; // set flag timer1 stopped + } + if (uT2TimerId != 0) // timer2 running + { + EnterCriticalSection(&csT2Lock); + { + Chipset.t2 = CalcT2(); // update chipset timer2 value + } + LeaveCriticalSection(&csT2Lock); + AbortT2(); // stop timer2 outside critical section + } + bStarted = FALSE; + if (bAccurateTimer) // "Accurate timer" running + { + timeEndPeriod(tc.wPeriodMin); // finish service + } + return; +} + +DWORD ReadT2(VOID) +{ + DWORD dwT2; + EnterCriticalSection(&csT2Lock); + { + dwT2 = CalcT2(); // calculate timer2 value or if stopped last timer value + CheckT2(dwT2); // update timer2 control bits + } + LeaveCriticalSection(&csT2Lock); + return dwT2; +} + +VOID SetT2(DWORD dwValue) +{ + // calling AbortT2() inside Critical Section handler may cause a dead lock + if (uT2TimerId != 0) // timer2 running + AbortT2(); // stop timer2 + EnterCriticalSection(&csT2Lock); + { + Chipset.t2 = dwValue; // set new value + CheckT2(Chipset.t2); // test timer2 control bits + if (bStarted) // timer running + RescheduleT2(TRUE); // restart timer2 + } + LeaveCriticalSection(&csT2Lock); + return; +} + +BYTE ReadT1(VOID) +{ + BYTE nT1; + EnterCriticalSection(&csT1Lock); + { + nT1 = Chipset.t1; // read timer1 value + CheckT1(nT1); // update timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + return nT1; +} + +VOID SetT1(BYTE byValue) +{ + BOOL bEqual; + + _ASSERT(byValue < 0x10); // timer1 is only a 4bit counter + + EnterCriticalSection(&csT1Lock); + { + bEqual = (Chipset.t1 == byValue); // check for same value + } + LeaveCriticalSection(&csT1Lock); + if (bEqual) return; // same value doesn't restart timer period + + if (uT1TimerId != 0) // timer1 running + { + timeKillEvent(uT1TimerId); // stop timer1 + uT1TimerId = 0; // set flag timer1 stopped + } + EnterCriticalSection(&csT1Lock); + { + Chipset.t1 = byValue; // set new timer1 value + CheckT1(Chipset.t1); // test timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + if (bStarted) // timer running + { + // restart timer1 to get full period of frequency + VERIFY(uT1TimerId = timeSetEvent(T1_FREQ,0,&TimeProc,1,TIME_PERIODIC)); + } + return; +} diff --git a/app/src/main/cpp/TYPES.H b/app/src/main/cpp/TYPES.H new file mode 100644 index 0000000..031687f --- /dev/null +++ b/app/src/main/cpp/TYPES.H @@ -0,0 +1,124 @@ +/* + * types.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +// HST bits +#define XM 1 +#define SB 2 +#define SR 4 +#define MP 8 + +#define SWORD SHORT // signed 16 Bit variable +#define QWORD ULONGLONG // unsigned 64 Bit variable + +#define CHIPSET Chipset_t +typedef struct +{ + SWORD nPosX; // position of window + SWORD nPosY; + BYTE type; // calculator type + + DWORD Port0Size; // real size of module in KB + DWORD Port1Size; // real size of module in KB + DWORD Port2Size; // real size of module in KB (HP49G only) + DWORD dwUnused0; // not used, was memory pointer Port0 + DWORD dwUnused1; // not used, was memory pointer Port1 + DWORD dwUnused2; // not used, was memory pointer Port2 + + DWORD pc; + DWORD d0; + DWORD d1; + DWORD rstkp; + DWORD rstk[8]; + BYTE A[16]; + BYTE B[16]; + BYTE C[16]; + BYTE D[16]; + BYTE R0[16]; + BYTE R1[16]; + BYTE R2[16]; + BYTE R3[16]; + BYTE R4[16]; + BYTE ST[4]; + BYTE HST; + BYTE P; + WORD out; + WORD in; + BOOL SoftInt; + BOOL Shutdn; + BOOL mode_dec; + BOOL inte; // interrupt status flag (FALSE = int in service) + BOOL intk; // 1 ms keyboard scan flag (TRUE = enable) + BOOL intd; // keyboard interrupt pending (TRUE = int pending) + BOOL carry; + + WORD crc; + WORD wPort2Crc; // fingerprint of port2 + WORD wRomCrc; // fingerprint of ROM +#if defined _USRDLL // DLL version + QWORD cycles; // oscillator cycles +#else // EXE version + DWORD cycles; // oscillator cycles + DWORD cycles_reserved; // reserved for MSB of oscillator cycles +#endif + DWORD dwKdnCycles; // cpu cycles at start of 1ms key handler + + UINT Bank_FF; // save state of HP48GX port2 or state of HP49G ROM FF + UINT FlashRomState; // WSM state of flash memory (unused) + BYTE cards_status; + BYTE IORam[64]; // I/O hardware register + UINT IOBase; // address of I/O modules page + BOOL IOCfig; // I/O module configuration flag + BYTE P0Base, BSBase, P1Base, P2Base; // address of modules first 2KB page + BYTE P0Size, BSSize, P1Size, P2Size; // mapped size of module in 2KB + BYTE P0End, BSEnd, P1End, P2End; // address of modules last 2KB page + BOOL P0Cfig, BSCfig, P1Cfig, P2Cfig; // module address configuration flag + BOOL P0Cfg2, BSCfg2, P1Cfg2, P2Cfg2; // module size configuration flag + + BYTE t1; + DWORD t2; + + BOOL bShutdnWake; // flag for wake up from SHUTDN mode + + BYTE Keyboard_Row[9]; + WORD IR15X; + UINT Keyboard_State; // not used + + signed short loffset; + signed int width; + UINT boffset; + UINT lcounter; + UINT sync; // not used + BYTE contrast; + BOOL dispon; // not used + DWORD start1; + DWORD start12; + DWORD end1; + DWORD start2, end2; + + // CdB for HP: add apples header + DWORD d0size; // no. of header display lines + BYTE d0memory[4096*2]; // memory for header display area + DWORD d0offset; // offset inside the header display for the content + DWORD d0address; // address in saturn addr area for d0memory (2 pages) +// BOOL d0Cfig; // modul configured +} Chipset_t; + +// additional Saturnator registers and Apple hardware +typedef struct +{ + BYTE byType[4]; // "SPL" Saturn PLus + + QWORD fld[7]; // user defined field masks F1-F7 + + BOOL d0Cfig; // display memory modul configured + DWORD d0address; // address in saturn addr area for d0memory (2 pages) + DWORD d0size; // no. of header display lines + DWORD d0offset; // offset inside the display memory for the content + BYTE d0memory[4096*2]; // 4KB memory for header display area +} APPLE_CHIPSET; diff --git a/app/src/main/cpp/UDP.C b/app/src/main/cpp/UDP.C new file mode 100644 index 0000000..e404943 --- /dev/null +++ b/app/src/main/cpp/UDP.C @@ -0,0 +1,79 @@ +/* + * udp.c + * + * This file is part of Emu48 + * + * Copyright (C) 2011 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +TCHAR szUdpServer[1024] = _T("localhost"); +WORD wUdpPort = 5025; // scpi-raw + +static IN_ADDR ip_addr = { 255, 255, 255, 255 }; + +VOID ResetUdp(VOID) +{ + ip_addr.s_addr = INADDR_NONE; // invalidate saved UDP address + return; +} + +BOOL SendByteUdp(BYTE byData) +{ + WSADATA wsd; + SOCKET sClient; + SOCKADDR_IN sServer; + + BOOL bErr = TRUE; + + VERIFY(WSAStartup(MAKEWORD(1,1),&wsd) == 0); + + if (ip_addr.s_addr == INADDR_NONE) // IP address not specified + { + LPSTR lpszIpAddr; + + #if defined _UNICODE + DWORD dwLength = lstrlen(szUdpServer) + 1; + + if ((lpszIpAddr = (LPSTR) _alloca(dwLength)) == NULL) + return TRUE; // server not found + + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + szUdpServer, dwLength, + lpszIpAddr, dwLength, NULL, NULL); + #else + lpszIpAddr = szUdpServer; + #endif + + ip_addr.s_addr = inet_addr(lpszIpAddr); + + // not a valid ip address -> try to get ip address from name server + if (ip_addr.s_addr == INADDR_NONE) + { + PHOSTENT host = gethostbyname(lpszIpAddr); + if (host == NULL) + { + return TRUE; // server not found + } + + ip_addr.s_addr = ((PIN_ADDR) host->h_addr_list[0])->s_addr; + } + } + + // create UDP socket + if ((sClient = socket(AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET) + { + sServer.sin_family = AF_INET; + sServer.sin_port = htons(wUdpPort); + sServer.sin_addr.s_addr = ip_addr.s_addr; + + // transmit data byte + bErr = sendto(sClient, (LPCCH) &byData, sizeof(byData), 0, (LPSOCKADDR) &sServer, sizeof(sServer)) == SOCKET_ERROR; + closesocket(sClient); + } + + WSACleanup(); // cleanup network stack + return bErr; +} diff --git a/app/src/main/cpp/emu48-jni.c b/app/src/main/cpp/emu48-jni.c new file mode 100644 index 0000000..3599425 --- /dev/null +++ b/app/src/main/cpp/emu48-jni.c @@ -0,0 +1,14 @@ +// +// Created by cosnier on 12/11/2018. +// +#include +#include + +JNIEXPORT jstring JNICALL +Java_com_regis_cosnier_emu48_MainActivity_stringFromJNI( + JNIEnv *env, + jobject thisz) { +// std::string hello = "Hello from C++"; +// return env->NewStringUTF(hello.c_str()); + return (*env)->NewStringUTF(env, "Hello from JNI !"); +} diff --git a/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java b/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java new file mode 100644 index 0000000..66c4090 --- /dev/null +++ b/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java @@ -0,0 +1,68 @@ +package com.regis.cosnier.emu48; + +import android.os.Bundle; +import android.widget.TextView; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; + +public class MainActivity extends AppCompatActivity { + + // Used to load the 'native-lib' library on application startup. + static { + System.loadLibrary("native-lib"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show(); + } + }); + + // Example of a call to a native method + TextView tv = (TextView) findViewById(R.id.sample_text); + tv.setText(stringFromJNI()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + /** + * A native method that is implemented by the 'native-lib' native library, + * which is packaged with this application. + */ + public native String stringFromJNI(); +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..971add5 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..eed7a42 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e76021c --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..bebda2a --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..625dca8 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..a26f6fb --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..a26f6fb --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..898f3ed Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..dffca36 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..64ba76f Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..dae5e08 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..e5ed465 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..14ed0af Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..6b3f6ab --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..2c00107 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..61d234e --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + Emu48 + Settings + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..80f09db --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + +