diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..02aea9b92 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,183 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*.{c,cpp,h}] +end_of_line = lf +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + + +#### Resharper specific + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppBlankLinesPageScheme.html +keep_blank_lines_in_declarations = 2 +keep_blank_lines_in_code = 2 +blank_lines_around_class_definition = 1 +blank_lines_around_function_declaration = 1 +blank_lines_around_function_definition = 1 +blank_lines_around_single_line_function_definition = 1 +blank_lines_around_namespace = 1 +blank_lines_around_other_declaration = 0 + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppBracesPageScheme.html +namespace_declaration_braces = next_line +linkage_specification_braces = next_line +type_declaration_braces = next_line +place_namespace_definitions_on_same_line = false +invocable_declaration_braces = next_line +anonymous_method_declaration_braces = next_line +case_block_braces = next_line +other_braces = next_line +expression_braces = inside +empty_block_style = multiline +simple_block_style = line_break + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppIndentStylePageSchema.html +indent_style = space +cpp_indent_size = 4 +#tab_width = 4 #see editorconfig site ? +cpp_alignment_tab_fill_style = use_spaces + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppOtherPageScheme.html +cpp_continuous_line_indent = single +# Indent namespace members ? +linkage_specification_indentation = all +# Indent access specifier from class ? +indent_wrapped_function_names = false +indent_switch_labels = false +indent_method_decl_pars = inside +indent_invocation_pars = inside +indent_statement_pars = inside +indent_preprocessor_directives = none +align_multiple_declaration = true +align_multiline_parameter = true +align_multiline_argument = true +align_first_arg_by_paren = true +align_multiline_type_parameter = true +align_multiline_type_argument = true +align_multiline_extends_list = true +align_multiline_ctor_init = true +outdent_commas = false +align_ternary = align_all +align_multiline_calls_chain = true +outdent_dots = false +align_multiline_binary_expressions_chain = true +# these two could be false +int_align_eq = true +int_align_declaration_names = true +int_align_comments = true + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppSpacesPageScheme.html + +## In Declarations +cpp_space_before_ptr_in_data_member = false +cpp_space_after_ptr_in_data_member = true +cpp_space_before_ptr_in_data_members = true +cpp_space_after_ptr_in_data_members = false +cpp_space_before_ptr_in_method = false +cpp_space_after_ptr_in_method = true +cpp_space_after_comma_in_declaration = true +cpp_space_before_comma_in_declaration = false +cpp_space_after_comma_in_method = true +cpp_space_before_comma_in_method = false +cpp_space_after_comma_in_enum = true +cpp_space_before_comma_in_enum = false +cpp_space_after_comma_in_base_clause = true +cpp_space_before_comma_in_base_clause = false +cpp_space_between_method_declaration_name_and_open_parenthesis = false +cpp_space_between_method_declaration_parameter_list_parentheses = false +cpp_space_between_method_declaration_empty_parameter_list_parentheses = false +cpp_space_before_template_params = true +cpp_space_within_template_params = false +cpp_space_within_empty_template_params = false +cpp_space_after_comma_in_template_params = true +cpp_space_before_comma_in_template_params = false +cpp_space_before_template_args = false +cpp_space_within_template_args = false +cpp_space_after_comma_in_template_args = true +cpp_space_before_comma_in_template_args = false +cpp_space_between_closing_angle_brackets_in_template_args = false +cpp_space_around_alias_eq = true +cpp_space_around_deref_in_trailing_return_type = false +cpp_space_before_colon_in_inheritance_clause = true +cpp_space_after_colon_in_inheritance_clause = true + +## In Control Statements +cpp_space_after_keywords_in_control_flow_statements = false +cpp_space_between_parentheses_of_control_flow_statements = false +cpp_space_before_semicolon_in_for_statement = false +cpp_space_after_semicolon_in_for_statement = true +cpp_space_before_for_colon = true +cpp_space_after_for_colon = true +cpp_space_before_colon_in_case = false +cpp_space_after_colon_in_case = true + +## In Expressions +cpp_space_around_binary_operator = true +cpp_space_around_assignment_operator = true +cpp_space_around_dot = false +cpp_space_within_parentheses = false +cpp_space_before_open_square_brackets = false +cpp_space_within_array_access_brackets = false +cpp_space_before_method_call_parentheses = false +cpp_space_before_empty_method_call_parentheses = false +cpp_space_between_typecast_parentheses = false +cpp_space_after_cast = false +cpp_space_between_method_call_parameter_list_parentheses = false +cpp_space_between_method_call_empty_parameter_list_parentheses = false +cpp_space_before_comma_in_method_call = false +cpp_space_after_comma_in_method_call = true +cpp_space_before_comma_in_initializer_braces = false +cpp_space_after_comma_in_initializer_braces = true +cpp_space_before_ternary_quest = true +cpp_space_after_ternary_quest = true +cpp_space_before_ternary_colon = true +cpp_space_after_ternary_colon = true +cpp_space_before_initializer_braces = true +cpp_space_within_initializer_braces = true +cpp_space_within_empty_initializer_braces = true + +## Other +cpp_space_before_trailing_comment = true +cpp_disable_space_changes_before_trailing_comment = false + + +### https://www.jetbrains.com/help/resharper/EditorConfig_CPP_CppWrappingPageScheme.html + +## Place on New Line +new_line_before_else = true +new_line_before_while = false +cpp_new_line_before_catch = true +simple_embedded_statement_style = line_break +cpp_simple_case_statement_style = line_break +function_definition_return_type_style = on_single_line +toplevel_function_definition_return_type_style = on_single_line +function_declaration_return_type_style = on_single_line +toplevel_function_declaration_return_type_style = on_single_line +cpp_break_template_declaration = line_break +cpp_member_initializer_list_style = line_break +line_break_before_comma_in_member_initializer_lists = false +line_break_after_comma_in_member_initializer_lists = true + +## Line Wrapping +cpp_max_line_length = 120 +cpp_wrap_before_comma = false +cpp_wrap_ternary_expr_style = chop_if_long +cpp_wrap_before_ternary_opsigns = true +cpp_wrap_before_colon = true +cpp_wrap_enumeration_style = chop_always +cpp_wrap_braced_init_list_style = wrap_if_long +cpp_wrap_base_clause_style = chop_if_long +cpp_wrap_ctor_initializer_style = chop_if_long +cpp_wrap_parameters_style = wrap_if_long +cpp_wrap_before_declaration_lpar = false +cpp_wrap_after_declaration_lpar = true +cpp_wrap_arguments_style = wrap_if_long +cpp_wrap_before_invocation_lpar = false +cpp_wrap_after_invocation_lpar = true diff --git a/.gitignore b/.gitignore index 56a8a825a..7150aa1ae 100644 --- a/.gitignore +++ b/.gitignore @@ -382,3 +382,4 @@ vs2015/libpng/linux-build/* /vs2015/libpdcurses/wincon/pdcurses.lib /vs2015/libpdcurses/wincon/ozdemo.exe /vs2015/libpdcurses/wincon/firework.exe +.vscode/settings.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..187fbe73f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,163 @@ +{ + "configurations": [ + { + "name": "SDL1 Debug Win32/Win64", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + }, + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/src", + "${workspaceFolder}/src/aviwriter", + "${workspaceFolder}/src/hardware/snd_pc98/cbus", + "${workspaceFolder}/src/hardware/snd_pc98/common", + "${workspaceFolder}/src/hardware/snd_pc98/generic", + "${workspaceFolder}/src/hardware/snd_pc98/sound", + "${workspaceFolder}/src/hardware/snd_pc98/sound/getsnd", + "${workspaceFolder}/src/hardware/snd_pc98/x11", + "${workspaceFolder}/src/mt32", + "${workspaceFolder}/vs2015", + "${workspaceFolder}/vs2015/libpdcurses", + "${workspaceFolder}/vs2015/libpng", + "${workspaceFolder}/vs2015/pcap", + "${workspaceFolder}/vs2015/sdl/include", + "${workspaceFolder}/vs2015/sdlnet", + "${workspaceFolder}/vs2015/zlib" + ], + "defines": [ + "_DEBUG", + "_MBCS", + "_CRT_SECURE_NO_WARNINGS", + "WIN32", + "__WIN32__", + "C_SDL1", + "_FILE_OFFSET_BITS=64" + ], + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x64" + }, + { + "name": "SDL1 Release Win32/Win64", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + }, + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/src", + "${workspaceFolder}/src/aviwriter", + "${workspaceFolder}/src/hardware/snd_pc98/cbus", + "${workspaceFolder}/src/hardware/snd_pc98/common", + "${workspaceFolder}/src/hardware/snd_pc98/generic", + "${workspaceFolder}/src/hardware/snd_pc98/sound", + "${workspaceFolder}/src/hardware/snd_pc98/sound/getsnd", + "${workspaceFolder}/src/hardware/snd_pc98/x11", + "${workspaceFolder}/src/mt32", + "${workspaceFolder}/vs2015", + "${workspaceFolder}/vs2015/libpdcurses", + "${workspaceFolder}/vs2015/libpng", + "${workspaceFolder}/vs2015/pcap", + "${workspaceFolder}/vs2015/sdl/include", + "${workspaceFolder}/vs2015/sdlnet", + "${workspaceFolder}/vs2015/zlib" + ], + "defines": [ + "_MBCS", + "_CRT_SECURE_NO_WARNINGS", + "WIN32", + "__WIN32__", + "C_SDL1", + "_FILE_OFFSET_BITS=64" + ], + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x64" + }, + { + "name": "SDL2 Debug Win32/Win64", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + }, + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/src", + "${workspaceFolder}/src/aviwriter", + "${workspaceFolder}/src/hardware/snd_pc98/cbus", + "${workspaceFolder}/src/hardware/snd_pc98/common", + "${workspaceFolder}/src/hardware/snd_pc98/generic", + "${workspaceFolder}/src/hardware/snd_pc98/sound", + "${workspaceFolder}/src/hardware/snd_pc98/sound/getsnd", + "${workspaceFolder}/src/hardware/snd_pc98/x11", + "${workspaceFolder}/src/mt32", + "${workspaceFolder}/vs2015", + "${workspaceFolder}/vs2015/libpdcurses", + "${workspaceFolder}/vs2015/libpng", + "${workspaceFolder}/vs2015/pcap", + "${workspaceFolder}/vs2015/sdl2/include", + "${workspaceFolder}/vs2015/sdlnet", + "${workspaceFolder}/vs2015/zlib" + ], + "defines": [ + "_DEBUG", + "_MBCS", + "_CRT_SECURE_NO_WARNINGS", + "WIN32", + "__WIN32__", + "C_SDL2", + "_FILE_OFFSET_BITS=64" + ], + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x64" + }, + { + "name": "SDL2 Release Win32/Win64", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + }, + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/src", + "${workspaceFolder}/src/aviwriter", + "${workspaceFolder}/src/hardware/snd_pc98/cbus", + "${workspaceFolder}/src/hardware/snd_pc98/common", + "${workspaceFolder}/src/hardware/snd_pc98/generic", + "${workspaceFolder}/src/hardware/snd_pc98/sound", + "${workspaceFolder}/src/hardware/snd_pc98/sound/getsnd", + "${workspaceFolder}/src/hardware/snd_pc98/x11", + "${workspaceFolder}/src/mt32", + "${workspaceFolder}/vs2015", + "${workspaceFolder}/vs2015/libpdcurses", + "${workspaceFolder}/vs2015/libpng", + "${workspaceFolder}/vs2015/pcap", + "${workspaceFolder}/vs2015/sdl2/include", + "${workspaceFolder}/vs2015/sdlnet", + "${workspaceFolder}/vs2015/zlib" + ], + "defines": [ + "_MBCS", + "_CRT_SECURE_NO_WARNINGS", + "WIN32", + "__WIN32__", + "C_SDL2", + "_FILE_OFFSET_BITS=64" + ], + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..53d08899c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,99 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Launch SDL1 Debug Win32", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/Win32/Debug/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/Win32/Debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL1 Debug Win64", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/x64/Debug/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/x64/Debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL1 Release Win32", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/Win32/Release/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/Win32/Release", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL1 Release Win64", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/x64/Release/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/x64/Release", + "environment": [], + "externalConsole": true + }, + + + + { + "name": "(Windows) Launch SDL2 Debug Win32", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/Win32/Debug SDL2/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/Win32/Debug SDL2", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL2 Debug Win64", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/x64/Debug SDL2/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/x64/Debug SDL2", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL2 Release Win32", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/Win32/Release SDL2/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/Win32/Release SDL2", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Launch SDL2 Release Win64", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/x64/Release SDL2/dosbox-x.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/x64/Release SDL2", + "environment": [], + "externalConsole": true + }, + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..06e7ee5c0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,146 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build Windows SDL1 Debug Win32", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration=Debug", + "/p:Platform=Win32", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL1 Debug Win64", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration=Debug", + "/p:Platform=x64", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL1 Release Win32", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration=Release", + "/p:Platform=Win32", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL1 Release Win64", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration=Release", + "/p:Platform=x64", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL2 Debug Win32", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration='Debug SDL2'", + "/p:Platform=Win32", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL2 Debug Win64", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration='Debug SDL2'", + "/p:Platform=x64", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL2 Release Win32", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration='Release SDL2'", + "/p:Platform=Win32", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build Windows SDL2 Release Win64", + "type": "shell", + "command": "msbuild", + "args": [ + "/property:GenerateFullPaths=true", + "/p:Configuration='Release SDL2'", + "/p:Platform=x64", + "/t:build", + "vs2015/dosbox-x.sln" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index f1a8abef9..78aa6153e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,27 @@ +TODO add version + - INT 10h AH=10h now ignores AL=3 in PCjr mode. + - Fixed keyboard handler bug in PCjr mode that caused some CPU + register corruption and general crashiness in games. + - Improved shell: (Aybe, Joncampbell123) + - Ctrl+Left and Ctrl+Right permits word-navigation. + - Added emulation of 'Ins' key behavior. + - Num Lock stays on at startup and is synchronized with host (Aybe) + when DOSBox-X window gains focus (Windows). (Aybe) + - Added visual feedback to Hat/D-pad buttons in mapper. (Aybe) + - Added documentation for 'dir' command sorting switches. (Aybe) + - Menu 'Show console' is now checked with '-console' (SDL1). (Aybe) + - Improved joystick support (see README.joystick): (Aybe) + - Added deadzone and response for joystick axes. + - Axes can be remapped for devices with questionable layout. + - User-settable deadzones for joystick bindings in mapper, + mappings like WSAD keys to axes is less frustrating. + - Improved mouse integration: + - Now by default DOSBox-X does not emulate mouse movement when the mouse + is not locked. This gives a consistent experience when compared to host OS. + For the old behavior, use [sdl] mouse_emulation=always. + - Added visual or auditive feedback that notifies about auto-lock state. + This feature can be switched off, use [sdl] autolock_feedback=none. + 0.82.7 - Mac OS X builds now honor showmenu=false by leaving the stock SDL menu in place at startup. diff --git a/README b/README index 58b948cb0..b3f59fc2c 100644 --- a/README +++ b/README @@ -49,11 +49,14 @@ NOTICE: Use the 32-bit toolchain from the main MinGW project, not the MinGW64 pr Visual Studio 2017 compile for 32/64-bit Windows Vista or higher Use the ./vs2015/dosbox-x.sln "solution" file and compile. You will need the DirectX 2010 SDK for Direct3D9 support. -See the README.Windows for extra information about this platform. As of 2018/06/06, VS2017 builds (32-bit and 64-bit) explicitly require a processor that supports the SSE instruction set. +Visual Studio Code is supported, too. + +Check the README.Windows file for more information about this platform. + XCode (on Mac OS X, from the Terminal) to target Mac OS X ./build-debug diff --git a/README.Windows b/README.Windows index cdd16a49a..669b6122d 100644 --- a/README.Windows +++ b/README.Windows @@ -1,9 +1,12 @@ Getting started with DOSBox-X development under Windows 10 -========================================================== + Visual Studio ------------- +Setting up the environment from scratch +--------------------------------------- + - download Visual Studio installer from https://www.visualstudio.com/downloads - launch the installer - in 'Workloads' tab, tick 'Desktop development with C++' @@ -13,10 +16,15 @@ Visual Studio - launch the installer and follow the instructions - if you encounter error S1023 during installation, follow the instructions at https://support.microsoft.com/en-us/help/2728613/s1023-error-when-you-install-the-directx-sdk-june-2010 - open the DOSBox-X Visual Studio solution located in 'vs2015' folder of the repository +- for debugging, you might want to change 'Working Directory' default value of $(ProjectDir) to $(TargetDir) -Visual Studio must be closed prior installing the DirectX SDK, else it won't pick up environment variables set by the DirectX SDK and this will result in building errors. +Note that Visual Studio must be closed prior installing the DirectX SDK, else it won't pick up environment variables set by the DirectX SDK and this will result in building errors. -You can pass additional options to the custom-build of SDL1 in the solution through the SDL1AdditionalOptions environment variable, e.g. + +Pass additional options to the custom-build of SDL1 +--------------------------------------------------- + +You can do that through the SDL1AdditionalOptions environment variable: REM adding a preprocessor define SET SDL1AdditionalOptions=/DMY_DEFINE @@ -49,35 +57,27 @@ Get and install Build Tools for Visual Studio 2017 from https://www.visualstudio 3. Setup VSCode build task - - open DOSBox-X folder in VSCode - - menu Tasks, Configure Default Build Task - - click 'Create tasks.json file from template' - - click 'MSBuild Executes the build target' +The default build task is 'build Windows SDL1 Debug Win32', you can switch to another in Tasks menu, Configure Default Build Task. -The tasks.json file is now visible but still needs some polish: +3. Setup C/C++ extension from Microsoft - - menu Tasks, Configure Default Build Task - - click 'build' - - in the 'tasks/args' section, add "vs2015/dosbox-x.sln" argument - -You can now build DOSBox-X using menu Tasks, Run Build Task. +Switch to the extensions pane and install it, its identifier is ms-vscode.cpptools. When done, open the Command Palette and search for 'C/Cpp: Select a Configuration' to select the appropriate configuration for your build task. -4. Setting up the debugging +4. Setup the debugging - - menu Debug, Start Debugging - - click 'More...' - - install C/C++ (extension identifier is ms-vscode.cpptools) - - menu Debug, Start Debugging - - click 'C++ (Windows)' - - in lauch.json 'configurations' section, adjust the first configuration 'program' value, e.g. "${workspaceFolder}/bin/Win32/Debug/dosbox-x.exe" +To change configuration, switch to the Debug pane and choose desired one in the combo box. -You can now debug DOSBox-X. +You can also do that from the bottom blue bar, but for some reason, VSCode does not always show it on startup; to remedy this problem execute the former procedure. + +You can now build and debug DOSBox-X. 5. Notes and TODO +Even though it leverages existing Visual Studio solution, you do not need Visual Studio to be installed on your system, build tools are sufficient. + These instructions were written in an already set up environment, should some step be not reproducible, open an issue with the exact details so this guide can be improved. -The simplest scenario was addressed with one configuration being set up. SDL1, SDL2, Debug and Release configurations should be addressed. +The section about configuring IntelliSense still needs to be written. CMake @@ -92,7 +92,7 @@ The CMakeLists.txt is provided as an alternative to the solution in 'vs2015' fol Joystick improvements for SDL1 build ------------------------------------ -XInput is now supported through the SDL_JOYSTICK_XINPUT define, this fixes the long plaguing issue where triggers were seen as the third axis on Xbox controllers. By enabling this flag, the third axis will be the horizontal axis of the right thumbstick and triggers will act the fifth and sixth axes when using the 'ch' joystick type. +XInput is now supported through the SDL_JOYSTICK_XINPUT define, this fixes the long plaguing issue where triggers were seen as the third axis on Xbox controllers. The resulting experience was an unsable input since both triggers shared the same the axis in the default DirectInput driver provided by Microsoft, for compatibility reasons. Some might have noticed that an earlier version of the device driver did not exhibit such behavior, at the cost of breaking legacy compatibility. By enabling this flag, XInput will be used in place of DirectInput. Consequently, the third axis will be the horizontal axis of the right thumbstick and triggers will act as the fifth and sixth axes when using the 'ch' joystick type. To enable it: diff --git a/README.joystick b/README.joystick new file mode 100644 index 000000000..fa600deee --- /dev/null +++ b/README.joystick @@ -0,0 +1,137 @@ +Overview of latest joystick parameters in dosbox.conf +===================================================== + +Sample configuration: + +[joystick] + +# Following settings define deadzone and response curve for DOS joystick axes. +# They are to be used when your software input method IS a joystick, +# contrarily to the settings in the [mapper] section below. + +# First joystick has twice as much settings as the second one, +# this because when your joystick type is 4axis, fcs or ch, +# they in fact do combine both joystick ports to make an uber DOS joystick. +# see https://www.dosbox.com/DOSBoxManual.html#Joystick for more information + +# Settings of interest per joystick type: + +# 2axis +# player 1: joy1deadzone1, joy1response1 +# player 2: joy2deadzone1, joy2response1 + +# 4axis, 4axis_2, fcs, ch +# player 1: joy1deadzone1, joy1deadzone2, joy1response1, joy1response2 +# player 2: N/A + +joy1deadzone1=0.26 +joy1deadzone2=0.26 +joy2deadzone1=0.26 + +joy1response1=3.00 +joy1response2=3.00 +joy2response1=3.00 + +# Following settings allows you to remap physical axes, this is useful +# when either your device or software has a fixed layout. + +joy1axis0=0 +joy1axis1=1 +joy1axis2=2 +joy1axis3=3 +joy1axis4=4 +joy1axis5=5 +joy1axis6=6 +joy1axis7=7 + +joy2axis0=0 +joy2axis1=1 +joy2axis2=2 +joy2axis3=3 +joy2axis4=4 +joy2axis5=5 +joy2axis6=6 +joy2axis7=7 + +# Fix Xbox One Controller triggers acting as 3rd axis (4axis): +# joy1axis2=4 +# Both directions of your right thumbstick are now working. + + +[mapper] + +# Following settings specify deadzone for a mapper binding to be triggerred. +# This is useful when you wish to map some physical joystick axes to act as keys, +# i.e. use left thumbstick to emit WSAD keys. + +joy1deadzone0-=0.60 +joy1deadzone0+=0.60 +joy1deadzone1-=0.60 +joy1deadzone1+=0.60 +joy1deadzone2-=0.20 +joy1deadzone2+=0.20 +joy1deadzone3-=0.20 +joy1deadzone3+=0.20 +joy1deadzone4-=0.20 +joy1deadzone4+=0.20 +joy1deadzone5-=0.20 +joy1deadzone5+=0.20 +joy1deadzone6-=0.20 +joy1deadzone6+=0.20 +joy1deadzone7-=0.20 +joy1deadzone7+=0.20 + +joy2deadzone0-=0.60 +joy2deadzone0+=0.60 +joy2deadzone1-=0.60 +joy2deadzone1+=0.60 +joy2deadzone2-=0.20 +joy2deadzone2+=0.20 +joy2deadzone3-=0.20 +joy2deadzone3+=0.20 +joy2deadzone4-=0.20 +joy2deadzone4+=0.20 +joy2deadzone5-=0.20 +joy2deadzone5+=0.20 +joy2deadzone6-=0.20 +joy2deadzone6+=0.20 +joy2deadzone7-=0.20 +joy2deadzone7+=0.20 + + +Deadzone +-------- + +Allows an axis to be at rest below a certain threshold. As a result, axis value at rest will stay fairly stable whereas previously lot of jittering could be seen. A default value of 0.26 will do fine on contemporary gamepads (e.g. Xbox One Controller). A higher value might be needed for not so new devices as they tend to loosen over time. + + +Response +-------- + +Smoothing of joystick movement. Player input is smoothed using a power function, 'response' being the exponent parameter. A value of 1.0 produces a linear (unfiltered) input. A value of 3.0 produces a cubic interpolation (good for aiming). Beware though that as you increase the exponent, the produced input is less uniform overall. As exponent increases, you gain precision at center but lose precision at edges; also the overall circle shape that a linear input produces 'morphs' to a rhombus shape. Recommended range is between 1.0 and 5.0. + +Tip 1: +Setting a negative value will generate the opposite behavior. + +Tip 2: +Do not set to 0.0 as this will invariably generate a movement. + + +Mapper deadzones +---------------- + +Tip 1: +To ensure the triggering of bi-directional command like move forward but strafe at the same time, deadzone values shall be less than ~0.7071 (the maximum possible extent when diagonally moving with a joystick with a circular restriction). + +Tip 2: +The mapper deadzones are affected by 'axes mapping', this is not likely to be a problem as you are looking to emulate keyboard presses using a joystick. However, make sure to set 'axes mapping' to their default value, or simply remove them from your configuration. + + +General notes +------------- + +You will want to try and experiment as on what settings works best for software A and software B, and will probably end up with software-specific configurations. + +Please note that most DOS software supporting joystick input will not react properly when you adjust CPU cycles while they are being run, i.e. either find the appropriate number of CPU cycles it requires first or restart it for a consistent joystick experience. + +DOSBox-X supports 6 buttons joysticks, to enable support, set joystick type to 4axis. You will find that 5th and 6th buttons are in fact the negative sides of the 2nd port joystick axes. To remedy this problem and use buttons instead, in the mapper add bindings to 'Axis 3 X-' and 'Axis 4 Y-' to buttons you which to act as 5th and 6th buttons on your device. Note that you can only have a single 6 buttons joystick at any time, this is due to how they are implemented (such device in fact use both joystick ports). diff --git a/README.xbrz b/README.xbrz new file mode 100644 index 000000000..001e04433 --- /dev/null +++ b/README.xbrz @@ -0,0 +1,60 @@ +xBRZ (https://sourceforge.net/projects/xbrz/) scaler is integrated into DOSBox-X. + +To enable, use + scaler=xbrz +or + scaler=xbrz_bilinear +option in [render] section. + +The difference between two options is in the post-scaling resize method. +xBRZ scaler uses fixed scale factors, 2X to 6X, which are usually not exactly +matching the display window. So nearest size xBRZ scaler output is then +resized (upscaled/dowscaled) using either nearest neighbor ('xbrz') or +bilinear ('xbrz_bilinear') algorithm to match the window size. + +It is highly recommended to use 'direct3d' or any of the 'opengl' output modes +with xBRZ because bilinear post-scaler performance is terrible and nearest +neighbor post-scaler does not provide good results. + +In 'direct3d' / 'opengl' output modes, there is no difference between +'xbrz' and 'xbrz_bilinear' variants, because software post-scaler is not used +and output is post-scaled by the output interface/hardware. + +Differences from reference implementation on xBRZ site / in Daum build: + +- DOSBox-X implementation supports 'surface', 'direct3d' and 'opengl' outputs + (including opengl variants 'openglnb' and 'openglhq') +- Windowed mode is fully working with xBRZ scaler enabled +- You can combine xBRZ with 'direct3d' mode post-scalers to get i.e. TV effect +- Enabling xBRZ scaler does not disable 'aspect' option, it is honored + +Things to notice: + +- The scaler is very CPU intensive so it will run with decent speed for + games and demos only on high-end CPUs. Keep this in mind. You can try + to correct performance for high output resolutions by using either + 'xbrz fixed scale factor' or 'xbrz max scale factor' options to limit + xBRZ scale factor (lower factors improve performance), of course this + reduces image quality for higher output resolutions proportionally. + +- Using 'direct3d' or 'opengl' mode actually improves performance a lot + because it gets rid of the software post-scaler pass. + +- xBRZ scaler code uses parallel processing internally. There is also + 'xbrz slice' option that affects parallelism, its default (16) gives + good results on 4-8 core CPUs. + +- When xBRZ scaler is enabled, internal DOSBox scaler options are disabled + because any pre-scaling will break xBRZ algorithm. For the same very reason, + 'doublescan' option is always turned off internally when xBRZ is enabled. + +- When using xBRZ in 'surface' output mode and window/screen sizes less than + 2x of the original DOS resolution, 'xbrz' scaler can degrade quality instead + of improving it, use 'direct3d'/'opengl' outputs or 'xbrz_bilinear' scaler + variant to avoid this. + +Caveats / issues / things to do / incomplete: + +- In case video adapter interface uses non-standard color scheme / byte order, + colors with xBRZ scaler enabled would most probably be garbled. + [YET TO REPRODUCE ANYWHERE] diff --git a/Ref-FM-Towns-Bochs/FMBochs.zip b/Ref-FM-Towns-Bochs/FMBochs.zip new file mode 100644 index 000000000..12ceec003 Binary files /dev/null and b/Ref-FM-Towns-Bochs/FMBochs.zip differ diff --git a/Ref-FM-Towns-Bochs/FMBochs_2.zip b/Ref-FM-Towns-Bochs/FMBochs_2.zip new file mode 100644 index 000000000..92f668e10 Binary files /dev/null and b/Ref-FM-Towns-Bochs/FMBochs_2.zip differ diff --git a/build_sdl1_xinput_flavor.bat b/build_sdl1_xinput_flavor.bat new file mode 100644 index 000000000..2d1bd13a8 --- /dev/null +++ b/build_sdl1_xinput_flavor.bat @@ -0,0 +1,8 @@ +@echo off +pushd +cd vs2015 +set SDL1AdditionalOptions=/DSDL_JOYSTICK_XINPUT +msbuild dosbox-x.sln /p:Configuration=Release /p:Platform=Win32 +msbuild dosbox-x.sln /p:Configuration=Release /p:Platform=x64 +set SDL1AdditionalOptions= +popd \ No newline at end of file diff --git a/docs/BDA - BIOS Data Area - PC Memory Map.txt b/docs/BDA - BIOS Data Area - PC Memory Map.txt new file mode 100644 index 000000000..1a64cba75 --- /dev/null +++ b/docs/BDA - BIOS Data Area - PC Memory Map.txt @@ -0,0 +1,330 @@ +http://stanislavs.org/helppc/bios_data_area.html + +BDA - BIOS Data Area - PC Memory Map + + Address Size Description + + 00:00 256dwords Interrupt vector table + 30:00 256bytes Stack area used during post and bootstrap + 40:00 word COM1 port address + 40:02 word COM2 port address + 40:04 word COM3 port address + 40:06 word COM4 port address + 40:08 word LPT1 port address + 40:0A word LPT2 port address + 40:0C word LPT3 port address + 40:0E word LPT4 port address (except PS/2) + Extended BIOS Data Area segment (PS/2, see EBDA) + 40:10 2 bytes Equipment list flags (see INT 11) + + |7|6|5|4|3|2|1|0| 40:10 (value in INT 11 register AL) + | | | | | | | `- IPL diskette installed + | | | | | | `-- math coprocessor + | | | | |-+--- old PC system board RAM < 256K + | | | | | `-- pointing device installed (PS/2) + | | | | `--- not used on PS/2 + | | `------ initial video mode + `--------- # of diskette drives, less 1 + + |7|6|5|4|3|2|1|0| 40:11 (value in INT 11 register AH) + | | | | | | | `- 0 if DMA installed + | | | | `------ number of serial ports + | | | `------- game adapter + | | `-------- not used, internal modem (PS/2) + `----------- number of printer ports + + 40:12 byte PCjr: infrared keyboard link error count + 40:13 word Memory size in Kbytes (see INT 12) + 40:15 byte Reserved + 40:16 byte PS/2 BIOS control flags + 40:17 byte Keyboard flag byte 0 (see KB FLAGS) + + |7|6|5|4|3|2|1|0| keyboard flag byte 0 + | | | | | | | `--- right shift key depressed + | | | | | | `---- left shift key depressed + | | | | | `----- CTRL key depressed + | | | | `------ ALT key depressed + | | | `------- scroll-lock is active + | | `-------- num-lock is active + | `--------- caps-lock is active + `---------- insert is active + + 40:18 byte Keyboard flag byte 1 (see KB FLAGS) + + |7|6|5|4|3|2|1|0| keyboard flag byte + | | | | | | | `--- left CTRL key depressed + | | | | | | `---- left ALT key depressed + | | | | | `----- system key depressed and held + | | | | `------ suspend key has been toggled + | | | `------- scroll lock key is depressed + | | `-------- num-lock key is depressed + | `--------- caps-lock key is depressed + `---------- insert key is depressed + + 40:19 byte Storage for alternate keypad entry + 40:1A word Offset from 40:00 to keyboard buffer head + 40:1C word Offset from 40:00 to keyboard buffer tail + 40:1E 32bytes Keyboard buffer (circular queue buffer) + 40:3E byte Drive recalibration status + + |7|6|5|4|3|2|1|0| drive recalibration status + | | | | | | | `-- 1=recalibrate drive 0 + | | | | | | `--- 1=recalibrate drive 1 + | | | | | `---- 1=recalibrate drive 2 + | | | | `----- 1=recalibrate drive 3 + | `---------- unused + `----------- 1=working interrupt flag + + 40:3F byte Diskette motor status + + |7|6|5|4|3|2|1|0| diskette motor status + | | | | | | | `-- 1=drive 0 motor on + | | | | | | `--- 1=drive 1 motor on + | | | | | `---- 1=drive 2 motor on + | | | | `----- 1=drive 3 motor on + | `---------- unused + `----------- 1=write operation + + 40:40 byte Motor shutoff counter (decremented by INT 8) + 40:41 byte Status of last diskette operation (see INT 13,1) + + |7|6|5|4|3|2|1|0| status of last diskette operation + | | | | | | | `--- invalid diskette command + | | | | | | `---- diskette address mark not found + | | | | | `----- sector not found + | | | | `------ diskette DMA error + | | | `------- CRC check / data error + | | `-------- diskette controller failure + | `--------- seek to track failed + `---------- diskette time-out + + 40:42 7 bytes NEC diskette controller status (see FDC) + 40:49 byte Current video mode (see VIDEO MODE) + 40:4A word Number of screen columns + 40:4C word Size of current video regen buffer in bytes + 40:4E word Offset of current video page in video regen buffer + 40:50 8 words Cursor position of pages 1-8, high order byte=row + low order byte=column; changing this data isn't + reflected immediately on the display + 40:60 byte Cursor ending (bottom) scan line (don't modify) + 40:61 byte Cursor starting (top) scan line (don't modify) + 40:62 byte Active display page number + 40:63 word Base port address for active 6845 CRT controller + 3B4h = mono, 3D4h = color + 40:65 byte 6845 CRT mode control register value (port 3x8h) + EGA/VGA values emulate those of the MDA/CGA + 40:66 byte CGA current color palette mask setting (port 3d9h) + EGA and VGA values emulate the CGA + 40:67 dword CS:IP for 286 return from protected mode + dword Temp storage for SS:SP during shutdown + dword Day counter on all products after AT + dword PS/2 Pointer to reset code with memory preserved + 5 bytes Cassette tape control (before AT) + 40:6C dword Daily timer counter, equal to zero at midnight; + incremented by INT 8; read/set by INT 1A + 40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs + 40:71 byte BIOS break flag, bit 7 is set if Ctrl-Break was + *ever* hit; set by INT 9 + 40:72 word Soft reset flag via Ctl-Alt-Del or JMP FFFF:0 + + 1234h Bypass memory tests & CRT initialization + 4321h Preserve memory + 5678h System suspend + 9ABCh Manufacturer test + ABCDh Convertible POST loop + ????h many other values are used during POST + + 40:74 byte Status of last hard disk operation (see INT 13,1) + 40:75 byte Number of hard disks attached + 40:76 byte XT fixed disk drive control byte + 40:77 byte Port offset to current fixed disk adapter + 40:78 4 bytes Time-Out value for LPT1,LPT2,LPT3(,LPT4 except PS/2) + 40:7C 4 bytes Time-Out value for COM1,COM2,COM3,COM4 + 40:80 word Keyboard buffer start offset (seg=40h,BIOS 10-27-82) + 40:82 word Keyboard buffer end offset (seg=40h,BIOS 10-27-82) + 40:84 byte Rows on the screen (less 1, EGA+) + 40:85 word Point height of character matrix (EGA+) + byte PCjr: character to be repeated if the typematic + repeat key takes effect + 40:86 byte PCjr: initial delay before repeat key action begins + 40:87 byte PCjr: current Fn function key number + byte Video mode options (EGA+) + + |7|6|5|4|3|2|1|0| Video mode options (EGA+) + | | | | | | | `-- 1=alphanumeric cursor emulation enabled + | | | | | | `--- 1=video subsystem attached to monochrome + | | | | | `---- reserved + | | | | `----- 1=video subsystem is inactive + | | | `------ reserved + | `--------- video RAM 00-64K 10-192K 01-128K 11-256K + `---------- video mode number passed to INT 10, function 0 + + 40:88 byte PCjr: third keyboard status byte + EGA feature bit switches, emulated on VGA + + |7|6|5|4|3|2|1|0| EGA feature bit switches (EGA+) + | | | | | | | `-- EGA SW1 config (1=off) + | | | | | | `--- EGA SW2 config (1=off) + | | | | | `---- EGA SW3 config (1=off) + | | | | `----- EGA SW4 config (1=off) + | | | `------ Input FEAT0 (ISR0 bit 5) after output on FCR0 + | | `------- Input FEAT0 (ISR0 bit 6) after output on FCR0 + | `-------- Input FEAT1 (ISR0 bit 5) after output on FCR1 + `--------- Input FEAT1 (ISR0 bit 6) after output on FCR1 + + 40:89 byte Video display data area (MCGA and VGA) + + |7|6|5|4|3|2|1|0| Video display data area (MCGA and VGA) + | | | | | | | `-- 1=VGA is active + | | | | | | `--- 1=gray scale is enabled + | | | | | `---- 1=using monochrome monitor + | | | | `----- 1=default palette loading is disabled + | | | `------ see table below + | | `------- reserved + | `-------- 1=display switching enabled + `--------- alphanumeric scan lines (see table below) + + Bit7 Bit4 Scan Lines + 0 0 350 line mode + 0 1 400 line mode + 1 0 200 line mode + 1 1 reserved + + 40:8A byte Display Combination Code (DCC) table index (EGA+) + 40:8B byte Last diskette data rate selected + + |7|6|5|4|3|2|1|0| last diskette data rate selected + | | | | `--------- reserved + | | `------------ last floppy drive step rate selected + `-------------- last floppy data rate selected + + Data Rate Step Rate + 00 500K bps 00 step rate time of 0C + 01 300K bps 01 step rate time of 0D + 10 250K bps 10 step rate time of 0A + 11 reserved 11 reserved + + 40:8C byte Hard disk status returned by controller + 40:8D byte Hard disk error returned by controller + 40:8E byte Hard disk interrupt control flag(bit 7=working int) + 40:8F byte Combination hard/floppy disk card when bit 0 set + 40:90 4 bytes Drive 0,1,2,3 media state + + |7|6|5|4|3|2|1|0| drive media state (4 copies) + | | | | | `------- drive/media state (see below) + | | | | `------- reserved + | | | `------- 1=media/drive established + | | `------- double stepping required + `--------- data rate: 00=500K bps 01=300K bps + 10=250K bps 11=reserved + Bits + 210 Drive Media State + 000 360Kb diskette/360Kb drive not established + 001 360Kb diskette/1.2Mb drive not established + 010 1.2Mb diskette/1.2Mb drive not established + 011 360Kb diskette/360Kb drive established + 100 360Kb diskette/1.2Mb drive established + 101 1.2Mb diskette/1.2Mb drive established + 110 Reserved + 111 None of the above + + 40:94 byte Track currently seeked to on drive 0 + 40:95 byte Track currently seeked to on drive 1 + 40:96 byte Keyboard mode/type + + |7|6|5|4|3|2|1|0| Keyboard mode/type + | | | | | | | `--- last code was the E1 hidden code + | | | | | | `---- last code was the E0 hidden code + | | | | | `----- right CTRL key depressed + | | | | `------ right ALT key depressed + | | | `------- 101/102 enhanced keyboard installed + | | `-------- force num-lock if Rd ID & KBX + | `--------- last char was first ID char + `---------- read ID in process + + 40:97 byte Keyboard LED flags + + |7|6|5|4|3|2|1|0| Keyboard LED flags + | | | | | | | `--- scroll lock indicator + | | | | | | `---- num-lock indicator + | | | | | `----- caps-lock indicator + | | | | `------ circus system indicator + | | | `------- ACK received + | | `-------- re-send received flag + | `--------- mode indicator update + `---------- keyboard transmit error flag + + 40:98 dword Pointer to user wait complete flag + 40:9C dword User wait Time-Out value in microseconds + 40:A0 byte RTC wait function flag + + |7|6|5|4|3|2|1|0| INT 15,86 RTC wait function flag + | | | | | | | `--- 1= wait pending + | `-------------- not used + `--------------- 1=INT 15,86 wait time elapsed + + 40:A1 byte LANA DMA channel flags + 40:A2 2 bytes Status of LANA 0,1 + 40:A4 dword Saved hard disk interrupt vector + 40:A8 dword BIOS Video Save/Override Pointer Table address + (see VIDEO TABLES) + 40:AC 8 bytes Reserved + 40:B4 byte Keyboard NMI control flags (convertible) + 40:B5 dword Keyboard break pending flags (convertible) + 40:B9 byte Port 60 single byte queue (convertible) + 40:BA byte Scan code of last key (convertible) + 40:BB byte NMI buffer head pointer (convertible) + 40:BC byte NMI buffer tail pointer (convertible) + 40:BD 16bytes NMI scan code buffer (convertible) + 40:CE word Day counter (convertible and after) + 40:F0 16bytes Intra-Applications Communications Area (IBM Technical + Reference incorrectly locates this at 50:F0-50:FF) + + + Address Size Description (BIOS/DOS Data Area) + + 50:00 byte Print screen status byte + 00 = PrtSc not active, + 01 = PrtSc in progress + FF = error + 50:01 3 bytes Used by BASIC + 50:04 byte DOS single diskette mode flag, 0=A:, 1=B: + 50:05 10bytes POST work area + 50:0F byte BASIC shell flag; set to 2 if current shell + 50:10 word BASICs default DS value (DEF SEG) + 50:12 dword Pointer to BASIC INT 1C interrupt handler + 50:16 dword Pointer to BASIC INT 23 interrupt handler + 50:1A dword Pointer to BASIC INT 24 disk error handler + 50:20 word DOS dynamic storage + 50:22 14bytes DOS diskette initialization table (INT 1E) + 50:30 4bytes MODE command + 70:00 I/O drivers from IO.SYS/IBMBIO.COM + +The following map varies in size and locus + + 07C0:0 Boot code is loaded here at startup (31k mark) + A000:0 EGA/VGA RAM for graphics display mode 0Dh & above + B000:0 MDA RAM, Hercules graphics display RAM + B800:0 CGA display RAM + C000:0 EGA/VGA BIOS ROM (thru C7FF) + C400:0 Video adapter ROM space + C600:0 256bytes PGA communication area + C800:0 16K Hard disk adapter BIOS ROM + C800:5 XT Hard disk ROM format, AH=Drive, AL=Interleave + D000:0 32K Cluster adapter BIOS ROM + D800:0 PCjr conventionalsoftware cartridge address + E000:0 64K Expansion ROM space (hardwired on AT+) + 128K PS/2 System ROM (thru F000) + F000:0 System monitor ROM + PCjr: software cartridge override address + F400:0 System expansion ROMs + F600:0 IBM ROM BASIC (AT) + F800:0 PCjr software cartridge override address + FC00:0 BIOS ROM + FF00:0 System ROM + FFA6:E ROM graphics character table + FFFF:0 ROM bootstrap code + FFFF:5 8 bytes ROM date (not applicable for all clones) + FFFF:E byte ROM machine id (see MACHINE ID) + + diff --git a/dosbox.reference.conf b/dosbox.reference.conf index 194183b2c..da791ef12 100644 --- a/dosbox.reference.conf +++ b/dosbox.reference.conf @@ -90,6 +90,7 @@ sst=false # output: What video system to use for output. # Possible values: surface, overlay, opengl, openglnb, openglhq, ddraw. # autolock: Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock) +# synced: Mouse position reported will be exactly where user hand has moved to. # sensitivity: Mouse sensitivity. # waitonerror: Wait before closing the console if dosbox has an error. # priority: Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized. @@ -106,6 +107,7 @@ fullresolution=desktop windowresolution=original output=surface autolock=true +synced=false sensitivity=100 waitonerror=true priority=higher,normal @@ -157,13 +159,6 @@ showmenu=true # mpegts-h264 Use MPEG transport stream + H.264 + AAC audio. Resolution & refresh rate changes can be contained # within one file with this choice, however not all software can support mid-stream format changes. # Possible values: default, avi-zmbv, mpegts-h264. -# mainline compatible mapping: If set, arrange private areas, UMBs, and DOS kernel structures by default in the same way the mainline branch would do it. -# If cleared, these areas are allocated dynamically which may improve available memory and emulation accuracy. -# If your DOS game breaks under DOSBox-X but works with mainline DOSBox setting this option may help. -# mainline compatible bios mapping: If set, arrange the BIOS area in the same way that the mainline branch would do it. -# If cleared, these areas are allocated dynamically which may improve available memory and emulation accuracy. -# If your DOS game breaks under DOSBox-X but works with mainline DOSBox setting this option may help. -# adapter rom is ram: Map adapter ROM as RAM (mainline DOSBox 0.74 behavior). When clear, unused adapter ROM is mapped out # shell environment size: Size of the initial DOSBox shell environment block, in bytes. This does not affect the environment block of sub-processes spawned from the shell. # This option has no effect unless dynamic kernel allocation is enabled. # private area size: Set DOSBox-X private memory area size. This area contains private memory structures used by the DOS kernel. @@ -185,7 +180,7 @@ showmenu=true # turn off a20 gate on boot: If enabled, A20 gate is switched off when booting a guest OS. # Enabled by default. Recommended for MS-DOS when HIMEM.SYS is not installed in the guest OS. # If disabled, and MS-DOS does not load HIMEM.SYS, programs and features that rely on the 1MB wraparound will fail. -# isa bus clock: ISA BCLK frequency. +# isa bus clock: ISA BCLK frequency, used to emulate I/O delay. # WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime. # std8.3 8.333MHz (typical 386-class or higher) # std8 8MHz @@ -197,7 +192,7 @@ showmenu=true # oc16 16MHz # Any integer or floating point value will be used as the clock frequency in Hz # If a ratio is given (num/den), the ratio will be used as the clock frequency -# pci bus clock: PCI bus frequency. +# pci bus clock: PCI bus frequency, used to emulate I/O delay. # WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime. # std33.3 33.333MHz (very common setting on motherboards) # std30 30MHz (some older mid-1990's Pentium systems) @@ -325,7 +320,7 @@ showmenu=true # There are some old DOS games and demos that rely on this behavior to sense keyboard input, and this behavior # has been verified to occur on some old (early 90s) BIOSes. # allow port 92 reset: If set (default), allow the application to reset the CPU through port 92h -# enable port 92: Emulate port 92h (PS/2 system control port A). If you want to emulate a system that predates the PS/2, set to 0. +# enable port 92: Emulate port 92h (PS/2 system control port A). If you want to emulate a system that pre-dates the PS/2, set to 0. # enable 1st dma controller: Emulate 1st (AT) DMA controller (default). Set to 0 if you wish to emulate a system that lacks DMA (PCjr and some Tandy systems) # enable 2nd dma controller: Emulate 2nd (AT) DMA controller (default). Set to 0 if you wish to emulate a PC/XT system without 16-bit DMA. # Note: mainline DOSBox automatically disables 16-bit DMA when machine=cga or machine=hercules, while DOSBox-X does not. @@ -393,6 +388,11 @@ showmenu=true # try setting this option. Else, leave it turned off. Changes to other VGA CRTC registers will trigger # a DOSBox mode change as normal regardless of this setting. # enable pci bus: Enable PCI bus emulation +# vga palette update on full load: If set, all three bytes of the palette entry must be loaded before taking the color, +# which is fairly typical SVGA behavior. If not set, partial changes are allowed. +# ignore odd-even mode in non-cga modes: Some demoscene productions use VGA Mode X but accidentally enable odd/even mode. +# Setting this option can correct for that and render the demo properly. +# This option forces VGA emulation to ignore odd/even mode except in text and CGA modes. language= title= dpi aware=true @@ -406,9 +406,6 @@ vmemsizekb=0 captures=capture capture chroma format=auto capture format=default -mainline compatible mapping=false -mainline compatible bios mapping=false -adapter rom is ram=false shell environment size=0 private area size=32768 a20=fast @@ -496,6 +493,8 @@ ignore vblank wraparound=false enable vga resize delay=false resize only on vga active display width increase=false enable pci bus=true +vga palette update on full load=true +ignore odd-even mode in non-cga modes=false [render] # frameskip: How many frames DOSBox skips before drawing one. @@ -614,7 +613,7 @@ cycleup=10 cycledown=20 use dynamic core with paging on=false ignore opcode 63=true -apmbios=false +apmbios=true apmbios pnp=false apmbios version=auto apmbios allow realmode=true @@ -633,7 +632,7 @@ realbig16=false # will reboot without this option using INT 19h # auxdevice: Type of PS/2 mouse attached to the AUX port # Possible values: none, 2button, 3button, intellimouse, intellimouse45. -aux=false +aux=true allow output port reset=true auxdevice=intellimouse @@ -963,24 +962,134 @@ ps1audio=off ps1audiorate=22050 [joystick] -# joysticktype: Type of joystick to emulate: auto (default), none, -# 2axis (supports two joysticks), -# 4axis (supports one joystick, first joystick used), -# 4axis_2 (supports one joystick, second joystick used), -# fcs (Thrustmaster), ch (CH Flightstick). -# none disables joystick emulation. -# auto chooses emulation depending on real joystick(s). -# (Remember to reset dosbox's mapperfile if you saved it earlier) -# Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none. -# timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away). -# autofire: continuously fires as long as you keep the button pressed. -# swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks. -# buttonwrap: enable button wrapping at the number of emulated buttons. +# joysticktype: Type of joystick to emulate: auto (default), none, +# 2axis (supports two joysticks), +# 4axis (supports one joystick, first joystick used), +# 4axis_2 (supports one joystick, second joystick used), +# fcs (Thrustmaster), ch (CH Flightstick). +# none disables joystick emulation. +# auto chooses emulation depending on real joystick(s). +# (Remember to reset dosbox's mapperfile if you saved it earlier) +# Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none. +# timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away). +# autofire: continuously fires as long as you keep the button pressed. +# swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks. +# buttonwrap: enable button wrapping at the number of emulated buttons. +# joy1deadzone1: deadzone for joystick 1 thumbstick 1. +# joy1deadzone2: deadzone for joystick 1 thumbstick 2. +# joy2deadzone1: deadzone for joystick 2 thumbstick 1. +# joy1response1: response for joystick 1 thumbstick 1. +# joy1response2: response for joystick 1 thumbstick 2. +# joy2response1: response for joystick 2 thumbstick 1. +# joy1axis0: axis for joystick 1 axis 0. +# joy1axis1: axis for joystick 1 axis 1. +# joy1axis2: axis for joystick 1 axis 2. +# joy1axis3: axis for joystick 1 axis 3. +# joy1axis4: axis for joystick 1 axis 4. +# joy1axis5: axis for joystick 1 axis 5. +# joy1axis6: axis for joystick 1 axis 6. +# joy1axis7: axis for joystick 1 axis 7. +# joy2axis0: axis for joystick 2 axis 0. +# joy2axis1: axis for joystick 2 axis 1. +# joy2axis2: axis for joystick 2 axis 2. +# joy2axis3: axis for joystick 2 axis 3. +# joy2axis4: axis for joystick 2 axis 4. +# joy2axis5: axis for joystick 2 axis 5. +# joy2axis6: axis for joystick 2 axis 6. +# joy2axis7: axis for joystick 2 axis 7. joysticktype=auto timed=true autofire=false swap34=false buttonwrap=false +joy1deadzone1=0.25 +joy1deadzone2=0.25 +joy2deadzone1=0.25 +joy1response1=1.00 +joy1response2=1.00 +joy2response1=1.00 +joy1axis0=0 +joy1axis1=1 +joy1axis2=2 +joy1axis3=3 +joy1axis4=4 +joy1axis5=5 +joy1axis6=6 +joy1axis7=7 +joy2axis0=0 +joy2axis1=1 +joy2axis2=2 +joy2axis3=3 +joy2axis4=4 +joy2axis5=5 +joy2axis6=6 +joy2axis7=7 + +[mapper] +# joy1deadzone0-: deadzone for joystick 1 axis 0- +# joy1deadzone0+: deadzone for joystick 1 axis 0+ +# joy1deadzone1-: deadzone for joystick 1 axis 1- +# joy1deadzone1+: deadzone for joystick 1 axis 1+ +# joy1deadzone2-: deadzone for joystick 1 axis 2- +# joy1deadzone2+: deadzone for joystick 1 axis 2+ +# joy1deadzone3-: deadzone for joystick 1 axis 3- +# joy1deadzone3+: deadzone for joystick 1 axis 3+ +# joy1deadzone4-: deadzone for joystick 1 axis 4- +# joy1deadzone4+: deadzone for joystick 1 axis 4+ +# joy1deadzone5-: deadzone for joystick 1 axis 5- +# joy1deadzone5+: deadzone for joystick 1 axis 5+ +# joy1deadzone6-: deadzone for joystick 1 axis 6- +# joy1deadzone6+: deadzone for joystick 1 axis 6+ +# joy1deadzone7-: deadzone for joystick 1 axis 7- +# joy1deadzone7+: deadzone for joystick 1 axis 7+ +# joy2deadzone0-: deadzone for joystick 2 axis 0- +# joy2deadzone0+: deadzone for joystick 2 axis 0+ +# joy2deadzone1-: deadzone for joystick 2 axis 1- +# joy2deadzone1+: deadzone for joystick 2 axis 1+ +# joy2deadzone2-: deadzone for joystick 2 axis 2- +# joy2deadzone2+: deadzone for joystick 2 axis 2+ +# joy2deadzone3-: deadzone for joystick 2 axis 3- +# joy2deadzone3+: deadzone for joystick 2 axis 3+ +# joy2deadzone4-: deadzone for joystick 2 axis 4- +# joy2deadzone4+: deadzone for joystick 2 axis 4+ +# joy2deadzone5-: deadzone for joystick 2 axis 5- +# joy2deadzone5+: deadzone for joystick 2 axis 5+ +# joy2deadzone6-: deadzone for joystick 2 axis 6- +# joy2deadzone6+: deadzone for joystick 2 axis 6+ +# joy2deadzone7-: deadzone for joystick 2 axis 7- +# joy2deadzone7+: deadzone for joystick 2 axis 7+ +joy1deadzone0-=0.60 +joy1deadzone0+=0.60 +joy1deadzone1-=0.60 +joy1deadzone1+=0.60 +joy1deadzone2-=0.60 +joy1deadzone2+=0.60 +joy1deadzone3-=0.60 +joy1deadzone3+=0.60 +joy1deadzone4-=0.60 +joy1deadzone4+=0.60 +joy1deadzone5-=0.60 +joy1deadzone5+=0.60 +joy1deadzone6-=0.60 +joy1deadzone6+=0.60 +joy1deadzone7-=0.60 +joy1deadzone7+=0.60 +joy2deadzone0-=0.60 +joy2deadzone0+=0.60 +joy2deadzone1-=0.60 +joy2deadzone1+=0.60 +joy2deadzone2-=0.60 +joy2deadzone2+=0.60 +joy2deadzone3-=0.60 +joy2deadzone3+=0.60 +joy2deadzone4-=0.60 +joy2deadzone4+=0.60 +joy2deadzone5-=0.60 +joy2deadzone5+=0.60 +joy2deadzone6-=0.60 +joy2deadzone6+=0.60 +joy2deadzone7-=0.60 +joy2deadzone7+=0.60 [serial] # serial1: set type of device connected to com port. @@ -1084,11 +1193,6 @@ dongle=false # enable dummy device mcb: If set (default), allocate a fake device MCB at the base of conventional memory. # Clearing this option can reclaim a small amount of conventional memory at the expense of # some minor DOS compatibility. -# enable loadfix padding: If set (default), allocate a small 1KB region at the base of conventional memory. -# Clearing this option can reclaim a small amount of conventional memory, but can also -# cause some DOS games to break especially if dynamic kernel allocation is enabled. -# enable dummy environment block: If set (default), allocate a dummy environment block at the base of conventional memory. -# You can clear this option to reclaim a small amount of conventional memory. # maximum environment block size on exec: Maximum environment block size to copy for child processes. Set to -1 for default. # additional environment block size on exec: When executing a program, compute the size of the parent block then add this amount to allow for a few additional variables. # If the subprocesses will never add/modify the environment block, you can free up a few additional bytes by setting this to 0. @@ -1137,7 +1241,6 @@ dongle=false # umb end: UMB region last segment # kernel allocation in umb: If set, dynamic kernel allocation=1, and private area in umb=1, all kernel structures will be allocated from the private area in UMB. # If you intend to run Windows 3.1 in DOSBox, you must set this option to false else Windows 3.1 will not start. -# dynamic kernel allocation: If set, DOS kernel structures are allocated dynamically. If clear, DOS kernel structures are fixed at specific segments (mainline DOSBox behavior) # keep umb on boot: If emulating UMBs, keep the UMB around after boot (Mainline DOSBox behavior). If clear, UMB is unmapped when you boot an operating system. # keep private area on boot: If set, keep the DOSBox private area around after boot (Mainline DOSBox behavior). If clear, unmap and discard the private area when you boot an operating system. # private area in umb: If set, keep private DOS segment in upper memory block, usually segment 0xC800 (Mainline DOSBox behavior) @@ -1192,8 +1295,6 @@ minimum dos initial private segment=0 minimum mcb segment=0 minimum mcb free=0 enable dummy device mcb=false -enable loadfix padding=false -enable dummy environment block=false maximum environment block size on exec=-1 additional environment block size on exec=-1 enable a20 on windows init=false @@ -1211,7 +1312,6 @@ umb=true umb start=0 umb end=0 kernel allocation in umb=false -dynamic kernel allocation=true keep umb on boot=false keep private area on boot=false private area in umb=true diff --git a/include/8255.h b/include/8255.h index 3f5269441..b6a2a38b4 100644 --- a/include/8255.h +++ b/include/8255.h @@ -2,76 +2,141 @@ #include #include +//! \brief Intel 8255 base emulation class +//! +//! \description Intel 8255 Programmable Peripheral Interface emulation class. +//! The base class handles the functions and register I/O to +//! emulate the 8255, while the subclass implements behavior +//! of hardware attached to the 8255. +//! +//! All emulation is written to follow Intel's datasheet as +//! closely as possible. +//! +//! Intel 8255A datasheet +//! +//! NOTE: Mode 2 emulation has NOT been tested yet! class Intel8255 { public: + //! + //! Port enumeration (A, B, and C) + //! enum { - PortA=0, - PortB=1, - PortC=2 + //! Port A (input or output) + PortA=0, + //! Port B (input or output) + PortB=1, + //! Port C (input, output, or half input half output) + PortC=2 }; public: + //! Constructor Intel8255(); + //! Destructor virtual ~Intel8255(); public: - void reset(void); + //! Reset state (as if activating reset signal) + void reset(void); - void ackPortA(void); - void ackPortB(void); + //! External acknowledgement of port A + void ackPortA(void); + //! External acknowledgement of port B + void ackPortB(void); - virtual void strobePortA(void); - virtual void strobePortB(void); + //! Strobed Input (latch to port A) + virtual void strobePortA(void); + //! Strobed Input (latch to port B) + virtual void strobePortB(void); - uint8_t readPortA(void); - uint8_t readPortB(void); - uint8_t readPortC(void); - uint8_t readControl(void); + //! Called when CPU reads port A + uint8_t readPortA(void); + //! Called when CPU reads port B + uint8_t readPortB(void); + //! Called when CPU reads port C + uint8_t readPortC(void); + //! Called when CPU reads control port + uint8_t readControl(void); - uint8_t readByPort(const uint8_t p03); + //! Called when CPU reads from the chip + uint8_t readByPort(const uint8_t p03); - void writePortA(const uint8_t data,uint8_t mask=0xFFU); - void writePortB(const uint8_t data,uint8_t mask=0xFFU); - void writePortC(const uint8_t data,uint8_t mask=0xFFU); - void writeControl(const uint8_t data); + //! Called when CPU writes port A + void writePortA(const uint8_t data,uint8_t mask=0xFFU); + //! Called when CPU writes port B + void writePortB(const uint8_t data,uint8_t mask=0xFFU); + //! Called when CPU writes port C + void writePortC(const uint8_t data,uint8_t mask=0xFFU); + //! Called when CPU writes control port + void writeControl(const uint8_t data); - void writeByPort(const uint8_t p03,const uint8_t data); + //! Called when CPU writes to the chip + void writeByPort(const uint8_t p03,const uint8_t data); public: - virtual uint8_t inPortA(void) const; - virtual uint8_t inPortB(void) const; - virtual uint8_t inPortC(void) const; + //! Called by 8255 emulation to latch from port A pins + virtual uint8_t inPortA(void) const; + //! Called by 8255 emulation to latch from port B pins + virtual uint8_t inPortB(void) const; + //! Called by 8255 emulation to latch from port C pins + virtual uint8_t inPortC(void) const; public: - virtual void outPortA(const uint8_t mask); - virtual void outPortB(const uint8_t mask); - virtual void outPortC(const uint8_t mask); + //! Called by 8255 emulation when latching to port A pins + virtual void outPortA(const uint8_t mask); + //! Called by 8255 emulation when latching to port B pins + virtual void outPortB(const uint8_t mask); + //! Called by 8255 emulation when latching to port C pins + virtual void outPortC(const uint8_t mask); public: - void updateINTR_A(void); - void updateINTR_B(void); + //! Internal 8255 emulation code to update INTR A signal + void updateINTR_A(void); + //! Internal 8255 emulation code to update INTR B signal + void updateINTR_B(void); public: - void checkINTR_A(void); - void checkINTR_B(void); + //! Internal 8255 emulation code to check INTR A change and dispatch signal + void checkINTR_A(void); + //! Internal 8255 emulation code to check INTR B change and dispatch signal + void checkINTR_B(void); public: - virtual void sigINTR_A(void); - virtual void sigINTR_B(void); + //! Called by 8255 emulation when INTR A signal changes to dispatch signal + virtual void sigINTR_A(void); + //! Called by 8255 emulation when INTR B signal changes to dispatch signal + virtual void sigINTR_B(void); public: + //! \brief Retrieve the name of this chip (for debug/UI purposes) inline const char* getName(void) const { return nil_if_null(ppiName); } public: + //! \brief Retrieve the name of a pin on the chip related to port A, B, or C (what it's connected to) (for debug/UI purposes) inline const char* pinName(const unsigned int port,const unsigned int i) const { return nil_if_null(pinNames[port][i]); } + //! \brief Retrieve the name of a port (A, B, or C) (for debug/UI purposes) inline const char* portName(const unsigned int port) const { return nil_if_null(portNames[port]); } public: - uint8_t portAWriteMask,portBWriteMask,portCWriteMask; + //! Port A write mask. Controls which bits are writeable + uint8_t portAWriteMask; + //! Port B write mask. Controls which bits are writeable + uint8_t portBWriteMask; + //! Port C write mask. Controls which bits are writeable + uint8_t portCWriteMask; public: - const char* ppiName; + //! PPI chip name (for debug/UI purposes) + const char* ppiName; public: - const char* pinNames[3/*port*/][8/*bit*/]; - const char* portNames[3/*port*/]; + //! Pin names (for debug/UI purposes) + const char* pinNames[3/*port*/][8/*bit*/]; + //! Port names (for debug/UI purposes) + const char* portNames[3/*port*/]; public: - uint8_t latchOutPortA,latchOutPortB,latchOutPortC; - uint8_t mode; + //! Port A output latch + uint8_t latchOutPortA; + //! Port B output latch + uint8_t latchOutPortB; + //! Port C output latch + uint8_t latchOutPortC; + //! PPI mode byte + uint8_t mode; /* bit[7:7] = 1 mode set flag * bit[6:5] = mode select 00=mode 0 01=mode 1 1x=mode 2 * bit[4:4] = Port A 1=input 0=output @@ -80,15 +145,34 @@ public: * bit[1:1] = Port B 1=input 0=output * bit[0:0] = Port C lower 1=input 0=output */ public: - bool IBF_A,IBF_B; - bool OBF_A,OBF_B; + //! Input Buffer Full, port A contains information (port A, Mode 1) + bool IBF_A; + //! Input Buffer Full, port B contains information (port B, Mode 1) + bool IBF_B; + //! Output Buffer Full, port A contains information for the external device (port A, Mode 1) + bool OBF_A; + //! Output Buffer Full, port B contains information for the external device (port B, Mode 1) + bool OBF_B; public: - bool INTR_A,INTR_B; - bool pINTR_A,pINTR_B; + //! Interrupt Request A (to the microprocessor) + bool INTR_A; + //! Interrupt Request B (to the microprocessor) + bool INTR_B; + //! Previous Interrupt Request A state (for change detection) + bool pINTR_A; + //! Previous Interrupt Request B state (for change detection) + bool pINTR_B; public: - bool INTE_1,INTE_2; /* mode 2 */ - bool INTE_A,INTE_B; + //! Interrupt 1 enable (mode 2) + bool INTE_1; + //! Interrupt 2 enable (mode 2) + bool INTE_2; /* mode 2 */ + //! Interrupt A enable + bool INTE_A; + //! Interrupt B enable + bool INTE_B; protected: + //! Return string "str", or "" (empty string) if str == NULL static inline const char *nil_if_null(const char *str) { return (str != NULL) ? str : ""; } diff --git a/include/bios.h b/include/bios.h index 8d8905e5e..874874540 100644 --- a/include/bios.h +++ b/include/bios.h @@ -40,7 +40,25 @@ /* #define bios_expansion_memory_size (*(unsigned int *) 0x415) */ #define BIOS_KEYBOARD_STATE 0x417 #define BIOS_KEYBOARD_FLAGS1 BIOS_KEYBOARD_STATE +#define BIOS_KEYBOARD_FLAGS1_RSHIFT_PRESSED (1 << 0) +#define BIOS_KEYBOARD_FLAGS1_LSHIFT_PRESSED (1 << 1) +#define BIOS_KEYBOARD_FLAGS1_CTRL_PRESSED (1 << 2) +#define BIOS_KEYBOARD_FLAGS1_ALT_PRESSED (1 << 3) +#define BIOS_KEYBOARD_FLAGS1_SCROLL_LOCK_ACTIVE (1 << 4) +#define BIOS_KEYBOARD_FLAGS1_NUMLOCK_ACTIVE (1 << 5) +#define BIOS_KEYBOARD_FLAGS1_CAPS_LOCK_ACTIVE (1 << 6) +#define BIOS_KEYBOARD_FLAGS1_INSERT_ACTIVE (1 << 7) + #define BIOS_KEYBOARD_FLAGS2 0x418 +#define BIOS_KEYBOARD_FLAGS2_LCTRL_PRESSED (1 << 0) +#define BIOS_KEYBOARD_FLAGS2_LALT_PRESSED (1 << 1) +#define BIOS_KEYBOARD_FLAGS2_SYSTEMKEY_HELD (1 << 2) +#define BIOS_KEYBOARD_FLAGS2_SUSPENDKEY_TOGGLED (1 << 3) +#define BIOS_KEYBOARD_FLAGS2_SCROLL_LOCK_PRESSED (1 << 4) +#define BIOS_KEYBOARD_FLAGS2_NUM_LOCK_PRESSED (1 << 5) +#define BIOS_KEYBOARD_FLAGS2_CAPS_LOCK_PRESSED (1 << 6) +#define BIOS_KEYBOARD_FLAGS2_INSERT_PRESSED (1 << 7) + #define BIOS_KEYBOARD_TOKEN 0x419 /* used for keyboard input with Alt-Number */ #define BIOS_KEYBOARD_BUFFER_HEAD 0x41a @@ -95,6 +113,15 @@ #define BIOS_VIDEO_COMBO 0x48a #define BIOS_KEYBOARD_FLAGS3 0x496 +#define BIOS_KEYBOARD_FLAGS3_HIDDEN_E1 (1 << 0) +#define BIOS_KEYBOARD_FLAGS3_HIDDEN_E0 (1 << 1) +#define BIOS_KEYBOARD_FLAGS3_RCTRL_PRESSED (1 << 2) +#define BIOS_KEYBOARD_FLAGS3_RALT_PRESSED (1 << 3) +#define BIOS_KEYBOARD_FLAGS3_ENHANCED_KEYBOARD (1 << 4) +#define BIOS_KEYBOARD_FLAGS3_NUM_LOCK_FORCED (1 << 5) +#define BIOS_KEYBOARD_FLAGS3_ID_CHAR_WAS_LAST (1 << 6) +#define BIOS_KEYBOARD_FLAGS3_ID_READ_IN_PROCESS (1 << 7) + #define BIOS_KEYBOARD_LEDS 0x497 #define BIOS_WAIT_FLAG_POINTER 0x498 @@ -107,6 +134,9 @@ #define BIOS_VIDEO_SAVEPTR 0x4a8 +#define CURSOR_SCAN_LINE_NORMAL (0x6) +#define CURSOR_SCAN_LINE_INSERT (0x4) +#define CURSOR_SCAN_LINE_END (0x7) //#define BIOS_DEFAULT_IRQ0_LOCATION (RealMake(0xf000,0xfea5)) //#define BIOS_DEFAULT_IRQ1_LOCATION (RealMake(0xf000,0xe987)) @@ -151,6 +181,9 @@ void INT10_ReloadRomFonts(); void BIOS_SetComPorts (Bit16u baseaddr[]); void BIOS_SetLPTPort (Bitu port, Bit16u baseaddr); +// \brief Synchronizes emulator num lock state with host. +void BIOS_SynchronizeNumLock(); + bool ISAPNP_RegisterSysDev(const unsigned char *raw,Bitu len,bool already=false); class ISAPnPDevice { diff --git a/include/bitop.h b/include/bitop.h index ded0a1064..a845761e1 100644 --- a/include/bitop.h +++ b/include/bitop.h @@ -45,13 +45,13 @@ template static inline constexpr T allones(void) { } -/* Return data type T with value negated +/* Return data type T with value inverted * * This is to avoid typecast messes when masking by the inverse of a constant * - * @return Type T with value negated + * @return Type T with value inverted */ -template static inline constexpr T negate(const T v) { +template static inline constexpr T invert(const T v) { return (T)( ~v ); } @@ -297,6 +297,7 @@ template static inline constexpr bool ispowerof2(const * * The constexpr templated version will trigger a static_assert if v == 0. * + * log2(2^32 - 1) == 31 2^32 - 1 = 0xFFFFFFFF * log2(2^31) == 31 * log2(2^30) == 30 * log2(2^16) == 16 2^16 == 65536 diff --git a/include/build_timestamp.h b/include/build_timestamp.h index ce14ca8a0..143329cea 100644 --- a/include/build_timestamp.h +++ b/include/build_timestamp.h @@ -1,3 +1,3 @@ /*auto-generated*/ -#define UPDATED_STR "Jun 1, 2018 12:28:50pm" +#define UPDATED_STR "Jun 27, 2018 11:42:01am" #define COPYRIGHT_END_YEAR "2018" diff --git a/include/dosbox.h b/include/dosbox.h index 021b377bb..e5b499d28 100644 --- a/include/dosbox.h +++ b/include/dosbox.h @@ -54,6 +54,12 @@ typedef Bitu cpu_cycles_countu_t; class Config; class Section; +#if defined(__GNUC__) +# define DEPRECATED __attribute__((deprecated)) +#else +# define DEPRECATED +#endif + enum MachineType { MCH_HERC, MCH_CGA, @@ -82,8 +88,8 @@ extern SVGACards svgaCard; extern MachineType machine; extern bool SDLNetInited; extern bool mono_cga; -extern bool mainline_compatible_mapping; -extern bool mainline_compatible_bios_mapping; +extern bool DEPRECATED mainline_compatible_mapping; +extern bool DEPRECATED mainline_compatible_bios_mapping; #ifdef __SSE__ extern bool sse1_available; @@ -122,13 +128,10 @@ void DOSBOX_Init(void); extern ClockDomain clockdom_PCI_BCLK; extern ClockDomain clockdom_ISA_OSC; extern ClockDomain clockdom_ISA_BCLK; -extern ClockDomain clockdom_8254_PIT; -extern ClockDomain clockdom_8250_UART; signed long long time_to_clockdom(ClockDomain &src,double t); unsigned long long update_clockdom_from_now(ClockDomain &dst); unsigned long long update_ISA_OSC_clock(); -unsigned long long update_8254_PIT_clock(); unsigned long long update_ISA_BCLK_clock(); unsigned long long update_PCI_BCLK_clock(); diff --git a/include/menu.h b/include/menu.h index c295061f4..981f4a945 100644 --- a/include/menu.h +++ b/include/menu.h @@ -59,6 +59,7 @@ extern bool DOSBox_Kor(void); extern unsigned int hdd_defsize; extern char hdd_size[20]; extern HWND GetHWND(void); +extern HWND GetSurfaceHWND(void); extern void GetDefaultSize(void); #define SCALER(opscaler,opsize) \ if ((render.scale.op==opscaler) && (render.scale.size==opsize)) diff --git a/include/mouse.h b/include/mouse.h index 0ee822593..23a706612 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -21,6 +21,13 @@ #ifndef DOSBOX_MOUSE_H #define DOSBOX_MOUSE_H +enum MOUSE_EMULATION +{ + MOUSE_EMULATION_NEVER, + MOUSE_EMULATION_ALWAYS, + MOUSE_EMULATION_INTEGRATION, + MOUSE_EMULATION_LOCKED, +}; void Mouse_ShowCursor(void); void Mouse_HideCursor(void); diff --git a/include/render.h b/include/render.h index ba6574853..7e2afab60 100644 --- a/include/render.h +++ b/include/render.h @@ -87,11 +87,13 @@ typedef struct { Bitu cachePitch; Bit8u *cacheRead; Bitu inHeight, inLine, outLine; + bool xBRZ; } scale; RenderPal_t pal; bool updating; bool active; bool aspect; + bool aspectOffload; bool fullFrame; bool forceUpdate; bool autofit; diff --git a/include/setup.h b/include/setup.h index e1690981f..e783e8d40 100644 --- a/include/setup.h +++ b/include/setup.h @@ -181,9 +181,23 @@ public: Prop_double(std::string const & _propname, Changeable::Value when, double _value) :Property(_propname,when){ default_value = value = _value; + min = max = -1.0; } + Prop_double(std::string const & propname, Changeable::Value when, double _value, double _min, double _max) + :Property(propname, when) + { + default_value = value = _value; + min = _min; + max = _max; + } + double getMin() const { return min; } + double getMax() const { return max; } + void SetMinMax(Value const& min, Value const& max) { this->min = min; this->max = max; } bool SetValue(std::string const& input); virtual ~Prop_double(){ } + virtual bool CheckValue(Value const& in, bool warn); +private: + Value min, max; }; class Prop_bool:public Property { diff --git a/include/vga.h b/include/vga.h index 6d1b5e410..7d3137dc4 100644 --- a/include/vga.h +++ b/include/vga.h @@ -30,19 +30,34 @@ class PageHandler; enum VGAModes { - M_CGA2, M_CGA4, - M_EGA, M_VGA, - M_LIN4, M_LIN8, M_LIN15, M_LIN16, M_LIN24, M_LIN32, - M_TEXT, - M_HERC_GFX, M_HERC_TEXT, - M_CGA16, M_TANDY2, M_TANDY4, M_TANDY16, M_TANDY_TEXT, M_AMSTRAD, + M_CGA2, // 0 + M_CGA4, + M_EGA, + M_VGA, + M_LIN4, + M_LIN8, // 5 + M_LIN15, + M_LIN16, + M_LIN24, + M_LIN32, + M_TEXT, // 10 + M_HERC_GFX, + M_HERC_TEXT, + M_CGA16, + M_TANDY2, + M_TANDY4, // 15 + M_TANDY16, + M_TANDY_TEXT, + M_AMSTRAD, M_PC98, + M_FM_TOWNS, // 20 STUB + M_ERROR, - M_FM_TOWNS,//STUB - - M_ERROR + M_MAX }; +extern const char* const mode_texts[M_MAX]; + enum VGA_Vsync { VS_Off, VS_On, @@ -391,8 +406,12 @@ typedef union { } VGA_Latch; typedef struct { - Bit8u* linear; - Bit8u* linear_orgptr; + Bit8u* linear = NULL; + Bit8u* linear_orgptr = NULL; + + uint32_t memsize = 0; + uint32_t memmask = 0; + uint32_t memmask_crtc = 0; // in CRTC-visible units (depends on byte/word/dword mode) } VGA_Memory; typedef struct { @@ -433,9 +452,6 @@ typedef struct { VGA_AMSTRAD amstrad; VGA_OTHER other; VGA_Memory mem; - Bit32u vmemwrap; /* this is assumed to be power of 2 */ - Bit32u vmemsize; - Bit32u vmemsize_alloced; VGA_LFB lfb; } VGA_Type; diff --git a/pc98-testme-1/contents/ANSIATRB.C b/pc98-testme-1/contents/ANSIATRB.C new file mode 100644 index 000000000..1c46a108a --- /dev/null +++ b/pc98-testme-1/contents/ANSIATRB.C @@ -0,0 +1,213 @@ +#define HEADER_LINE "Testing text attribute changes via escape sequences..." + +#define pc98_text_cursor_x() *(char far*)(0x071C) +#define pc98_text_cursor_y() *(char far*)(0x0710) + +unsigned char far* pc98_tram_at(unsigned char x, unsigned char y) +{ + return (unsigned char far *)((int __seg *)(0xA000) + ((y * 80) + x)); +} + +unsigned char far* pc98_aram_at(unsigned char x, unsigned char y) +{ + return (unsigned char far *)((int __seg *)(0xA200) + ((y * 80) + x)); +} + +void putc(const char c) +{ + __asm { + mov dl, c + mov ah, 02h + int 21h + } +} + +void put_3_digit_number(unsigned int n) +{ + unsigned int units = n % 10; + unsigned int tens = (n / 10) % 10; + unsigned int hundreds = n / 100; + if(hundreds >= 1) { + putc('0' + hundreds); + putc('0' + tens); + } else if(tens >= 1) { + putc('0' + tens); + } + putc('0' + units); +} + +void puts(const char *str) +{ + while(*str) { + putc(*str); + str++; + } +} + +void puts_escapeseq(const char *seq) +{ + puts("\x1B["); + puts(seq); + putc('m'); +} + +#define NUM_COLORS 8 +#define elementsof(x) (sizeof(x) / sizeof(x[0])) + +typedef struct { + unsigned int taken; + unsigned int passed; +} test_state_t; + +typedef struct { + const char name[17]; + const char seqs[NUM_COLORS][5]; + const unsigned char attrib_expected[NUM_COLORS]; +} color_test_t; + +void attrib_test( + test_state_t *state, + const char *seq, + unsigned char dispchar, + unsigned char exp +) +{ + int passed; + unsigned char cx = pc98_text_cursor_x(); + unsigned char cy = pc98_text_cursor_y(); + unsigned char far *aram = pc98_aram_at(cx, cy); + + state->taken++; + + puts("\x1B["); + puts(seq); + putc('m'); + putc(' '); + putc(dispchar); + putc(' '); + + passed = (aram[0] == exp) && (aram[2] == exp) && (aram[4] == exp); + state->passed += passed; + if(!passed) { + *(pc98_tram_at(cx + 1, cy)) = 'F'; + } +} + +void color_test( + test_state_t *state, + const color_test_t *test, + char prefix_attrib, + unsigned char prefix_attrib_flag +) +{ + static const char testchars[] = { + '_', 'R', 'G', 'Y', 'B', 'P', 'C', 'W' + }; + int i; + + if(test->name[0]) { + puts("\r\n* "); + puts(test->name); + putc(' '); + } + + for(i = 0; i < elementsof(testchars); i++) { + char final_seq[7] = { '\0' }; + char *p = final_seq; + const char *q = test->seqs[i]; + if(prefix_attrib) { + *p++ = prefix_attrib; + *p++ = ';'; + } + while(*q) { + *p++ = *q++; + } + attrib_test( + state, + final_seq, + testchars[i], + test->attrib_expected[i] | prefix_attrib_flag + ); + puts_escapeseq("0"); + } +} + +void hr(int len) +{ + int i; + for(i = 0; i < len; i++) { + puts("\x86\x44"); + } + puts("\r\n"); +} + +int main(void) +{ + static const color_test_t tests[] = { + { + "Foreground set 1", + { "16", "17", "20", "21", "18", "19", "22", "23" }, + { 0xE0, 0x41, 0x81, 0xC1, 0x21, 0x61, 0xA1, 0xE1 } + }, { + "", + { "30", "31", "32", "33", "34", "35", "36", "37" }, + { 0x01, 0x41, 0x81, 0xC1, 0x21, 0x61, 0xA1, 0xE1 } + }, { + "Reversed via FG ", + { "8;7", "17;7", "20;7", "21;7", "18;7", "19;7", "22;7", "23;7" }, + { 0xE4, 0x45, 0x85, 0xC5, 0x25, 0x65, 0xA5, 0xE5 } + }, { + "", + { "30;7", "31;7", "32;7", "33;7", "34;7", "35;7", "36;7", "37;7" }, + { 0x05, 0x45, 0x85, 0xC5, 0x25, 0x65, 0xA5, 0xE5 } + }, { + "Reversed, direct", + { "40", "41", "42", "43", "44", "45", "46", "47" }, + { 0x05, 0x45, 0x85, 0xC5, 0x25, 0x65, 0xA5, 0xE5 } + } + }; + + int i; + test_state_t state = {0}; + + int HEADER_LINE_LEN = sizeof(HEADER_LINE) - 1; + puts(HEADER_LINE); + puts("\r\n"); + hr(HEADER_LINE_LEN); + puts("Regular:"); + for(i = 0; i < elementsof(tests); i++) { + color_test(&state, &tests[i], 0, 0); + } + puts("\r\nBlinking:"); + for(i = 0; i < elementsof(tests); i++) { + color_test(&state, &tests[i], '5', 0x02); + } + puts("\r\nUnderline:"); + for(i = 0; i < elementsof(tests); i++) { + color_test(&state, &tests[i], '4', 0x08); + } + puts("\r\nBit 4:"); + for(i = 0; i < elementsof(tests); i++) { + color_test(&state, &tests[i], '2', 0x10); + } + + puts("\r\n"); + hr(HEADER_LINE_LEN); + + // Secret attribute + attrib_test(&state, "8", '.', 0xE0); + // Reset for invalid codes + attrib_test(&state, "99", '.', 0xE1); + + if(state.passed == state.taken) { + puts_escapeseq("42"); + } else if(state.passed < state.taken) { + puts_escapeseq("41"); + } + put_3_digit_number(state.passed); + puts(" of "); + put_3_digit_number(state.taken); + puts(" tests passed.\r\n"); + puts_escapeseq("0"); + return state.passed != state.taken; +} diff --git a/pc98-testme-1/contents/ANSIATRB.COM b/pc98-testme-1/contents/ANSIATRB.COM new file mode 100755 index 000000000..ac27f7dbd Binary files /dev/null and b/pc98-testme-1/contents/ANSIATRB.COM differ diff --git a/pc98-testme-1/contents/BUILD.BAT b/pc98-testme-1/contents/BUILD.BAT new file mode 100755 index 000000000..153f08e60 --- /dev/null +++ b/pc98-testme-1/contents/BUILD.BAT @@ -0,0 +1 @@ +tcc -mt -lt -O -Z -3 ansiatrb.c diff --git a/pc98-testme-1/contents/COLORBUG.C b/pc98-testme-1/contents/COLORBUG.C new file mode 100644 index 000000000..cd95a3b48 --- /dev/null +++ b/pc98-testme-1/contents/COLORBUG.C @@ -0,0 +1,94 @@ +#include +#include + +typedef unsigned char uchar; + +void pascal near __scroll(uchar __dir, uchar __x1, uchar __y1, uchar __x2, uchar __y2, uchar __lines); +unsigned int _NEC_WHEREXY(void); + +typedef struct +{ + uchar windowx1; + uchar windowy1; + uchar windowx2; + uchar windowy2; + uchar attribute; + uchar normattr; + uchar currmode; + uchar screenheight; + uchar screenwidth; + uchar graphicsmode; + uchar columns; + union { + char far * p; + struct { unsigned off,seg; } u; + } displayptr; + uchar unknown_0; + char far * unknown_1; + unsigned int unknown_2; + unsigned int unknown_3; +} VIDEOREC; + +extern VIDEOREC _video; + +void print_wherexy(void) +{ + register int xy = _NEC_WHEREXY(); + printf("Where: %u, %u\n", (xy & 0xFF00) << 8, (xy & 0xFF)); +} + +#define V_SET_MODE 0x00 +#define V_SET_CURSOR_POS 0x02 +#define V_GET_CURSOR_POS 0x03 +#define V_SCROLL_UP 0x06 +#define V_SCROLL_DOWN 0x07 +#define V_RD_CHAR_ATTR 0x08 +#define V_WR_CHAR_ATTR 0x09 +#define V_WR_CHAR 0x0a +#define V_WR_TTY 0x0e +#define V_GET_MODE 0x0f + +void main (void) +{ + // print_wherexy(); + textcolor(GREEN); + cputs("Green on black. "); + cputs("Call #2.\r\n"); + + /*printf( + "Video structure:\n" + "* windowx1: %u\n" + "* windowy1: %u\n" + "* windowx2: %u\n" + "* windowy2: %u\n" + "* attribute: %u\n" + "* normattr: %u\n" + "* currmode: %u\n" + "* screenheight: %u\n" + "* screenwidth: %u\n" + "* graphicsmode: %u\n" + "* columns: %u\n" + "* displayptr: %Fp\n" + "* unknown_0: %u\n" + "* unknown_1: %Fp\n", + _video.windowx1, + _video.windowy1, + _video.windowx2, + _video.windowy2, + _video.attribute, + _video.normattr, + _video.currmode, + _video.screenheight, + _video.screenwidth, + _video.graphicsmode, + _video.columns, + _video.displayptr.p, + _video.unknown_0, + _video.unknown_1 + );*/ + + // print_wherexy(); + textattr(GREEN | REVERSE); + cputs("Black on green.\r\n"); + // __scroll(V_SCROLL_UP, 0, 0, 80, 23, 1); +} diff --git a/pc98-testme-1/contents/COLORBUG.COM b/pc98-testme-1/contents/COLORBUG.COM new file mode 100644 index 000000000..ef323a405 Binary files /dev/null and b/pc98-testme-1/contents/COLORBUG.COM differ diff --git a/pc98-testme-1/contents/COLORBUG.MAP b/pc98-testme-1/contents/COLORBUG.MAP new file mode 100644 index 000000000..279a202af --- /dev/null +++ b/pc98-testme-1/contents/COLORBUG.MAP @@ -0,0 +1,596 @@ + + Start Stop Length Name Class + + 00000H 0306AH 0306BH _TEXT CODE + 03070H 03070H 00000H _FARDATA FAR_DATA + 03070H 03070H 00000H _FARBSS FAR_BSS + 03070H 03564H 004F5H _DATA DATA + 03566H 03567H 00002H _CVTSEG DATA + 03568H 0356DH 00006H _SCNSEG DATA + 0356EH 0356EH 00000H _CONST CONST + 0356EH 0357FH 00012H _INIT_ INITDATA + 03580H 03580H 00000H _INITEND_ INITDATA + 03580H 03580H 00000H _EXIT_ EXITDATA + 03580H 03580H 00000H _EXITEND_ EXITDATA + 03580H 035D5H 00056H _BSS BSS + 035D6H 035D6H 00000H _COMDEF_ BSS + 035D6H 035D6H 00000H _BSSEND BSS + + +Detailed map of segments + + 0000:0000 0367 C=CODE S=_TEXT G=DGROUP M=c0.ASM ACBP=28 + 0000:0367 004B C=CODE S=_TEXT G=DGROUP M=colorbug.c ACBP=28 + 0000:03B2 0015 C=CODE S=_TEXT G=DGROUP M=_abort ACBP=28 + 0000:03C7 0028 C=CODE S=_TEXT G=DGROUP M=atexit ACBP=28 + 0000:03EF 001F C=CODE S=_TEXT G=DGROUP M=errormsg ACBP=28 + 0000:040E 00A0 C=CODE S=_TEXT G=DGROUP M=exit ACBP=28 + 0000:04AE 0000 C=CODE S=_TEXT G=DGROUP M=files ACBP=28 + 0000:04AE 0000 C=CODE S=_TEXT G=DGROUP M=files2 ACBP=28 + 0000:04AE 0000 C=CODE S=_TEXT G=DGROUP M=heaplen ACBP=28 + 0000:04AE 0052 C=CODE S=_TEXT G=DGROUP M=ioerror ACBP=28 + 0000:0500 0015 C=CODE S=_TEXT G=DGROUP M=isatty ACBP=28 + 0000:0515 009E C=CODE S=_TEXT G=DGROUP M=longtoa ACBP=28 + 0000:05B3 002D C=CODE S=_TEXT G=DGROUP M=lseek ACBP=28 + 0000:05E0 009B C=CODE S=_TEXT G=DGROUP M=setupio ACBP=28 + 0000:067B 0000 C=CODE S=_TEXT G=DGROUP M=stklen ACBP=28 + 0000:067B 04CB C=CODE S=_TEXT G=DGROUP M=vprinter ACBP=28 + 0000:0B46 007F C=CODE S=_TEXT G=DGROUP M=brk ACBP=28 + 0000:0BC5 0277 C=CODE S=_TEXT G=DGROUP M=nearheap ACBP=28 + 0000:0E3C 0091 C=CODE S=_TEXT G=DGROUP M=fflush ACBP=28 + 0000:0ECD 0042 C=CODE S=_TEXT G=DGROUP M=flushall ACBP=28 + 0000:0F0F 01A6 C=CODE S=_TEXT G=DGROUP M=fseek ACBP=28 + 0000:10B5 001F C=CODE S=_TEXT G=DGROUP M=memcpy ACBP=28 + 0000:10D4 001B C=CODE S=_TEXT G=DGROUP M=printf ACBP=28 + 0000:10EF 0327 C=CODE S=_TEXT G=DGROUP M=putc ACBP=28 + 0000:1416 00E5 C=CODE S=_TEXT G=DGROUP M=setvbuf ACBP=28 + 0000:14FB 001C C=CODE S=_TEXT G=DGROUP M=strlen ACBP=28 + 0000:1517 0000 C=CODE S=_TEXT G=DGROUP M=sysnerr ACBP=28 + 0000:1517 0125 C=CODE S=_TEXT G=DGROUP M=write ACBP=28 + 0000:163C 0055 C=CODE S=_TEXT G=DGROUP M=writea ACBP=28 + 0000:1691 0035 C=CODE S=_TEXT G=DGROUP M=xfflush ACBP=28 + 0000:16C6 001F C=CODE S=_TEXT G=DGROUP M=cvtfak ACBP=28 + 0000:16E5 0004 C=CODE S=_TEXT G=DGROUP M=realcvt ACBP=28 + 0000:16E9 00C0 C=CODE S=_TEXT G=DGROUP M=ccomcolr ACBP=28 + 0000:17A9 00BA C=CODE S=_TEXT G=DGROUP M=ccomgptx ACBP=28 + 0000:1863 002B C=CODE S=_TEXT G=DGROUP M=ccomgtxy ACBP=28 + 0000:188E 001B C=CODE S=_TEXT G=DGROUP M=ccompbel ACBP=28 + 0000:18A9 0077 C=CODE S=_TEXT G=DGROUP M=ccomputn ACBP=28 + 0000:1920 0041 C=CODE S=_TEXT G=DGROUP M=ccomscrl ACBP=28 + 0000:1961 003F C=CODE S=_TEXT G=DGROUP M=ccomscrn ACBP=28 + 0000:19A0 001F C=CODE S=_TEXT G=DGROUP M=ccomwhxy ACBP=28 + 0000:19BF 00A0 C=CODE S=_TEXT G=DGROUP M=cibmcolr ACBP=28 + 0000:1A5F 0215 C=CODE S=_TEXT G=DGROUP M=cibmgptx ACBP=28 + 0000:1C74 0018 C=CODE S=_TEXT G=DGROUP M=cibmgtxy ACBP=28 + 0000:1C8C 000C C=CODE S=_TEXT G=DGROUP M=cibmpbel ACBP=28 + 0000:1C98 01B1 C=CODE S=_TEXT G=DGROUP M=cibmputn ACBP=28 + 0000:1E49 00B8 C=CODE S=_TEXT G=DGROUP M=cibmscrl ACBP=28 + 0000:1F01 03A9 C=CODE S=_TEXT G=DGROUP M=cibmscrn ACBP=28 + 0000:22AA 00C9 C=CODE S=_TEXT G=DGROUP M=cibmvram ACBP=28 + 0000:2373 000E C=CODE S=_TEXT G=DGROUP M=cibmwhxy ACBP=28 + 0000:2381 00DE C=CODE S=_TEXT G=DGROUP M=cneccolr ACBP=28 + 0000:245F 01DB C=CODE S=_TEXT G=DGROUP M=cnecgptx ACBP=28 + 0000:263A 004A C=CODE S=_TEXT G=DGROUP M=cnecgtxy ACBP=28 + 0000:2684 00D0 C=CODE S=_TEXT G=DGROUP M=cnecinit ACBP=28 + 0000:2754 000E C=CODE S=_TEXT G=DGROUP M=cnecpbel ACBP=28 + 0000:2762 00F7 C=CODE S=_TEXT G=DGROUP M=cnecputn ACBP=28 + 0000:2859 00AA C=CODE S=_TEXT G=DGROUP M=cnecscrl ACBP=28 + 0000:2903 014D C=CODE S=_TEXT G=DGROUP M=cnecscrn ACBP=28 + 0000:2A50 0012 C=CODE S=_TEXT G=DGROUP M=cnecwhxy ACBP=28 + 0000:2A62 0000 C=CODE S=_TEXT G=DGROUP M=coniotyp ACBP=28 + 0000:2A62 002A C=CODE S=_TEXT G=DGROUP M=conioini ACBP=28 + 0000:2A8C 0215 C=CODE S=_TEXT G=DGROUP M=cputn ACBP=28 + 0000:2CA1 0019 C=CODE S=_TEXT G=DGROUP M=cputs ACBP=28 + 0000:2CBA 0062 C=CODE S=_TEXT G=DGROUP M=crtinit ACBP=28 + 0000:2D1C 007E C=CODE S=_TEXT G=DGROUP M=screen ACBP=28 + 0000:2D9A 003F C=CODE S=_TEXT G=DGROUP M=validate ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wclrnorm ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wedgectl ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wgarbage ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wkanji ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wnewline ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=wscroll ACBP=28 + 0000:2DD9 0000 C=CODE S=_TEXT G=DGROUP M=MBCTYPE ACBP=28 + 0000:2DD9 0292 C=CODE S=_TEXT G=DGROUP M=cibminit ACBP=28 + 0000:3070 0000 C=FAR_DATA S=_FARDATA G=DGROUP M=c0.ASM ACBP=68 + 0000:3070 0000 C=FAR_BSS S=_FARBSS G=DGROUP M=c0.ASM ACBP=68 + 0000:3070 006E C=DATA S=_DATA G=DGROUP M=c0.ASM ACBP=68 + 0000:30DE 003D C=DATA S=_DATA G=DGROUP M=colorbug.c ACBP=48 + 0000:311C 001F C=DATA S=_DATA G=DGROUP M=_abort ACBP=48 + 0000:313C 0002 C=DATA S=_DATA G=DGROUP M=atexit ACBP=48 + 0000:313E 0000 C=DATA S=_DATA G=DGROUP M=errormsg ACBP=48 + 0000:313E 0006 C=DATA S=_DATA G=DGROUP M=exit ACBP=48 + 0000:3144 0140 C=DATA S=_DATA G=DGROUP M=files ACBP=48 + 0000:3284 002A C=DATA S=_DATA G=DGROUP M=files2 ACBP=48 + 0000:32AE 0002 C=DATA S=_DATA G=DGROUP M=heaplen ACBP=48 + 0000:32B0 005B C=DATA S=_DATA G=DGROUP M=ioerror ACBP=48 + 0000:330C 0000 C=DATA S=_DATA G=DGROUP M=isatty ACBP=48 + 0000:330C 0000 C=DATA S=_DATA G=DGROUP M=longtoa ACBP=48 + 0000:330C 0000 C=DATA S=_DATA G=DGROUP M=lseek ACBP=48 + 0000:330C 0000 C=DATA S=_DATA G=DGROUP M=setupio ACBP=48 + 0000:330C 0002 C=DATA S=_DATA G=DGROUP M=stklen ACBP=48 + 0000:330E 0067 C=DATA S=_DATA G=DGROUP M=vprinter ACBP=48 + 0000:3376 0000 C=DATA S=_DATA G=DGROUP M=brk ACBP=48 + 0000:3376 0006 C=DATA S=_DATA G=DGROUP M=nearheap ACBP=48 + 0000:337C 0000 C=DATA S=_DATA G=DGROUP M=fflush ACBP=48 + 0000:337C 0000 C=DATA S=_DATA G=DGROUP M=flushall ACBP=48 + 0000:337C 0000 C=DATA S=_DATA G=DGROUP M=fseek ACBP=48 + 0000:337C 0000 C=DATA S=_DATA G=DGROUP M=memcpy ACBP=48 + 0000:337C 0000 C=DATA S=_DATA G=DGROUP M=printf ACBP=48 + 0000:337C 0001 C=DATA S=_DATA G=DGROUP M=putc ACBP=48 + 0000:337E 0004 C=DATA S=_DATA G=DGROUP M=setvbuf ACBP=48 + 0000:3382 0000 C=DATA S=_DATA G=DGROUP M=strlen ACBP=48 + 0000:3382 0002 C=DATA S=_DATA G=DGROUP M=sysnerr ACBP=48 + 0000:3384 0000 C=DATA S=_DATA G=DGROUP M=write ACBP=48 + 0000:3384 0000 C=DATA S=_DATA G=DGROUP M=writea ACBP=48 + 0000:3384 0000 C=DATA S=_DATA G=DGROUP M=xfflush ACBP=48 + 0000:3384 0031 C=DATA S=_DATA G=DGROUP M=cvtfak ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=realcvt ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomcolr ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomgptx ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomgtxy ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccompbel ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomputn ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomscrl ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomscrn ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=ccomwhxy ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmcolr ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmgptx ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmgtxy ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmpbel ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmputn ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmscrl ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmscrn ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmvram ACBP=48 + 0000:33B6 0000 C=DATA S=_DATA G=DGROUP M=cibmwhxy ACBP=48 + 0000:33B6 0080 C=DATA S=_DATA G=DGROUP M=cneccolr ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecgptx ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecgtxy ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecinit ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecpbel ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecputn ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecscrl ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecscrn ACBP=48 + 0000:3436 0000 C=DATA S=_DATA G=DGROUP M=cnecwhxy ACBP=48 + 0000:3436 0002 C=DATA S=_DATA G=DGROUP M=coniotyp ACBP=48 + 0000:3438 0000 C=DATA S=_DATA G=DGROUP M=conioini ACBP=48 + 0000:3438 0002 C=DATA S=_DATA G=DGROUP M=cputn ACBP=48 + 0000:343A 0000 C=DATA S=_DATA G=DGROUP M=cputs ACBP=48 + 0000:343A 0016 C=DATA S=_DATA G=DGROUP M=crtinit ACBP=48 + 0000:3450 0000 C=DATA S=_DATA G=DGROUP M=screen ACBP=48 + 0000:3450 0000 C=DATA S=_DATA G=DGROUP M=validate ACBP=48 + 0000:3450 0002 C=DATA S=_DATA G=DGROUP M=wclrnorm ACBP=48 + 0000:3452 0002 C=DATA S=_DATA G=DGROUP M=wedgectl ACBP=48 + 0000:3454 0002 C=DATA S=_DATA G=DGROUP M=wgarbage ACBP=48 + 0000:3456 0002 C=DATA S=_DATA G=DGROUP M=wkanji ACBP=48 + 0000:3458 0002 C=DATA S=_DATA G=DGROUP M=wnewline ACBP=48 + 0000:345A 0002 C=DATA S=_DATA G=DGROUP M=wscroll ACBP=48 + 0000:345C 0101 C=DATA S=_DATA G=DGROUP M=MBCTYPE ACBP=48 + 0000:355E 0007 C=DATA S=_DATA G=DGROUP M=cibminit ACBP=48 + 0000:3566 0000 C=DATA S=_CVTSEG G=DGROUP M=c0.ASM ACBP=48 + 0000:3566 0002 C=DATA S=_CVTSEG G=DGROUP M=cvtfak ACBP=48 + 0000:3568 0000 C=DATA S=_CVTSEG G=DGROUP M=realcvt ACBP=48 + 0000:3568 0000 C=DATA S=_SCNSEG G=DGROUP M=c0.ASM ACBP=48 + 0000:3568 0006 C=DATA S=_SCNSEG G=DGROUP M=cvtfak ACBP=48 + 0000:356E 0000 C=CONST S=_CONST G=DGROUP M=c0.ASM ACBP=48 + 0000:356E 0000 C=INITDATA S=_INIT_ G=DGROUP M=c0.ASM ACBP=48 + 0000:356E 0006 C=INITDATA S=_INIT_ G=DGROUP M=setupio ACBP=48 + 0000:3574 0006 C=INITDATA S=_INIT_ G=DGROUP M=conioini ACBP=48 + 0000:357A 0006 C=INITDATA S=_INIT_ G=DGROUP M=crtinit ACBP=48 + 0000:3580 0000 C=INITDATA S=_INITEND_ G=DGROUP M=c0.ASM ACBP=28 + 0000:3580 0000 C=EXITDATA S=_EXIT_ G=DGROUP M=c0.ASM ACBP=48 + 0000:3580 0000 C=EXITDATA S=_EXITEND_ G=DGROUP M=c0.ASM ACBP=28 + 0000:3580 0000 C=BSS S=_BSS G=DGROUP M=c0.ASM ACBP=48 + 0000:3580 0000 C=BSS S=_BSS G=DGROUP M=colorbug.c ACBP=48 + 0000:3580 0040 C=BSS S=_BSS G=DGROUP M=atexit ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=ioerror ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=isatty ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=longtoa ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=lseek ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=setupio ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=vprinter ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=brk ACBP=48 + 0000:35C0 0000 C=BSS S=_BSS G=DGROUP M=memcpy ACBP=48 + 0000:35C0 0001 C=BSS S=_BSS G=DGROUP M=putc ACBP=48 + 0000:35C2 0000 C=BSS S=_BSS G=DGROUP M=strlen ACBP=48 + 0000:35C2 0000 C=BSS S=_BSS G=DGROUP M=writea ACBP=48 + 0000:35C2 0000 C=BSS S=_BSS G=DGROUP M=cibmgptx ACBP=48 + 0000:35C2 000C C=BSS S=_BSS G=DGROUP M=cibmputn ACBP=48 + 0000:35CE 0000 C=BSS S=_BSS G=DGROUP M=cibmscrn ACBP=48 + 0000:35CE 0000 C=BSS S=_BSS G=DGROUP M=cibmvram ACBP=48 + 0000:35CE 0000 C=BSS S=_BSS G=DGROUP M=cnecgptx ACBP=48 + 0000:35CE 0000 C=BSS S=_BSS G=DGROUP M=cnecgtxy ACBP=48 + 0000:35CE 0000 C=BSS S=_BSS G=DGROUP M=cnecinit ACBP=48 + 0000:35CE 0004 C=BSS S=_BSS G=DGROUP M=cnecputn ACBP=48 + 0000:35D2 0000 C=BSS S=_BSS G=DGROUP M=cnecscrn ACBP=48 + 0000:35D2 0000 C=BSS S=_BSS G=DGROUP M=cnecwhxy ACBP=48 + 0000:35D2 0000 C=BSS S=_BSS G=DGROUP M=conioini ACBP=48 + 0000:35D2 0004 C=BSS S=_BSS G=DGROUP M=cputn ACBP=48 + 0000:35D6 0000 C=BSS S=_BSS G=DGROUP M=crtinit ACBP=48 + 0000:35D6 0000 C=BSS S=_BSS G=DGROUP M=cibminit ACBP=48 + 0000:35D6 0000 C=BSS S=_COMDEF_ G=DGROUP M=c0.ASM ACBP=48 + 0000:35D6 0000 C=BSS S=_BSSEND G=DGROUP M=c0.ASM ACBP=28 + + Address Publics by Name + + 0000:3070 idle DATASEG@ + 0000:0363 idle DGROUP@ + 0000:03C7 idle _atexit + 0000:0BA1 idle _brk + 0000:2CA1 _cputs + 0000:30CA _errno + 0000:0463 _exit + 0000:0E3C _fflush + 0000:0ECD _flushall + 0000:110A idle _fputc + 0000:124E idle _fputchar + 0000:0BC5 _free + 0000:0F7F _fseek + 0000:0FE8 idle _ftell + 0000:17A9 _gettext + 0000:1758 idle _highvideo + 0000:0500 _isatty + 0000:1773 idle _lowvideo + 0000:05B3 _lseek + 0000:038B _main + 0000:0C94 _malloc + 0000:10B5 _memcpy + 0000:178E idle _normvideo + 0000:10D4 _printf + 0000:0367 idle _print_wherexy + 0000:17E9 _puttext + 0000:0DEB idle _realloc + 0000:0BB1 idle _sbrk + 0000:1416 _setvbuf + 0000:14FB _strlen + 0000:1733 _textattr + 0000:170E idle _textbackground + 0000:16E9 _textcolor + 0000:03B2 __abort + 0000:1000 idle __AHINCR + 0000:000C idle __AHSHIFT + 0000:313C __atexitcnt + 0000:3580 __atexittbl + 0000:30D6 idle __brklvl + 0000:30BA idle __C0argc + 0000:30BC idle __C0argv + 0000:2CBA idle __c0crtinit + 0000:30BE idle __C0environ + 0000:048C idle __cexit + 0000:0255 __checknull + 0000:0242 __cleanup + 0000:188E __cputbell + 0000:2C44 __CPUTN + 0000:0000 Abs __cvtfak + 0000:049E idle __c_exit + 0000:344E __directvideo + 0000:32B0 idle __doserrno + 0000:04EC idle __DOSERROR + 0000:32B2 idle __dosErrorToSV + 0000:18DF __END_VRAM_ACCESS + 0000:30C0 idle __envLng + 0000:30C2 idle __envseg + 0000:30C4 idle __envSize + 0000:30CC idle __Exception_list + 0000:0476 __exit + 0000:313E __exitbuf + 0000:3140 idle __exitfopen + 0000:3142 idle __exitopen + 0000:3376 idle __first + 0000:10EF idle __fputc + 0000:1263 __FPUTN + 0000:1829 idle __getputtextsize + 0000:1863 __GOTOXY + 0000:30D2 idle __heapbase + 0000:32AE __heaplen + 0000:30DA idle __heaptop + 0000:20CD __IBM_CHECK_DBCS_TRAIL + 0000:1D16 __IBM_END_VRAM_ACCESS + 0000:2167 __IBM_FILL_SPACE + 0000:1C74 __IBM_GOTOXY + 0000:21E6 __IBM_MOVELINE + 0000:21A9 __IBM_OFF_CURSOR + 0000:1DAE __IBM_OUTPUT_CHAR + 0000:2348 __IBM_REFRESH_VRAM + 0000:1C98 __IBM_RESET_VRAM_ACCESS + 0000:2082 __IBM_SCREENIO + 0000:1E49 __IBM_SCROLL + 0000:21D3 __IBM_SET_CURSOR + 0000:1CA3 __IBM_START_VRAM_ACCESS + 0000:2E19 __IBM_VIDEOINT + 0000:2EE3 __IBM_VIDEOINTESBP + 0000:2EC9 __IBM_VIDEOINTESDI + 0000:22AA __IBM_VPTR + 0000:22CE __IBM_VRAM + 0000:2373 __IBM_WHEREXY + 0000:30AA idle __Int0Vector + 0000:30AE idle __Int4Vector + 0000:30B2 idle __Int5Vector + 0000:30B6 idle __Int6Vector + 0000:04AE __IOERROR + 0000:3378 idle __last + 0000:0515 __LONGTOA + 0000:345C __mbctype + 0000:0365 idle __MMODEL + 0000:1961 __MOVELINE + 0000:2D1C __MOVETEXT + 0000:27C0 __NEC_END_VRAM_ACCESS + 0000:263A __NEC_GOTOXY + 0000:29B6 __NEC_MOVELINE + 0000:2802 __NEC_OUTPUT_CHAR + 0000:2762 __NEC_RESET_VRAM_ACCESS + 0000:2859 __NEC_SCROLL + 0000:276D __NEC_START_VRAM_ACCESS + 0000:0000 idle __nec_turboCrt + 0000:2746 __NEC_VIDEOINT18 + 0000:274D __NEC_VIDEOINTDC + 0000:2A50 __NEC_WHEREXY + 0000:2943 __NEC_ZAP_LINE + 0000:3284 __nfile + 0000:3286 __openfd + 0000:30C8 idle __osmajor + 0000:30C9 idle __osminor + 0000:30C8 idle __osversion + 0000:18FA __OUTPUT_CHAR + 0000:30C6 idle __psp + 0000:16E5 __REALCVT + 0000:3566 __RealCvtVector + 0000:18A9 __RESET_VRAM_ACCESS + 0000:02B1 __restorezero + 0000:337A idle __rover + 0000:163C __rtl_write + 0000:3568 idle __ScanTodVector + 0000:1920 __SCROLL + 0000:05E0 __setupio + 0000:18C4 __START_VRAM_ACCESS + 0000:330C __stklen + 0000:3144 __streams + 0000:3382 __sys_nerr + 0000:0256 __terminate + 0000:0000 idle __turboCrt + 0000:0592 idle __UTOA + 0000:2D9A __VALIDATEXY + 0000:30C8 idle __version + 0000:343A __video + 0000:0693 __VPRINTER + 0000:3450 __wclrnorm + 0000:3452 __wedgectrl + 0000:3454 __wgarbage + 0000:19A0 __WHEREXY + 0000:3456 __wkanji + 0000:3458 __wnewline + 0000:1679 idle __write + 0000:345A __wscroll + 0000:1691 __xfflush + 0000:0B46 ___brk + 0000:30D0 ___brklvl + 0000:35D2 ___col + 0000:3436 ___conio_type + 0000:2A62 ___conio_type_init + 0000:03EF ___ErrorMessage + 0000:2CD8 ___get_screen_height + 0000:2CFA ___get_windowy2 + 0000:30CE idle ___heapbase + 0000:2F1A ___ibm_c0crtinit + 0000:1C8C ___ibm_cputbell + 0000:2F3D idle ___ibm_crtinit + 0000:1C59 ___ibm_getputtextsize + 0000:1A5F ___ibm_gettext + 0000:3039 ___ibm_get_screen_height + 0000:304E ___ibm_get_windowy2 + 0000:1A40 ___ibm_highvideo + 0000:1A4A ___ibm_lowvideo + 0000:1A54 ___ibm_normvideo + 0000:1B39 ___ibm_puttext + 0000:1A27 ___ibm_textattr + 0000:19EA ___ibm_textbackground + 0000:19BF ___ibm_textcolor + 0000:2684 ___nec_c0crtinit + 0000:2754 ___nec_cputbell + 0000:261D ___nec_getputtextsize + 0000:245F ___nec_gettext + 0000:2715 ___nec_get_screen_height + 0000:2729 ___nec_get_windowy2 + 0000:2430 ___nec_highvideo + 0000:2442 ___nec_lowvideo + 0000:2454 ___nec_normvideo + 0000:24CB ___nec_puttext + 0000:23DB ___nec_textattr + 0000:23B5 ___nec_textbackground + 0000:2381 ___nec_textcolor + 0000:35D4 ___row + 0000:0B6C ___sbrk + 0000:1517 ___write + + Address Publics by Value + + 0000:0000 Abs __cvtfak + 0000:0000 idle __turboCrt + 0000:0000 idle __nec_turboCrt + 0000:000C idle __AHSHIFT + 0000:1000 idle __AHINCR + 0000:0242 __cleanup + 0000:0255 __checknull + 0000:0256 __terminate + 0000:02B1 __restorezero + 0000:0363 idle DGROUP@ + 0000:0365 idle __MMODEL + 0000:0367 idle _print_wherexy + 0000:038B _main + 0000:03B2 __abort + 0000:03C7 idle _atexit + 0000:03EF ___ErrorMessage + 0000:0463 _exit + 0000:0476 __exit + 0000:048C idle __cexit + 0000:049E idle __c_exit + 0000:04AE __IOERROR + 0000:04EC idle __DOSERROR + 0000:0500 _isatty + 0000:0515 __LONGTOA + 0000:0592 idle __UTOA + 0000:05B3 _lseek + 0000:05E0 __setupio + 0000:0693 __VPRINTER + 0000:0B46 ___brk + 0000:0B6C ___sbrk + 0000:0BA1 idle _brk + 0000:0BB1 idle _sbrk + 0000:0BC5 _free + 0000:0C94 _malloc + 0000:0DEB idle _realloc + 0000:0E3C _fflush + 0000:0ECD _flushall + 0000:0F7F _fseek + 0000:0FE8 idle _ftell + 0000:10B5 _memcpy + 0000:10D4 _printf + 0000:10EF idle __fputc + 0000:110A idle _fputc + 0000:124E idle _fputchar + 0000:1263 __FPUTN + 0000:1416 _setvbuf + 0000:14FB _strlen + 0000:1517 ___write + 0000:163C __rtl_write + 0000:1679 idle __write + 0000:1691 __xfflush + 0000:16E5 __REALCVT + 0000:16E9 _textcolor + 0000:170E idle _textbackground + 0000:1733 _textattr + 0000:1758 idle _highvideo + 0000:1773 idle _lowvideo + 0000:178E idle _normvideo + 0000:17A9 _gettext + 0000:17E9 _puttext + 0000:1829 idle __getputtextsize + 0000:1863 __GOTOXY + 0000:188E __cputbell + 0000:18A9 __RESET_VRAM_ACCESS + 0000:18C4 __START_VRAM_ACCESS + 0000:18DF __END_VRAM_ACCESS + 0000:18FA __OUTPUT_CHAR + 0000:1920 __SCROLL + 0000:1961 __MOVELINE + 0000:19A0 __WHEREXY + 0000:19BF ___ibm_textcolor + 0000:19EA ___ibm_textbackground + 0000:1A27 ___ibm_textattr + 0000:1A40 ___ibm_highvideo + 0000:1A4A ___ibm_lowvideo + 0000:1A54 ___ibm_normvideo + 0000:1A5F ___ibm_gettext + 0000:1B39 ___ibm_puttext + 0000:1C59 ___ibm_getputtextsize + 0000:1C74 __IBM_GOTOXY + 0000:1C8C ___ibm_cputbell + 0000:1C98 __IBM_RESET_VRAM_ACCESS + 0000:1CA3 __IBM_START_VRAM_ACCESS + 0000:1D16 __IBM_END_VRAM_ACCESS + 0000:1DAE __IBM_OUTPUT_CHAR + 0000:1E49 __IBM_SCROLL + 0000:2082 __IBM_SCREENIO + 0000:20CD __IBM_CHECK_DBCS_TRAIL + 0000:2167 __IBM_FILL_SPACE + 0000:21A9 __IBM_OFF_CURSOR + 0000:21D3 __IBM_SET_CURSOR + 0000:21E6 __IBM_MOVELINE + 0000:22AA __IBM_VPTR + 0000:22CE __IBM_VRAM + 0000:2348 __IBM_REFRESH_VRAM + 0000:2373 __IBM_WHEREXY + 0000:2381 ___nec_textcolor + 0000:23B5 ___nec_textbackground + 0000:23DB ___nec_textattr + 0000:2430 ___nec_highvideo + 0000:2442 ___nec_lowvideo + 0000:2454 ___nec_normvideo + 0000:245F ___nec_gettext + 0000:24CB ___nec_puttext + 0000:261D ___nec_getputtextsize + 0000:263A __NEC_GOTOXY + 0000:2684 ___nec_c0crtinit + 0000:2715 ___nec_get_screen_height + 0000:2729 ___nec_get_windowy2 + 0000:2746 __NEC_VIDEOINT18 + 0000:274D __NEC_VIDEOINTDC + 0000:2754 ___nec_cputbell + 0000:2762 __NEC_RESET_VRAM_ACCESS + 0000:276D __NEC_START_VRAM_ACCESS + 0000:27C0 __NEC_END_VRAM_ACCESS + 0000:2802 __NEC_OUTPUT_CHAR + 0000:2859 __NEC_SCROLL + 0000:2943 __NEC_ZAP_LINE + 0000:29B6 __NEC_MOVELINE + 0000:2A50 __NEC_WHEREXY + 0000:2A62 ___conio_type_init + 0000:2C44 __CPUTN + 0000:2CA1 _cputs + 0000:2CBA idle __c0crtinit + 0000:2CD8 ___get_screen_height + 0000:2CFA ___get_windowy2 + 0000:2D1C __MOVETEXT + 0000:2D9A __VALIDATEXY + 0000:2E19 __IBM_VIDEOINT + 0000:2EC9 __IBM_VIDEOINTESDI + 0000:2EE3 __IBM_VIDEOINTESBP + 0000:2F1A ___ibm_c0crtinit + 0000:2F3D idle ___ibm_crtinit + 0000:3039 ___ibm_get_screen_height + 0000:304E ___ibm_get_windowy2 + 0000:3070 idle DATASEG@ + 0000:30AA idle __Int0Vector + 0000:30AE idle __Int4Vector + 0000:30B2 idle __Int5Vector + 0000:30B6 idle __Int6Vector + 0000:30BA idle __C0argc + 0000:30BC idle __C0argv + 0000:30BE idle __C0environ + 0000:30C0 idle __envLng + 0000:30C2 idle __envseg + 0000:30C4 idle __envSize + 0000:30C6 idle __psp + 0000:30C8 idle __osversion + 0000:30C8 idle __osmajor + 0000:30C8 idle __version + 0000:30C9 idle __osminor + 0000:30CA _errno + 0000:30CC idle __Exception_list + 0000:30CE idle ___heapbase + 0000:30D0 ___brklvl + 0000:30D2 idle __heapbase + 0000:30D6 idle __brklvl + 0000:30DA idle __heaptop + 0000:313C __atexitcnt + 0000:313E __exitbuf + 0000:3140 idle __exitfopen + 0000:3142 idle __exitopen + 0000:3144 __streams + 0000:3284 __nfile + 0000:3286 __openfd + 0000:32AE __heaplen + 0000:32B0 idle __doserrno + 0000:32B2 idle __dosErrorToSV + 0000:330C __stklen + 0000:3376 idle __first + 0000:3378 idle __last + 0000:337A idle __rover + 0000:3382 __sys_nerr + 0000:3436 ___conio_type + 0000:343A __video + 0000:344E __directvideo + 0000:3450 __wclrnorm + 0000:3452 __wedgectrl + 0000:3454 __wgarbage + 0000:3456 __wkanji + 0000:3458 __wnewline + 0000:345A __wscroll + 0000:345C __mbctype + 0000:3566 __RealCvtVector + 0000:3568 idle __ScanTodVector + 0000:3580 __atexittbl + 0000:35D2 ___col + 0000:35D4 ___row + + diff --git a/pc98-testme-1/zip/COLORBUG.zip b/pc98-testme-1/zip/COLORBUG.zip new file mode 100644 index 000000000..5da5b77c8 Binary files /dev/null and b/pc98-testme-1/zip/COLORBUG.zip differ diff --git a/src/cpu/callback.cpp b/src/cpu/callback.cpp index 29e336c50..b6fde809a 100644 --- a/src/cpu/callback.cpp +++ b/src/cpu/callback.cpp @@ -748,23 +748,7 @@ void CALLBACK_HandlerObject::Set_RealVec(Bit8u vec,bool reinstall){ } void CALLBACK_Init() { - if (mainline_compatible_bios_mapping) { - LOG(LOG_MISC,LOG_DEBUG)("Initializing DOSBox callback instruction system (mainline compatible)"); - - CB_SOFFSET=0x1000; - CB_SEG=0xF000; - - /* mark the fixed callback location as off-limits */ - if (ROMBIOS_GetMemory((CB_MAX*CB_SIZE)+(256*6),"DOSBox callbacks region",1,PhysMake(CB_SEG,CB_SOFFSET)) == 0) - E_Exit("Mainline compat bios mapping: failed to declare entire BIOS area off-limits"); - - vm86_fake_io_seg = 0xF000; /* unused area in BIOS for IO instruction */ - vm86_fake_io_off = 0x0700; - /* mark the vm86 hack as off-limits */ - if (ROMBIOS_GetMemory(14/*2+2+3+2+2+3*/,"DOSBox vm86 hack",1,(vm86_fake_io_seg<<4)+vm86_fake_io_off) == 0) - E_Exit("Mainline compat bios mapping: failed to declare entire BIOS area off-limits"); - } - else { + { /* NTS: Layout of the callback area: * * CB_MAX entries CB_SIZE each, where executable x86 code is written per callback, diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h index 66d7e8c6e..80cfcd04c 100644 --- a/src/dos/cdrom.h +++ b/src/dos/cdrom.h @@ -56,55 +56,93 @@ enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI }; +//! \brief CD-ROM time stamp +//! +//! \description CD-ROM time is represented as minutes, seconds, and frames (75 per second) typedef struct SMSF { - unsigned char min; - unsigned char sec; - unsigned char fr; + //! \brief Time, minutes field + unsigned char min; + //! \brief Time, seconds field + unsigned char sec; + //! \brief Time, frame field + unsigned char fr; } TMSF; +//! \brief Output and channel control state typedef struct SCtrl { - Bit8u out[4]; // output channel - Bit8u vol[4]; // channel volume + //! \brief output channel + Bit8u out[4]; + //! \brief channel volume + Bit8u vol[4]; } TCtrl; extern int CDROM_GetMountType(char* path, int force); +//! \brief Base CD-ROM interface class +//! +//! \brief This provides the base C++ class for a CD-ROM interface in CD-ROM emulation class CDROM_Interface { public: // CDROM_Interface (void); virtual ~CDROM_Interface (void) {}; + //! \brief Set the device associated with this interface, if supported by emulation virtual bool SetDevice (char* path, int forceCD) = 0; + //! \brief Get UPC string from the CD-ROM virtual bool GetUPC (unsigned char& attr, char* upc) = 0; + //! \brief Retrieve start and end tracks and lead out position virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut) = 0; + + //! \brief Retrieve start and attributes for a specific track virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr) = 0; + + //! \brief Get subchannel data of the sectors at the current position, and retrieve current position virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) = 0; + + //! \brief Get audio playback status virtual bool GetAudioStatus (bool& playing, bool& pause) = 0; + + //! \brief Get media tray status virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen) = 0; + //! \brief Initiate audio playback starting at sector and for how many virtual bool PlayAudioSector (unsigned long start,unsigned long len) = 0; + + //! \brief Pause audio playback virtual bool PauseAudio (bool resume) = 0; + + //! \brief Stop audio playback virtual bool StopAudio (void) = 0; + + //! \brief Set channel control data (TODO: clarify) virtual void ChannelControl (TCtrl ctrl) = 0; - + + //! \brief Read sector data into guest memory virtual bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num) = 0; - /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ + + //! \brief Read sector data into host memory (for IDE emulation) virtual bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num) = 0; + //! \brief Load (close/spin up) or unload (eject/spin down) media virtual bool LoadUnloadMedia (bool unload) = 0; - + + //! \brief TODO? virtual void InitNewMedia (void) {}; }; +//! \brief CD-ROM interface to SDL 1.x CD-ROM support +//! +//! \brief This connects CD-ROM emulation to the CD-ROM functions provided by SDL 1.x class CDROM_Interface_SDL : public CDROM_Interface { public: CDROM_Interface_SDL (void); virtual ~CDROM_Interface_SDL(void); + /* base C++ class overrides, no documentation needed */ virtual bool SetDevice (char* path, int forceCD); virtual bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; }; virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); @@ -119,20 +157,26 @@ public: virtual bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return false; }; /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ virtual bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); - virtual bool LoadUnloadMedia (bool unload); private: + //! \brief Open the device bool Open (void); + + //! \brief Close the device void Close (void); #if !defined(C_SDL2) + //! \brief SDL 1.x CD-ROM device object SDL_CD* cd; #endif int driveID; Uint32 oldLeadOut; }; +//! \brief Dummy CD-ROM interface +//! +//! \brief CD-ROM emulation when no actual emulation is available class CDROM_Interface_Fake : public CDROM_Interface { public: @@ -154,16 +198,21 @@ public: bool LoadUnloadMedia (bool /*unload*/) { return true; }; }; +//! \brief Image CD-ROM interface +//! +//! \brief This provides CD-ROM emulation from .ISO and .BIN/.CUE images on the host system class CDROM_Interface_Image : public CDROM_Interface { private: + //! \brief Base C++ class for reading the image class TrackFile { public: virtual bool read(Bit8u *buffer, int seek, int count) = 0; virtual int getLength() = 0; virtual ~TrackFile() { }; }; - + + //! \brief Binary file reader for the image class BinaryFile : public TrackFile { public: BinaryFile(const char *filename, bool &error); @@ -175,6 +224,7 @@ private: std::ifstream *file; }; + //! \brief CD-ROM track definition struct Track { int number; int attr; @@ -187,8 +237,10 @@ private: }; public: + //! \brief Constructor, with parameter for subunit CDROM_Interface_Image (Bit8u subUnit); virtual ~CDROM_Interface_Image (void); + void InitNewMedia (void); bool SetDevice (char* path, int forceCD); bool GetUPC (unsigned char& attr, char* upc); @@ -205,10 +257,22 @@ public: /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); bool LoadUnloadMedia (bool unload); + + //! \brief Sector read (one sector), where the image decoding is done. bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector); + + //! \brief Indicate whether the image has a data track bool HasDataTrack (void); - + + //! \brief Flag to track if images have been initialized + //! + //! \description Whether images[] has been initialized. + //! Note that images_init and images[] are static and + //! they are not specific to any one C++ class instance. static bool images_init; + //! \brief Array of CD-ROM images, one per drive letter. + //! + //! \description images[] is static and not specific to any C++ class instance. static CDROM_Interface_Image* images[26]; private: @@ -216,6 +280,10 @@ private: static void CDAudioCallBack(Bitu len); int GetTrack(int sector); + //! \brief Virtual CD audio "player" + //! + //! \description This struct is used to maintain state to emulate playing CD audio + //! tracks from the image. static struct imagePlayer { CDROM_Interface_Image *cd; MixerChannel *channel; diff --git a/src/dos/dev_con.h b/src/dos/dev_con.h index 1a110ac1b..ee9242bb4 100644 --- a/src/dos/dev_con.h +++ b/src/dos/dev_con.h @@ -38,6 +38,10 @@ Bit16u last_int16_code = 0; static size_t dev_con_pos=0,dev_con_max=0; static char dev_con_readbuf[64]; +Bit8u DefaultANSIAttr() { + return IS_PC98_ARCH ? 0xE1 : 0x07; +} + class device_CON : public DOS_Device { public: device_CON(); @@ -57,7 +61,7 @@ private: bool sci; bool pc98rab; // PC-98 ESC [ > ... (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence bool enabled; - Bit8u attr; + Bit8u attr; // machine-specific Bit8u data[NUMBER_ANSI_DATA]; Bit8u numberofarg; Bit16u nrows; @@ -65,6 +69,11 @@ private: Bit8u savecol; Bit8u saverow; bool warned; + + void Disable() { + enabled = false; + attr = DefaultANSIAttr(); + } } ansi; static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) { @@ -101,6 +110,9 @@ private: * will not return anything and will block. */ bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) { switch (code) { + case 0x38: // INS + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2; + break; case 0x39: // DEL dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x44; dev_con_pos=0; dev_con_max=2; return true; @@ -147,7 +159,6 @@ private: dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x5A; dev_con_pos=0; dev_con_max=2; return true; #if 0 - // INS 0x1B 0x50 0x1B 0x50 0x1B 0x50 // ROLL UP -- -- -- // POLL DOWN-- -- -- // COPY -- -- -- @@ -238,6 +249,7 @@ private: static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) { BIOS_NCOLS;BIOS_NROWS; + auto defattr = DefaultANSIAttr(); //Need a new line? if(cur_col==ncols) { @@ -245,16 +257,16 @@ private: cur_row++; if (!IS_PC98_ARCH) - Real_INT10_TeletypeOutput('\r',0x7); + Real_INT10_TeletypeOutput('\r',defattr); } //Reached the bottom? if(cur_row==nrows) { if (IS_PC98_ARCH) - INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,0x07,0); + INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0); else - Real_INT10_TeletypeOutput('\n',0x7); //Scroll up + Real_INT10_TeletypeOutput('\n',defattr); //Scroll up cur_row--; } @@ -369,10 +381,11 @@ private: bool device_CON::Read(Bit8u * data,Bit16u * size) { Bit16u oldax=reg_ax; Bit16u count=0; + auto defattr=DefaultANSIAttr(); INT10_SetCurMode(); if ((readcache) && (*size)) { data[count++]=readcache; - if(dos.echo) Real_INT10_TeletypeOutput(readcache,7); + if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr); readcache=0; } while (*size>count) { @@ -402,8 +415,8 @@ bool device_CON::Read(Bit8u * data,Bit16u * size) { *size=count; reg_ax=oldax; if(dos.echo) { - Real_INT10_TeletypeOutput(13,7); //maybe don't do this ( no need for it actually ) (but it's compatible) - Real_INT10_TeletypeOutput(10,7); + Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible) + Real_INT10_TeletypeOutput(10,defattr); } return true; break; @@ -411,8 +424,8 @@ bool device_CON::Read(Bit8u * data,Bit16u * size) { if(*size==1) data[count++]=reg_al; //one char at the time so give back that BS else if(count) { //Remove data if it exists (extended keys don't go right) data[count--]=0; - Real_INT10_TeletypeOutput(8,7); - Real_INT10_TeletypeOutput(' ',7); + Real_INT10_TeletypeOutput(8,defattr); + Real_INT10_TeletypeOutput(' ',defattr); } else { continue; //no data read yet so restart whileloop. } @@ -446,7 +459,7 @@ bool device_CON::Read(Bit8u * data,Bit16u * size) { } if(dos.echo) { //what to do if *size==1 and character is BS ????? // TODO: If CTRL+C checking is applicable do not echo (reg_al == 3) - Real_INT10_TeletypeOutput(reg_al,7); + Real_INT10_TeletypeOutput(reg_al,defattr); } } *size=count; @@ -485,8 +498,8 @@ bool device_CON::Write(const Bit8u * data,Bit16u * size) { } else { /* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */ if((data[count] == '\n') && (lastwrite != '\r')) Real_INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled); - /* use ansi attribute if ansi is enabled, otherwise use DOS default attribute*/ - Real_INT10_TeletypeOutputAttr(data[count],ansi.enabled?ansi.attr:7,true); + /* ansi attribute will be set to the default if ansi is disabled */ + Real_INT10_TeletypeOutputAttr(data[count],ansi.attr,true); lastwrite = data[count++]; continue; } @@ -542,6 +555,7 @@ bool device_CON::Write(const Bit8u * data,Bit16u * size) { case 1: // show/hide function key row void update_pc98_function_row(bool enable); update_pc98_function_row(data[count] == 'l'); + ansi.nrows = real_readb(0x60,0x110)+1; break; case 3: // clear screen (doesn't matter if l or h) INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); @@ -567,89 +581,90 @@ bool device_CON::Write(const Bit8u * data,Bit16u * size) { else { switch(data[count]){ case 'm': /* SGR */ + // NEC's ANSI driver always resets at the beginning + if(IS_PC98_ARCH) { + ansi.attr = DefaultANSIAttr(); + } for(i=0;i<=ansi.numberofarg;i++){ + const Bit8u COLORFLAGS[][8] = { + // Black Red Green Yellow Blue Pink Cyan White + { 0x0, 0x4, 0x2, 0x6, 0x1, 0x5, 0x3, 0x7 }, /* IBM */ + { 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */ + }; + const auto &flagset = COLORFLAGS[IS_PC98_ARCH]; + + if(IS_PC98_ARCH) { + // Convert alternate color codes to regular ones + if(ansi.data[i] >= 17 && ansi.data[i] <= 23) { + const Bit8u convtbl[] = { + 31, 34, 35, 32, 33, 36, 37 + }; + ansi.data[i] = convtbl[ansi.data[i] - 17]; + } + } + ansi.enabled=true; switch(ansi.data[i]){ case 0: /* normal */ - ansi.attr=0x07;//Real ansi does this as well. (should do current defaults) - ansi.enabled=false; + //Real ansi does this as well. (should do current defaults) + ansi.Disable(); break; case 1: /* bold mode on*/ - ansi.attr|=0x08; + // FIXME: According to http://www.ninton.co.jp/?p=11, this + // should set some sort of "highlight" flag in monochrome + // mode, but I have no idea how to even enter that mode. + ansi.attr |= IS_PC98_ARCH ? 0 : 0x08; + break; + case 2: /* PC-98 "Bit 4" */ + ansi.attr |= IS_PC98_ARCH ? 0x10 : 0; break; case 4: /* underline */ - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI:no support for underline yet"); + if(IS_PC98_ARCH) { + ansi.attr |= 0x08; + } else { + LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet"); + } break; case 5: /* blinking */ - ansi.attr|=0x80; + ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80; break; case 7: /* reverse */ - ansi.attr=0x70;//Just like real ansi. (should do use current colors reversed) + //Just like real ansi. (should do use current colors reversed) + if(IS_PC98_ARCH) { + ansi.attr |= 0x04; + } else { + ansi.attr = 0x70; + } + break; + case 8: /* PC-98 secret */ + case 16: + ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF; break; case 30: /* fg color black */ - ansi.attr&=0xf8; - ansi.attr|=0x0; - break; - case 31: /* fg color red */ - ansi.attr&=0xf8; - ansi.attr|=0x4; - break; - case 32: /* fg color green */ - ansi.attr&=0xf8; - ansi.attr|=0x2; - break; + case 31: /* fg color red */ + case 32: /* fg color green */ case 33: /* fg color yellow */ - ansi.attr&=0xf8; - ansi.attr|=0x6; - break; case 34: /* fg color blue */ - ansi.attr&=0xf8; - ansi.attr|=0x1; - break; case 35: /* fg color magenta */ - ansi.attr&=0xf8; - ansi.attr|=0x5; - break; case 36: /* fg color cyan */ - ansi.attr&=0xf8; - ansi.attr|=0x3; - break; case 37: /* fg color white */ - ansi.attr&=0xf8; - ansi.attr|=0x7; + ansi.attr &= ~(flagset[7]); + ansi.attr |= (flagset[ansi.data[i] - 30]); break; case 40: - ansi.attr&=0x8f; - ansi.attr|=0x0; - break; case 41: - ansi.attr&=0x8f; - ansi.attr|=0x40; - break; case 42: - ansi.attr&=0x8f; - ansi.attr|=0x20; - break; case 43: - ansi.attr&=0x8f; - ansi.attr|=0x60; - break; case 44: - ansi.attr&=0x8f; - ansi.attr|=0x10; - break; case 45: - ansi.attr&=0x8f; - ansi.attr|=0x50; - break; case 46: - ansi.attr&=0x8f; - ansi.attr|=0x30; - break; - case 47: - ansi.attr&=0x8f; - ansi.attr|=0x70; + case 47: { + Bit8u shift = IS_PC98_ARCH ? 0 : 4; + ansi.attr &= ~(flagset[7] << shift); + ansi.attr |= (flagset[ansi.data[i] - 40] << shift); + ansi.attr |= IS_PC98_ARCH ? 0x04 : 0; break; + } default: break; } @@ -874,10 +889,19 @@ device_CON::device_CON() { SetName("CON"); readcache=0; lastwrite=0; - ansi.enabled=false; - ansi.attr=0x7; - ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented - ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; + ansi.Disable(); + if (IS_PC98_ARCH) { + // NTS: On real hardware, the BIOS does NOT manage the console at all. + // TTY handling is entirely handled by MS-DOS. + ansi.ncols=80; + ansi.nrows=25 - 1; + // the DOS kernel will call on this function to disable, and SDLmain + // will call on to enable + } + else { + ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented + ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; + } ansi.saverow=0; ansi.savecol=0; ansi.warned=false; diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp index f774c3ac6..70e68ad8a 100644 --- a/src/dos/dos.cpp +++ b/src/dos/dos.cpp @@ -148,8 +148,6 @@ Bit16u DOS_PRIVATE_SEGMENT_END=0;//0xd000; Bitu DOS_PRIVATE_SEGMENT_Size=0x800; // 32KB (0x800 pages), mainline DOSBox behavior -bool enable_dummy_environment_block = true; -bool enable_dummy_loadfix_padding = true; bool enable_dummy_device_mcb = true; extern unsigned int MAXENV;// = 32768u; @@ -403,6 +401,9 @@ void DOS_BreakAction() { * --J.C. */ bool disk_io_unmask_irq0 = true; +//! \brief Is a DOS program running ? (set by INT21 4B/4C) +bool dos_program_running = false; + #define DOSNAMEBUF 256 static Bitu DOS_21Handler(void) { bool unmask_irq0 = false; @@ -423,6 +424,7 @@ static Bitu DOS_21Handler(void) { case 0x00: /* Terminate Program */ DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0); if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ + dos_program_running = false; break; case 0x01: /* Read character from STDIN, with echo */ { @@ -978,6 +980,7 @@ static Bitu DOS_21Handler(void) { DOS_ResizeMemory(dos.psp(),®_dx); DOS_Terminate(dos.psp(),true,reg_al); if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ + dos_program_running = false; break; case 0x1f: /* Get drive parameter block for default drive */ case 0x32: /* Get drive parameter block for specific drive */ @@ -1345,12 +1348,14 @@ static Bitu DOS_21Handler(void) { reg_ax=dos.errorcode; CALLBACK_SCF(true); } + dos_program_running = true; } break; //TODO Check for use of execution state AL=5 case 0x4c: /* EXIT Terminate with return code */ DOS_Terminate(dos.psp(),false,reg_al); if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ + dos_program_running = false; break; case 0x4d: /* Get Return code */ reg_al=dos.return_code;/* Officially read from SDA and clear when read */ @@ -1864,11 +1869,9 @@ static Bitu DOS_26Handler(void) { return CBRET_NONE; } -extern bool mainline_compatible_mapping; bool iret_only_for_debug_interrupts = true; bool enable_collating_uppercase = true; bool keep_private_area_on_boot = false; -bool dynamic_dos_kernel_alloc = false; bool private_always_from_umb = false; bool private_segment_in_umb = true; Bit16u DOS_IHSEG = 0; @@ -1988,7 +1991,6 @@ public: minimum_mcb_segment = section->Get_hex("minimum mcb segment"); private_segment_in_umb = section->Get_bool("private area in umb"); enable_collating_uppercase = section->Get_bool("collating and uppercase"); - dynamic_dos_kernel_alloc = section->Get_bool("dynamic kernel allocation"); private_always_from_umb = section->Get_bool("kernel allocation in umb"); minimum_dos_initial_private_segment = section->Get_hex("minimum dos initial private segment"); dos_con_use_int16_to_detect_input = section->Get_bool("con device use int 16h to detect keyboard input"); @@ -1996,8 +1998,6 @@ public: dbg_zero_on_dos_allocmem = section->Get_bool("zero memory on int 21h memory allocation"); MAXENV = (unsigned int)section->Get_int("maximum environment block size on exec"); ENV_KEEPFREE = (unsigned int)section->Get_int("additional environment block size on exec"); - enable_dummy_environment_block = section->Get_bool("enable dummy environment block"); - enable_dummy_loadfix_padding = section->Get_bool("enable loadfix padding"); enable_dummy_device_mcb = section->Get_bool("enable dummy device mcb"); int15_wait_force_unmask_irq = section->Get_bool("int15 wait force unmask irq"); disk_io_unmask_irq0 = section->Get_bool("unmask timer on disk io"); @@ -2047,8 +2047,8 @@ public: } } - if ((int)MAXENV < 0) MAXENV = mainline_compatible_mapping ? 32768 : 65535; - if ((int)ENV_KEEPFREE < 0) ENV_KEEPFREE = mainline_compatible_mapping ? 83 : 1024; + if ((int)MAXENV < 0) MAXENV = 65535; + if ((int)ENV_KEEPFREE < 0) ENV_KEEPFREE = 1024; LOG(LOG_MISC,LOG_DEBUG)("DOS: MAXENV=%u ENV_KEEPFREE=%u",MAXENV,ENV_KEEPFREE); @@ -2059,105 +2059,57 @@ public: LOG_MSG("Debug option enabled: INT 21h memory allocation will always clear memory block before returning\n"); } - if (!dynamic_dos_kernel_alloc || mainline_compatible_mapping) { - LOG_MSG("kernel allocation in umb option incompatible with other settings, disabling.\n"); - private_always_from_umb = false; - } - if (minimum_mcb_segment > 0x8000) minimum_mcb_segment = 0x8000; /* FIXME: Clip against available memory */ - if (dynamic_dos_kernel_alloc) { - /* we make use of the DOS_GetMemory() function for the dynamic allocation */ - if (mainline_compatible_mapping) { - DOS_IHSEG = 0x70; - DOS_PRIVATE_SEGMENT = 0x80; + /* we make use of the DOS_GetMemory() function for the dynamic allocation */ + if (private_always_from_umb) { + DOS_GetMemory_Choose(); /* the pool starts in UMB */ + if (minimum_mcb_segment == 0) + DOS_MEM_START = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ + else + DOS_MEM_START = minimum_mcb_segment; - if (IS_PC98_ARCH) - LOG_MSG("WARNING: mainline compatible mapping is not recommended for PC-98 emulation"); - } - else if (private_always_from_umb) { - DOS_GetMemory_Choose(); /* the pool starts in UMB */ - if (minimum_mcb_segment == 0) - DOS_MEM_START = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ - else - DOS_MEM_START = minimum_mcb_segment; + if (DOS_MEM_START < 0x40) + LOG_MSG("DANGER, DANGER! DOS_MEM_START has been set to within the interrupt vector table! Proceed at your own risk!"); + else if (DOS_MEM_START < 0x50) + LOG_MSG("WARNING: DOS_MEM_START has been assigned to the BIOS data area! Proceed at your own risk!"); + else if (DOS_MEM_START < 0x51) + LOG_MSG("WARNING: DOS_MEM_START has been assigned to segment 0x50, which some programs may use as the Print Screen flag"); + else if (DOS_MEM_START < 0x80 && IS_PC98_ARCH) + LOG_MSG("CAUTION: DOS_MEM_START is less than 0x80 which may cause problems with some DOS games or applications relying on PC-98 BIOS state"); + else if (DOS_MEM_START < 0x70) + LOG_MSG("CAUTION: DOS_MEM_START is less than 0x70 which may cause problems with some DOS games or applications"); + } + else { + if (minimum_dos_initial_private_segment == 0) + DOS_PRIVATE_SEGMENT = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ + else + DOS_PRIVATE_SEGMENT = minimum_dos_initial_private_segment; - if (DOS_MEM_START < 0x40) - LOG_MSG("DANGER, DANGER! DOS_MEM_START has been set to within the interrupt vector table! Proceed at your own risk!"); - else if (DOS_MEM_START < 0x50) - LOG_MSG("WARNING: DOS_MEM_START has been assigned to the BIOS data area! Proceed at your own risk!"); - else if (DOS_MEM_START < 0x51) - LOG_MSG("WARNING: DOS_MEM_START has been assigned to segment 0x50, which some programs may use as the Print Screen flag"); - else if (DOS_MEM_START < 0x80 && IS_PC98_ARCH) - LOG_MSG("CAUTION: DOS_MEM_START is less than 0x80 which may cause problems with some DOS games or applications relying on PC-98 BIOS state"); - else if (DOS_MEM_START < 0x70) - LOG_MSG("CAUTION: DOS_MEM_START is less than 0x70 which may cause problems with some DOS games or applications"); - } - else { - if (minimum_dos_initial_private_segment == 0) - DOS_PRIVATE_SEGMENT = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ - else - DOS_PRIVATE_SEGMENT = minimum_dos_initial_private_segment; + if (DOS_PRIVATE_SEGMENT < 0x50) + LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low!"); + if (DOS_PRIVATE_SEGMENT < 0x80 && IS_PC98_ARCH) + LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low for PC-98 emulation!"); - if (DOS_PRIVATE_SEGMENT < 0x50) - LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low!"); - if (DOS_PRIVATE_SEGMENT < 0x80 && IS_PC98_ARCH) - LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low for PC-98 emulation!"); - } + if (MEM_TotalPages() > 0x9C) + DOS_PRIVATE_SEGMENT_END = 0x9C00; + else + DOS_PRIVATE_SEGMENT_END = (MEM_TotalPages() << (12 - 4)) - 1; /* NTS: Remember DOSBox's implementation reuses the last paragraph for UMB linkage */ + } - if (!private_always_from_umb) { - if (MEM_TotalPages() > 0x9C) - DOS_PRIVATE_SEGMENT_END = 0x9C00; - else - DOS_PRIVATE_SEGMENT_END = (MEM_TotalPages() << (12 - 4)) - 1; /* NTS: Remember DOSBox's implementation reuses the last paragraph for UMB linkage */ - } + LOG(LOG_MISC,LOG_DEBUG)("DOS kernel structures will be allocated from pool 0x%04x-0x%04x", + DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1); - LOG(LOG_MISC,LOG_DEBUG)("Dynamic DOS kernel mode, structures will be allocated from pool 0x%04x-0x%04x", - DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1); + DOS_IHSEG = DOS_GetMemory(1,"DOS_IHSEG"); - if (!mainline_compatible_mapping) DOS_IHSEG = DOS_GetMemory(1,"DOS_IHSEG"); + /* DOS_INFOBLOCK_SEG contains the entire List of Lists, though the INT 21h call returns seg:offset with offset nonzero */ + DOS_INFOBLOCK_SEG = DOS_GetMemory(0xC0,"DOS_INFOBLOCK_SEG"); // was 0x80 - /* DOS_INFOBLOCK_SEG contains the entire List of Lists, though the INT 21h call returns seg:offset with offset nonzero */ - DOS_INFOBLOCK_SEG = DOS_GetMemory(0xC0,"DOS_INFOBLOCK_SEG"); // was 0x80 - - DOS_CONDRV_SEG = DOS_GetMemory(0x08,"DOS_CONDRV_SEG"); // was 0xA0 - DOS_CONSTRING_SEG = DOS_GetMemory(0x0A,"DOS_CONSTRING_SEG"); // was 0xA8 - DOS_SDA_SEG = DOS_GetMemory(DOS_SDA_SEG_SIZE>>4,"DOS_SDA_SEG"); // was 0xB2 (0xB2 + 0x56 = 0x108) - DOS_SDA_OFS = 0; - DOS_CDS_SEG = DOS_GetMemory(0x10,"DOS_CDA_SEG"); // was 0x108 - } - else { - if (MEM_TotalPages() < 2) E_Exit("Not enough RAM for mainline compatible fixed kernel mapping"); - - DOS_IHSEG = 0x70; - DOS_INFOBLOCK_SEG = 0x80; // sysvars (list of lists) - DOS_CONDRV_SEG = 0xa0; - DOS_CONSTRING_SEG = 0xa8; - DOS_SDA_SEG = 0xb2; // dos swappable area - DOS_SDA_SEG_SIZE = 0x560; - DOS_SDA_OFS = 0; - DOS_CDS_SEG = 0x108; - - if (!private_segment_in_umb) { - /* If private segment is not being placed in UMB, then it must follow the DOS kernel. */ - unsigned int seg; - unsigned int segend; - - seg = DOS_MEM_START; - DOS_MEM_START += DOS_PRIVATE_SEGMENT_Size; - segend = DOS_MEM_START; - - if (segend >= (MEM_TotalPages() << (12 - 4))) - E_Exit("Insufficient room for private area"); - - DOS_PRIVATE_SEGMENT = seg; - DOS_PRIVATE_SEGMENT_END = segend; - DOS_MEM_START = DOS_PRIVATE_SEGMENT_END; - DOS_GetMemory_reset(); - LOG(LOG_MISC,LOG_DEBUG)("Private area, not stored in UMB on request, occupies 0x%04x-0x%04x", - DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1); - } - } + DOS_CONDRV_SEG = DOS_GetMemory(0x08,"DOS_CONDRV_SEG"); // was 0xA0 + DOS_CONSTRING_SEG = DOS_GetMemory(0x0A,"DOS_CONSTRING_SEG"); // was 0xA8 + DOS_SDA_SEG = DOS_GetMemory(DOS_SDA_SEG_SIZE>>4,"DOS_SDA_SEG"); // was 0xB2 (0xB2 + 0x56 = 0x108) + DOS_SDA_OFS = 0; + DOS_CDS_SEG = DOS_GetMemory(0x10,"DOS_CDA_SEG"); // was 0x108 LOG(LOG_MISC,LOG_DEBUG)("DOS kernel alloc:"); LOG(LOG_MISC,LOG_DEBUG)(" IHSEG: seg 0x%04x",DOS_IHSEG); @@ -2243,17 +2195,12 @@ public: DOS_SetupDevices(); /* Setup dos devices */ DOS_SetupTables(); - /* having allowed the setup functions so far to alloc private space, set the pointer as the base - * of memory. the DOS_SetupMemory() function will finalize it into the first MCB. Having done that, - * we then need to move the DOS private segment somewere else so that additional allocations do not - * corrupt the MCB chain */ - if (dynamic_dos_kernel_alloc && !private_always_from_umb) - DOS_MEM_START = DOS_GetMemory(0,"DOS_MEM_START"); // was 0x158 (pass 0 to alloc nothing, get the pointer) - /* move the private segment elsewhere to avoid conflict with the MCB structure. * either set to 0 to cause the decision making to choose an upper memory address, * or allocate an additional private area and start the MCB just after that */ - if (dynamic_dos_kernel_alloc && !private_always_from_umb) { + if (!private_always_from_umb) { + DOS_MEM_START = DOS_GetMemory(0,"DOS_MEM_START"); // was 0x158 (pass 0 to alloc nothing, get the pointer) + DOS_GetMemory_reset(); DOS_PRIVATE_SEGMENT = 0; DOS_PRIVATE_SEGMENT_END = 0; diff --git a/src/dos/dos_memory.cpp b/src/dos/dos_memory.cpp index 9754d44a4..3a872e592 100644 --- a/src/dos/dos_memory.cpp +++ b/src/dos/dos_memory.cpp @@ -518,8 +518,6 @@ static Bitu DOS_default_handler(void) { extern Bit16u DOS_IHSEG; -extern bool enable_dummy_environment_block; -extern bool enable_dummy_loadfix_padding; extern bool enable_dummy_device_mcb; extern bool iret_only_for_debug_interrupts; @@ -566,32 +564,19 @@ void DOS_SetupMemory(void) { if (enable_dummy_device_mcb) { // Create a dummy device MCB with PSPSeg=0x0008 + LOG_MSG("Dummy device MCB at segment 0x%x",DOS_MEM_START+mcb_sizes); DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START+mcb_sizes); mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices - mcb_devicedummy.SetSize(1); + mcb_devicedummy.SetSize(16); mcb_devicedummy.SetType(0x4d); // More blocks will follow - mcb_sizes+=1+1; + mcb_sizes+=1+16; + +// We DO need to mark this area as 'SD' but leaving it blank so far +// confuses MEM.EXE (shows ???????) which suggests other software +// might have a problem with it as well. // mcb_devicedummy.SetFileName("SD "); } - if (enable_dummy_environment_block) { - // Create a small empty MCB (result from a growing environment block) - DOS_MCB tempmcb((Bit16u)DOS_MEM_START+mcb_sizes); - tempmcb.SetPSPSeg(MCB_FREE); - tempmcb.SetSize(4); - mcb_sizes+=4+1; - tempmcb.SetType(0x4d); - } - - if (enable_dummy_loadfix_padding) { - // Lock the previous empty MCB - DOS_MCB tempmcb2((Bit16u)DOS_MEM_START+mcb_sizes); - tempmcb2.SetPSPSeg(0x40); // can be removed by loadfix - tempmcb2.SetSize(16); - mcb_sizes+=16+1; - tempmcb2.SetType(0x4d); - } - DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes); mcb.SetPSPSeg(MCB_FREE); //Free mcb.SetType(0x5a); //Last Block diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index 424ffedf1..2d33495c2 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -546,6 +546,7 @@ static void SHOWGUI_ProgramStart(Program * * make) { } #endif +extern bool custom_bios; extern Bit32u floppytype; extern bool dos_kernel_disabled; extern bool boot_debug_break; @@ -691,6 +692,8 @@ public: /*! \brief Program entry point, when the command is run */ void Run(void) { + std::string bios; + bool bios_boot = false; bool swaponedrive = false; bool force = false; @@ -704,6 +707,9 @@ public: if (cmd->FindExist("-force",true)) force = true; + if (cmd->FindString("-bios",bios,true)) + bios_boot = true; + //Hack To allow long commandlines ChangeToLongCmd(); /* In secure mode don't allow people to boot stuff. @@ -713,6 +719,51 @@ public: return; } + if (bios_boot) { + Bit32u isz1,isz2; + + if (bios.empty()) { + WriteOut("Must specify BIOS image to boot\n"); + return; + } + + // NOTES: + // + // Regarding PC-98 mode, you should use an older BIOS image. + // The PC-9821 ROM image(s) I have appear to rely on bank + // switching parts of itself to boot up and operate. + // + // Update: I found some PC-9801 ROM BIOS images online, which + // ALSO seem to have a BIOS.ROM, ITF.ROM, etc... + // + // So, this command will not be able to run those + // images until port 43Dh (the I/O port used for + // bank switching) is implemented in DOSBox-X. + // + // In IBM PC/AT mode, this should hopefully allow using old + // 386/486 BIOSes in DOSBox-X. + + /* load it */ + FILE *romfp = getFSFile(bios.c_str(), &isz1, &isz2); + if (romfp == NULL) { + WriteOut("Unable to open BIOS image\n"); + return; + } + Bitu loadsz = (isz2 + 0xFU) & (~0xFU); + if (loadsz == 0) loadsz = 0x10; + if (loadsz > (IS_PC98_ARCH ? 0x18000 : 0x20000)) loadsz = (IS_PC98_ARCH ? 0x18000 : 0x20000); + Bitu segbase = 0x100000 - loadsz; + LOG_MSG("Loading BIOS image %s to 0x%lx, 0x%lx bytes",bios.c_str(),(unsigned long)segbase,(unsigned long)loadsz); + fseek(romfp, 0, SEEK_SET); + fread(GetMemBase()+segbase,loadsz,1,romfp); + fclose(romfp); + + custom_bios = true; + + /* boot it */ + throw int(8); + } + bool bootbyDrive=false; FILE *usefile_1=NULL; FILE *usefile_2=NULL; @@ -1799,7 +1850,7 @@ restart_int: WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),temp_line.c_str()); return; } - if(fseeko64(f,(off_t)(size-1ull),SEEK_SET)) { + if(fseeko64(f,static_cast(size - 1ull),SEEK_SET)) { WriteOut(MSG_Get("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE"),size); return; } diff --git a/src/dos/dos_tables.cpp b/src/dos/dos_tables.cpp index bba9708a5..497cfc083 100644 --- a/src/dos/dos_tables.cpp +++ b/src/dos/dos_tables.cpp @@ -44,8 +44,6 @@ GCC_ATTRIBUTE (packed); RealPt DOS_TableUpCase; RealPt DOS_TableLowCase; -extern bool mainline_compatible_mapping; - static Bitu call_casemap = 0; void DOS_Casemap_Free(void) { @@ -82,17 +80,10 @@ void DOS_GetMemory_unmap() { void DOS_GetMemory_Choose() { if (DOS_PRIVATE_SEGMENT == 0) { - if (mainline_compatible_mapping) { - /* DOSBox mainline compatible: private area 0xC800-0xCFFF */ - DOS_PRIVATE_SEGMENT=0xc800; - DOS_PRIVATE_SEGMENT_END=0xc800 + DOS_PRIVATE_SEGMENT_Size; - } - else { - /* DOSBox-X non-compatible: Position ourself just past the VGA BIOS */ - /* NTS: Code has been arranged so that DOS kernel init follows BIOS INT10h init */ - DOS_PRIVATE_SEGMENT=VGA_BIOS_SEG_END; - DOS_PRIVATE_SEGMENT_END=DOS_PRIVATE_SEGMENT + DOS_PRIVATE_SEGMENT_Size; - } + /* DOSBox-X non-compatible: Position ourself just past the VGA BIOS */ + /* NTS: Code has been arranged so that DOS kernel init follows BIOS INT10h init */ + DOS_PRIVATE_SEGMENT=VGA_BIOS_SEG_END; + DOS_PRIVATE_SEGMENT_END=DOS_PRIVATE_SEGMENT + DOS_PRIVATE_SEGMENT_Size; if (DOS_PRIVATE_SEGMENT >= 0xA000) { memset(GetMemBase()+((unsigned int)DOS_PRIVATE_SEGMENT<<4u),0x00,(unsigned int)(DOS_PRIVATE_SEGMENT_END-DOS_PRIVATE_SEGMENT)<<4u); diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 294d5b1e0..8613f34d9 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -125,7 +125,6 @@ extern bool VIDEO_BIOS_always_carry_14_high_font; extern bool VIDEO_BIOS_always_carry_16_high_font; extern bool VIDEO_BIOS_enable_CGA_8x8_second_half; extern bool allow_more_than_640kb; -extern bool adapter_rom_is_ram; bool dos_con_use_int16_to_detect_input = true; @@ -154,19 +153,6 @@ ClockDomain clockdom_ISA_OSC(NTSC_COLOR_SUBCARRIER_NUM*4,NTSC_COLOR_SUBC * PCI bus systems: PCI bus clock 33MHz / 4 = 8.333MHz (especially Intel chipsets according to PIIX datasheets) */ ClockDomain clockdom_ISA_BCLK(25000000,3); /* MASTER 25000000Hz / 3 = 8.333333MHz */ -/* 8254 PIT. slave to a clock determined by motherboard. - * PC/XT: slave to ISA busclock (4.77MHz / 4) = 1.193181MHz - * AT/later: ISA oscillator clock (14.31818MHz / 12) */ -/* 14.1818MHz / 12 == (NTSC * 4) / 12 == (NTSC * 4) / (4*3) == NTSC / 3 */ -ClockDomain clockdom_8254_PIT(NTSC_COLOR_SUBCARRIER_NUM,NTSC_COLOR_SUBCARRIER_DEN*3); - -/* 8250 UART. - * PC/XT: ??? What did IBM use on the motherboard to drive the UART? Is it some divisor of the ISA OSC clock?? Closest I can calculate: 14.31818MHz / 8 = 1.78MHz. - * Other hardware (guess): Independent clock crystal: 115200 * 16 = 1843200Hz = 1.843200MHz based on datasheet (http://www.ti.com/lit/ds/symlink/pc16550d.pdf) - * - * Feel free to correct me if I'm wrong. */ -ClockDomain clockdom_8250_UART(115200 * 16); - Config* control; MachineType machine; bool PS1AudioCard; // Perhaps have PS1 as a machine type...? @@ -177,8 +163,6 @@ Bit32u ticksScheduled; bool ticksLocked; bool mono_cga=false; bool ignore_opcode_63 = true; -bool mainline_compatible_mapping = true; -bool mainline_compatible_bios_mapping = true; int dynamic_core_cache_block_size = 32; Bitu VGA_BIOS_Size_override = 0; Bitu VGA_BIOS_SEG = 0xC000; @@ -292,12 +276,6 @@ unsigned long long update_ISA_OSC_clock() { return update_clockdom_from_now(clockdom_ISA_OSC); } -/* for PIT emulation. The PIT ticks at exactly 1/12 the ISA OSC clock. */ -unsigned long long update_8254_PIT_clock() { - clockdom_8254_PIT.counter = update_ISA_OSC_clock() / 12ULL; - return clockdom_8254_PIT.counter; -} - /* for ISA components */ unsigned long long update_ISA_BCLK_clock() { return update_clockdom_from_now(clockdom_ISA_BCLK); @@ -654,8 +632,8 @@ void Init_VGABIOS() { VIDEO_BIOS_always_carry_16_high_font = section->Get_bool("video bios always offer 16-pixel high rom font"); VIDEO_BIOS_enable_CGA_8x8_second_half = section->Get_bool("video bios enable cga second half rom font"); /* NTS: mainline compatible mapping demands the 8x8 CGA font */ - rom_bios_8x8_cga_font = mainline_compatible_bios_mapping || section->Get_bool("rom bios 8x8 CGA font"); - rom_bios_vptable_enable = mainline_compatible_bios_mapping || section->Get_bool("rom bios video parameter table"); + rom_bios_8x8_cga_font = section->Get_bool("rom bios 8x8 CGA font"); + rom_bios_vptable_enable = section->Get_bool("rom bios video parameter table"); /* sanity check */ if (VGA_BIOS_dont_duplicate_CGA_first_half && !rom_bios_8x8_cga_font) /* can't point at the BIOS copy if it's not there */ @@ -664,19 +642,15 @@ void Init_VGABIOS() { if (VGA_BIOS_Size_override >= 512 && VGA_BIOS_Size_override <= 65536) VGA_BIOS_Size = (VGA_BIOS_Size_override + 0x7FFU) & (~0xFFFU); else if (IS_VGA_ARCH) - VGA_BIOS_Size = mainline_compatible_mapping ? 0x8000 : 0x3000; /* <- Experimentation shows the S3 emulation can fit in 12KB, doesn't need all 32KB */ + VGA_BIOS_Size = 0x3000; /* <- Experimentation shows the S3 emulation can fit in 12KB, doesn't need all 32KB */ else if (machine == MCH_EGA) { - if (mainline_compatible_mapping) - VGA_BIOS_Size = 0x8000; - else if (VIDEO_BIOS_always_carry_16_high_font) + if (VIDEO_BIOS_always_carry_16_high_font) VGA_BIOS_Size = 0x3000; else VGA_BIOS_Size = 0x2000; } else { - if (mainline_compatible_mapping) - VGA_BIOS_Size = 0x8000; - else if (VIDEO_BIOS_always_carry_16_high_font && VIDEO_BIOS_always_carry_14_high_font) + if (VIDEO_BIOS_always_carry_16_high_font && VIDEO_BIOS_always_carry_14_high_font) VGA_BIOS_Size = 0x3000; else if (VIDEO_BIOS_always_carry_16_high_font || VIDEO_BIOS_always_carry_14_high_font) VGA_BIOS_Size = 0x2000; @@ -718,10 +692,6 @@ void DOSBOX_RealInit() { // TODO: these should be parsed by DOS kernel at startup dosbox_shell_env_size = (unsigned int)section->Get_int("shell environment size"); - /* these ARE general DOSBox configuration options */ - mainline_compatible_mapping = section->Get_bool("mainline compatible mapping"); - adapter_rom_is_ram = section->Get_bool("adapter rom is ram"); - // TODO: a bit of a challenge: if we put it in the ROM area as mainline DOSBox does then the init // needs to read this from the BIOS where it can map the memory appropriately. if the allocation // is dynamic and the private area is down at the base of memory like real DOS, then the BIOS @@ -732,7 +702,6 @@ void DOSBOX_RealInit() { DOS_PRIVATE_SEGMENT_Size = (Bitu)((section->Get_int("private area size") + 8) / 16); // TODO: these should be parsed by BIOS startup - mainline_compatible_bios_mapping = section->Get_bool("mainline compatible bios mapping"); allow_more_than_640kb = section->Get_bool("allow more than 640kb base memory"); // TODO: should be parsed by motherboard emulation @@ -816,8 +785,6 @@ void DOSBOX_RealInit() { parse_busclk_setting_str(&clockdom_PCI_BCLK,pcibclk.c_str()); clockdom_ISA_OSC.set_name("ISA OSC"); - clockdom_8254_PIT.set_name("8254 PIT"); - clockdom_8250_UART.set_name("8250 UART"); clockdom_ISA_BCLK.set_name("ISA BCLK"); clockdom_PCI_BCLK.set_name("PCI BCLK"); @@ -868,6 +835,8 @@ void DOSBOX_SetupConfigSections(void) { const char* guspantables[] = { "old", "accurate", "default", 0 }; const char *sidbaseno[] = { "240", "220", "260", "280", "2a0", "2c0", "2e0", "300", 0 }; const char* joytypes[] = { "auto", "2axis", "4axis", "4axis_2", "fcs", "ch", "none",0}; + const char* joydeadzone[] = { "0.26", 0 }; + const char* joyresponse[] = { "1.0", 0 }; const char* iosgus[] = { "240", "220", "260", "280", "2a0", "2c0", "2e0", "300", "210", "230", "250", 0 }; const char* ios[] = { "220", "240", "260", "280", "2a0", "2c0", "2e0", "300", 0 }; const char* ems_settings[] = { "true", "emsboard", "emm386", "false", 0}; @@ -906,6 +875,9 @@ void DOSBOX_SetupConfigSections(void) { "tv2x", "tv3x", "rgb2x", "rgb3x", "scan2x", "scan3x", #endif "hardware_none", "hardware2x", "hardware3x", "hardware4x", "hardware5x", +#if C_XBRZ + "xbrz", "xbrz_bilinear", +#endif 0 }; const char* cores[] = { "auto", @@ -944,6 +916,7 @@ void DOSBOX_SetupConfigSections(void) { Pbool = secprop->Add_bool("keyboard hook", Property::Changeable::Always, false); Pbool->Set_help("Use keyboard hook (currently only on Windows) to catch special keys and synchronize the keyboard LEDs with the host"); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Pbool = secprop->Add_bool("weitek",Property::Changeable::WhenIdle,false); Pbool->Set_help("If set, emulate the Weitek coprocessor. This option only has effect if cputype=386 or cputype=486."); @@ -1002,19 +975,6 @@ void DOSBOX_SetupConfigSections(void) { "mpegts-h264 Use MPEG transport stream + H.264 + AAC audio. Resolution & refresh rate changes can be contained\n" " within one file with this choice, however not all software can support mid-stream format changes."); - Pbool = secprop->Add_bool("mainline compatible mapping",Property::Changeable::OnlyAtStart,false); - Pbool->Set_help("If set, arrange private areas, UMBs, and DOS kernel structures by default in the same way the mainline branch would do it.\n" - "If cleared, these areas are allocated dynamically which may improve available memory and emulation accuracy.\n" - "If your DOS game breaks under DOSBox-X but works with mainline DOSBox setting this option may help."); - - Pbool = secprop->Add_bool("mainline compatible bios mapping",Property::Changeable::OnlyAtStart,false); - Pbool->Set_help("If set, arrange the BIOS area in the same way that the mainline branch would do it.\n" - "If cleared, these areas are allocated dynamically which may improve available memory and emulation accuracy.\n" - "If your DOS game breaks under DOSBox-X but works with mainline DOSBox setting this option may help."); - - Pbool = secprop->Add_bool("adapter rom is ram",Property::Changeable::OnlyAtStart,false); - Pbool->Set_help("Map adapter ROM as RAM (mainline DOSBox 0.74 behavior). When clear, unused adapter ROM is mapped out"); - Pint = secprop->Add_int("shell environment size",Property::Changeable::OnlyAtStart,0); Pint->SetMinMax(0,65280); Pint->Set_help("Size of the initial DOSBox shell environment block, in bytes. This does not affect the environment block of sub-processes spawned from the shell.\n" @@ -1048,7 +1008,7 @@ void DOSBOX_SetupConfigSections(void) { "If disabled, and MS-DOS does not load HIMEM.SYS, programs and features that rely on the 1MB wraparound will fail."); Pstring = secprop->Add_string("isa bus clock",Property::Changeable::WhenIdle,"std8.3"); - Pstring->Set_help("ISA BCLK frequency.\n" + Pstring->Set_help("ISA BCLK frequency, used to emulate I/O delay.\n" "WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime.\n" " std8.3 8.333MHz (typical 386-class or higher)\n" " std8 8MHz\n" @@ -1062,7 +1022,7 @@ void DOSBOX_SetupConfigSections(void) { " If a ratio is given (num/den), the ratio will be used as the clock frequency"); Pstring = secprop->Add_string("pci bus clock",Property::Changeable::WhenIdle,"std33.3"); - Pstring->Set_help("PCI bus frequency.\n" + Pstring->Set_help("PCI bus frequency, used to emulate I/O delay.\n" "WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime.\n" " std33.3 33.333MHz (very common setting on motherboards)\n" " std30 30MHz (some older mid-1990's Pentium systems)\n" @@ -1109,6 +1069,7 @@ void DOSBOX_SetupConfigSections(void) { Pint->SetMinMax(-1,100000); Pint->Set_help( "I/O delay for 32-bit transfers. -1 to use default, 0 to disable."); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Pstring = secprop->Add_string("acpi", Property::Changeable::OnlyAtStart,"off"); Pstring->Set_values(acpisettings); Pstring->Set_help("ACPI emulation, and what version of the specification to follow.\n" @@ -1116,16 +1077,20 @@ void DOSBOX_SetupConfigSections(void) { " Intended for use with ACPI-aware OSes including Linux and Windows 98/ME. This option will also slightly reduce available\n" " system memory to make room for the ACPI tables, just as real BIOSes do, and reserve an IRQ for ACPI functions."); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Pstring = secprop->Add_string("acpi rsd ptr location", Property::Changeable::OnlyAtStart,"auto"); Pstring->Set_values(acpi_rsd_ptr_settings); Pstring->Set_help("Where to store the Root System Description Pointer structure. You can have it stored in the ROM BIOS area, or the Extended Bios Data Area."); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Pint = secprop->Add_int("acpi sci irq", Property::Changeable::WhenIdle,-1); Pint->Set_help("IRQ to assign as ACPI system control interrupt. set to -1 to automatically assign."); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Phex = secprop->Add_hex("acpi iobase",Property::Changeable::WhenIdle,0); Phex->Set_help("I/O port base for the ACPI device Power Management registers. Set to 0 for automatic assignment."); + // STUB OPTION, NOT YET FULLY IMPLEMENTED Pint = secprop->Add_int("acpi reserved size", Property::Changeable::WhenIdle,0); Pint->Set_help("Amount of memory at top to reserve for ACPI structures and tables. Set to 0 for automatic assignment."); @@ -1304,7 +1269,7 @@ void DOSBOX_SetupConfigSections(void) { Pbool->Set_help("If set (default), allow the application to reset the CPU through port 92h"); Pbool = secprop->Add_bool("enable port 92",Property::Changeable::WhenIdle,true); - Pbool->Set_help("Emulate port 92h (PS/2 system control port A). If you want to emulate a system that predates the PS/2, set to 0."); + Pbool->Set_help("Emulate port 92h (PS/2 system control port A). If you want to emulate a system that pre-dates the PS/2, set to 0."); Pbool = secprop->Add_bool("enable 1st dma controller",Property::Changeable::WhenIdle,true); Pbool->Set_help("Emulate 1st (AT) DMA controller (default). Set to 0 if you wish to emulate a system that lacks DMA (PCjr and some Tandy systems)"); @@ -1444,6 +1409,15 @@ void DOSBOX_SetupConfigSections(void) { Pbool = secprop->Add_bool("enable pci bus",Property::Changeable::OnlyAtStart,true); Pbool->Set_help("Enable PCI bus emulation"); + Pbool = secprop->Add_bool("vga palette update on full load",Property::Changeable::Always,true); + Pbool->Set_help("If set, all three bytes of the palette entry must be loaded before taking the color,\n" + "which is fairly typical SVGA behavior. If not set, partial changes are allowed."); + + Pbool = secprop->Add_bool("ignore odd-even mode in non-cga modes",Property::Changeable::Always,false); + Pbool->Set_help("Some demoscene productions use VGA Mode X but accidentally enable odd/even mode.\n" + "Setting this option can correct for that and render the demo properly.\n" + "This option forces VGA emulation to ignore odd/even mode except in text and CGA modes."); + secprop=control->AddSection_prop("render",&Null_Init,true); Pint = secprop->Add_int("frameskip",Property::Changeable::Always,0); Pint->SetMinMax(0,10); @@ -1473,6 +1447,20 @@ void DOSBOX_SetupConfigSections(void) { Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,""); Pstring->Set_values(force); +#if C_XBRZ + Pint = secprop->Add_int("xbrz slice",Property::Changeable::OnlyAtStart,16); + Pint->SetMinMax(1,1024); + Pint->Set_help("Number of screen lines to process in single xBRZ scaler taskset task, affects xBRZ performance, 16 is the default"); + + Pint = secprop->Add_int("xbrz fixed scale factor",Property::Changeable::OnlyAtStart, 0); + Pint->SetMinMax(0,6); + Pint->Set_help("To use fixed xBRZ scale factor (i.e. to attune performance), set it to 2-6, 0 - use automatic calculation (default)"); + + Pint = secprop->Add_int("xbrz max scale factor",Property::Changeable::OnlyAtStart, 0); + Pint->SetMinMax(0,6); + Pint->Set_help("To cap maximum xBRZ scale factor used (i.e. to attune performance), set it to 2-6, 0 - use scaler allowed maximum (default)"); +#endif + Pbool = secprop->Add_bool("autofit",Property::Changeable::Always,true); Pbool->Set_help( "Best fits image to window\n" @@ -1585,7 +1573,7 @@ void DOSBOX_SetupConfigSections(void) { Pbool->Set_help("When debugging, do not report illegal opcode 0x63.\n" "Enable this option to ignore spurious errors while debugging from within Windows 3.1/9x/ME"); - Pbool = secprop->Add_bool("apmbios",Property::Changeable::WhenIdle,false); + Pbool = secprop->Add_bool("apmbios",Property::Changeable::WhenIdle,true); Pbool->Set_help("Emulate Advanced Power Management BIOS calls"); Pbool = secprop->Add_bool("apmbios pnp",Property::Changeable::WhenIdle,false); @@ -1624,7 +1612,7 @@ void DOSBOX_SetupConfigSections(void) { "then jump to realmode with B still set (aka Huge Unreal mode). Needed for Project Angel."); secprop=control->AddSection_prop("keyboard",&Null_Init); - Pbool = secprop->Add_bool("aux",Property::Changeable::OnlyAtStart,false); + Pbool = secprop->Add_bool("aux",Property::Changeable::OnlyAtStart,true); Pbool->Set_help("Enable emulation of the 8042 auxiliary port. PS/2 mouse emulation requires this to be enabled.\n" "You should enable this if you will be running Windows ME or any other OS that does not use the BIOS to receive mouse events."); @@ -2098,6 +2086,80 @@ void DOSBOX_SetupConfigSections(void) { Pbool = secprop->Add_bool("buttonwrap",Property::Changeable::WhenIdle,false); Pbool->Set_help("enable button wrapping at the number of emulated buttons."); + /*improved joystick + * each axis has its own deadzone and response + * each axis index can be remapped, e.g. fix poor driver mappings + */ + + /* logical axes settings*/ + std::vector sticks = { 2, 1 }; + for (auto i = 0u; i < sticks.size(); i++) + { + const auto count = sticks[i]; + for (auto j = 0; j < count; j++) + { + const auto joy = std::to_string(i + 1); + const auto stick = std::to_string(j + 1); + const auto name = "joy" + joy + "deadzone" + stick; + const auto help = "deadzone for joystick " + joy + " thumbstick " + stick + "."; + Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 0.25); + Pdouble->SetMinMax(0.0, 1.0); + Pdouble->Set_help(help); + } + } + for (auto i = 0u; i < sticks.size(); i++) + { + const auto count = sticks[i]; + for (auto j = 0; j < count; j++) + { + const auto joy = std::to_string(i + 1); + const auto stick = std::to_string(j + 1); + const auto name = "joy" + joy + "response" + stick; + const auto help = "response for joystick " + joy + " thumbstick " + stick + "."; + Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 1.0); + Pdouble->SetMinMax(-5.0, 5.0); + Pdouble->Set_help(help); + } + } + + const auto joysticks = 2; + const auto axes = 8; + for (auto i = 0; i < joysticks; i++) + { + for (auto j = 0; j < axes; j++) + { + const auto joy = std::to_string(i + 1); + const auto axis = std::to_string(j); + const auto propname = "joy" + joy + "axis" + axis; + Pint = secprop->Add_int(propname, Property::Changeable::WhenIdle, j); + Pint->SetMinMax(0, axes - 1); + const auto help = "axis for joystick " + joy + " axis " + axis + "."; + Pint->Set_help(help); + } + } + /*physical axes settings*/ + secprop = control->AddSection_prop("mapper", &Null_Init, true); + + const auto directions = 2; + for (auto i = 0; i < joysticks; i++) + { + for (auto j = 0; j < axes; j++) + { + for (auto k = 0; k < directions; k++) + { + const auto joy = std::to_string(i + 1); + const auto axis = std::to_string(j); + const auto dir = k == 0 ? "-" : "+"; + const auto name = "joy" + joy + "deadzone" + axis + dir; + Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 0.6); + Pdouble->SetMinMax(0.0, 1.0); + const auto help = "deadzone for joystick " + joy + " axis " + axis + dir; + Pdouble->Set_help(help); + } + } + } + + secprop=control->AddSection_prop("serial",&Null_Init,true); Pmulti_remain = secprop->Add_multiremain("serial1",Property::Changeable::WhenIdle," "); @@ -2251,20 +2313,13 @@ void DOSBOX_SetupConfigSections(void) { "be loaded too low in memory. This differs from 'minimum mcb segment' in that this affects\n" "the lowest free block instead of the starting point of the mcb chain."); + // TODO: Enable by default WHEN the 'SD' signature becomes valid, and a valid device list within + // is emulated properly. Pbool = secprop->Add_bool("enable dummy device mcb",Property::Changeable::OnlyAtStart,false); Pbool->Set_help("If set (default), allocate a fake device MCB at the base of conventional memory.\n" "Clearing this option can reclaim a small amount of conventional memory at the expense of\n" "some minor DOS compatibility."); - Pbool = secprop->Add_bool("enable loadfix padding",Property::Changeable::OnlyAtStart,false); - Pbool->Set_help("If set (default), allocate a small 1KB region at the base of conventional memory.\n" - "Clearing this option can reclaim a small amount of conventional memory, but can also\n" - "cause some DOS games to break especially if dynamic kernel allocation is enabled."); - - Pbool = secprop->Add_bool("enable dummy environment block",Property::Changeable::OnlyAtStart,false); - Pbool->Set_help("If set (default), allocate a dummy environment block at the base of conventional memory.\n" - "You can clear this option to reclaim a small amount of conventional memory."); - Pint = secprop->Add_int("maximum environment block size on exec", Property::Changeable::WhenIdle,-1); Pint->SetMinMax(-1,65535); Pint->Set_help("Maximum environment block size to copy for child processes. Set to -1 for default."); @@ -2275,6 +2330,7 @@ void DOSBOX_SetupConfigSections(void) { "If the subprocesses will never add/modify the environment block, you can free up a few additional bytes by setting this to 0.\n" "Set to -1 for default setting."); + // DEPRECATED, REMOVE Pbool = secprop->Add_bool("enable a20 on windows init",Property::Changeable::OnlyAtStart,false); Pbool->Set_help("If set, DOSBox will enable the A20 gate when Windows 3.1/9x broadcasts the INIT message\n" "at startup. Windows 3.1 appears to make assumptions at some key points on startup about\n" @@ -2350,9 +2406,6 @@ void DOSBOX_SetupConfigSections(void) { Pbool->Set_help("If set, dynamic kernel allocation=1, and private area in umb=1, all kernel structures will be allocated from the private area in UMB.\n" "If you intend to run Windows 3.1 in DOSBox, you must set this option to false else Windows 3.1 will not start."); - Pbool = secprop->Add_bool("dynamic kernel allocation",Property::Changeable::OnlyAtStart,true); - Pbool->Set_help("If set, DOS kernel structures are allocated dynamically. If clear, DOS kernel structures are fixed at specific segments (mainline DOSBox behavior)"); - Pbool = secprop->Add_bool("keep umb on boot",Property::Changeable::OnlyAtStart,false); Pbool->Set_help("If emulating UMBs, keep the UMB around after boot (Mainline DOSBox behavior). If clear, UMB is unmapped when you boot an operating system."); @@ -2412,6 +2465,7 @@ void DOSBOX_SetupConfigSections(void) { Pint = secprop->Add_int("files",Property::Changeable::OnlyAtStart,127); Pint->Set_help("Number of file handles available to DOS programs. (equivalent to \"files=\" in config.sys)"); + // DEPRECATED, REMOVE Pbool = secprop->Add_bool("con device use int 16h to detect keyboard input",Property::Changeable::OnlyAtStart,true); Pbool->Set_help("If set, use INT 16h to detect keyboard input (MS-DOS 6.22 behavior). If clear, detect keyboard input by\n" "peeking into the BIOS keyboard buffer (Mainline DOSBox behavior). You will need to set this\n" diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index f2597edfe..6e6762c89 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src/aviwriter "-DRESDIR=\"$(resdir)\"" +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src/aviwriter "-DRESDIR=\"$(resdir)\"" -I$(top_srcdir)/src noinst_LIBRARIES = libgui.a libgui_a_SOURCES = \ diff --git a/src/gui/bitop.cpp b/src/gui/bitop.cpp index 995b6be12..b820041a3 100644 --- a/src/gui/bitop.cpp +++ b/src/gui/bitop.cpp @@ -232,19 +232,19 @@ void self_test(void) { assert(log2(1ull) == 0); assert(log2(0ull) == ~0u); - static_assert(negate(0) == ~0u, "whoops"); - static_assert(negate(1) == ~1u, "whoops"); - static_assert(negate(0) == ~0ul, "whoops"); - static_assert(negate(1) == ~1ul, "whoops"); - static_assert(negate(0) == ~0ull, "whoops"); - static_assert(negate(1) == ~1ull, "whoops"); + static_assert(invert(0) == ~0u, "whoops"); + static_assert(invert(1) == ~1u, "whoops"); + static_assert(invert(0) == ~0ul, "whoops"); + static_assert(invert(1) == ~1ul, "whoops"); + static_assert(invert(0) == ~0ull, "whoops"); + static_assert(invert(1) == ~1ull, "whoops"); - assert(negate(0) == ~0u); - assert(negate(1) == ~1u); - assert(negate(0) == ~0ul); - assert(negate(1) == ~1ul); - assert(negate(0) == ~0ull); - assert(negate(1) == ~1ull); + assert(invert(0) == ~0u); + assert(invert(1) == ~1u); + assert(invert(0) == ~0ul); + assert(invert(1) == ~1ul); + assert(invert(0) == ~0ull); + assert(invert(1) == ~1ull); assert(bitseqlengthandpos(0) == bitseqlengthandpos_ret_t(0,0)); assert(bitseqlengthandpos(1) == bitseqlengthandpos_ret_t(0,1)); diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp index 299c9c8c7..568075d8f 100644 --- a/src/gui/menu.cpp +++ b/src/gui/menu.cpp @@ -1224,8 +1224,10 @@ void DOSBox_RefreshMenu(void) { void DOSBox_CheckOS(int &id, int &major, int &minor) { id=major=minor=0; } +#endif -# if defined(HX_DOS) +#if defined(WIN32) +# if defined(HX_DOS) || !defined(C_SDL2) HWND GetHWND(void) { SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); @@ -1253,26 +1255,6 @@ HWND GetSurfaceHWND(void) { extern void RENDER_CallBack( GFX_CallBackFunctions_t function ); -HWND GetHWND(void) { - SDL_SysWMinfo wmi; - SDL_VERSION(&wmi.version); - - if(!SDL_GetWMInfo(&wmi)) { - return NULL; - } - return wmi.window; -} - -HWND GetSurfaceHWND(void) { - SDL_SysWMinfo wmi; - SDL_VERSION(&wmi.version); - - if (!SDL_GetWMInfo(&wmi)) { - return NULL; - } - return wmi.child_window; -} - void GetDefaultSize(void) { char sizetemp[20]="512,32,32765,"; char sizetemp2[20]=""; diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 76dee94be..34b560e13 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -339,7 +339,9 @@ void RENDER_Reset( void ) { Bitu gfx_flags, xscale, yscale; ScalerSimpleBlock_t *simpleBlock = &ScaleNormal1x; ScalerComplexBlock_t *complexBlock = 0; - if (render.aspect) { + gfx_scalew = 1; + gfx_scaleh = 1; + if (render.aspect && !render.aspectOffload) { if (render.src.ratio>1.0) { gfx_scalew = 1; gfx_scaleh = render.src.ratio; @@ -347,9 +349,6 @@ void RENDER_Reset( void ) { gfx_scalew = (1.0/render.src.ratio); gfx_scaleh = 1; } - } else { - gfx_scalew = 1; - gfx_scaleh = 1; } if ((dblh && dblw) || (render.scale.forced && !dblh && !dblw)) { /* Initialize always working defaults */ @@ -791,6 +790,10 @@ void RENDER_UpdateFromScalerSetting(void) { std::string f = prop->GetSection()->Get_string("force"); std::string scaler = prop->GetSection()->Get_string("type"); +#if C_XBRZ + render.scale.xBRZ = false; +#endif + render.scale.forced = false; if(f == "forced") render.scale.forced = true; @@ -823,6 +826,10 @@ void RENDER_UpdateFromScalerSetting(void) { else if (scaler == "hardware3x") { render.scale.op = scalerOpNormal; render.scale.size = 6; render.scale.hardware=true; } else if (scaler == "hardware4x") { render.scale.op = scalerOpNormal; render.scale.size = 8; render.scale.hardware=true; } else if (scaler == "hardware5x") { render.scale.op = scalerOpNormal; render.scale.size = 10; render.scale.hardware=true; } +#if C_XBRZ + else if (scaler == "xbrz") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware = false; render.scale.xBRZ = true; } + else if (scaler == "xbrz_bilinear") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware = false; render.scale.xBRZ = true; } +#endif } void RENDER_Init() { @@ -872,8 +879,14 @@ void RENDER_Init() { RENDER_UpdateFromScalerSetting(); - render.autofit=section->Get_bool("autofit"); +#if C_XBRZ + if (render.scale.xBRZ) { + // xBRZ requirements + vga.draw.doublescan_set = false; + } +#endif + render.autofit=section->Get_bool("autofit"); //If something changed that needs a ReInit // Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet) diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp index aded5aaa5..77765eb05 100644 --- a/src/gui/sdl_mapper.cpp +++ b/src/gui/sdl_mapper.cpp @@ -87,6 +87,64 @@ enum BC_Types { #define MAXAXIS 8 #define MAXHAT 2 +//! \brief Get value sign, i.e. less than zero: -1, zero: 0, greater than zero: 1. +template int sgn(T val) { + + // http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c + return (T(0) < val) - (val < T(0)); +} + +//! \brief Floating-point vector with 2 components. +struct Vector2 +{ + float X, Y; + + Vector2(float x, float y) : X(x), Y(y) + { + + } + + Vector2() : X(0.0f), Y(0.0f) + { + + } + + Vector2 clamp(Vector2 min, Vector2 max) const + { + float x = this->X; + float y = this->Y; + float xmin = min.X; + float xmax = max.X; + float ymin = min.Y; + float ymax = max.Y; + x = x < xmin ? xmin : x > xmax ? xmax : x; + y = y < ymin ? ymin : y > ymax ? ymax : y; + Vector2 clamp = Vector2(x, y); + return clamp; + } + + float magnitude() const + { + return sqrt(sqrMagnitude()); + } + + float sqrMagnitude() const + { + return X * X + Y * Y; + } + + Vector2 normalized() const + { + float m = this->magnitude(); + return m > 0.0f ? Vector2(this->X / m, this->Y / m) : Vector2(); + } + + Vector2 operator*(float f) const + { + return Vector2(this->X * f, this->Y * f); + } +}; + class CEvent; class CHandlerEvent; class CButton; @@ -117,13 +175,24 @@ class CEvent; CEvent *get_mapper_event_by_name(const std::string &x); +//! \brief Base CEvent class for mapper events class CEvent { public: + //! \brief Type of CEvent class, if the code needs to use the specific type + //! + //! \description This is used by other parts of the mapper if it needs to retrieve + //! additional information that is only provided by the handler event class enum event_type { event_t=0, handler_event_t }; public: + //! \brief CEvent constructor + //! + //! \description This constructor takes a mapper entry name and event type. + //! Subclasses will call down to this constructor as well. + //! The handler event class will fill in the _type field to + //! identify itself. CEvent(char const * const _entry,const enum event_type _type = event_t) { safe_strncpy(entry,_entry,sizeof(entry)); @@ -143,7 +212,15 @@ public: assert(get_mapper_event_by_name(entry) == this); } + + //! \brief Retrieve binding string for display in the menu + //! + //! \description Retrieve text string to show as the assigned mapper binding in a + //! menu item's displayable area so that the user knows what keyboard + //! input will trigger the shortcut. virtual std::string GetBindMenuText(void); + + //! \brief Update the menu item for the mapper shortcut with the latest text and keyboard shortcut void update_menu_shortcut(void) { if (!eventname.empty()) { DOSBoxMenu::item& item = mainMenu.get_item(std::string("mapper_") + std::string(eventname)); @@ -153,29 +230,61 @@ public: // LOG_MSG("%s",str.c_str()); } } + + //! \brief Add binding to the bindlist void AddBind(CBind * bind); + virtual ~CEvent(); + + //! \brief Change whether the event is activated or not virtual void Active(bool yesno) { active = yesno; } + + //! \brief Activate the event, act on it virtual void ActivateEvent(bool ev_trigger,bool skip_action)=0; + + //! \brief Deactivate the event virtual void DeActivateEvent(bool ev_trigger)=0; + + //! \brief Deactivate all bindings void DeActivateAll(void); + + //! \brief Set the value of the event (such as joystick position) void SetValue(Bits value){ current_value=value; } + + //! \brief Get the value of the event Bits GetValue(void) { return current_value; } + + //! \brief Retrieve the name of the event char * GetName(void) { return entry; } + + //! \brief Indicate whether the event is a trigger or continuous input virtual bool IsTrigger(void)=0; + + //! \brief Event name std::string eventname; + + //! \brief event type enum event_type type; + + //! \brief Bind list to trigger on activation/deactivation CBindList bindlist; + + //! \brief Whether the event is active or not bool active; protected: + //! \brief Activity counter Bitu activity; + + //! \brief Mapper entry name char entry[16]; + + //! \brief Current value of the event (such as joystick position) Bits current_value; }; @@ -192,14 +301,20 @@ CEvent *get_mapper_event_by_name(const std::string &x) { return NULL; } -/* class for events which can be ON/OFF only: key presses, joystick buttons, joystick hat */ +//! \brief class for events which can be ON/OFF only: key presses, joystick buttons, joystick hat class CTriggeredEvent : public CEvent { public: + //! \brief Constructor, with event name CTriggeredEvent(char const * const _entry) : CEvent(_entry) {} + + // methods below this line have sufficient documentation inherited from the base class + virtual ~CTriggeredEvent() {} + virtual bool IsTrigger(void) { return true; } + void ActivateEvent(bool ev_trigger,bool skip_action) { if (current_value>25000) { /* value exceeds boundary, trigger event if not active */ @@ -213,20 +328,27 @@ public: } } } + void DeActivateEvent(bool /*ev_trigger*/) { activity--; if (!activity) Active(false); } }; -/* class for events which have a non-boolean state: joystick axis movement */ +//! \brief class for events which have a non-boolean state: joystick axis movement class CContinuousEvent : public CEvent { public: + //! \brief Constructor, with event name CContinuousEvent(char const * const _entry) : CEvent(_entry) {} + + // methods below this line have sufficient documentation inherited from the base class + virtual ~CContinuousEvent() {} + virtual bool IsTrigger(void) { return false; } + void ActivateEvent(bool ev_trigger,bool skip_action) { if (ev_trigger) { activity++; @@ -237,6 +359,7 @@ public: if (!GetActivityCount()) Active(true); } } + void DeActivateEvent(bool ev_trigger) { if (ev_trigger) { if (activity>0) activity--; @@ -250,14 +373,20 @@ public: if (!GetActivityCount()) Active(false); } } + + //! \brief Retrieve activity counter virtual Bitu GetActivityCount(void) { return activity; } + + //! \brief Re-post activity virtual void RepostActivity(void) {} }; +//! \brief Base C++ class for a binding assigned in the mapper interface (or by default settings) class CBind { public: + //! \brief Bind class type, for runtime detection enum CBindType { bind_t=0, keybind_t @@ -266,6 +395,8 @@ public: virtual ~CBind () { list->remove(this); } + + //! \brief Constructor, to define the binding and type. This constructor adds the CBind object itself to the list CBind(CBindList * _list,enum CBindType _type = bind_t) { list=_list; _list->push_back(this); @@ -274,10 +405,16 @@ public: active=holding=false; type = _type; } + + //! \brief Get modifier text virtual std::string GetModifierText(void); + + //! \brief Get binding text, for display in the menu item virtual std::string GetBindMenuText(void) { return GetModifierText(); } + + //! \brief Append modifier text to a string, for use in recording bindings to the mapper file void AddFlags(char * buf) { if (mods & BMOD_Mod1) strcat(buf," mod1"); if (mods & BMOD_Mod2) strcat(buf," mod2"); @@ -285,6 +422,8 @@ public: if (mods & BMOD_Host) strcat(buf," host"); if (flags & BFLG_Hold) strcat(buf," hold"); } + + //! \brief Read modifier flags from a string, for use in parsing bindings from the mapper file void SetFlags(char * buf) { char * word; while (*(word=StripWord(buf))) { @@ -295,7 +434,9 @@ public: if (!strcasecmp(word,"hold")) flags|=BFLG_Hold; } } - void ActivateBind(Bits _value,bool ev_trigger,bool skip_action=false) { + + //! \brief Activate bindings + virtual void ActivateBind(Bits _value,bool ev_trigger,bool skip_action=false) { if (event->IsTrigger()) { /* use value-boundary for on/off events */ if (_value>25000) { @@ -315,6 +456,8 @@ public: event->ActivateEvent(ev_trigger,false); } } + + //! \brief Deactivate bindings void DeActivateBind(bool ev_trigger) { if (event->IsTrigger()) { if (!active) return; @@ -334,15 +477,35 @@ public: event->DeActivateEvent(ev_trigger); } } - virtual void ConfigName(char * buf)=0; - virtual void BindName(char * buf)=0; - - Bitu mods,flags; - Bit16s value; - CEvent * event; - CBindList * list; - bool active,holding; + //! \brief Get configuration name, for use in writing the mapper file + virtual void ConfigName(char * buf)=0; + + //! \brief Get binding name, for display in the mapper UI + virtual void BindName(char * buf)=0; + + //! \brief Modifiers (shift, ctrl, alt) + Bitu mods; + + //! \brief Flags (hold) + Bitu flags; + + //! \brief Binding value (TODO?) + Bit16s value; + + //! \brief Event object this binding is bound to (for visual UI purposes) + CEvent * event; + + //! \brief List that this object is part of + CBindList * list; + + //! \brief Active status + bool active; + + //! \brief Holding status + bool holding; + + //! \brief Binding type enum CBindType type; }; @@ -789,10 +952,11 @@ class CJHatBind; class CJAxisBind : public CBind { public: - CJAxisBind(CBindList * _list,CBindGroup * _group,Bitu _axis,bool _positive) : CBind(_list){ + CJAxisBind(CBindList * _list,CBindGroup * _group, Bitu _joystick, Bitu _axis,bool _positive) : CBind(_list){ group = _group; axis = _axis; positive = _positive; + joystick = _joystick; } virtual ~CJAxisBind() {} void ConfigName(char * buf) { @@ -801,10 +965,51 @@ public: void BindName(char * buf) { sprintf(buf,"%s Axis %d%s",group->BindStart(),(int)axis,positive ? "+" : "-"); } + + //! \brief Gets the joystick index for this instance. + Bitu GetJoystick() const { return joystick; }; + + //! \brief Gets the axis index for this instance. + Bitu GetAxis() const { return axis; } + + //! \brief Gets the axis direction for this instance. + bool GetPositive() const { return positive; } + + //! \brief Gets the deadzone for a joystick axis direction. + static int GetJoystickDeadzone(int joystick, int axis, bool positive) + { + auto section = control->GetSection("mapper"); + auto prop = static_cast(section); + auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(axis) + (positive ? "+" : "-"); + auto value = prop->Get_double(name); + auto deadzone = static_cast(value * 32767.0); + return deadzone; + } + + void ActivateBind(Bits _value, bool ev_trigger, bool skip_action = false) override + { + /* Since codebase is flawed, we do a simple hack: + * If user-set deadzone exceeds hard-coded value of 25000 we just set it to 25001. + * Other code works as usual, CTriggeredEvent does not have to check if it handles a joy axis. + */ + + // activate if we exceed user-defined deadzone + const auto joystick = this->GetJoystick(); + const auto axis = this->GetAxis(); + const auto positive = this->GetPositive(); + const auto deadzone = GetJoystickDeadzone(joystick, axis, positive); + + if (_value > deadzone && event->IsTrigger()) + _value = 25000 + 1; + + CBind::ActivateBind(_value, ev_trigger, skip_action); + } + protected: CBindGroup * group; Bitu axis; bool positive; + Bitu joystick; }; class CJButtonBind : public CBind { @@ -855,6 +1060,12 @@ protected: bool autofire = false; +//! \brief map of joystick 1 axes +int joy1axes[8]; + +//! \brief map of joystick 2 axes +int joy2axes[8]; + class CStickBindGroup : public CBindGroup { public: CStickBindGroup(Bitu _stick,Bitu _emustick,bool _dummy=false) : CBindGroup (){ @@ -925,6 +1136,15 @@ public: #else LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName(stick),(int)axes,(int)buttons,(int)hats); #endif + + // fetching these at every call simply freezes DOSBox at times so we do it once + // (game tested : Terminal Velocity @ joystick calibration page) + joy1dz1 = static_cast(GetAxisDeadzone(0, 0)); + joy1rs1 = static_cast(GetAxisResponse(0, 0)); + joy1dz2 = static_cast(GetAxisDeadzone(0, 1)); + joy1rs2 = static_cast(GetAxisResponse(0, 1)); + joy2dz1 = static_cast(GetAxisDeadzone(1, 0)); + joy2rs1 = static_cast(GetAxisResponse(1, 0)); } virtual ~CStickBindGroup() { SDL_JoystickClose(sdl_joystick); @@ -1025,8 +1245,9 @@ public: JOYSTICK_Button(emustick,i,button_pressed[i]); } - JOYSTICK_Move_X(emustick,((float)virtual_joysticks[emustick].axis_pos[0])/32768.0f); - JOYSTICK_Move_Y(emustick,((float)virtual_joysticks[emustick].axis_pos[1])/32768.0f); + auto v = GetJoystickVector(emustick, 0, 0, 1); + JOYSTICK_Move_X(emustick, v.X); + JOYSTICK_Move_Y(emustick, v.Y); } void ActivateJoystickBoundEvents() { @@ -1049,9 +1270,11 @@ public: old_button_state[i]=button_pressed[i]; } } - + + int* axis_map = stick == 0 ? &joy1axes[0] : &joy2axes[0]; for (i=0; i1) { if (old_neg_axis_state[i]) { @@ -1107,10 +1330,11 @@ public: } private: + float joy1dz1, joy1rs1, joy1dz2, joy1rs2, joy2dz1, joy2rs1; CBind * CreateAxisBind(Bitu axis,bool positive) { if (axisGetSection("joystick"); + auto prop = static_cast(section); + auto name = "joy" + std::to_string(joystick + 1) + "deadzone" + std::to_string(thumbStick + 1); + auto deadzone = static_cast(prop->Get_double(name)); + return deadzone; + } + + static float GetAxisResponse(int joystick, int thumbStick) + { + auto section = control->GetSection("joystick"); + auto prop = static_cast(section); + auto name = "joy" + std::to_string(joystick + 1) + "response" + std::to_string(thumbStick + 1); + auto response = static_cast(prop->Get_double(name)); + return response; + } + + static void ProcessInput(Bit16s x, Bit16s y, float deadzone, Vector2& joy) + { + // http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html + + joy = Vector2((x + 0.5f) / 32767.5f, (y + 0.5f) / 32767.5f); + + float m = joy.magnitude(); + Vector2 n = joy.normalized(); + joy = m < deadzone ? Vector2() : n * ((m - deadzone) / (1.0f - deadzone)); + + Vector2 min = Vector2(-1.0f, -1.0f); + Vector2 max = Vector2(+1.0f, +1.0f); + joy = joy.clamp(min, max); + } + protected: CBindList * pos_axis_lists; CBindList * neg_axis_lists; @@ -1155,6 +1412,38 @@ protected: bool old_neg_axis_state[MAXAXIS]; Uint8 old_hat_state[16]; bool is_dummy; + + Vector2 GetJoystickVector(int joystick, int thumbStick, int xAxis, int yAxis) const + { + Bit16s x = virtual_joysticks[joystick].axis_pos[xAxis]; + Bit16s y = virtual_joysticks[joystick].axis_pos[yAxis]; + float deadzone; + float response; + if (joystick == 0) + { + if (thumbStick == 0) + { + deadzone = joy1dz1; + response = joy1rs1; + } + else + { + deadzone = joy1dz2; + response = joy1rs2; + } + } + else + { + deadzone = joy2dz1; + response = joy2rs1; + } + Vector2 v; + ProcessInput(x, y, deadzone, v); + float x1 = sgn(v.X) * abs(pow(v.X, response)); + float y1 = sgn(v.Y) * abs(pow(v.Y, response)); + Vector2 v1(x1, y1); + return v1; + } }; class C4AxisBindGroup : public CStickBindGroup { @@ -1221,10 +1510,12 @@ public: JOYSTICK_Button(i>>1,i&1,button_pressed[i]); } - JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f); - JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f); - JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f); - JOYSTICK_Move_Y(1,((float)virtual_joysticks[0].axis_pos[3])/32768.0f); + auto v1 = GetJoystickVector(0, 0, 0, 1); + auto v2 = GetJoystickVector(0, 1, 2, 3); + JOYSTICK_Move_X(0, v1.X); + JOYSTICK_Move_Y(0, v1.Y); + JOYSTICK_Move_X(1, v2.X); + JOYSTICK_Move_Y(1, v2.Y); } }; @@ -1302,9 +1593,11 @@ public: JOYSTICK_Button(i>>1,i&1,button_pressed[i]); } - JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f); - JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f); - JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f); + auto v1 = GetJoystickVector(0, 0, 0, 1); + auto v2 = GetJoystickVector(0, 1, 2, 3); + JOYSTICK_Move_X(0, v1.X); + JOYSTICK_Move_Y(0, v1.Y); + JOYSTICK_Move_X(1, v2.X); Uint8 hat_pos=0; if (virtual_joysticks[0].hat_pressed[0]) hat_pos|=SDL_HAT_UP; @@ -1449,10 +1742,12 @@ public: /* query SDL joystick and activate bindings */ ActivateJoystickBoundEvents(); - JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f); - JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f); - JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f); - JOYSTICK_Move_Y(1,((float)virtual_joysticks[0].axis_pos[3])/32768.0f); + auto v1 = GetJoystickVector(0, 0, 0, 1); + auto v2 = GetJoystickVector(0, 1, 2, 3); + JOYSTICK_Move_X(0, v1.X); + JOYSTICK_Move_X(0, v1.Y); + JOYSTICK_Move_X(1, v2.X); + JOYSTICK_Move_X(1, v2.Y); Bitu bt_state=15; @@ -1853,12 +2148,16 @@ protected: BC_Types type; }; +//! \brief Keyboard key trigger event class CKeyEvent : public CTriggeredEvent { public: + //! \brief Constructor to specify mapper event, and KBD_* key enumeration constant CKeyEvent(char const * const _entry,KBD_KEYS _key) : CTriggeredEvent(_entry), notify_button(NULL) { key=_key; } + virtual ~CKeyEvent() {} + virtual void Active(bool yesno) { if (MAPPER_DemoOnly()) { if (notify_button != NULL) @@ -1870,15 +2169,23 @@ public: active=yesno; }; + + //! \brief Associate this object with a text button in the mapper UI void notifybutton(CTextButton *n) { notify_button = n; } + + //! \brief Text button in the mapper UI to indicate our status by CTextButton *notify_button; + + //! \brief KBD_* key enumeration value to transmit to keyboard emulation KBD_KEYS key; }; +//! \brief Joystick axis event handling for the mapper class CJAxisEvent : public CContinuousEvent { public: + //! \brief Constructor, to describe entry, joystick, axis, direction, and the opposing axis CJAxisEvent(char const * const _entry,Bitu _stick,Bitu _axis,bool _positive,CJAxisEvent * _opposite_axis) : CContinuousEvent(_entry) { notify_button=NULL; stick=_stick; @@ -1889,41 +2196,63 @@ public: _opposite_axis->SetOppositeAxis(this); } } + virtual ~CJAxisEvent() {} + virtual void Active(bool /*moved*/) { if (notify_button != NULL) notify_button->SetPartialInvert(GetValue()/32768.0); virtual_joysticks[stick].axis_pos[axis]=(Bit16s)(GetValue()*(positive?1:-1)); } + virtual Bitu GetActivityCount(void) { return activity|opposite_axis->activity; } + virtual void RepostActivity(void) { /* caring for joystick movement into the opposite direction */ opposite_axis->Active(true); } + + //! \brief Associate this object with a text button in the mapper GUI so that joystick position can be displayed at all times void notifybutton(CTextButton *n) { notify_button = n; } + + //! \brief Text button to use to display joystick position CTextButton *notify_button; protected: + //! \brief Associate this object with the opposing joystick axis void SetOppositeAxis(CJAxisEvent * _opposite_axis) { opposite_axis=_opposite_axis; } - Bitu stick,axis; + + //! \brief Joystick to follow + Bitu stick; + + //! \brief Joystick axis to track + Bitu axis; + + //! \brief Whether joystick axis is positive or negative bool positive; + + //! \brief Opposing joystick axis object CJAxisEvent * opposite_axis; }; +//! \brief Joystick button trigger class CJButtonEvent : public CTriggeredEvent { public: + //! \brief Constructor, describing mapper event, joystick, and which button CJButtonEvent(char const * const _entry,Bitu _stick,Bitu _button) : CTriggeredEvent(_entry) { stick=_stick; button=_button; notify_button=NULL; } + virtual ~CJButtonEvent() {} + virtual void Active(bool pressed) { if (notify_button != NULL) notify_button->SetInvert(pressed); @@ -1931,35 +2260,66 @@ public: virtual_joysticks[stick].button_pressed[button]=pressed; active=pressed; } + + //! \brief Associate this object with a text button in the mapper UI void notifybutton(CTextButton *n) { notify_button = n; } + + //! \brief Text button in the mapper UI to indicate our status by CTextButton *notify_button; protected: - Bitu stick,button; + //! \brief Which joystick + Bitu stick; + + //! \brief Which button + Bitu button; }; +//! \brief Joystick hat event class CJHatEvent : public CTriggeredEvent { public: + //! \brief Constructor to describe mapper event, joystick, hat, and direction CJHatEvent(char const * const _entry,Bitu _stick,Bitu _hat,Bitu _dir) : CTriggeredEvent(_entry) { stick=_stick; hat=_hat; dir=_dir; + notify_button = NULL; } + virtual ~CJHatEvent() {} + virtual void Active(bool pressed) { + if (notify_button != NULL) + notify_button->SetInvert(pressed); virtual_joysticks[stick].hat_pressed[(hat<<2)+dir]=pressed; } + void notifybutton(CTextButton *n) + { + notify_button = n; + } + CTextButton *notify_button; protected: - Bitu stick,hat,dir; + //! \brief Which joystick + Bitu stick; + + //! \brief Which hat + Bitu hat; + + //! \brief Direction of hat + Bitu dir; }; +//! \brief Modifier trigger event, for modifier keys. This permits the user to change modifier key bindings. class CModEvent : public CTriggeredEvent { public: + //! \brief Constructor to provide entry name and the index of the modifier button CModEvent(char const * const _entry,Bitu _wmod) : CTriggeredEvent(_entry), notify_button(NULL) { wmod=_wmod; } + virtual ~CModEvent() {} + virtual void Active(bool yesno) { if (notify_button != NULL) notify_button->SetInvert(yesno); @@ -1967,11 +2327,16 @@ public: if (yesno) mapper.mods|=(1u << (wmod-1u)); else mapper.mods&=~(1u << (wmod-1u)); }; + + //! \brief Associate this object with a text button in the mapper UI void notifybutton(CTextButton *n) { notify_button = n; } + + //! \brief Mapper UI text button to indicate status by CTextButton *notify_button; protected: + //! \brief Modifier button index Bitu wmod; }; @@ -1991,8 +2356,10 @@ std::string CBind::GetModifierText(void) { return r; } +//! \brief Mapper shortcut event. Keyboard triggerable only. class CHandlerEvent : public CTriggeredEvent { public: + //! \brief Constructor, to specify the entry, handler (callback), key (according to MapKeys enumeration), and text to display for the shortcut in the mapper UO CHandlerEvent(char const * const _entry,MAPPER_Handler * _handler,MapKeys _key,Bitu _mod,char const * const _buttonname) : CTriggeredEvent(_entry), notify_button(NULL) { handler=_handler; defmod=_mod; @@ -2001,7 +2368,9 @@ public: handlergroup.push_back(this); type = handler_event_t; } + virtual ~CHandlerEvent() {} + virtual void Active(bool yesno) { if (MAPPER_DemoOnly()) { if (notify_button != NULL) @@ -2013,9 +2382,13 @@ public: active=yesno; }; + + //! \brief Retrieve the button name (for display in the mapper UI) const char * ButtonName(void) { return buttonname; } + + //! \brief Generate a default binding from the MapKeys enumeration #if defined(C_SDL2) void MakeDefaultBind(char * buf) { Bitu key=0; @@ -2205,15 +2578,24 @@ public: ); } #endif + //! \brief Associate this object with a text button in the mapper UI void notifybutton(CTextButton *n) { notify_button = n; } + + //! \brief Text button in the mapper UI to indicate status by CTextButton *notify_button; + + //! \brief Mapper handler shortcut MAPPER_Handler * handler; protected: + //! \brief MapKeys enumeration for keyboard shortcut MapKeys defkey; + + //! \brief Default modifiers Bitu defmod; public: + //! \brief Button name const char * buttonname; }; @@ -2364,7 +2746,8 @@ static void AddJHatButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title char buf[64]; sprintf(buf,"jhat_%d_%d_%d",(int)_stick,(int)_hat,(int)_dir); CJHatEvent * event=new CJHatEvent(buf,_stick,_hat,_dir); - new CEventButton(x,y,dx,dy,title,event); + CEventButton* evbutton = new CEventButton(x,y,dx,dy,title,event); + event->notifybutton(evbutton); } static void AddModButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _mod) { diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index bced7977e..eb5b0b89e 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -106,6 +106,20 @@ void GFX_OpenGLRedrawScreen(void); #include "keymap.h" #include "control.h" +#ifdef _MSC_VER +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#else +# define MIN(a,b) std::min(a,b) +# define MAX(a,b) std::max(a,b) +#endif + +#if C_XBRZ +#include +#include +#include +#endif + #if !defined(C_SDL2) # include "SDL_version.h" # ifndef SDL_DOSBOX_X_SPECIAL @@ -156,8 +170,10 @@ const char *scaler_menu_opts[][2] = { { "2xsai", "2xSai" }, { "super2xsai", "Super2xSai" }, { "supereagle", "SuperEagle" }, - { "xbrz", "xBRZ" }, /* <- FIXME: Not implemented? No ref in the code! */ - +#if C_XBRZ + { "xbrz", "xBRZ" }, + { "xbrz_bilinear", "xBRZ Bilinear" }, +#endif { NULL, NULL } }; @@ -466,6 +482,14 @@ CDirect3D* d3d = NULL; # include #endif +enum AUTOLOCK_FEEDBACK +{ + AUTOLOCK_FEEDBACK_NONE, + AUTOLOCK_FEEDBACK_BEEP, + AUTOLOCK_FEEDBACK_FLASH +}; + +// do not specify any defaults inside, it is zeroed at start of main() struct SDL_Block { bool inited; bool active; //If this isn't set don't draw @@ -544,10 +568,12 @@ struct SDL_Block { SDL_cond *cond; struct { bool autolock; + AUTOLOCK_FEEDBACK autolock_feedback; bool autoenable; bool requestlock; bool locked; Bitu sensitivity; + MOUSE_EMULATION emulation; } mouse; SDL_Rect updateRects[1024]; Bitu overscan_color; @@ -562,11 +588,47 @@ struct SDL_Block { bool must_redraw_all; bool deferred_resize; bool init_ignore; - unsigned int gfx_force_redraw_count = 0; + unsigned int gfx_force_redraw_count; + struct { + int x; + int y; + double xToY; + double yToX; + } srcAspect; +#if C_XBRZ + struct { + bool enable; + bool postscale_bilinear; + int task_granularity; + int fixed_scale_factor; + int max_scale_factor; + std::vector renderbuf; + std::vector pixbuf; + bool scale_on; + int scale_factor; + } xBRZ; +#endif }; static SDL_Block sdl; +#if defined(C_SDL2) +# if defined(WIN32) +HWND GetHWND() +{ + SDL_SysWMinfo wmi; + SDL_VERSION(&wmi.version); + if (!SDL_GetWindowWMInfo(sdl.window, &wmi)) + return nullptr; + return wmi.info.win.window; +} + +HWND GetSurfaceHWND() +{ + return GetHWND(); +} +# endif +#endif void SDL_rect_cliptoscreen(SDL_Rect &r) { if (r.x < 0) { r.w += r.x; @@ -1204,32 +1266,48 @@ static SDL_Surface * GFX_SetupSurfaceScaledOpenGL(Bit32u sdl_flags, Bit32u bpp) sdl.clip.h=windowHeight=(Bit16u)Voodoo_OGL_GetHeight(); } else if (fixedWidth && fixedHeight) { - if (render.aspect) { - double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex); - double ratio_h=(double)fixedHeight/(sdl.draw.height*sdl.draw.scaley); + sdl.clip.w = windowWidth = fixedWidth; + sdl.clip.h = windowHeight = fixedHeight; - if (ratio_w < ratio_h) { - sdl.clip.w=(Bit16u)fixedWidth; - sdl.clip.h=(Bit16u)floor((sdl.draw.height*sdl.draw.scaley*ratio_w)+0.5); - } else { - sdl.clip.w=(Bit16u)floor((sdl.draw.width*sdl.draw.scalex*ratio_h)+0.5); - sdl.clip.h=(Bit16u)fixedHeight; + // adjust resulting image aspect ratio + if (render.aspect) { + if (fixedWidth > sdl.srcAspect.xToY * fixedHeight) // output broader than input => black bars left and right + { + sdl.clip.w = static_cast(fixedHeight * sdl.srcAspect.xToY); + } + else // black bars top and bottom + { + sdl.clip.h = static_cast(fixedWidth * sdl.srcAspect.yToX); } - } - else { - sdl.clip.w=fixedWidth; - sdl.clip.h=fixedHeight; } sdl.clip.x = (fixedWidth - sdl.clip.w) / 2; sdl.clip.y = (fixedHeight - sdl.clip.h) / 2; - windowWidth = fixedWidth; - windowHeight = fixedHeight; } else { - sdl.clip.x=0;sdl.clip.y=0; - sdl.clip.w=windowWidth=(Bit16u)(sdl.draw.width*sdl.draw.scalex); - sdl.clip.h=windowHeight=(Bit16u)(sdl.draw.height*sdl.draw.scaley); + sdl.clip.w = windowWidth = (Bit16u)(sdl.draw.width*sdl.draw.scalex); + sdl.clip.h = windowHeight = (Bit16u)(sdl.draw.height*sdl.draw.scaley); + + if (render.aspect) { + // we solve problem of aspect ratio based window extension here when window size is not set explicitly + if (windowWidth*sdl.srcAspect.y != windowHeight*sdl.srcAspect.x) + { + // abnormal aspect ratio detected, apply correction + if (windowWidth*sdl.srcAspect.y > windowHeight*sdl.srcAspect.x) + { + // wide pixel ratio, height should be extended to fit + sdl.clip.h = windowHeight = (Bitu)floor((double)windowWidth * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); + } + else + { + // long pixel ratio, width should be extended + sdl.clip.w = windowWidth = (Bitu)floor((double)windowHeight * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); + } + } + } + + sdl.clip.x = (windowWidth - sdl.clip.w) / 2; + sdl.clip.y = (windowHeight - sdl.clip.h) / 2; } LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize OpenGL window=%ux%u clip=x,y,w,h=%d,%d,%d,%d", @@ -1738,6 +1816,36 @@ void GFX_DrawSDLMenu(DOSBoxMenu &menu,DOSBoxMenu::displaylist &dl) { bool initedOpenGL = false; #endif +#if C_XBRZ +// returns true if scaling possible/enabled, false otherwise +bool xBRZ_SetScaleParameters(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { + sdl.xBRZ.scale_factor = (sdl.xBRZ.fixed_scale_factor == 0) ? + static_cast(std::sqrt((double)dstWidth * dstHeight / (srcWidth * srcHeight)) + 0.5) : + sdl.xBRZ.fixed_scale_factor; + + // enable minimal scaling if upscale is still possible but requires post-downscale + // having aspect ratio correction on always implies enabled scaler because it gives better quality than DOSBox own method + if (sdl.xBRZ.scale_factor == 1 && (render.aspect || dstWidth > srcWidth || dstHeight > srcHeight)) + sdl.xBRZ.scale_factor = 2; + + if (sdl.xBRZ.scale_factor >= 2) + { + // ok to scale, now clamp scale factor if corresponding max option is set + sdl.xBRZ.scale_factor = min(sdl.xBRZ.scale_factor, sdl.xBRZ.max_scale_factor); + sdl.xBRZ.scale_on = true; + } + else + { + // scaling impossible + sdl.xBRZ.scale_on = false; + } + + return sdl.xBRZ.scale_on; +} +#endif + +void RENDER_Reset(void); + Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) { if (width == 0 || height == 0) { E_Exit("GFX_SetSize with width=%d height=%d zero dimensions not allowed",(int)width,(int)height); @@ -1866,6 +1974,29 @@ dosurface: } #endif +#if C_XBRZ + if (sdl.xBRZ.enable) { + // there is a small problem we need to solve here: aspect corrected windows can be smaller than needed due to source with non-4:3 pixel ratio + // if we detect non-4:3 pixel ratio here with aspect correction on, we correct it so original fits into resized window properly + if (render.aspect) { + if (width*sdl.srcAspect.y != height * sdl.srcAspect.x) + { + // abnormal aspect ratio detected, apply correction + if (width*sdl.srcAspect.y > height*sdl.srcAspect.x) + { + // wide pixel ratio, height should be extended to fit + height = (Bitu)floor((double)width * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); + } + else + { + // long pixel ratio, width should be extended + width = (Bitu)floor((double)height * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); + } + } + } + } +#endif + sdl.desktop.type=SCREEN_SURFACE; sdl.clip.w=width; sdl.clip.h=height; @@ -1873,8 +2004,8 @@ dosurface: Uint32 wflags = SDL_FULLSCREEN | SDL_HWPALETTE | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0); - if (sdl.desktop.full.fixed - ) { + if (sdl.desktop.full.fixed) + { sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); sdl.surface=SDL_SetVideoMode(sdl.desktop.full.width, @@ -2026,6 +2157,34 @@ dosurface: (Uint32)0u); /* If this one fails be ready for some flickering... */ } + +#if C_XBRZ + if (sdl.xBRZ.enable) + { + // pre-calculate scaling factor and adjust aspect rate correction offload state + int clipWidth = sdl.surface->w; + int clipHeight = sdl.surface->h; + + if (render.aspect) { + if (clipWidth > sdl.srcAspect.xToY * clipHeight) + { + clipWidth = static_cast(clipHeight * sdl.srcAspect.xToY); + } + else // black bars top and bottom + { + clipHeight = static_cast(clipWidth * sdl.srcAspect.yToX); + } + } + + xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, clipWidth, clipHeight); + if (sdl.xBRZ.scale_on != render.aspectOffload) { + // when we are scaling, we ask render code not to do any aspect correction + // when we are not scaling, render code is allowed to do aspect correction at will + render.aspectOffload = sdl.xBRZ.scale_on; + PIC_AddEvent(VGA_SetupDrawing, 50); // schedule another resize here, render has already been initialized at this point and we have just changed its option + } + } +#endif } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW @@ -2074,7 +2233,18 @@ dosurface: glGetIntegerv(GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize); //if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface; - int texsize=2 << int_log2(width > height ? width : height); + + Bitu adjTexWidth = sdl.draw.width; + Bitu adjTexHeight = sdl.draw.height; +#if C_XBRZ + // we do the same as with Direct3D: precreate pixel buffer adjusted for xBRZ + if (sdl.xBRZ.enable && xBRZ_SetScaleParameters(adjTexWidth, adjTexHeight, sdl.clip.w, sdl.clip.h)) + { + adjTexWidth = adjTexWidth * sdl.xBRZ.scale_factor; + adjTexHeight = adjTexHeight * sdl.xBRZ.scale_factor; + } +#endif + int texsize=2 << int_log2(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight); if (texsize>sdl.opengl.max_texsize) { LOG_MSG("SDL:OPENGL:No support for texturesize of %d (max size is %d), falling back to surface",texsize,sdl.opengl.max_texsize); goto dosurface; @@ -2083,12 +2253,11 @@ dosurface: if (sdl.opengl.pixel_buffer_object) { glGenBuffersARB(1, &sdl.opengl.buffer); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, (GLsizeiptrARB)(width*height*4), NULL, GL_STREAM_DRAW_ARB); + glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, adjTexWidth*adjTexHeight*4, NULL, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); - } else { - sdl.opengl.framebuf=calloc(width*height, 4); //32 bit color - } - sdl.opengl.pitch=width*4; + } else + sdl.opengl.framebuf=calloc(adjTexWidth*adjTexHeight, 4); //32 bit color + sdl.opengl.pitch=adjTexWidth*4; glBindTexture(GL_TEXTURE_2D,0); @@ -2155,13 +2324,13 @@ dosurface: glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glBegin(GL_QUADS); // lower left - glTexCoord2i(0, 0 ); glVertex2i(sdl.clip.x, sdl.clip.y ); + glTexCoord2i(0, 0 ); glVertex2i(sdl.clip.x, sdl.clip.y ); // lower right - glTexCoord2i(width,0 ); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y ); + glTexCoord2i(adjTexWidth,0 ); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y ); // upper right - glTexCoord2i(width,height); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y+sdl.clip.h); + glTexCoord2i(adjTexWidth,adjTexHeight); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y+sdl.clip.h); // upper left - glTexCoord2i(0, height); glVertex2i(sdl.clip.x, sdl.clip.y+sdl.clip.h); + glTexCoord2i(0, adjTexHeight); glVertex2i(sdl.clip.x, sdl.clip.y+sdl.clip.h); glEnd(); glEndList(); @@ -2247,18 +2416,8 @@ dosurface: Bit16u fixedHeight; Bit16u windowWidth; Bit16u windowHeight; - - // Calculate texture size - if((!d3d->square) && (!d3d->pow2)) { - d3d->dwTexWidth=width; - d3d->dwTexHeight=height; - } else if(d3d->square) { - int texsize=2 << int_log2(width > height ? width : height); - d3d->dwTexWidth=d3d->dwTexHeight=texsize; - } else { - d3d->dwTexWidth=2 << int_log2(width); - d3d->dwTexHeight=2 << int_log2(height); - } + Bitu adjTexWidth = width; + Bitu adjTexHeight = height; if (sdl.desktop.fullscreen) { fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; @@ -2298,21 +2457,18 @@ dosurface: #endif if (fixedWidth && fixedHeight) { - if (render.aspect) { - double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex); - double ratio_h=(double)fixedHeight/(sdl.draw.height*sdl.draw.scaley); + sdl.clip.w = fixedWidth; + sdl.clip.h = fixedHeight; - if (ratio_w < ratio_h) { - sdl.clip.w=(Bit16u)fixedWidth; - sdl.clip.h=(Bit16u)floor((sdl.draw.height*sdl.draw.scaley*ratio_w)+0.5); - } else { - sdl.clip.w=(Bit16u)floor((sdl.draw.width*sdl.draw.scalex*ratio_h)+0.5); - sdl.clip.h=(Bit16u)fixedHeight; + if (render.aspect) { + if (fixedWidth > sdl.srcAspect.xToY * fixedHeight) // output broader than input => black bars left and right + { + sdl.clip.w = static_cast(fixedHeight * sdl.srcAspect.xToY); + } + else // black bars top and bottom + { + sdl.clip.h = static_cast(fixedWidth * sdl.srcAspect.yToX); } - } - else { - sdl.clip.w=fixedWidth; - sdl.clip.h=fixedHeight; } sdl.clip.x = (fixedWidth - sdl.clip.w) / 2; @@ -2321,9 +2477,50 @@ dosurface: windowHeight = fixedHeight; } else { - sdl.clip.x=0;sdl.clip.y=0; sdl.clip.w=windowWidth=(Bit16u)(sdl.draw.width*sdl.draw.scalex); sdl.clip.h=windowHeight=(Bit16u)(sdl.draw.height*sdl.draw.scaley); + + if (render.aspect) { + // we solve problem of aspect ratio based window extension here when window size is not set explicitly + if (windowWidth*sdl.srcAspect.y != windowHeight * sdl.srcAspect.x) + { + // abnormal aspect ratio detected, apply correction + if (windowWidth*sdl.srcAspect.y > windowHeight*sdl.srcAspect.x) + { + // wide pixel ratio, height should be extended to fit + sdl.clip.h = windowHeight = (Bitu)floor((double)windowWidth * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); + } + else + { + // long pixel ratio, width should be extended + sdl.clip.w = windowWidth = (Bitu)floor((double)windowHeight * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); + } + } + } + + sdl.clip.x = (windowWidth - sdl.clip.w) / 2; + sdl.clip.y = (windowHeight - sdl.clip.h) / 2; + } + + // when xBRZ scaler is used, we can adjust render target size to exactly what xBRZ scaler will output, leaving final scaling to default D3D scaler / shaders +#if C_XBRZ + if (sdl.xBRZ.enable && xBRZ_SetScaleParameters(width, height, sdl.clip.w, sdl.clip.h)) { + adjTexWidth = width * sdl.xBRZ.scale_factor; + adjTexHeight = height * sdl.xBRZ.scale_factor; + } +#endif + // Calculate texture size + if ((!d3d->square) && (!d3d->pow2)) { + d3d->dwTexWidth = adjTexWidth; + d3d->dwTexHeight = adjTexHeight; + } + else if (d3d->square) { + int texsize = 2 << int_log2(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight); + d3d->dwTexWidth = d3d->dwTexHeight = texsize; + } + else { + d3d->dwTexWidth = 2 << int_log2(adjTexWidth); + d3d->dwTexHeight = 2 << int_log2(adjTexHeight); } LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize Direct3D texture=%ux%u window=%ux%u clip=x,y,w,h=%d,%d,%d,%d", @@ -2381,8 +2578,8 @@ dosurface: SDL1_hax_inhibit_WM_PAINT = 1; - if(GCC_UNLIKELY(d3d->Resize3DEnvironment(windowWidth,windowHeight,sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h,width, - height,sdl.desktop.fullscreen) != S_OK)) { + if(GCC_UNLIKELY(d3d->Resize3DEnvironment(windowWidth,windowHeight,sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h,adjTexWidth, + adjTexHeight,sdl.desktop.fullscreen) != S_OK)) { retFlags = 0; } #if LOG_D3D @@ -2736,9 +2933,62 @@ void GFX_UpdateSDLCaptureState(void) { GFX_SetTitle(-1,-1,-1,false); } +#if WIN32 +void CaptureMouseNotifyWin32() +{ + const auto lck = sdl.mouse.locked; + switch (sdl.mouse.autolock_feedback) + { + case AUTOLOCK_FEEDBACK_NONE: break; + case AUTOLOCK_FEEDBACK_BEEP: + { + const auto lo = 1000; + const auto hi = 2000; + const auto t1 = 50; + const auto t2 = 25; + const auto f1 = lck ? hi : lo; + const auto f2 = lck ? lo : hi; + const auto tt = lck ? t1 : t2; + Beep(f1, tt); + Beep(f2, tt); + } + break; + case AUTOLOCK_FEEDBACK_FLASH: + { + const auto cnt = lck ? 4 : 2; + const auto tim = lck ? 80 : 40; + const auto wnd = GetHWND(); + if (wnd != nullptr) + { + FLASHWINFO fi; + fi.cbSize = sizeof(FLASHWINFO); + fi.hwnd = wnd; + fi.dwFlags = FLASHW_CAPTION; + fi.uCount = cnt; + fi.dwTimeout = tim; + FlashWindowEx(&fi); + } + break; + } + default: ; + } +} +#endif + +void CaptureMouseNotify() +{ +#if WIN32 + CaptureMouseNotifyWin32(); +#else + // TODO +#endif +} + static void CaptureMouse(bool pressed) { if (!pressed) return; + + CaptureMouseNotify(); GFX_CaptureMouse(); } @@ -3146,36 +3396,68 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { return false; switch (sdl.desktop.type) { case SCREEN_SURFACE: - if (sdl.blit.surface) { - if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface)) - return false; - pixels=(Bit8u *)sdl.blit.surface->pixels; - pitch=sdl.blit.surface->pitch; - } else { - if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) - return false; - pixels=(Bit8u *)sdl.surface->pixels; - pixels+=sdl.clip.y*sdl.surface->pitch; - pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel; - pitch=sdl.surface->pitch; +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); + pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); + pitch = sdl.draw.width * sizeof(uint32_t); + } + else +#endif + { + if (sdl.blit.surface) { + if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface)) + return false; + pixels = (Bit8u *)sdl.blit.surface->pixels; + pitch = sdl.blit.surface->pitch; + } + else { + if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) + return false; + pixels = (Bit8u *)sdl.surface->pixels; + pixels += sdl.clip.y*sdl.surface->pitch; + pixels += sdl.clip.x*sdl.surface->format->BytesPerPixel; + pitch = sdl.surface->pitch; + } } SDL_Overscan(); sdl.updating=true; return true; #if C_OPENGL case SCREEN_OPENGL: - if(sdl.opengl.pixel_buffer_object) { - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); - pixels=(Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); - } else - pixels=(Bit8u *)sdl.opengl.framebuf; - pitch=sdl.opengl.pitch; +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); + pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); + pitch = sdl.draw.width * sizeof(uint32_t); + } + else +#endif + { + if (sdl.opengl.pixel_buffer_object) { + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); + pixels = (Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); + } + else + pixels = (Bit8u *)sdl.opengl.framebuf; + pitch = sdl.opengl.pitch; + } + sdl.updating=true; return true; #endif #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: - sdl.updating=d3d->LockTexture(pixels, pitch); +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); + pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); + pitch = sdl.draw.width * sizeof(uint32_t); + sdl.updating = true; + } + else +#endif + sdl.updating = d3d->LockTexture(pixels, pitch); return sdl.updating; #endif default: @@ -3206,6 +3488,51 @@ void GFX_OpenGLRedrawScreen(void) { #endif } +#if C_XBRZ +void xBRZ_Render(const uint32_t* renderBuf, uint32_t* xbrzBuf, const Bit16u *changedLines, const int srcWidth, const int srcHeight, int scalingFactor) +{ + if (changedLines) // perf: in worst case similar to full input scaling + { + concurrency::task_group tg; // perf: task_group is slightly faster than pure prallel_for + int yLast = 0; + Bitu y = 0, index = 0; + while (y < sdl.draw.height) + { + if (!(index & 1)) + y += changedLines[index]; + else + { + const int sliceFirst = y; + const int sliceLast = y + changedLines[index]; + y += changedLines[index]; + + int yFirst = max(yLast, sliceFirst - 2); // we need to update two adjacent lines as well since they are analyzed by xBRZ! + yLast = min(srcHeight, sliceLast + 2); // (and make sure to not overlap with last slice!) + + for (int i = yFirst; i < yLast; i += sdl.xBRZ.task_granularity) + { + const int iLast = min(i + sdl.xBRZ.task_granularity, yLast); + tg.run([=] { xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast); }); + } + } + index++; + } + tg.wait(); + } + else // process complete input image + { + concurrency::task_group tg; + for (int i = 0; i < srcHeight; i += sdl.xBRZ.task_granularity) + tg.run([=] + { + const int iLast = min(i + sdl.xBRZ.task_granularity, srcHeight); + xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast); + }); + tg.wait(); + } +} +#endif + void GFX_EndUpdate( const Bit16u *changedLines ) { /* don't present our output if 3Dfx is in OpenGL mode */ if (sdl.desktop.prevent_fullscreen) @@ -3224,49 +3551,142 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif - if (SDL_MUSTLOCK(sdl.surface)) { - if (sdl.blit.surface) { - SDL_UnlockSurface(sdl.blit.surface); - int Blit = SDL_BlitSurface( sdl.blit.surface, 0, sdl.surface, &sdl.clip ); - LOG(LOG_MISC,LOG_WARN)("BlitSurface returned %d",Blit); - } else { - SDL_UnlockSurface(sdl.surface); - } - if(changedLines && (changedLines[0] == sdl.draw.height)) - return; - if(!menu.hidecycles && !sdl.desktop.fullscreen) frames++; -#if !defined(C_SDL2) - SDL_Flip(sdl.surface); +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + const int srcWidth = sdl.draw.width; + const int srcHeight = sdl.draw.height; + if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) + { + // we assume render buffer is *not* scaled! + const int outputHeight = sdl.surface->h; + const int outputWidth = sdl.surface->w; + int clipWidth = outputWidth; + int clipHeight = outputHeight; + int clipX = 0; + int clipY = 0; + + if (render.aspect) { + if (outputWidth > sdl.srcAspect.xToY * outputHeight) // output broader than input => black bars left and right + { + clipWidth = static_cast(outputHeight * sdl.srcAspect.xToY); + clipX = (outputWidth - clipWidth) / 2; + } + else // black bars top and bottom + { + clipHeight = static_cast(outputWidth * sdl.srcAspect.yToX); + clipY = (outputHeight - clipHeight) / 2; + } + } + + // 1. xBRZ-scale render buffer into xbrz pixel buffer + int xbrzWidth = 0; + int xbrzHeight = 0; + uint32_t* xbrzBuf; + xbrzWidth = srcWidth * sdl.xBRZ.scale_factor; + xbrzHeight = srcHeight * sdl.xBRZ.scale_factor; + sdl.xBRZ.pixbuf.resize(xbrzWidth * xbrzHeight); + + const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value + xbrzBuf = &sdl.xBRZ.pixbuf[0]; + xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); + + // 2. nearest neighbor/bilinear scale xbrz buffer into output surface clipping area + const bool mustLock = SDL_MUSTLOCK(sdl.surface); + if (mustLock) SDL_LockSurface(sdl.surface); + if (sdl.surface->pixels) // if locking fails, this can be nullptr, also check if we really need to draw + { + uint32_t* clipTrg = reinterpret_cast(static_cast(sdl.surface->pixels) + clipY * sdl.surface->pitch + clipX * sizeof(uint32_t)); + + if (sdl.xBRZ.postscale_bilinear) { + concurrency::task_group tg; + for (int i = 0; i < clipHeight; i += sdl.xBRZ.task_granularity) + tg.run([=] + { + const int iLast = min(i + sdl.xBRZ.task_granularity, clipHeight); + + xbrz::bilinearScale(&xbrzBuf[0], // const uint32_t* src, + xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), // int srcWidth, int srcHeight, int srcPitch, + clipTrg, //PixTrg* trg, + clipWidth, clipHeight, sdl.surface->pitch, // int trgWidth, int trgHeight, int trgPitch, + i, iLast, // int yFirst, int yLast, + [](uint32_t pix) { return pix; }); // PixConverter pixCvrt + }); + tg.wait(); + } + else + { + concurrency::task_group tg; + for (int i = 0; i < clipHeight; i += sdl.xBRZ.task_granularity) + tg.run([=] + { + const int iLast = min(i + sdl.xBRZ.task_granularity, clipHeight); + + xbrz::nearestNeighborScale(&xbrzBuf[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), + clipTrg, clipWidth, clipHeight, sdl.surface->pitch, + i, iLast, [](uint32_t pix) { return pix; }); // perf: going over target is by factor 4 faster than going over source for similar image sizes + }); + tg.wait(); + } + } + if (mustLock) SDL_UnlockSurface(sdl.surface); + if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; +#if defined(C_SDL2) + SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 1); +#else + SDL_Flip(sdl.surface); #endif - } else if (sdl.must_redraw_all) { + } + } + else +#endif /*C_XBRZ*/ + { + if (SDL_MUSTLOCK(sdl.surface)) { + if (sdl.blit.surface) { + SDL_UnlockSurface(sdl.blit.surface); + int Blit = SDL_BlitSurface(sdl.blit.surface, 0, sdl.surface, &sdl.clip); + LOG(LOG_MISC, LOG_WARN)("BlitSurface returned %d", Blit); + } + else { + SDL_UnlockSurface(sdl.surface); + } + if (changedLines && (changedLines[0] == sdl.draw.height)) + return; + if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; #if !defined(C_SDL2) - if (changedLines != NULL) SDL_Flip(sdl.surface); + SDL_Flip(sdl.surface); +#endif + } + else if (sdl.must_redraw_all) { +#if !defined(C_SDL2) + if (changedLines != NULL) SDL_Flip(sdl.surface); #endif } else if (changedLines) { - if(changedLines[0] == sdl.draw.height) - return; - if(!menu.hidecycles && !sdl.desktop.fullscreen) frames++; - Bitu y = 0, index = 0, rectCount = 0; - while (y < sdl.draw.height) { - if (!(index & 1)) { - y += changedLines[index]; - } else { - SDL_Rect *rect = &sdl.updateRects[rectCount++]; - rect->x = sdl.clip.x; - rect->y = (unsigned int)sdl.clip.y + (unsigned int)y; - rect->w = (Bit16u)sdl.draw.width; - rect->h = changedLines[index]; - y += changedLines[index]; - SDL_rect_cliptoscreen(*rect); + if (changedLines[0] == sdl.draw.height) + return; + if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; + Bitu y = 0, index = 0, rectCount = 0; + while (y < sdl.draw.height) { + if (!(index & 1)) { + y += changedLines[index]; + } + else { + SDL_Rect *rect = &sdl.updateRects[rectCount++]; + rect->x = sdl.clip.x; + rect->y = sdl.clip.y + y; + rect->w = (Bit16u)sdl.draw.width; + rect->h = changedLines[index]; + y += changedLines[index]; + SDL_rect_cliptoscreen(*rect); + } + index++; } - index++; - } - if (rectCount) { + if (rectCount) { #if defined(C_SDL2) - SDL_UpdateWindowSurfaceRects( sdl.window, sdl.updateRects, rectCount ); + SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, rectCount); #else - SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects ); + SDL_UpdateRects(sdl.surface, rectCount, sdl.updateRects); #endif + } } } break; @@ -3289,7 +3709,57 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { #endif } - if (sdl.opengl.pixel_buffer_object) { +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + // OpenGL pixel buffer is precreated for direct xBRZ output, while xBRZ render buffer is used for rendering + const int srcWidth = sdl.draw.width; + const int srcHeight = sdl.draw.height; + + if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) + { + // we assume render buffer is *not* scaled! + const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value + uint32_t* trgTex; + if (sdl.opengl.pixel_buffer_object) { + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); + trgTex = (uint32_t *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); + } + else + trgTex = reinterpret_cast(static_cast(sdl.opengl.framebuf)); + + if (trgTex) + xBRZ_Render(renderBuf, trgTex, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); + } + + // and here we go repeating some stuff with xBRZ related modifications + if (sdl.opengl.pixel_buffer_object) + { + glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + sdl.draw.width * sdl.xBRZ.scale_factor, sdl.draw.height * sdl.xBRZ.scale_factor, GL_BGRA_EXT, + GL_UNSIGNED_INT_8_8_8_8_REV, 0); + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); + } + else + { + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + sdl.draw.width * sdl.xBRZ.scale_factor, sdl.draw.height * sdl.xBRZ.scale_factor, GL_BGRA_EXT, +#if defined (MACOSX) + // needed for proper looking graphics on macOS 10.12, 10.13 + GL_UNSIGNED_INT_8_8_8_8, +#else + // works on Linux + GL_UNSIGNED_INT_8_8_8_8_REV, +#endif + (Bit8u *)sdl.opengl.framebuf); + } + glCallList(sdl.opengl.displaylist); + SDL_GL_SwapBuffers(); + } else +#endif /*C_XBRZ*/ + if (sdl.opengl.pixel_buffer_object) { if(changedLines && (changedLines[0] == sdl.draw.height)) return; glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); @@ -3375,6 +3845,42 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { #endif #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: +#if C_XBRZ + if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { + // we have xBRZ pseudo render buffer to be output to the pre-sized texture, do the xBRZ part + const int srcWidth = sdl.draw.width; + const int srcHeight = sdl.draw.height; + if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) + { + // we assume render buffer is *not* scaled! + int xbrzWidth = srcWidth * sdl.xBRZ.scale_factor; + int xbrzHeight = srcHeight * sdl.xBRZ.scale_factor; + sdl.xBRZ.pixbuf.resize(xbrzWidth * xbrzHeight); + + const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value + uint32_t* xbrzBuf = &sdl.xBRZ.pixbuf[0]; + xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); + + // now copy xBRZ buffer to the texture, adjusting for texture pitch + Bit8u *tgtPix; + Bitu tgtPitch; + if (d3d->LockTexture(tgtPix, tgtPitch) && tgtPix) // if locking fails, target texture can be nullptr + { + uint32_t* tgtTex = reinterpret_cast(static_cast(tgtPix)); + concurrency::task_group tg; + for (int i = 0; i < xbrzHeight; i += sdl.xBRZ.task_granularity) + tg.run([=] + { + const int iLast = min(i + sdl.xBRZ.task_granularity, xbrzHeight); + xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), tgtPitch, i, iLast, + [](uint32_t pix) { return pix; }); + }); + tg.wait(); + } + } + } +#endif + if(!menu.hidecycles) frames++; //implemented if(GCC_UNLIKELY(!d3d->UnlockTexture(changedLines))) { E_Exit("Failed to draw screen!"); @@ -3684,36 +4190,96 @@ static void GUI_StartUp() { sdl.mouse.autoenable=section->Get_bool("autolock"); if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE); sdl.mouse.autolock=false; + + const std::string feedback = section->Get_string("autolock_feedback"); + if (feedback == "none") + sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_NONE; + else if (feedback == "beep") + sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_BEEP; + else if (feedback == "flash") + sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_FLASH; + sdl.mouse.sensitivity=(unsigned int)section->Get_int("sensitivity"); std::string output=section->Get_string("output"); + const std::string emulation = section->Get_string("mouse_emulation"); + if (emulation == "always") + sdl.mouse.emulation = MOUSE_EMULATION_ALWAYS; + else if (emulation == "locked") + sdl.mouse.emulation = MOUSE_EMULATION_LOCKED; + else if (emulation == "integration") + sdl.mouse.emulation = MOUSE_EMULATION_INTEGRATION; + else if (emulation == "never") + sdl.mouse.emulation = MOUSE_EMULATION_NEVER; + /* Setup Mouse correctly if fullscreen */ if(sdl.desktop.fullscreen) GFX_CaptureMouse(); + // pre-set render aspect offload to false, altered by using xBRZ scaler or Direct3D/OpenGL modes that do aspect correction themselves and don't need render code to mess with it + render.aspectOffload = false; + +#if C_XBRZ + // yes, we read render section settings here, because xBRZ is integrated here but has settings in "render" + { + Section_prop * r_section = static_cast(control->GetSection("render")); + Prop_multival* r_prop = r_section->Get_multival("scaler"); + std::string r_scaler = r_prop->GetSection()->Get_string("type"); + sdl.xBRZ.enable = ((r_scaler == "xbrz") || (r_scaler == "xbrz_bilinear")); + sdl.xBRZ.postscale_bilinear = (r_scaler == "xbrz_bilinear"); + sdl.xBRZ.task_granularity = r_section->Get_int("xbrz slice"); + sdl.xBRZ.fixed_scale_factor = r_section->Get_int("xbrz fixed scale factor"); + sdl.xBRZ.max_scale_factor = r_section->Get_int("xbrz max scale factor"); + if ((sdl.xBRZ.max_scale_factor < 2) || (sdl.xBRZ.max_scale_factor > xbrz::SCALE_FACTOR_MAX)) + sdl.xBRZ.max_scale_factor = xbrz::SCALE_FACTOR_MAX; + if ((sdl.xBRZ.fixed_scale_factor < 2) || (sdl.xBRZ.fixed_scale_factor > xbrz::SCALE_FACTOR_MAX)) + sdl.xBRZ.fixed_scale_factor = 0; + } + + if (sdl.xBRZ.enable) { + // xBRZ requirements + if ((output != "surface") && (output != "direct3d") && (output != "opengl") && (output != "openglhq") && (output != "openglnb")) + output = "surface"; + render.aspectOffload = true; // render aspect ratio correction voids xBRZ algorithm, so scaler does aspect correction itself (and resorts to render code when no scaling is applied) + } +#endif + + // output type selection + // "overlay" was removed, pre-map to Direct3D or OpenGL or surface + if (output == "overlay") { +#if (HAVE_D3D9_H) && defined(WIN32) + output = "direct3d"; +#elif C_OPENGL + output = "opengl"; +#else + output = "surface"; +#endif + } + if (output == "surface") { sdl.desktop.want_type=SCREEN_SURFACE; } else if (output == "ddraw") { sdl.desktop.want_type=SCREEN_SURFACE; - } else if (output == "overlay") { - sdl.desktop.want_type=SCREEN_OPENGL; /* "overlay" was removed, map to OpenGL */ #if C_OPENGL } else if (output == "opengl" || output == "openglhq") { sdl.desktop.want_type=SCREEN_OPENGL; - sdl.opengl.bilinear=true; + sdl.opengl.bilinear = true; + render.aspectOffload = true; // OpenGL code does aspect correction itself, we don't need render thread to do it } else if (output == "openglnb") { sdl.desktop.want_type=SCREEN_OPENGL; - sdl.opengl.bilinear=false; + sdl.opengl.bilinear = false; + render.aspectOffload = true; // OpenGL code does aspect correction itself, we don't need render thread to do it #endif #if (HAVE_D3D9_H) && defined(WIN32) } else if (output == "direct3d") { sdl.desktop.want_type=SCREEN_DIRECT3D; + render.aspectOffload = true; // Direct3D code does aspect correction itself, we don't need render thread to do it #if LOG_D3D LOG_MSG("SDL:Direct3D activated"); #endif #endif } else { LOG_MSG("SDL:Unsupported output device %s, switching back to surface",output.c_str()); - sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore + sdl.desktop.want_type=SCREEN_SURFACE; // SHOULDN'T BE POSSIBLE anymore } sdl.overscan_width=(unsigned int)section->Get_int("overscan"); // sdl.overscan_color=section->Get_int("overscancolor"); @@ -4007,6 +4573,7 @@ static void HandleVideoResize(void * event) { extern unsigned int mouse_notify_mode; bool user_cursor_locked = false; +MOUSE_EMULATION user_cursor_emulation = MOUSE_EMULATION_NEVER; int user_cursor_x = 0,user_cursor_y = 0; int user_cursor_sw = 640,user_cursor_sh = 480; @@ -4163,31 +4730,38 @@ static void HandleMouseMotion(SDL_MouseMotionEvent * motion) { } } #endif - user_cursor_x = motion->x - sdl.clip.x; - user_cursor_y = motion->y - sdl.clip.y; + user_cursor_x = motion->x - sdl.clip.x; + user_cursor_y = motion->y - sdl.clip.y; user_cursor_locked = sdl.mouse.locked; - user_cursor_sw = sdl.clip.w; - user_cursor_sh = sdl.clip.h; + user_cursor_emulation = sdl.mouse.emulation; + user_cursor_sw = sdl.clip.w; + user_cursor_sh = sdl.clip.h; - if (sdl.mouse.locked || !sdl.mouse.autoenable) - Mouse_CursorMoved((float)motion->xrel*sdl.mouse.sensitivity/100.0f, - (float)motion->yrel*sdl.mouse.sensitivity/100.0f, - (float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.sensitivity/100.0f, - (float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.sensitivity/100.0f, - sdl.mouse.locked); - else if (mouse_notify_mode != 0) { /* for mouse integration driver */ - Mouse_CursorMoved(0,0,0,0,sdl.mouse.locked); - if (motion->x >= sdl.clip.x && motion->y >= sdl.clip.y && - motion->x < (sdl.clip.x+sdl.clip.w) && motion->y < (sdl.clip.y+sdl.clip.h)) - SDL_ShowCursor(SDL_DISABLE); /* TODO: If guest has not read mouse cursor position within 250ms show cursor again */ - else if (Mouse_GetButtonState() != 0) - SDL_ShowCursor(SDL_DISABLE); /* TODO: If guest has not read mouse cursor position within 250ms show cursor again */ - else - SDL_ShowCursor(SDL_ENABLE); + auto xrel = static_cast(motion->xrel) * sdl.mouse.sensitivity / 100.0f; + auto yrel = static_cast(motion->yrel) * sdl.mouse.sensitivity / 100.0f; + auto x = static_cast(motion->x - sdl.clip.x) / (sdl.clip.w - 1) * sdl.mouse.sensitivity / 100.0f; + auto y = static_cast(motion->y - sdl.clip.y) / (sdl.clip.h - 1) * sdl.mouse.sensitivity / 100.0f; + auto emu = sdl.mouse.locked; + + if (mouse_notify_mode != 0) + { + /* for mouse integration driver */ + xrel = yrel = x = y = 0.0f; + emu = sdl.mouse.locked; + const auto isdown = Mouse_GetButtonState() != 0; + const auto inside = + motion->x >= sdl.clip.x && motion->x < sdl.clip.x + sdl.clip.w && + motion->y >= sdl.clip.y && motion->y < sdl.clip.y + sdl.clip.h; + SDL_ShowCursor(isdown || inside ? SDL_DISABLE : SDL_ENABLE); + /* TODO: If guest has not read mouse cursor position within 250ms show cursor again */ } - else { - SDL_ShowCursor(SDL_ENABLE); + bool MOUSE_IsHidden(); + if (!user_cursor_locked) + { + /* Show only when DOS app is not using mouse */ + SDL_ShowCursor(MOUSE_IsHidden() ? SDL_ENABLE : SDL_DISABLE); } + Mouse_CursorMoved(xrel, yrel, x, y, emu); } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ @@ -4720,6 +5294,7 @@ static void HandleMouseButton(SDL_MouseButtonEvent * button) { case SDL_PRESSED: if (inMenu) return; if (sdl.mouse.requestlock && !sdl.mouse.locked && mouse_notify_mode == 0) { + CaptureMouseNotify(); GFX_CaptureMouse(); // Dont pass klick to mouse handler break; @@ -5365,12 +5940,90 @@ static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger) { } #endif -void RENDER_Reset(void); - #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message); #endif +struct mouse_pos +{ + long x = 0; + long y = 0; +} mouse_pos; + +bool mouse_inside = false; + +void GFX_EventsMouseProcess(const long x, const long y, const long rx, const long ry) +{ + const auto x1 = sdl.clip.x; + const auto x2 = x1 + sdl.clip.w - 1; + const auto y1 = sdl.clip.y; + const auto y2 = y1 + sdl.clip.h - 1; + const auto in = x >= x1 && x <= x2 && y >= y1 && y <= y2; + + if (mouse_inside && !in) + { + const auto x3 = max((int)x1, min((int)x2, (int)x)); + const auto y3 = max((int)y1, min((int)y2, (int)y)); + SDL_Event evt; + evt.type = SDL_MOUSEMOTION; + evt.motion.state = 0; + evt.motion.which = 0; + evt.motion.x = x3; + evt.motion.y = y3; + evt.motion.xrel = rx; + evt.motion.yrel = ry; + SDL_PushEvent(&evt); + } + + mouse_inside = in; +} + +#if defined(WIN32) +void GFX_EventsMouseWin32() +{ + /* Compute relative mouse movement */ + + POINT point; + + if (!GetCursorPos(&point)) + return; + + const auto hwnd = GetSurfaceHWND(); + + if (hwnd == nullptr || !ScreenToClient(hwnd, &point)) + return; + + const auto x = point.x; + const auto y = point.y; + const auto rx = x - mouse_pos.x; + const auto ry = y - mouse_pos.y; + + mouse_pos.x = x; + mouse_pos.y = y; + + /* Let the method do the heavy uplifting */ + GFX_EventsMouseProcess(x, y, rx, ry); +} +#endif + +/** + * \brief Processes mouse movements when outside the window. + * + * This method will send an extra mouse event to the SDL pump + * when some relative movement has occurred. + */ +void GFX_EventsMouse() +{ + if (sdl.desktop.fullscreen || sdl.mouse.locked) + return; + +#if WIN32 + GFX_EventsMouseWin32(); +#else + // TODO +#endif +} + void GFX_Events() { CheckMapperKeyboardLayout(); #if defined(C_SDL2) /* SDL 2.x---------------------------------- */ @@ -5380,10 +6033,16 @@ void GFX_Events() { int time=GetTicks(); if (time-poll_delay>20) { poll_delay=time; - if (sdl.num_joysticks>0) SDL_JoystickUpdate(); - MAPPER_UpdateJoysticks(); + if (sdl.num_joysticks>0) + { + SDL_JoystickUpdate(); + MAPPER_UpdateJoysticks(); + } } #endif + + GFX_EventsMouse(); + while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_WINDOWEVENT: @@ -5405,6 +6064,7 @@ void GFX_Events() { break; case SDL_WINDOWEVENT_FOCUS_LOST: if (sdl.mouse.locked) { + CaptureMouseNotify(); GFX_CaptureMouse(); } SetPriority(sdl.priority.nofocus); @@ -5523,10 +6183,16 @@ void GFX_Events() { uint32_t time=GetTicks(); if ((int32_t)(time-poll_delay)>20) { poll_delay=time; - if (sdl.num_joysticks>0) SDL_JoystickUpdate(); - MAPPER_UpdateJoysticks(); + if (sdl.num_joysticks>0) + { + SDL_JoystickUpdate(); + MAPPER_UpdateJoysticks(); + } } #endif + + GFX_EventsMouse(); + while (SDL_PollEvent(&event)) { switch (event.type) { #ifdef __WIN32__ @@ -5585,8 +6251,13 @@ void GFX_Events() { GFX_CaptureMouse(); SetPriority(sdl.priority.focus); CPU_Disable_SkipAutoAdjust(); - } else { - if (sdl.mouse.locked) GFX_CaptureMouse(); + BIOS_SynchronizeNumLock(); + } else { + if (sdl.mouse.locked) + { + CaptureMouseNotify(); + GFX_CaptureMouse(); + } #if defined(WIN32) if (sdl.desktop.fullscreen) @@ -6034,13 +6705,31 @@ void SDL_SetupConfigSection() { Pstring->Set_help("What video system to use for output."); Pstring->Set_values(outputs); - Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true); + Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always, false); Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)"); + const char* feeds[] = { "none", "beep", "flash", nullptr}; + Pstring = sdl_sec->Add_string("autolock_feedback", Property::Changeable::Always, feeds[1]); + Pstring->Set_help("Autolock status feedback type, i.e. visual, auditive, none."); + Pstring->Set_values(feeds); + Pint = sdl_sec->Add_int("sensitivity",Property::Changeable::Always,100); Pint->SetMinMax(1,1000); Pint->Set_help("Mouse sensitivity."); + const char * emulation[] = {"integration", "locked", "always", "never", nullptr}; + Pstring = sdl_sec->Add_string("mouse_emulation", Property::Changeable::Always, emulation[1]); + Pstring->Set_help( + "When is mouse emulated ?\n" + "integration: when not locked\n" + "locked: when locked\n" + "always: every time\n" + "never: at no time\n" + "If disabled, the mouse position in DOSBox-X is exactly where the host OS reports it.\n" + "When using a high DPI mouse, the emulation of mouse movement can noticeably reduce the\n" + "sensitiveness of your device, i.e. the mouse is slower but more precise."); + Pstring->Set_values(emulation); + Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true); Pbool->Set_help("Wait before closing the console if dosbox has an error."); @@ -6375,8 +7064,10 @@ void CheckNumLockState(void) { BYTE keyState[256]; GetKeyboardState((LPBYTE)(&keyState)); - if (keyState[VK_NUMLOCK] & 1) numlock_stat=true; - if (numlock_stat) SetNumLock(); + if (keyState[VK_NUMLOCK] & 1) { + numlock_stat = true; + startup_state_numlock = true; + } #endif } @@ -7425,6 +8116,8 @@ void OutputSettingMenuUpdate(void) { #endif } +bool custom_bios = false; + //extern void UI_Init(void); int main(int argc, char* argv[]) SDL_MAIN_NOEXCEPT { CommandLine com_line(argc,argv); @@ -7435,6 +8128,16 @@ int main(int argc, char* argv[]) SDL_MAIN_NOEXCEPT { memset(&sdl,0,sizeof(sdl)); // struct sdl isn't initialized anywhere that I can tell + // initialize some defaults in SDL structure here + sdl.srcAspect.x = 4; sdl.srcAspect.y = 3; + sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y; + sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x; +#if C_XBRZ + sdl.xBRZ.task_granularity = 16; + sdl.xBRZ.max_scale_factor = xbrz::SCALE_FACTOR_MAX; +#endif + + control=&myconf; #if defined(WIN32) && !defined(HX_DOS) /* Microsoft's IME does not play nice with DOSBox */ @@ -8268,6 +8971,12 @@ fresh_boot: wait_debugger = true; dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } + else if (x == 8) { /* Booting to a BIOS, shutting down DOSBox BIOS */ + LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot into BIOS image"); + + reboot_machine = true; + dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ + } else { LOG(LOG_MISC,LOG_DEBUG)("Emulation threw DOSBox kill switch signal"); @@ -8413,6 +9122,21 @@ fresh_boot: DispatchVMEvent(VM_EVENT_RESET); DispatchVMEvent(VM_EVENT_RESET_END); + /* HACK: EGA/VGA modes will need VGA BIOS mapped in, ready to go */ + if (IS_EGAVGA_ARCH) { + void INT10_Startup(Section *sec); + INT10_Startup(NULL); + } + +#if C_DEBUG + if (boot_debug_break) { + boot_debug_break = false; + + void DEBUG_Enable(bool pressed); + DEBUG_Enable(true); + } +#endif + /* run again */ goto fresh_boot; } diff --git a/src/hardware/joystick.cpp b/src/hardware/joystick.cpp index 0fd4dc9e8..47a340624 100644 --- a/src/hardware/joystick.cpp +++ b/src/hardware/joystick.cpp @@ -50,6 +50,8 @@ static bool swap34 = false; bool button_wrapping_enabled = true; extern bool autofire; //sdl_mapper.cpp +extern int joy1axes[]; //sdl_mapper.cpp +extern int joy2axes[]; //sdl_mapper.cpp static Bitu read_p201(Bitu port,Bitu iolen) { (void)iolen;//UNUSED @@ -259,6 +261,26 @@ void JOYSTICK_Init() { stick[1].enabled = false; stick[0].xtick = stick[0].ytick = stick[1].xtick = stick[1].ytick = PIC_FullIndex(); + + // retrieves axes mapping + auto joysticks = 2; + auto axes = 8; + for (auto i = 0; i < joysticks; i++) + { + for (auto j = 0; j < axes; j++) + { + auto propname = "joy" + std::to_string(i + 1) + "axis" + std::to_string(j); + auto axis = section->Get_int(propname); + if (i == 0) + { + joy1axes[j] = axis; + } + else + { + joy2axes[j] = axis; + } + } + } } AddExitFunction(AddExitFunctionFuncPair(JOYSTICK_Destroy),true); diff --git a/src/hardware/keyboard.cpp b/src/hardware/keyboard.cpp index e329215ab..4b186a387 100644 --- a/src/hardware/keyboard.cpp +++ b/src/hardware/keyboard.cpp @@ -1652,6 +1652,44 @@ extern bool gdc_5mhz_mode; bool PC98_SHUT0=true,PC98_SHUT1=true; +//! \brief PC-98 System 8255 PPI emulation (Intel 8255A device) +//! +//! \description NEC PC-98 systems use a 8255 to enable reading +//! the DIP switches on the front (port A), +//! some hardware state (Port B), and to control +//! some hardware and signal the BIOS for +//! shutdown/reset (Port C). +//! +//! This PPI is connected to I/O ports 0x31-0x37 odd. +//! - 0x31 Port A +//! - 0x33 Port B +//! - 0x35 Port C +//! - 0x37 control/mode +//! +//! Note that on real hardware, PC-9801 systems +//! had physical DIP switches until sometime around +//! the early nineties when the DIP switches became +//! virtual, and were set by the BIOS according to +//! configuration stored elsewhere. +//! +//! This PPI is connected to: +//! - Port A (in): DIP switches 2-1 through 2-8 +//! - Port B (in): Parity error, expansion bus INT 3, +//! and RS-232C (8251) status +//! - Port C (out): RS-232C interrupt enable, speaker (buzzer) +//! inhibit, parity check enable, and +//! shutdown flags. +//! +//! The two "shutdown" bit flags serve to instruct +//! the BIOS on what to do after a soft CPU reset. +//! One particular setting allows 286-class systems +//! to reset to real mode through a FAR return stack, +//! for the same reasons that IBM PC/AT systems enable +//! reset to real mode through a reset byte written +//! to RTC CMOS and a far pointer to jump to. +//! A well known PC-9821 expanded memory emulator, +//! VEMM486.EXE, relies on the shutdown bits in that +//! manner when it initializes. class PC98_System_8255 : public Intel8255 { public: PC98_System_8255() : Intel8255() { @@ -2025,6 +2063,38 @@ bool p7fd8_8255_mouse_irq_signal = false; extern uint8_t MOUSE_IRQ; +//! \brief PC-98 System Bus Mouse PPI emulation (Intel 8255A device) +//! +//! \description NEC PC-98 systems use a 8255 to interface a bus mouse +//! to the system. +//! +//! This PPI is connected to I/O ports 0x7FD9-0x7FDF odd. +//! - 0x7FD9 is Port A (input) +//! - 0x7FDB is Port B (input) +//! - 0x7FDD is Port C (bits[7:4] output, bits[3:0] input) +//! - 0x7FDF is control/mode +//! +//! Button state is read directly as 3 bits (left, right, middle). +//! +//! Mouse movement is latched on command and read 4 bits +//! (one nibble at a time) to obtain two signed 8-bit +//! x and y mickey counts (to detect movement). +//! +//! Mouse data is read from port A. +//! +//! Interrupt inhibit, selection of the nibble, and the +//! command to latch mouse movement is done by writing +//! to port C. +//! +//! According to some documentation found online, port B +//! is attached to various unrelated signals and/or status. +//! +//! The interrupt line is connected to IRQ 13 of the +//! interrupt controller on PC-98 systems. +//! +//! There is at least one PC-98 game "Metal Force" known +//! to abuse this interface as a periodic interrupt source +//! instead of using it as an interface for mouse input. class PC98_Mouse_8255 : public Intel8255 { public: PC98_Mouse_8255() : Intel8255() { @@ -2105,7 +2175,7 @@ public: /* TODO */ return 0x00; } - /* port C is output (both halves) */ + /* port C is input[3:0] and output[7:4] */ virtual void outPortC(const uint8_t mask) { if (mask & 0x80) { /* bit 7 */ /* changing from 0 to 1 latches counters and clears them */ diff --git a/src/hardware/memory.cpp b/src/hardware/memory.cpp index d365d8a9e..1d0317698 100644 --- a/src/hardware/memory.cpp +++ b/src/hardware/memory.cpp @@ -73,14 +73,6 @@ extern Bitu rombios_minimum_location; extern bool VIDEO_BIOS_always_carry_14_high_font; extern bool VIDEO_BIOS_always_carry_16_high_font; -/* if set: mainline DOSBox behavior where adapter ROM (0xA0000-0xFFFFF) except for - * areas explicitly mapped to the ROM handler, are mapped the same as system RAM. - * - * if clear: associate any adapter ROM region not used by the BIOS, VGA BIOS, or - * VGA, with the Illegal handler (not mapped). Actual RAM behind the storage does - * not show up and reads return 0xFF, just like real hardware. */ -bool adapter_rom_is_ram = false; - static struct MemoryBlock { MemoryBlock() : pages(0), handler_pages(0), reported_pages(0), phandlers(NULL), mhandles(NULL), mem_alias_pagemask(0), mem_alias_pagemask_active(0), address_bits(0) { } @@ -1307,6 +1299,7 @@ void On_Software_286_reset_vector(unsigned char code) { void CPU_Exception_Level_Reset(); +extern bool custom_bios; extern bool PC98_SHUT0,PC98_SHUT1; void On_Software_CPU_Reset() { @@ -1314,7 +1307,16 @@ void On_Software_CPU_Reset() { CPU_Exception_Level_Reset(); - if (IS_PC98_ARCH) { + if (custom_bios) { + /* DO NOTHING */ + LOG_MSG("CPU RESET: Doing nothing, custom BIOS loaded"); + + if (IS_PC98_ARCH) + LOG_MSG("CPU RESET: SHUT0=%u SHUT1=%u",PC98_SHUT0,PC98_SHUT1); + else + LOG_MSG("CPU RESET: CMOS BYTE 0x%02x",CMOS_GetShutdownByte()); + } + else if (IS_PC98_ARCH) { /* From Undocumented 9801, 9821 Volume 2: * * SHUT0 | SHUT1 | Meaning @@ -1576,8 +1578,6 @@ bool MEM_map_ROM_alias_physmem(Bitu start,Bitu end) { HostPt GetMemBase(void) { return MemBase; } -extern bool mainline_compatible_mapping; - /*! \brief REDOS.COM utility command on drive Z: to trigger restart of the DOS kernel */ class REDOS : public Program { @@ -1822,13 +1822,11 @@ void Init_RAM() { for (;i < memory.handler_pages;i++) memory.phandlers[i] = NULL;//&illegal_page_handler; - if (!adapter_rom_is_ram) { - /* FIXME: VGA emulation will selectively respond to 0xA0000-0xBFFFF according to the video mode, - * what we want however is for the VGA emulation to assign illegal_page_handler for - * address ranges it is not responding to when mapping changes. */ - for (i=0xa0;i<0x100;i++) /* we want to make sure adapter ROM is unmapped entirely! */ - memory.phandlers[i] = NULL;//&unmapped_page_handler; - } + /* FIXME: VGA emulation will selectively respond to 0xA0000-0xBFFFF according to the video mode, + * what we want however is for the VGA emulation to assign illegal_page_handler for + * address ranges it is not responding to when mapping changes. */ + for (i=0xa0;i<0x100;i++) /* we want to make sure adapter ROM is unmapped entirely! */ + memory.phandlers[i] = NULL;//&unmapped_page_handler; } static IO_ReadHandleObject PS2_Port_92h_ReadHandler; diff --git a/src/hardware/vga.cpp b/src/hardware/vga.cpp index b31d716ff..ee6de3d83 100644 --- a/src/hardware/vga.cpp +++ b/src/hardware/vga.cpp @@ -181,6 +181,9 @@ bool vga_sierra_lock_565 = false; bool enable_vga_resize_delay = false; bool vga_ignore_hdispend_change_if_smaller = false; bool ignore_vblank_wraparound = false; +bool non_cga_ignore_oddeven = false; +bool non_cga_ignore_oddeven_engage = false; +bool vga_palette_update_on_full_load = true; bool vga_double_buffered_line_compare = false; bool pc98_allow_scanline_effect = true; bool pc98_allow_4_display_partitions = false; @@ -406,6 +409,17 @@ void VGA_SetCGA4Table(Bit8u val0,Bit8u val1,Bit8u val2,Bit8u val3) { class VFRCRATE : public Program { public: void Run(void) { + WriteOut("Video refresh rate.\n\n"); + if (cmd->FindExist("/?", false)) { + WriteOut("VFRCRATE [SET [OFF|PAL|NTSC|rate]\n"); + WriteOut(" SET OFF unlock\n"); + WriteOut(" SET PAL lock to PAL frame rate\n"); + WriteOut(" SET NTSC lock to NTSC frame rate\n"); + WriteOut(" SET rate lock to integer frame rate, e.g. 15\n"); + WriteOut(" SET rate lock to decimal frame rate, e.g. 29.97\n"); + WriteOut(" SET rate lock to fractional frame rate, e.g. 60000/1001\n"); + return; + } if (cmd->FindString("SET",temp_line,false)) { char *x = (char*)temp_line.c_str(); @@ -434,11 +448,11 @@ public: VGA_SetupHandlers(); VGA_StartResize(); } - + if (vga_force_refresh_rate > 0) - WriteOut("Video refresh rate locked to %.3ffps\n",vga_force_refresh_rate); + WriteOut("Locked to %.3f fps\n",vga_force_refresh_rate); else - WriteOut("Video refresh rate unlocked\n"); + WriteOut("Unlocked\n"); } }; @@ -587,6 +601,8 @@ void VGA_Reset(Section*) { ignore_vblank_wraparound = section->Get_bool("ignore vblank wraparound"); vga_enable_hretrace_effects = section->Get_bool("allow hretrace effects"); enable_page_flip_debugging_marker = section->Get_bool("page flip debug line"); + vga_palette_update_on_full_load = section->Get_bool("vga palette update on full load"); + non_cga_ignore_oddeven = section->Get_bool("ignore odd-even mode in non-cga modes"); enable_vretrace_poll_debugging_marker = section->Get_bool("vertical retrace poll debug line"); vga_double_buffered_line_compare = section->Get_bool("double-buffered line compare"); hack_lfb_yadjust = section->Get_int("vesa lfb base scanline adjust"); @@ -667,19 +683,19 @@ void VGA_Reset(Section*) { * various motherboard chipsets known to "steal" * off the top of system RAM, like Intel and * Chips & Tech VGA implementations? */ - vga.vmemsize = _MB_bytes(section->Get_int("vmemsize")); - vga.vmemsize += _KB_bytes(section->Get_int("vmemsizekb")); - vga.vmemsize = (vga.vmemsize + 0xFFFu) & (~0xFFFu); + vga.mem.memsize = _MB_bytes(section->Get_int("vmemsize")); + vga.mem.memsize += _KB_bytes(section->Get_int("vmemsizekb")); + vga.mem.memsize = (vga.mem.memsize + 0xFFFu) & (~0xFFFu); /* mainline compatible: vmemsize == 0 means 512KB */ - if (vga.vmemsize == 0) vga.vmemsize = _KB_bytes(512); + if (vga.mem.memsize == 0) vga.mem.memsize = _KB_bytes(512); /* round up to the nearest power of 2 (TODO: Any video hardware that uses non-power-of-2 sizes?). * A lot of DOSBox's VGA emulation code assumes power-of-2 VRAM sizes especially when wrapping * memory addresses with (a & (vmemsize - 1)) type code. */ - if (!is_power_of_2(vga.vmemsize)) { - Bitu i = int_log2(vga.vmemsize) + 1u; - vga.vmemsize = 1u << i; - LOG(LOG_VGA,LOG_WARN)("VGA RAM size requested is not a power of 2, rounding up to %uKB",vga.vmemsize>>10); + if (!is_power_of_2(vga.mem.memsize)) { + Bitu i = int_log2(vga.mem.memsize) + 1u; + vga.mem.memsize = 1u << i; + LOG(LOG_VGA,LOG_WARN)("VGA RAM size requested is not a power of 2, rounding up to %uKB",vga.mem.memsize>>10); } /* sanity check according to adapter type. @@ -687,38 +703,51 @@ void VGA_Reset(Section*) { * for selecting machine type AND video card. */ switch (machine) { case MCH_HERC: /* FIXME: MCH_MDA (4KB) vs MCH_HERC (64KB?) */ - if (vga.vmemsize < _KB_bytes(64)) vga.vmemsize = _KB_bytes(64); + if (vga.mem.memsize < _KB_bytes(64)) vga.mem.memsize = _KB_bytes(64); break; case MCH_CGA: - if (vga.vmemsize < _KB_bytes(16)) vga.vmemsize = _KB_bytes(16); + if (vga.mem.memsize < _KB_bytes(16)) vga.mem.memsize = _KB_bytes(16); break; case MCH_TANDY: case MCH_PCJR: - if (vga.vmemsize < _KB_bytes(128)) vga.vmemsize = _KB_bytes(128); /* FIXME: Right? */ + if (vga.mem.memsize < _KB_bytes(128)) vga.mem.memsize = _KB_bytes(128); /* FIXME: Right? */ break; case MCH_EGA: - if (vga.vmemsize <= _KB_bytes(128)) vga.vmemsize = _KB_bytes(128); /* Either 128KB or 256KB */ - else vga.vmemsize = _KB_bytes(256); + // EGA cards supported either 64KB, 128KB or 256KB. + if (vga.mem.memsize <= _KB_bytes(64)) vga.mem.memsize = _KB_bytes(64); + else if (vga.mem.memsize <= _KB_bytes(128)) vga.mem.memsize = _KB_bytes(128); + else vga.mem.memsize = _KB_bytes(256); break; case MCH_VGA: - if (vga.vmemsize < _KB_bytes(256)) vga.vmemsize = _KB_bytes(256); + // TODO: There are reports of VGA cards that have less than 256KB in the early days of VGA. + // How does that work exactly, especially when 640x480 requires about 37KB per plane? + // Did these cards have some means to chain two bitplanes odd/even in the same way + // tha EGA did it? + if (vga.mem.memsize < _KB_bytes(256)) vga.mem.memsize = _KB_bytes(256); break; case MCH_AMSTRAD: - if (vga.vmemsize < _KB_bytes(64)) vga.vmemsize = _KB_bytes(64); /* FIXME: Right? */ + if (vga.mem.memsize < _KB_bytes(64)) vga.mem.memsize = _KB_bytes(64); /* FIXME: Right? */ break; case MCH_PC98: - if (vga.vmemsize < _KB_bytes(512)) vga.vmemsize = _KB_bytes(512); + if (vga.mem.memsize < _KB_bytes(512)) vga.mem.memsize = _KB_bytes(512); break; default: E_Exit("Unexpected machine"); }; - vga.vmemwrap = 256*1024; // default to 256KB VGA mem wrap + /* I'm sorry, emulating 640x350 4-color chained EGA graphics is + * harder than I thought and would require revision of quite a + * bit of VGA planar emulation to update only bitplane 0 and 2 + * in such a manner. --J.C. */ + if (IS_EGA_ARCH && vga.mem.memsize < _KB_bytes(128)) + LOG_MSG("WARNING: EGA 64KB emulation is very experimental and not well supported"); if (!IS_PC98_ARCH) SVGA_Setup_Driver(); // svga video memory size is set here, possibly over-riding the user's selection - LOG(LOG_VGA,LOG_NORMAL)("Video RAM: %uKB",vga.vmemsize>>10); + vga.mem.memmask = vga.mem.memsize - 1u; + + LOG(LOG_VGA,LOG_NORMAL)("Video RAM: %uKB",vga.mem.memsize>>10); VGA_SetupMemory(); // memory is allocated here if (!IS_PC98_ARCH) { @@ -912,7 +941,7 @@ void VGA_OnEnterPC98(Section *sec) { for (unsigned int i=0;i < 16;i++) vga.dac.combine[i] = i; vga.mode=M_PC98; - assert(vga.vmemsize >= 0x80000); + assert(vga.mem.memsize >= 0x80000); memset(vga.mem.linear,0,0x80000); VGA_StartResize(); @@ -1067,7 +1096,6 @@ void VGA_Init() { vga.tandy.draw_base = NULL; vga.tandy.mem_base = NULL; - vga.vmemsize_alloced = 0; LOG(LOG_MISC,LOG_DEBUG)("Initializing VGA"); VGA_TweakUserVsyncOffset(0.0f); @@ -1141,7 +1169,6 @@ void SVGA_Setup_Driver(void) { SVGA_Setup_ParadisePVGA1A(); break; default: - vga.vmemwrap = 256*1024; break; } } diff --git a/src/hardware/vga_crtc.cpp b/src/hardware/vga_crtc.cpp index 757dbeff3..f1d6055e2 100644 --- a/src/hardware/vga_crtc.cpp +++ b/src/hardware/vga_crtc.cpp @@ -33,6 +33,16 @@ void VGA_MapMMIO(void); void VGA_UnmapMMIO(void); void page_flip_debug_notify(); +void VGA_CheckAddrShift() { + //Byte,word,dword mode + if ( IS_VGA_ARCH && crtc(underline_location) & 0x40 ) + vga.config.addr_shift = 2u; + else if ( IS_EGAVGA_ARCH && crtc( mode_control) & 0x40 ) + vga.config.addr_shift = 0u; + else + vga.config.addr_shift = 1u; +} + void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen); Bitu DEBUG_EnableDebugger(void); @@ -295,18 +305,7 @@ void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen) { break; case 0x14: /* Underline Location Register */ crtc(underline_location)=val; - if (IS_VGA_ARCH) { - //Byte,word,dword mode - if ( crtc(underline_location) & 0x40 ) - vga.config.addr_shift = 2; - else if ( crtc( mode_control) & 0x40 ) - vga.config.addr_shift = 0; - else - vga.config.addr_shift = 1; - } else { - vga.config.addr_shift = 1; - } - + VGA_CheckAddrShift(); VGA_CheckScanLength(); /* 0-4 Position of underline within Character cell. @@ -339,13 +338,6 @@ void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen) { case 0x17: /* Mode Control Register */ crtc(mode_control)=val; vga.tandy.line_mask = (~val) & 3u; - //Byte,word,dword mode - if ( crtc(underline_location) & 0x40 ) - vga.config.addr_shift = 2u; - else if ( crtc( mode_control) & 0x40 ) - vga.config.addr_shift = 0u; - else - vga.config.addr_shift = 1u; if ( vga.tandy.line_mask ) { vga.tandy.line_shift = 13u; @@ -354,6 +346,8 @@ void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen) { vga.tandy.addr_mask = ~0u; vga.tandy.line_shift = 0; } + + VGA_CheckAddrShift(); VGA_CheckScanLength(); //Should we really need to do a determinemode here? diff --git a/src/hardware/vga_dac.cpp b/src/hardware/vga_dac.cpp index 93dd34c7c..4fbeeb906 100644 --- a/src/hardware/vga_dac.cpp +++ b/src/hardware/vga_dac.cpp @@ -178,50 +178,76 @@ Bitu read_p3c8(Bitu port, Bitu iolen){ return vga.dac.write_index; } +extern bool vga_palette_update_on_full_load; + +static unsigned char tmp_dac[3] = {0,0,0}; + void write_p3c9(Bitu port,Bitu val,Bitu iolen) { + bool update = false; + (void)iolen;//UNUSED (void)port;//UNUSED vga.dac.hidac_counter=0; val&=0x3f; - switch (vga.dac.pel_index) { - case 0: - vga.dac.rgb[vga.dac.write_index].red=val; - vga.dac.pel_index=1; - break; - case 1: - vga.dac.rgb[vga.dac.write_index].green=val; - vga.dac.pel_index=2; - break; - case 2: - vga.dac.rgb[vga.dac.write_index].blue=val; - switch (vga.mode) { - case M_VGA: - case M_LIN8: - VGA_DAC_UpdateColor( vga.dac.write_index ); - if ( GCC_UNLIKELY( vga.dac.pel_mask != 0xff)) { - Bitu index = vga.dac.write_index; - if ( (index & vga.dac.pel_mask) == index ) { - for ( Bitu i = index+1;i<256;i++) - if ( (i & vga.dac.pel_mask) == index ) - VGA_DAC_UpdateColor( i ); - } - } - break; - default: - /* Check for attributes and DAC entry link */ - for (Bitu i=0;i<16;i++) { - if (vga.dac.combine[i]==vga.dac.write_index) { - VGA_DAC_SendColor( i, vga.dac.write_index ); - } + + if (vga.dac.pel_index < 3) { + tmp_dac[vga.dac.pel_index]=val; + + if (!vga_palette_update_on_full_load) { + /* update palette right away, partial change */ + switch (vga.dac.pel_index) { + case 0: + vga.dac.rgb[vga.dac.write_index].red=tmp_dac[0]; + break; + case 1: + vga.dac.rgb[vga.dac.write_index].green=tmp_dac[1]; + break; + case 2: + vga.dac.rgb[vga.dac.write_index].blue=tmp_dac[2]; + break; } + update = true; } - vga.dac.read_index=vga.dac.write_index++; // NTS: Paradise SVGA behavior - vga.dac.pel_index=0; - break; - default: - LOG(LOG_VGAGFX,LOG_NORMAL)("VGA:DAC:Illegal Pel Index"); //If this can actually happen that will be the day - break; - }; + else if (vga.dac.pel_index == 2) { + /* update palette ONLY when all three are given */ + vga.dac.rgb[vga.dac.write_index].red=tmp_dac[0]; + vga.dac.rgb[vga.dac.write_index].green=tmp_dac[1]; + vga.dac.rgb[vga.dac.write_index].blue=tmp_dac[2]; + update = true; + } + + if ((++vga.dac.pel_index) >= 3) + vga.dac.pel_index = 0; + } + + if (update) { + switch (vga.mode) { + case M_VGA: + case M_LIN8: + VGA_DAC_UpdateColor( vga.dac.write_index ); + if ( GCC_UNLIKELY( vga.dac.pel_mask != 0xff)) { + Bitu index = vga.dac.write_index; + if ( (index & vga.dac.pel_mask) == index ) { + for ( Bitu i = index+1;i<256;i++) + if ( (i & vga.dac.pel_mask) == index ) + VGA_DAC_UpdateColor( i ); + } + } + break; + default: + /* Check for attributes and DAC entry link */ + for (Bitu i=0;i<16;i++) { + if (vga.dac.combine[i]==vga.dac.write_index) { + VGA_DAC_SendColor( i, vga.dac.write_index ); + } + } + break; + } + + /* only if we just completed a color should we advance */ + if (vga.dac.pel_index == 0) + vga.dac.read_index = vga.dac.write_index++; // NTS: Paradise SVGA behavior + } } Bitu read_p3c9(Bitu port,Bitu iolen) { diff --git a/src/hardware/vga_draw.cpp b/src/hardware/vga_draw.cpp index 7e6b756a4..7ec56e323 100644 --- a/src/hardware/vga_draw.cpp +++ b/src/hardware/vga_draw.cpp @@ -40,6 +40,31 @@ #include "pc98_gdc.h" #include "pc98_gdc_const.h" +const char* const mode_texts[M_MAX] = { + "M_CGA2", // 0 + "M_CGA4", + "M_EGA", + "M_VGA", + "M_LIN4", + "M_LIN8", // 5 + "M_LIN15", + "M_LIN16", + "M_LIN24", + "M_LIN32", + "M_TEXT", // 10 + "M_HERC_GFX", + "M_HERC_TEXT", + "M_CGA16", + "M_TANDY2", + "M_TANDY4", // 15 + "M_TANDY16", + "M_TANDY_TEXT", + "M_AMSTRAD", + "M_PC98", + "M_FM_TOWNS", // 20 STUB + "M_ERROR" +}; + #if defined(_MSC_VER) # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ # pragma warning(disable:4305) /* truncation from double to float */ @@ -480,29 +505,58 @@ static Bit8u * EGA_Draw_VGA_Planar_Xlat8_Line(Bitu vidstart, Bitu /*line*/) { Bit8u* temps = (Bit8u*) TempLine; Bit32u t1,t2,tmp; - for (Bitu i = 0; i < ((vga.draw.line_length)+vga.draw.panning); i += 8) { - t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ])); - t1 = (t1 >> 4) & 0x0f0f0f0f; - t2 &= 0x0f0f0f0f; - vidstart += 4; + if (vga.seq.clocking_mode&4) { /* odd/even mode serialization */ + for (Bitu i = 0; i < ((vga.draw.line_length)+vga.draw.panning);) { + if (vidstart > vga.draw.linear_mask) + vidstart = (vidstart + 4u) & vga.draw.linear_mask; - tmp = Expand16Table[0][(t1>>0)&0xFF] | - Expand16Table[1][(t1>>8)&0xFF] | - Expand16Table[2][(t1>>16)&0xFF] | - Expand16Table[3][(t1>>24)&0xFF]; - temps[i+0] = vga.attr.palette[(tmp>>0)&0xFF]; - temps[i+1] = vga.attr.palette[(tmp>>8)&0xFF]; - temps[i+2] = vga.attr.palette[(tmp>>16)&0xFF]; - temps[i+3] = vga.attr.palette[(tmp>>24)&0xFF]; + t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ])); + t1 = (t1 >> 4) & 0x0f0f0f0f; + t2 &= 0x0f0f0f0f; + vidstart += 4 * 2; - tmp = Expand16Table[0][(t2>>0)&0xFF] | - Expand16Table[1][(t2>>8)&0xFF] | - Expand16Table[2][(t2>>16)&0xFF] | - Expand16Table[3][(t2>>24)&0xFF]; - temps[i+4] = vga.attr.palette[(tmp>>0)&0xFF]; - temps[i+5] = vga.attr.palette[(tmp>>8)&0xFF]; - temps[i+6] = vga.attr.palette[(tmp>>16)&0xFF]; - temps[i+7] = vga.attr.palette[(tmp>>24)&0xFF]; + for (Bitu w = 0;w < 2;w++,t1>>=8,t2>>=8,i+=8) { + tmp = Expand16Table[0][(t1>>0)&0xFF] | + Expand16Table[2][(t1>>16)&0xFF]; + temps[i+0] = vga.attr.palette[(tmp>>0)&0xFF]; + temps[i+1] = vga.attr.palette[(tmp>>8)&0xFF]; + temps[i+2] = vga.attr.palette[(tmp>>16)&0xFF]; + temps[i+3] = vga.attr.palette[(tmp>>24)&0xFF]; + + tmp = Expand16Table[0][(t2>>0)&0xFF] | + Expand16Table[2][(t2>>16)&0xFF]; + temps[i+4] = vga.attr.palette[(tmp>>0)&0xFF]; + temps[i+5] = vga.attr.palette[(tmp>>8)&0xFF]; + temps[i+6] = vga.attr.palette[(tmp>>16)&0xFF]; + temps[i+7] = vga.attr.palette[(tmp>>24)&0xFF]; + } + } + } + else { + for (Bitu i = 0; i < ((vga.draw.line_length)+vga.draw.panning); i += 8) { + t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ])); + t1 = (t1 >> 4) & 0x0f0f0f0f; + t2 &= 0x0f0f0f0f; + vidstart += 4; + + tmp = Expand16Table[0][(t1>>0)&0xFF] | + Expand16Table[1][(t1>>8)&0xFF] | + Expand16Table[2][(t1>>16)&0xFF] | + Expand16Table[3][(t1>>24)&0xFF]; + temps[i+0] = vga.attr.palette[(tmp>>0)&0xFF]; + temps[i+1] = vga.attr.palette[(tmp>>8)&0xFF]; + temps[i+2] = vga.attr.palette[(tmp>>16)&0xFF]; + temps[i+3] = vga.attr.palette[(tmp>>24)&0xFF]; + + tmp = Expand16Table[0][(t2>>0)&0xFF] | + Expand16Table[1][(t2>>8)&0xFF] | + Expand16Table[2][(t2>>16)&0xFF] | + Expand16Table[3][(t2>>24)&0xFF]; + temps[i+4] = vga.attr.palette[(tmp>>0)&0xFF]; + temps[i+5] = vga.attr.palette[(tmp>>8)&0xFF]; + temps[i+6] = vga.attr.palette[(tmp>>16)&0xFF]; + temps[i+7] = vga.attr.palette[(tmp>>24)&0xFF]; + } } return TempLine + (vga.draw.panning); @@ -1590,7 +1644,7 @@ static void VGA_DisplayStartLatch(Bitu /*val*/) { * a point of reference how far to displace the scanline when wavy effects are * made */ vga_display_start_hretrace = vga.crtc.start_horizontal_retrace; - vga.config.real_start=vga.config.display_start & (vga.vmemwrap-1); + vga.config.real_start=vga.config.display_start & vga.mem.memmask; vga.draw.bytes_skip = vga.config.bytes_skip; } @@ -1796,8 +1850,11 @@ static void VGA_VerticalTimer(Bitu /*val*/) { switch (vga.mode) { case M_EGA: - if (!(vga.crtc.mode_control&0x1u)) vga.draw.linear_mask &= ~0x10000u; - else vga.draw.linear_mask |= 0x10000u; + if (vga.mem.memmask >= 0x1FFFFu) { + if (!(vga.crtc.mode_control&0x1u)) vga.draw.linear_mask &= ~0x10000u; + else vga.draw.linear_mask |= 0x10000u; + } + /* fall through */ case M_LIN4: vga.draw.byte_panning_shift = 4u; vga.draw.address += vga.draw.bytes_skip; @@ -1924,6 +1981,9 @@ void VGA_CheckScanLength(void) { vga.draw.address_add=vga.config.scan_len*16; else vga.draw.address_add=vga.config.scan_len*8; + + if (IS_EGA_ARCH && (vga.seq.clocking_mode&4)) + vga.draw.address_add*=2; break; case M_VGA: case M_LIN8: @@ -2473,7 +2533,7 @@ void VGA_SetupDrawing(Bitu /*val*/) { break; } vga.draw.linear_base = vga.mem.linear; - vga.draw.linear_mask = vga.vmemwrap - 1; + vga.draw.linear_mask = vga.mem.memmask; vga.draw.planar_mask = vga.draw.linear_mask >> 2; Bitu pix_per_char = 8; switch (vga.mode) { @@ -2752,20 +2812,6 @@ void VGA_SetupDrawing(Bitu /*val*/) { vga.draw.delay.vblkstart,vga.draw.delay.vblkend, vga.draw.delay.vrstart,vga.draw.delay.vrend); - const char* const mode_texts[] = { - "M_CGA2", "M_CGA4", - "M_EGA", "M_VGA", - "M_LIN4", "M_LIN8", "M_LIN15", "M_LIN16", "M_LIN24", "M_LIN32", - "M_TEXT", - "M_HERC_GFX", "M_HERC_TEXT", - "M_CGA16", "M_TANDY2", "M_TANDY4", "M_TANDY16", "M_TANDY_TEXT", - "M_AMSTRAD", "M_PC98", - - "M_FM_TOWNS",//STUB - - "M_ERROR" - }; - LOG(LOG_VGA,LOG_NORMAL)("video clock: %3.2fMHz mode %s", oscclock/1000000.0, mode_texts[vga.mode]); #endif diff --git a/src/hardware/vga_memory.cpp b/src/hardware/vga_memory.cpp index 87675ec09..6d23ded98 100644 --- a/src/hardware/vga_memory.cpp +++ b/src/hardware/vga_memory.cpp @@ -32,22 +32,25 @@ #include "pc98_cg.h" #include "pc98_gdc.h" +extern bool non_cga_ignore_oddeven; +extern bool non_cga_ignore_oddeven_engage; + #ifndef C_VGARAM_CHECKED #define C_VGARAM_CHECKED 1 #endif #if C_VGARAM_CHECKED // Checked linear offset -#define CHECKED(v) ((v)&(vga.vmemwrap-1)) +#define CHECKED(v) ((v)&vga.mem.memmask) // Checked planar offset (latched access) -#define CHECKED2(v) ((v)&((vga.vmemwrap>>2)-1)) +#define CHECKED2(v) ((v)&(vga.mem.memmask>>2)) #else #define CHECKED(v) (v) #define CHECKED2(v) (v) #endif -#define CHECKED3(v) ((v)&(vga.vmemwrap-1)) -#define CHECKED4(v) ((v)&((vga.vmemwrap>>2)-1)) +#define CHECKED3(v) ((v)&vga.mem.memmask) +#define CHECKED4(v) ((v)&(vga.mem.memmask>>2)) #define TANDY_VIDBASE(_X_) &MemBase[ 0x80000 + (_X_)] @@ -151,173 +154,115 @@ INLINE static Bit32u ModeOperation(Bit8u val) { static struct { Bitu base, mask; } vgapages; - -class VGA_UnchainedRead_Handler : public PageHandler { -public: - VGA_UnchainedRead_Handler(Bitu flags) : PageHandler(flags) {} - Bitu readHandler(PhysPt start) { - PhysPt memstart = start; - unsigned char bplane; - if (vga.gfx.miscellaneous&2) /* Odd/Even mode */ - memstart &= ~1u; +static inline Bitu VGA_Generic_Read_Handler(PhysPt planeaddr,PhysPt rawaddr,unsigned char plane) { + const unsigned char hobit_n = (vga.seq.memory_mode&2/*Extended Memory*/) ? 16u : 14u; - vga.latch.d=((Bit32u*)vga.mem.linear)[memstart]; - switch (vga.config.read_mode) { - case 0: - bplane = vga.config.read_map_select; - /* NTS: We check the sequencer AND the GC to know whether we mask the bitplane line this, - * even though in TEXT mode we only check the sequencer. Without this extra check, - * Windows 95 and Windows 3.1 will exhibit glitches in the standard VGA 640x480x16 - * planar mode */ - if (!(vga.seq.memory_mode&4) && (vga.gfx.miscellaneous&2)) /* FIXME: How exactly do SVGA cards determine this? */ - bplane = (bplane & ~1u) + (start & 1u); /* FIXME: Is this what VGA cards do? It makes sense to me */ - return (vga.latch.b[bplane]); - case 1: - VGA_Latch templatch; - templatch.d=(vga.latch.d & FillTable[vga.config.color_dont_care]) ^ FillTable[vga.config.color_compare & vga.config.color_dont_care]; - return (Bit8u)~(templatch.b[0] | templatch.b[1] | templatch.b[2] | templatch.b[3]); - } - return 0; - } -public: - Bitu readb(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED2(addr); - return readHandler(addr); - } - Bitu readw(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED2(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - return ret; - } - Bitu readd(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED2(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - ret |= (readHandler(addr+2) << 16); - ret |= (readHandler(addr+3) << 24); - return ret; - } -}; + /* Sequencer Memory Mode Register (04h) + * bits[3:3] = Chain 4 enable + * bits[2:2] = Odd/Even Host Memory Write Addressing Disable + * bits[1:1] = Extended memory (when EGA cards have > 64KB of RAM) + * + * NTS: Real hardware experience says that despite the name, the Odd/Even bit affects reading as well */ + if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */ + plane = (plane & ~1u) + (rawaddr & 1u); -class VGA_ChainedEGA_Handler : public PageHandler { -public: - Bitu readHandler(PhysPt addr) { - return vga.mem.linear[addr]; - } - void writeHandler(PhysPt start, Bit8u val) { - /* FIXME: "Chained EGA" how does that work?? */ - ModeOperation(val); - /* Update video memory and the pixel buffer */ - vga.mem.linear[start] = val; - } -public: - VGA_ChainedEGA_Handler() : PageHandler(PFLAG_NOCODE) {} - void writeb(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - } - void writew(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - } - void writed(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - writeHandler(addr+2,(Bit8u)(val >> 16)); - writeHandler(addr+3,(Bit8u)(val >> 24)); - } - Bitu readb(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - return readHandler(addr); - } - Bitu readw(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - return ret; - } - Bitu readd(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - ret |= (readHandler(addr+2) << 16); - ret |= (readHandler(addr+3) << 24); - return ret; - } -}; + /* Graphics Controller: Miscellaneous Graphics Register register (06h) + * bits[3:2] = memory map select + * bits[1:1] = Chain Odd/Even Enable + * bits[0:0] = Alphanumeric Mode Disable + * + * http://www.osdever.net/FreeVGA/vga/graphreg.htm + * + * When enabled, address bit A0 (bit 0) becomes bit 0 of the plane index. + * Then when addressing VRAM A0 is replaced by a "higher order bit", which is + * probably A14 or A16 depending on Extended Memory bit 1 in Sequencer register 04h memory mode */ + if ((vga.gfx.miscellaneous&2) && !non_cga_ignore_oddeven_engage) {/* Odd/Even enable */ + const PhysPt mask = (1u << hobit_n) - 2u; + const PhysPt hobit = (planeaddr >> hobit_n) & 1u; + /* 1 << 14 = 0x4000 + * 1 << 14 - 1 = 0x3FFF + * 1 << 14 - 2 = 0x3FFE + * The point is to mask upper bit AND the LSB */ + planeaddr = (planeaddr & mask & (vga.mem.memmask >> 2u)) + hobit; + } + else { + const PhysPt mask = (1u << hobit_n) - 1u; + planeaddr &= mask & (vga.mem.memmask >> 2u); + } -class VGA_UnchainedEGA_Handler : public VGA_UnchainedRead_Handler { -public: - VGA_UnchainedEGA_Handler(Bitu flags) : VGA_UnchainedRead_Handler(flags) {} - template< bool wrapping> - void writeHandler(PhysPt start, Bit8u val) { - Bit32u data=ModeOperation(val); - /* Update video memory and the pixel buffer */ - VGA_Latch pixels; - pixels.d=((Bit32u*)vga.mem.linear)[start]; - pixels.d&=vga.config.full_not_map_mask; - pixels.d|=(data & vga.config.full_map_mask); - ((Bit32u*)vga.mem.linear)[start]=pixels.d; - } -public: - VGA_UnchainedEGA_Handler() : VGA_UnchainedRead_Handler(PFLAG_NOCODE) {} - void writeb(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - } - void writew(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - } - void writed(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - writeHandler(addr+2,(Bit8u)(val >> 16)); - writeHandler(addr+3,(Bit8u)(val >> 24)); - } -}; + vga.latch.d=((Bit32u*)vga.mem.linear)[planeaddr]; + switch (vga.config.read_mode) { + case 0: + return (vga.latch.b[plane]); + case 1: + VGA_Latch templatch; + templatch.d=(vga.latch.d & FillTable[vga.config.color_dont_care]) ^ FillTable[vga.config.color_compare & vga.config.color_dont_care]; + return (Bit8u)~(templatch.b[0] | templatch.b[1] | templatch.b[2] | templatch.b[3]); + } + + return 0; +} + +template static inline void VGA_Generic_Write_Handler(PhysPt planeaddr,PhysPt rawaddr,Bit8u val) { + const unsigned char hobit_n = (vga.seq.memory_mode&2/*Extended Memory*/) ? 16u : 14u; + Bit32u mask = vga.config.full_map_mask; + + /* Sequencer Memory Mode Register (04h) + * bits[3:3] = Chain 4 enable + * bits[2:2] = Odd/Even Host Memory Write Addressing Disable + * bits[1:1] = Extended memory (when EGA cards have > 64KB of RAM) + * + * NTS: Real hardware experience says that despite the name, the Odd/Even bit affects reading as well */ + if (chained) { + if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */ + mask &= 0xFF00FFu << ((rawaddr & 1u) * 8u); + else + mask &= 0xFFu << ((rawaddr & 3u) * 8u); + } + else { + if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */ + mask &= 0xFF00FFu << ((rawaddr & 1u) * 8u); + } + + /* Graphics Controller: Miscellaneous Graphics Register register (06h) + * bits[3:2] = memory map select + * bits[1:1] = Chain Odd/Even Enable + * bits[0:0] = Alphanumeric Mode Disable + * + * http://www.osdever.net/FreeVGA/vga/graphreg.htm + * + * When enabled, address bit A0 (bit 0) becomes bit 0 of the plane index. + * Then when addressing VRAM A0 is replaced by a "higher order bit", which is + * probably A14 or A16 depending on Extended Memory bit 1 in Sequencer register 04h memory mode */ + if ((vga.gfx.miscellaneous&2) && !non_cga_ignore_oddeven_engage) {/* Odd/Even enable */ + const PhysPt mask = (1u << hobit_n) - 2u; + const PhysPt hobit = (planeaddr >> hobit_n) & 1u; + /* 1 << 14 = 0x4000 + * 1 << 14 - 1 = 0x3FFF + * 1 << 14 - 2 = 0x3FFE + * The point is to mask upper bit AND the LSB */ + planeaddr = (planeaddr & mask & (vga.mem.memmask >> 2u)) + hobit; + } + else { + const PhysPt mask = (1u << hobit_n) - 1u; + planeaddr &= mask & (vga.mem.memmask >> 2u); + } + + Bit32u data=ModeOperation(val); + VGA_Latch pixels; + + pixels.d =((Bit32u*)vga.mem.linear)[planeaddr]; + pixels.d&=~mask; + pixels.d|=(data & mask); + + /* FIXME: A better method (I think) is to have the VGA text drawing code + * directly reference the font data in bitplane #2 instead of + * this hack */ + vga.draw.font[planeaddr] = pixels.b[2]; + + ((Bit32u*)vga.mem.linear)[planeaddr]=pixels.d; +} // Slow accurate emulation. // This version takes the Graphics Controller bitmask and ROPs into account. @@ -334,29 +279,28 @@ class VGA_ChainedVGA_Slow_Handler : public PageHandler { public: VGA_ChainedVGA_Slow_Handler() : PageHandler(PFLAG_NOCODE) {} static INLINE Bitu readHandler8(PhysPt addr ) { - vga.latch.d=((Bit32u*)vga.mem.linear)[addr&~3u]; - return vga.latch.b[addr&3]; + // planar byte offset = addr & ~3u (discard low 2 bits) + // planer index = addr & 3u (use low 2 bits as plane index) + // FIXME: Does chained mode use the lower 2 bits of the CPU address or does it use the read mode select??? + return VGA_Generic_Read_Handler(addr&~3u, addr, addr&3u); } static INLINE void writeHandler8(PhysPt addr, Bitu val) { - VGA_Latch pixels; - - /* byte-sized template specialization with masking */ - pixels.d = ModeOperation(val); - /* Update video memory and the pixel buffer */ - hostWrite( &vga.mem.linear[((addr&~3u)<<2u)+(addr&3u)], pixels.b[addr&3u] ); + // planar byte offset = addr & ~3u (discard low 2 bits) + // planer index = addr & 3u (use low 2 bits as plane index) + return VGA_Generic_Write_Handler(addr&~3u, addr, val); } Bitu readb(PhysPt addr ) { VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); return readHandler8( addr ); } Bitu readw(PhysPt addr ) { VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); Bitu ret = (readHandler8( addr+0 ) << 0 ); ret |= (readHandler8( addr+1 ) << 8 ); return ret; @@ -365,7 +309,7 @@ public: VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); Bitu ret = (readHandler8( addr+0 ) << 0 ); ret |= (readHandler8( addr+1 ) << 8 ); ret |= (readHandler8( addr+2 ) << 16 ); @@ -376,14 +320,14 @@ public: VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr, val ); } void writew(PhysPt addr,Bitu val) { VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr+0, val >> 0 ); writeHandler8( addr+1, val >> 8 ); } @@ -391,7 +335,7 @@ public: VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr+0, val >> 0 ); writeHandler8( addr+1, val >> 8 ); writeHandler8( addr+2, val >> 16 ); @@ -399,196 +343,31 @@ public: } }; -//Slighly unusual version, will directly write 8,16,32 bits values -class VGA_ChainedVGA_Handler : public PageHandler { -public: - VGA_ChainedVGA_Handler() : PageHandler(PFLAG_NOCODE) {} - template - static INLINE Bitu readHandler(PhysPt addr ) { - return hostRead( &vga.mem.linear[((addr&0xFFFC)<<2)+(addr&3)] ); - } - template - static INLINE void writeHandler(PhysPt addr, Bitu val) { - // No need to check for compatible chains here, this one is only enabled if that bit is set - hostWrite( &vga.mem.linear[((addr&0xFFFC)<<2)+(addr&3)], val ); - } - Bitu readb(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - return readHandler( addr ); - } - Bitu readw(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 1)) { - Bitu ret = (readHandler( addr+0 ) << 0 ); - ret |= (readHandler( addr+1 ) << 8 ); - return ret; - } else - return readHandler( addr ); - } - Bitu readd(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 3)) { - Bitu ret = (readHandler( addr+0 ) << 0 ); - ret |= (readHandler( addr+1 ) << 8 ); - ret |= (readHandler( addr+2 ) << 16 ); - ret |= (readHandler( addr+3 ) << 24 ); - return ret; - } else - return readHandler( addr ); - } - void writeb(PhysPt addr, Bitu val ) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - writeHandler( addr, val ); - } - void writew(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 1)) { - writeHandler( addr+0, val >> 0 ); - writeHandler( addr+1, val >> 8 ); - } else { - writeHandler( addr, val ); - } - } - void writed(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 3)) { - writeHandler( addr+0, val >> 0 ); - writeHandler( addr+1, val >> 8 ); - writeHandler( addr+2, val >> 16 ); - writeHandler( addr+3, val >> 24 ); - } else { - writeHandler( addr, val ); - } - } -}; - -// alternate version for ET4000 emulation. -// ET4000 cards implement 256-color chain-4 differently than most cards. -class VGA_ET4000_ChainedVGA_Handler : public PageHandler { -public: - VGA_ET4000_ChainedVGA_Handler() : PageHandler(PFLAG_NOCODE) {} - template - static INLINE Bitu readHandler(PhysPt addr ) { - return hostRead( &vga.mem.linear[addr] ); - } - template - static INLINE void writeHandler(PhysPt addr, Bitu val) { - // No need to check for compatible chains here, this one is only enabled if that bit is set - hostWrite( &vga.mem.linear[addr], val ); - } - Bitu readb(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - return readHandler( addr ); - } - Bitu readw(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 1)) { - Bitu ret = (readHandler( addr+0 ) << 0 ); - ret |= (readHandler( addr+1 ) << 8 ); - return ret; - } else - return readHandler( addr ); - } - Bitu readd(PhysPt addr ) { - VGAMEM_USEC_read_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_read_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 3)) { - Bitu ret = (readHandler( addr+0 ) << 0 ); - ret |= (readHandler( addr+1 ) << 8 ); - ret |= (readHandler( addr+2 ) << 16 ); - ret |= (readHandler( addr+3 ) << 24 ); - return ret; - } else - return readHandler( addr ); - } - void writeb(PhysPt addr, Bitu val ) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - writeHandler( addr, val ); - } - void writew(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 1)) { - writeHandler( addr+0, val >> 0 ); - writeHandler( addr+1, val >> 8 ); - } else { - writeHandler( addr, val ); - } - } - void writed(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - addr += vga.svga.bank_write_full; - addr = CHECKED(addr); - if (GCC_UNLIKELY(addr & 3)) { - writeHandler( addr+0, val >> 0 ); - writeHandler( addr+1, val >> 8 ); - writeHandler( addr+2, val >> 16 ); - writeHandler( addr+3, val >> 24 ); - } else { - writeHandler( addr, val ); - } - } -}; - class VGA_ET4000_ChainedVGA_Slow_Handler : public PageHandler { public: VGA_ET4000_ChainedVGA_Slow_Handler() : PageHandler(PFLAG_NOCODE) {} static INLINE Bitu readHandler8(PhysPt addr ) { - vga.latch.d=((Bit32u*)vga.mem.linear)[addr>>2]; - return vga.latch.b[addr&3]; + // planar byte offset = addr >> 2 (shift 2 bits to the right) + // planer index = addr & 3u (use low 2 bits as plane index) + return VGA_Generic_Read_Handler(addr>>2u, addr, addr&3u); } static INLINE void writeHandler8(PhysPt addr, Bitu val) { - VGA_Latch pixels; - - /* byte-sized template specialization with masking */ - pixels.d = ModeOperation(val); - /* Update video memory and the pixel buffer */ - hostWrite( &vga.mem.linear[addr], pixels.b[addr&3] ); + // planar byte offset = addr >> 2 (shift 2 bits to the right) + // planer index = addr & 3u (use low 2 bits as plane index) + return VGA_Generic_Write_Handler(addr>>2u, addr, val); } Bitu readb(PhysPt addr ) { VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); return readHandler8( addr ); } Bitu readw(PhysPt addr ) { VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); Bitu ret = (readHandler8( addr+0 ) << 0 ); ret |= (readHandler8( addr+1 ) << 8 ); return ret; @@ -597,7 +376,7 @@ public: VGAMEM_USEC_read_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_read_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); Bitu ret = (readHandler8( addr+0 ) << 0 ); ret |= (readHandler8( addr+1 ) << 8 ); ret |= (readHandler8( addr+2 ) << 16 ); @@ -608,14 +387,14 @@ public: VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr, val ); } void writew(PhysPt addr,Bitu val) { VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr+0, val >> 0 ); writeHandler8( addr+1, val >> 8 ); } @@ -623,7 +402,7 @@ public: VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED(addr); +// addr = CHECKED(addr); writeHandler8( addr+0, val >> 0 ); writeHandler8( addr+1, val >> 8 ); writeHandler8( addr+2, val >> 16 ); @@ -631,73 +410,57 @@ public: } }; -class VGA_UnchainedVGA_Handler : public VGA_UnchainedRead_Handler { +class VGA_UnchainedVGA_Handler : public PageHandler { public: - void writeHandler( PhysPt addr, Bit8u val ) { - PhysPt memaddr = addr; - Bit32u data=ModeOperation(val); - VGA_Latch pixels; - - if (vga.gfx.miscellaneous&2) /* Odd/Even mode masks off A0 */ - memaddr &= ~1u; - - pixels.d=((Bit32u*)vga.mem.linear)[memaddr]; - - /* Odd/even emulation, emulation fix for Windows 95's boot screen */ - if (!(vga.seq.memory_mode&4)) { - /* You're probably wondering what the hell odd/even mode has to do with Windows 95's boot - * screen, right? Well, hopefully you won't puke when you read the following... - * - * When Windows 95 starts up and shows it's boot logo, it calls INT 10h to set mode 0x13. - * But it calls INT 10h with AX=0x93 which means set mode 0x13 and don't clear VRAM. Then, - * it uses mode X to write the logo to the BOTTOM half of VGA RAM, at 0x8000 to be exact, - * and of course, reprograms the CRTC offset register to make that visible. - * THEN, it reprograms the registers to map VRAM at 0xB800, disable Chain 4, re-enable - * odd/even mode, and then allows both DOS and the BIOS to write to the top half of VRAM - * as if still running in 80x25 alphanumeric text mode. It even sets the video mode byte - * at 0x40:0x49 to 0x03 to continue the illusion! - * - * When Windows 95 is ready to restore text mode, it just switches back (this time, calling - * the saved INT 10h pointer directly) again without clearing VRAM. - * - * So if you wonder why I would spend time implementing odd/even emulation for VGA unchained - * mode... that's why. You can thank Microsoft for that. */ - if (addr & 1) { - if (vga.seq.map_mask & 0x2) /* bitplane 1: attribute RAM */ - pixels.b[1] = data >> 8; - if (vga.seq.map_mask & 0x8) /* bitplane 3: unused RAM */ - pixels.b[3] = data >> 24; - } - else { - if (vga.seq.map_mask & 0x1) /* bitplane 0: character RAM */ - pixels.b[0] = data; - if (vga.seq.map_mask & 0x4) { /* bitplane 2: font RAM */ - pixels.b[2] = data >> 16; - vga.draw.font[memaddr] = data >> 16; - } - } - } - else { - pixels.d&=vga.config.full_not_map_mask; - pixels.d|=(data & vga.config.full_map_mask); - } - - ((Bit32u*)vga.mem.linear)[memaddr]=pixels.d; + Bitu readHandler(PhysPt start) { + return VGA_Generic_Read_Handler(start, start, vga.config.read_map_select); } public: - VGA_UnchainedVGA_Handler() : VGA_UnchainedRead_Handler(PFLAG_NOCODE) {} + Bitu readb(PhysPt addr) { + VGAMEM_USEC_read_delay(); + addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; + addr += vga.svga.bank_read_full; +// addr = CHECKED2(addr); + return readHandler(addr); + } + Bitu readw(PhysPt addr) { + VGAMEM_USEC_read_delay(); + addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; + addr += vga.svga.bank_read_full; +// addr = CHECKED2(addr); + Bitu ret = (readHandler(addr+0) << 0); + ret |= (readHandler(addr+1) << 8); + return ret; + } + Bitu readd(PhysPt addr) { + VGAMEM_USEC_read_delay(); + addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; + addr += vga.svga.bank_read_full; +// addr = CHECKED2(addr); + Bitu ret = (readHandler(addr+0) << 0); + ret |= (readHandler(addr+1) << 8); + ret |= (readHandler(addr+2) << 16); + ret |= (readHandler(addr+3) << 24); + return ret; + } +public: + void writeHandler(PhysPt start, Bit8u val) { + VGA_Generic_Write_Handler(start, start, val); + } +public: + VGA_UnchainedVGA_Handler() : PageHandler(PFLAG_NOCODE) {} void writeb(PhysPt addr,Bitu val) { VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); +// addr = CHECKED2(addr); writeHandler(addr+0,(Bit8u)(val >> 0)); } void writew(PhysPt addr,Bitu val) { VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); +// addr = CHECKED2(addr); writeHandler(addr+0,(Bit8u)(val >> 0)); writeHandler(addr+1,(Bit8u)(val >> 8)); } @@ -705,7 +468,7 @@ public: VGAMEM_USEC_write_delay(); addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; addr += vga.svga.bank_write_full; - addr = CHECKED2(addr); +// addr = CHECKED2(addr); writeHandler(addr+0,(Bit8u)(val >> 0)); writeHandler(addr+1,(Bit8u)(val >> 8)); writeHandler(addr+2,(Bit8u)(val >> 16)); @@ -1729,58 +1492,6 @@ public: } }; -class VGA_TEXT_PageHandler : public PageHandler { -public: - VGA_TEXT_PageHandler() : PageHandler(PFLAG_NOCODE) {} - Bitu readb(PhysPt addr) { - unsigned char bplane; - - VGAMEM_USEC_read_delay(); - - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - bplane = vga.gfx.read_map_select; - - if (!(vga.seq.memory_mode&4)) - bplane = (bplane & ~1u) + (addr & 1u); /* FIXME: Is this what VGA cards do? It makes sense to me */ - if (vga.gfx.miscellaneous&2) /* Odd/Even mode */ - addr &= ~1u; - - return vga.mem.linear[CHECKED3(vga.svga.bank_read_full+(addr<<2)+bplane)]; - } - void writeb(PhysPt addr,Bitu val){ - VGA_Latch pixels; - Bitu memaddr; - - VGAMEM_USEC_write_delay(); - - addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask; - memaddr = addr; - - /* Chain Odd/Even enable: A0 is replaced by a "higher order bit" (0 apparently) */ - if (vga.gfx.miscellaneous&2) - memaddr &= ~1u; - - pixels.d=((Bit32u*)vga.mem.linear)[memaddr]; - - if ((vga.seq.memory_mode&4)/*Odd/Even disable*/ || (addr & 1)) { - if (vga.seq.map_mask & 0x2) /* bitplane 1: attribute RAM */ - pixels.b[1] = val; - if (vga.seq.map_mask & 0x8) /* bitplane 3: unused RAM */ - pixels.b[3] = val; - } - if ((vga.seq.memory_mode&4)/*Odd/Even disable*/ || !(addr & 1)) { - if (vga.seq.map_mask & 0x1) /* bitplane 0: character RAM */ - pixels.b[0] = val; - if (vga.seq.map_mask & 0x4) { /* bitplane 2: font RAM */ - pixels.b[2] = val; - vga.draw.font[memaddr] = val; - } - } - - ((Bit32u*)vga.mem.linear)[memaddr]=pixels.d; - } -}; - class VGA_Map_Handler : public PageHandler { public: VGA_Map_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {} @@ -1815,63 +1526,12 @@ public: }; -class VGA_LIN4_Handler : public VGA_UnchainedEGA_Handler { -public: - VGA_LIN4_Handler() : VGA_UnchainedEGA_Handler(PFLAG_NOCODE) {} - void writeb(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - } - void writew(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - } - void writed(PhysPt addr,Bitu val) { - VGAMEM_USEC_write_delay(); - addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - writeHandler(addr+0,(Bit8u)(val >> 0)); - writeHandler(addr+1,(Bit8u)(val >> 8)); - writeHandler(addr+2,(Bit8u)(val >> 16)); - writeHandler(addr+3,(Bit8u)(val >> 24)); - } - Bitu readb(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - return readHandler(addr); - } - Bitu readw(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - return ret; - } - Bitu readd(PhysPt addr) { - VGAMEM_USEC_read_delay(); - addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff); - addr = CHECKED4(addr); - Bitu ret = (readHandler(addr+0) << 0); - ret |= (readHandler(addr+1) << 8); - ret |= (readHandler(addr+2) << 16); - ret |= (readHandler(addr+3) << 24); - return ret; - } -}; - class VGA_LFB_Handler : public PageHandler { public: VGA_LFB_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {} HostPt GetHostReadPt( Bitu phys_page ) { phys_page -= vga.lfb.page; - phys_page &= (vga.vmemsize >> 12) - 1; + phys_page &= (vga.mem.memsize >> 12) - 1; return &vga.mem.linear[CHECKED3(phys_page * 4096)]; } HostPt GetHostWritePt( Bitu phys_page ) { @@ -2162,19 +1822,19 @@ public: static struct vg { VGA_Map_Handler map; VGA_Slow_CGA_Handler slow; - VGA_TEXT_PageHandler text; +// VGA_TEXT_PageHandler text; VGA_CGATEXT_PageHandler cgatext; VGA_TANDY_PageHandler tandy; - VGA_ChainedEGA_Handler cega; - VGA_ChainedVGA_Handler cvga; +// VGA_ChainedEGA_Handler cega; +// VGA_ChainedVGA_Handler cvga; VGA_ChainedVGA_Slow_Handler cvga_slow; - VGA_ET4000_ChainedVGA_Handler cvga_et4000; +// VGA_ET4000_ChainedVGA_Handler cvga_et4000; VGA_ET4000_ChainedVGA_Slow_Handler cvga_et4000_slow; - VGA_UnchainedEGA_Handler uega; +// VGA_UnchainedEGA_Handler uega; VGA_UnchainedVGA_Handler uvga; VGA_PCJR_Handler pcjr; VGA_HERC_Handler herc; - VGA_LIN4_Handler lin4; +// VGA_LIN4_Handler lin4; VGA_LFB_Handler lfb; VGA_MMIO_Handler mmio; VGA_AMS_Handler ams; @@ -2189,8 +1849,6 @@ void VGA_ChangedBank(void) { void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages); void MEM_ResetPageHandler_RAM(Bitu phys_page, Bitu pages); -extern bool adapter_rom_is_ram; - void VGA_SetupHandlers(void) { vga.svga.bank_read_full = vga.svga.bank_read*vga.svga.bank_size; vga.svga.bank_write_full = vga.svga.bank_write*vga.svga.bank_size; @@ -2263,58 +1921,42 @@ void VGA_SetupHandlers(void) { case M_ERROR: default: return; - case M_LIN4: - newHandler = &vgaph.lin4; - break; case M_LIN15: case M_LIN16: case M_LIN24: case M_LIN32: newHandler = &vgaph.map; break; - case M_LIN8: - case M_VGA: - if (vga.config.chained) { - bool slow = false; - - /* NTS: Most demos and games do not use the Graphics Controller ROPs or bitmask in chained - * VGA modes. But, for the few that do, we have a "slow and accurate" implementation - * that will handle these demos properly at the expense of some emulation speed. - * - * This fixes: - * Impact Studios 'Legend' demo (1993) */ - if (vga.config.full_bit_mask != 0xFFFFFFFF) - slow = true; - - if (slow || vga.config.compatible_chain4) { - /* NTS: ET4000AX cards appear to have a different chain4 implementation from everyone else: - * the planar memory byte address is address >> 2 and bits A0-A1 select the plane, - * where all other clones I've tested seem to write planar memory byte (address & ~3) - * (one byte per 4 bytes) and bits A0-A1 select the plane. */ - /* FIXME: Different chain4 implementation on ET4000 noted---is it true also for ET3000? */ - if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) - newHandler = slow ? ((PageHandler*)(&vgaph.cvga_et4000_slow)) : ((PageHandler*)(&vgaph.cvga_et4000)); - else - newHandler = slow ? ((PageHandler*)(&vgaph.cvga_slow)) : ((PageHandler*)(&vgaph.cvga)); - } - else { - newHandler = &vgaph.map; - } - } else { - newHandler = &vgaph.uvga; - } - break; - case M_EGA: - if (vga.config.chained) - newHandler = &vgaph.cega; - else - newHandler = &vgaph.uega; - break; case M_TEXT: case M_CGA2: case M_CGA4: - newHandler = &vgaph.text; - break; + /* EGA/VGA emulate CGA modes as chained */ + /* fall through */ + case M_LIN8: + case M_LIN4: + case M_VGA: + case M_EGA: + if (vga.config.chained) { + if (vga.config.compatible_chain4) { + /* NTS: ET4000AX cards appear to have a different chain4 implementation from everyone else: + * the planar memory byte address is address >> 2 and bits A0-A1 select the plane, + * where all other clones I've tested seem to write planar memory byte (address & ~3) + * (one byte per 4 bytes) and bits A0-A1 select the plane. */ + /* FIXME: Different chain4 implementation on ET4000 noted---is it true also for ET3000? */ + if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) + newHandler = &vgaph.cvga_et4000_slow; + else + newHandler = &vgaph.cvga_slow; + } + else { + /* this is needed for SVGA modes (Paradise, Tseng, S3) because SVGA + * modes do NOT use the chain4 configuration */ + newHandler = &vgaph.map; + } + } else { + newHandler = &vgaph.uvga; + } + break; case M_PC98: newHandler = &vgaph.pc98; @@ -2331,54 +1973,46 @@ void VGA_SetupHandlers(void) { } switch ((vga.gfx.miscellaneous >> 2) & 3) { case 0: - vgapages.base = VGA_PAGE_A0; - switch (svgaCard) { - case SVGA_TsengET3K: - break; - /* NTS: Looking at the official ET4000 programming guide, it does in fact support the full 128KB */ - case SVGA_S3Trio: - default: - vgapages.mask = 0x1ffff; - break; + vgapages.base = VGA_PAGE_A0; + switch (svgaCard) { + case SVGA_TsengET3K: + case SVGA_TsengET4K: + vgapages.mask = 0x1ffff & vga.mem.memmask; + break; + /* NTS: Looking at the official ET4000 programming guide, it does in fact support the full 128KB */ + case SVGA_S3Trio: + default: + vgapages.mask = 0xffff & vga.mem.memmask; + break; } MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler ); break; case 1: vgapages.base = VGA_PAGE_A0; - vgapages.mask = 0xffff; + vgapages.mask = 0xffff & vga.mem.memmask; MEM_SetPageHandler( VGA_PAGE_A0, 16, newHandler ); - if (adapter_rom_is_ram) MEM_ResetPageHandler_RAM( VGA_PAGE_B0, 16); - else MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 16); + MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 16); break; case 2: vgapages.base = VGA_PAGE_B0; - vgapages.mask = 0x7fff; + vgapages.mask = 0x7fff & vga.mem.memmask; MEM_SetPageHandler( VGA_PAGE_B0, 8, newHandler ); - if (adapter_rom_is_ram) { - MEM_ResetPageHandler_RAM( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler_RAM( VGA_PAGE_B8, 8 ); - } - else { - MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler_Unmapped( VGA_PAGE_B8, 8 ); - } - break; + MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 ); + MEM_ResetPageHandler_Unmapped( VGA_PAGE_B8, 8 ); + break; case 3: vgapages.base = VGA_PAGE_B8; - vgapages.mask = 0x7fff; + vgapages.mask = 0x7fff & vga.mem.memmask; MEM_SetPageHandler( VGA_PAGE_B8, 8, newHandler ); - if (adapter_rom_is_ram) { - MEM_ResetPageHandler_RAM( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler_RAM( VGA_PAGE_B0, 8 ); - } - else { - MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 8 ); - } - break; + MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 ); + MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 8 ); + break; } if(svgaCard == SVGA_S3Trio && (vga.s3.ext_mem_ctrl & 0x10)) MEM_SetPageHandler(VGA_PAGE_A0, 16, &vgaph.mmio); + + non_cga_ignore_oddeven_engage = (non_cga_ignore_oddeven && !(vga.mode == M_TEXT || vga.mode == M_CGA2 || vga.mode == M_CGA4)); + range_done: PAGING_ClearTLB(); } @@ -2418,7 +2052,7 @@ void VGA_StartUpdateLFB(void) { vga.lfb.page = (unsigned int)vga.s3.la_window << 4u; vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u; vga.lfb.handler = &vgaph.lfb; - MEM_SetLFB((unsigned int)vga.s3.la_window << 4u,(unsigned int)vga.vmemsize/4096u, vga.lfb.handler, &vgaph.mmio); + MEM_SetLFB((unsigned int)vga.s3.la_window << 4u,(unsigned int)vga.mem.memsize/4096u, vga.lfb.handler, &vgaph.mmio); } } @@ -2439,13 +2073,12 @@ void VGA_SetupMemory() { vga.svga.bank_read = vga.svga.bank_write = 0; vga.svga.bank_read_full = vga.svga.bank_write_full = 0; - if (1 || vga.vmemsize_alloced != vga.vmemsize) { + if (vga.mem.linear == NULL) { VGA_Memory_ShutDown(NULL); - vga.mem.linear_orgptr = new Bit8u[vga.vmemsize+32u]; - memset(vga.mem.linear_orgptr,0,vga.vmemsize+32u); + vga.mem.linear_orgptr = new Bit8u[vga.mem.memsize+32u]; + memset(vga.mem.linear_orgptr,0,vga.mem.memsize+32u); vga.mem.linear=(Bit8u*)(((uintptr_t)vga.mem.linear_orgptr + 16ull-1ull) & ~(16ull-1ull)); - vga.vmemsize_alloced = vga.vmemsize; /* HACK. try to avoid stale pointers */ vga.draw.linear_base = vga.mem.linear; @@ -2456,9 +2089,6 @@ void VGA_SetupMemory() { VGA_SetupHandlers(); } - // In most cases these values stay the same. Assumptions: vmemwrap is power of 2, vmemwrap <= vmemsize - vga.vmemwrap = vga.vmemsize; - vga.svga.bank_read = vga.svga.bank_write = 0; vga.svga.bank_read_full = vga.svga.bank_write_full = 0; vga.svga.bank_size = 0x10000; /* most common bank size is 64K */ diff --git a/src/hardware/vga_paradise.cpp b/src/hardware/vga_paradise.cpp index 75d05acda..12a47222a 100644 --- a/src/hardware/vga_paradise.cpp +++ b/src/hardware/vga_paradise.cpp @@ -165,10 +165,10 @@ void FinishSetMode_PVGA1A(Bitu /*crtc_base*/, VGA_ModeExtraData* modeData) { if(vga.mode != M_VGA) { vga.config.compatible_chain4 = false; - vga.vmemwrap = vga.vmemsize; +// vga.vmemwrap = vga.mem.memsize; } else { vga.config.compatible_chain4 = true; - vga.vmemwrap = 256*1024; +// vga.vmemwrap = 256*1024; } vga.config.compatible_chain4 = false; @@ -202,7 +202,7 @@ Bitu GetClock_PVGA1A() { } bool AcceptsMode_PVGA1A(Bitu mode) { - return VideoModeMemSize(mode) < vga.vmemsize; + return VideoModeMemSize(mode) < vga.mem.memsize; } void SVGA_Setup_ParadisePVGA1A(void) { @@ -221,14 +221,14 @@ void SVGA_Setup_ParadisePVGA1A(void) { VGA_SetClock(3,35900); // Adjust memory, default to 512K - if (vga.vmemsize == 0) - vga.vmemsize = 512*1024; + if (vga.mem.memsize == 0) + vga.mem.memsize = 512*1024; - if (vga.vmemsize < 512*1024) { - vga.vmemsize = 256*1024; + if (vga.mem.memsize < 512*1024) { + vga.mem.memsize = 256*1024; pvga1a.PR1 = 1<<6; - } else if (vga.vmemsize > 512*1024) { - vga.vmemsize = 1024*1024; + } else if (vga.mem.memsize > 512*1024) { + vga.mem.memsize = 1024*1024; pvga1a.PR1 = 3<<6; } else { pvga1a.PR1 = 2<<6; diff --git a/src/hardware/vga_s3.cpp b/src/hardware/vga_s3.cpp index e488dbc08..385136c40 100644 --- a/src/hardware/vga_s3.cpp +++ b/src/hardware/vga_s3.cpp @@ -30,8 +30,8 @@ void SVGA_S3_WriteCRTC(Bitu reg,Bitu val,Bitu iolen) { //TODO Base address vga.s3.reg_31 = val; vga.config.compatible_chain4 = !(val&0x08); - if (vga.config.compatible_chain4) vga.vmemwrap = 256*1024; - else vga.vmemwrap = vga.vmemsize; +// if (vga.config.compatible_chain4) vga.vmemwrap = 256*1024; +// else vga.vmemwrap = vga.mem.memsize; vga.config.display_start = (vga.config.display_start&~0x30000ul)|((val&0x30u)<<12ul); VGA_DetermineMode(); VGA_SetupHandlers(); @@ -140,7 +140,7 @@ void SVGA_S3_WriteCRTC(Bitu reg,Bitu val,Bitu iolen) { case 0x4c: /* HGC start address high byte*/ vga.s3.hgc.startaddr &=0xff; vga.s3.hgc.startaddr |= ((val & 0xf) << 8); - if ((((Bitu)vga.s3.hgc.startaddr)<<10)+((64*64*2)/8) > vga.vmemsize) { + if ((((Bitu)vga.s3.hgc.startaddr)<<10)+((64*64*2)/8) > vga.mem.memsize) { vga.s3.hgc.startaddr &= 0xff; // put it back to some sane area; // if read back of this address is ever implemented this needs to change LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:S3:CRTC: HGC pattern address beyond video memory" ); @@ -541,7 +541,7 @@ bool SVGA_S3_HWCursorActive(void) { } bool SVGA_S3_AcceptsMode(Bitu mode) { - return VideoModeMemSize(mode) < vga.vmemsize; + return VideoModeMemSize(mode) < vga.mem.memsize; } void SVGA_Setup_S3Trio(void) { @@ -559,27 +559,27 @@ void SVGA_Setup_S3Trio(void) { svga.hardware_cursor_active = &SVGA_S3_HWCursorActive; svga.accepts_mode = &SVGA_S3_AcceptsMode; - //if (vga.vmemsize == 0) - // vga.vmemsize = 2*1024*1024; // the most common S3 configuration + //if (vga.mem.memsize == 0) + // vga.mem.memsize = 2*1024*1024; // the most common S3 configuration // Set CRTC 36 to specify amount of VRAM and PCI - if (vga.vmemsize < 1024*1024) { - vga.vmemsize = 512*1024; + if (vga.mem.memsize < 1024*1024) { + vga.mem.memsize = 512*1024; vga.s3.reg_36 = 0xfa; // less than 1mb fast page mode - } else if (vga.vmemsize < 2048*1024) { - vga.vmemsize = 1024*1024; + } else if (vga.mem.memsize < 2048*1024) { + vga.mem.memsize = 1024*1024; vga.s3.reg_36 = 0xda; // 1mb fast page mode - } else if (vga.vmemsize < 3072*1024) { - vga.vmemsize = 2048*1024; + } else if (vga.mem.memsize < 3072*1024) { + vga.mem.memsize = 2048*1024; vga.s3.reg_36 = 0x9a; // 2mb fast page mode - } else if (vga.vmemsize < 4096*1024) { - vga.vmemsize = 3072*1024; + } else if (vga.mem.memsize < 4096*1024) { + vga.mem.memsize = 3072*1024; vga.s3.reg_36 = 0x5a; // 3mb fast page mode - } else if (vga.vmemsize < 8192*1024) { // Trio64 supported only up to 4M - vga.vmemsize = 4096*1024; + } else if (vga.mem.memsize < 8192*1024) { // Trio64 supported only up to 4M + vga.mem.memsize = 4096*1024; vga.s3.reg_36 = 0x1a; // 4mb fast page mode } else { // 8M - vga.vmemsize = 8192*1024; + vga.mem.memsize = 8192*1024; vga.s3.reg_36 = 0x7a; // 8mb fast page mode } diff --git a/src/hardware/vga_tseng.cpp b/src/hardware/vga_tseng.cpp index df46d392a..7bb19b25c 100644 --- a/src/hardware/vga_tseng.cpp +++ b/src/hardware/vga_tseng.cpp @@ -159,7 +159,7 @@ void write_p3d5_et4k(Bitu reg,Bitu val,Bitu iolen) { case 0x37: if (val != et4k.store_3d4_37) { et4k.store_3d4_37 = val; - vga.vmemwrap = ((64u*1024u)<<((val&8u)>>2u))<<((val&3u)-1u); + vga.mem.memmask = (((64u*1024u*((val&8u)?4u:1u))<<((val&3u)-1u)) - 1u) & (vga.mem.memsize-1u); VGA_SetupHandlers(); } break; @@ -371,7 +371,7 @@ void FinishSetMode_ET4K(Bitu crtc_base, VGA_ModeExtraData* modeData) { IO_Write(crtc_base,0x33);IO_Write(crtc_base+1,0); IO_Write(crtc_base,0x34);IO_Write(crtc_base+1,0); IO_Write(crtc_base,0x36);IO_Write(crtc_base+1,0); - IO_Write(crtc_base,0x37);IO_Write(crtc_base+1,0x0c|(vga.vmemsize==1024*1024?3:vga.vmemsize==512*1024?2:1)); + IO_Write(crtc_base,0x37);IO_Write(crtc_base+1,0x0c|(vga.mem.memsize==1024*1024?3:vga.mem.memsize==512*1024?2:1)); // Clear ext SEQ IO_Write(0x3c4,0x06);IO_Write(0x3c5,0); IO_Write(0x3c4,0x07);IO_Write(0x3c5,0); @@ -400,7 +400,7 @@ void FinishSetMode_ET4K(Bitu crtc_base, VGA_ModeExtraData* modeData) { // Verified (on real hardware and in a few games): Tseng ET4000 used chain4 implementation // different from standard VGA. It was also not limited to 64K in regular mode 13h. vga.config.compatible_chain4 = false; - vga.vmemwrap = vga.vmemsize; +// vga.vmemwrap = vga.mem.memsize; VGA_SetupHandlers(); } @@ -437,7 +437,7 @@ Bitu GetClock_ET4K() { } bool AcceptsMode_ET4K(Bitu mode) { - return VideoModeMemSize(mode) < vga.vmemsize; + return VideoModeMemSize(mode) < vga.mem.memsize; // return mode != 0x3d; } @@ -620,15 +620,15 @@ void SVGA_Setup_TsengET4K(void) { IO_RegisterWriteHandler(0x3cd,write_p3cd_et4k,IO_MB); // Default to 1M of VRAM - if (vga.vmemsize == 0) - vga.vmemsize = 1024*1024; + if (vga.mem.memsize == 0) + vga.mem.memsize = 1024*1024; - if (vga.vmemsize < 512*1024) - vga.vmemsize = 256*1024; - else if (vga.vmemsize < 1024*1024) - vga.vmemsize = 512*1024; + if (vga.mem.memsize < 512*1024) + vga.mem.memsize = 256*1024; + else if (vga.mem.memsize < 1024*1024) + vga.mem.memsize = 512*1024; else - vga.vmemsize = 1024*1024; + vga.mem.memsize = 1024*1024; // Tseng ROM signature phys_writes(PhysMake(0xc000,0)+0x0075, " Tseng ", 8); @@ -919,7 +919,7 @@ void FinishSetMode_ET3K(Bitu crtc_base, VGA_ModeExtraData* modeData) { // Verified on functioning (at last!) hardware: Tseng ET3000 is the same as ET4000 when // it comes to chain4 architecture vga.config.compatible_chain4 = false; - vga.vmemwrap = vga.vmemsize; +// vga.vmemwrap = vga.mem.memsize; VGA_SetupHandlers(); } @@ -948,7 +948,7 @@ Bitu GetClock_ET3K() { } bool AcceptsMode_ET3K(Bitu mode) { - return mode <= 0x37 && mode != 0x2f && VideoModeMemSize(mode) < vga.vmemsize; + return mode <= 0x37 && mode != 0x2f && VideoModeMemSize(mode) < vga.mem.memsize; } void SVGA_Setup_TsengET3K(void) { @@ -977,7 +977,7 @@ void SVGA_Setup_TsengET3K(void) { IO_RegisterReadHandler(0x3cd,read_p3cd_et3k,IO_MB); IO_RegisterWriteHandler(0x3cd,write_p3cd_et3k,IO_MB); - vga.vmemsize = 512*1024; // Cannot figure how this was supposed to work on the real card + vga.mem.memsize = 512*1024; // Cannot figure how this was supposed to work on the real card // Tseng ROM signature PhysPt rom_base=PhysMake(0xc000,0); diff --git a/src/hardware/vga_xga.cpp b/src/hardware/vga_xga.cpp index 6f5c87331..303f63251 100644 --- a/src/hardware/vga_xga.cpp +++ b/src/hardware/vga_xga.cpp @@ -143,19 +143,19 @@ void XGA_DrawPoint(Bitu x, Bitu y, Bitu c) { during windows dragging. */ switch(XGA_COLOR_MODE) { case M_LIN8: - if (GCC_UNLIKELY(memaddr >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr >= vga.mem.memsize)) break; vga.mem.linear[memaddr] = c; break; case M_LIN15: - if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr*2 >= vga.mem.memsize)) break; ((Bit16u*)(vga.mem.linear))[memaddr] = (Bit16u)(c&0x7fff); break; case M_LIN16: - if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr*2 >= vga.mem.memsize)) break; ((Bit16u*)(vga.mem.linear))[memaddr] = (Bit16u)(c&0xffff); break; case M_LIN32: - if (GCC_UNLIKELY(memaddr*4 >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr*4 >= vga.mem.memsize)) break; ((Bit32u*)(vga.mem.linear))[memaddr] = c; break; default: @@ -169,14 +169,14 @@ Bitu XGA_GetPoint(Bitu x, Bitu y) { switch(XGA_COLOR_MODE) { case M_LIN8: - if (GCC_UNLIKELY(memaddr >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr >= vga.mem.memsize)) break; return vga.mem.linear[memaddr]; case M_LIN15: case M_LIN16: - if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr*2 >= vga.mem.memsize)) break; return ((Bit16u*)(vga.mem.linear))[memaddr]; case M_LIN32: - if (GCC_UNLIKELY(memaddr*4 >= vga.vmemsize)) break; + if (GCC_UNLIKELY(memaddr*4 >= vga.mem.memsize)) break; return ((Bit32u*)(vga.mem.linear))[memaddr]; default: break; diff --git a/src/hardware/voodoo_data.h b/src/hardware/voodoo_data.h index c2b783c82..9c70b5153 100644 --- a/src/hardware/voodoo_data.h +++ b/src/hardware/voodoo_data.h @@ -163,7 +163,7 @@ static const UINT8 dither_matrix_2x2[16] = (c) = (int)((((unsigned int)(val) << 2u) & 0xf8u) | (((unsigned int)(val) >> 3u) & 0x07u)); \ #define EXTRACT_1555_TO_8888(val, a, b, c, d) \ - (a) = (int)(((unsigned int)(val) >> 15u) & 0xffu); \ + (a) = ((INT16)(val) >> 15) & 0xff; \ EXTRACT_x555_TO_888(val, b, c, d) \ #define EXTRACT_5551_TO_8888(val, a, b, c, d) \ diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index cdca72cb1..381ce35ba 100644 --- a/src/ints/bios.cpp +++ b/src/ints/bios.cpp @@ -56,7 +56,6 @@ extern bool PS1AudioCard; /* mouse.cpp */ extern bool en_bios_ps2mouse; -extern bool mainline_compatible_bios_mapping; extern bool rom_bios_8x8_cga_font; extern bool pcibus_enable; @@ -2334,11 +2333,10 @@ const char *pc98_shcut_key[10][2] = { void update_pc98_function_row(bool enable) { pc98_function_row = enable; - mem_writeb(0x712,25 - 1 - (pc98_function_row ? 1 : 0)); - real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,25 - 1 - (pc98_function_row ? 1 : 0)); + real_writeb(0x60,0x112,25 - 1 - (pc98_function_row ? 1 : 0)); - unsigned char c = real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS); - unsigned char r = real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+1); + unsigned char c = real_readb(0x60,0x11C); + unsigned char r = real_readb(0x60,0x110); unsigned int o = 80 * 24; if (pc98_function_row) { @@ -2392,8 +2390,8 @@ void update_pc98_function_row(bool enable) { } } - real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS,c); - real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+1,r); + real_writeb(0x60,0x11C,c); + real_writeb(0x60,0x110,r); void vga_pc98_direct_cursor_pos(Bit16u address); vga_pc98_direct_cursor_pos((r*80)+c); @@ -6041,10 +6039,7 @@ private: unsigned int i; unsigned char c,tmp[256]; - if (mainline_compatible_bios_mapping) - isapnp_biosstruct_base = base = 0xFE100; /* take the unused space just after the fake BIOS signature */ - else - isapnp_biosstruct_base = base = ROMBIOS_GetMemory(0x21,"ISA Plug & Play BIOS struct",/*paragraph alignment*/0x10); + isapnp_biosstruct_base = base = ROMBIOS_GetMemory(0x21,"ISA Plug & Play BIOS struct",/*paragraph alignment*/0x10); if (base == 0) E_Exit("Unable to allocate ISA PnP struct"); LOG_MSG("ISA Plug & Play BIOS enabled"); @@ -6751,22 +6746,12 @@ public: } /* pick locations */ - if (!IS_PC98_ARCH && mainline_compatible_bios_mapping) { /* mapping BIOS the way mainline DOSBox does */ - BIOS_DEFAULT_RESET_LOCATION = RealMake(0xf000,0xe05b); - BIOS_DEFAULT_HANDLER_LOCATION = RealMake(0xf000,0xff53); - BIOS_DEFAULT_IRQ0_LOCATION = RealMake(0xf000,0xfea5); - BIOS_DEFAULT_IRQ1_LOCATION = RealMake(0xf000,0xe987); - BIOS_DEFAULT_IRQ07_DEF_LOCATION = RealMake(0xf000,0xff55); - BIOS_DEFAULT_IRQ815_DEF_LOCATION = RealMake(0xf000,0xe880); - } - else { - BIOS_DEFAULT_RESET_LOCATION = PhysToReal416(ROMBIOS_GetMemory(64/*several callbacks*/,"BIOS default reset location",/*align*/4)); - BIOS_DEFAULT_HANDLER_LOCATION = PhysToReal416(ROMBIOS_GetMemory(1/*IRET*/,"BIOS default handler location",/*align*/4)); - BIOS_DEFAULT_IRQ0_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x13/*see callback.cpp for IRQ0*/,"BIOS default IRQ0 location",/*align*/4)); - BIOS_DEFAULT_IRQ1_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x15/*see callback.cpp for IRQ1*/,"BIOS default IRQ1 location",/*align*/4)); - BIOS_DEFAULT_IRQ07_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(7/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ2-7 location",/*align*/4)); - BIOS_DEFAULT_IRQ815_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(9/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ8-15 location",/*align*/4)); - } + BIOS_DEFAULT_RESET_LOCATION = PhysToReal416(ROMBIOS_GetMemory(64/*several callbacks*/,"BIOS default reset location",/*align*/4)); + BIOS_DEFAULT_HANDLER_LOCATION = PhysToReal416(ROMBIOS_GetMemory(1/*IRET*/,"BIOS default handler location",/*align*/4)); + BIOS_DEFAULT_IRQ0_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x13/*see callback.cpp for IRQ0*/,"BIOS default IRQ0 location",/*align*/4)); + BIOS_DEFAULT_IRQ1_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x15/*see callback.cpp for IRQ1*/,"BIOS default IRQ1 location",/*align*/4)); + BIOS_DEFAULT_IRQ07_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(7/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ2-7 location",/*align*/4)); + BIOS_DEFAULT_IRQ815_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(9/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ8-15 location",/*align*/4)); write_FFFF_signature(); @@ -7226,7 +7211,7 @@ void write_ID_version_string() { str_ver_at = 0xFE061; str_id_len = strlen(bios_type_string)+1; str_ver_len = strlen(bios_version_string)+1; - if (!mainline_compatible_bios_mapping && !IS_PC98_ARCH) { + if (!IS_PC98_ARCH) { /* need to mark these strings off-limits so dynamic allocation does not overwrite them */ ROMBIOS_GetMemory((Bitu)str_id_len+1,"BIOS ID string",1,str_id_at); ROMBIOS_GetMemory((Bitu)str_ver_len+1,"BIOS version string",1,str_ver_at); @@ -7256,7 +7241,7 @@ void ROMBIOS_Init() { if (IS_PC98_ARCH) oi = 96u; // BIOS standard range is E8000-FFFFF else - oi = (mainline_compatible_bios_mapping && machine != MCH_PCJR) ? 128u : 64u; + oi = 64u; } if (oi < 8) oi = 8; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ rombios_minimum_size = (oi << 10); /* convert to minimum, using size coming downward from 1MB */ @@ -7268,19 +7253,13 @@ void ROMBIOS_Init() { if (IS_PC98_ARCH) oi = 96u; else - oi = (mainline_compatible_bios_mapping && machine != MCH_PCJR) ? 128u : 64u; + oi = 64u; } if (oi < 8u) oi = 8u; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ oi <<= 10u; if (oi < rombios_minimum_size) oi = rombios_minimum_size; rombios_minimum_location = 0x100000ul - oi; /* convert to minimum, using size coming downward from 1MB */ - /* in mainline compatible, make sure we cover the 0xF0000-0xFFFFF range */ - if (!IS_PC98_ARCH && mainline_compatible_bios_mapping && rombios_minimum_location > 0xF0000u) { - rombios_minimum_location = 0xF0000u; - rombios_minimum_size = 0x10000u; - } - LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range: 0x%05X-0xFFFFF",(int)rombios_minimum_location); LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range according to minimum size: 0x%05X-0xFFFFF",(int)(0x100000 - rombios_minimum_size)); @@ -7312,7 +7291,7 @@ void ROMBIOS_Init() { write_ID_version_string(); /* some structures when enabled are fixed no matter what */ - if (!mainline_compatible_bios_mapping && rom_bios_8x8_cga_font && !IS_PC98_ARCH) { + if (rom_bios_8x8_cga_font && !IS_PC98_ARCH) { /* line 139, int10_memory.cpp: the 8x8 font at 0xF000:FA6E, first 128 chars. * allocate this NOW before other things get in the way */ if (ROMBIOS_GetMemory(128*8,"BIOS 8x8 font (first 128 chars)",1,0xFFA6E) == 0) { @@ -7327,13 +7306,6 @@ void ROMBIOS_Init() { } } - if (!IS_PC98_ARCH && mainline_compatible_bios_mapping) { - /* then mark the region 0xE000-0xFFF0 as off-limits. - * believe it or not, there's this whole range between 0xF3000 and 0xFE000 that remains unused! */ - if (ROMBIOS_GetMemory(0xFFFF0-0xFE000,"BIOS with fixed layout",1,0xFE000) == 0) - E_Exit("Mainline compat bios mapping: failed to declare entire BIOS area off-limits"); - } - /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute after reset. * we allow this for both hacker curiosity and automated CPU testing. */ { @@ -7405,3 +7377,21 @@ void ROMBIOS_Init() { } } +void BIOS_SynchronizeNumLock() +{ +#if defined(WIN32) + auto flag = mem_readb(BIOS_KEYBOARD_FLAGS1); + auto leds = mem_readb(BIOS_KEYBOARD_LEDS); + auto stat = GetKeyState(VK_NUMLOCK); + if (stat & 1) { + flag |= 0x20; + leds |= 0x02; + } + else { + flag &= ~0x20; + leds &= ~0x02; + } + mem_writeb(BIOS_KEYBOARD_FLAGS1, flag); + mem_writeb(BIOS_KEYBOARD_LEDS, leds); +#endif +} diff --git a/src/ints/bios_keyboard.cpp b/src/ints/bios_keyboard.cpp index 8476edd13..17c3a4c87 100644 --- a/src/ints/bios_keyboard.cpp +++ b/src/ints/bios_keyboard.cpp @@ -26,6 +26,7 @@ #include "inout.h" #include "dos_inc.h" #include "SDL.h" +#include "int10.h" #if defined(_MSC_VER) # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ @@ -455,15 +456,13 @@ static Bitu IRQ1_Handler(void) { } else { flags1 ^=0x10;flags2 &=~0x10;leds ^=0x01;break; /* Scroll Lock released */ } -// case 0x52:flags2|=128;break;//See numpad /* Insert */ - case 0xd2: - if(flags3&0x02) { /* Maybe honour the insert on keypad as well */ - flags1^=0x80; - flags2&=~0x80; - break; - } else { - goto irq1_end;/*Normal release*/ - } + case 0xd2: /* NUMPAD insert, ironically, regular one is handled by 0x52 */ + if (flags3 & BIOS_KEYBOARD_FLAGS3_HIDDEN_E0 || !(flags1 & BIOS_KEYBOARD_FLAGS1_NUMLOCK_ACTIVE)) + { + flags1 ^= BIOS_KEYBOARD_FLAGS1_INSERT_ACTIVE; + flags2 &= BIOS_KEYBOARD_FLAGS2_INSERT_PRESSED; + } + break; case 0x47: /* Numpad */ case 0x48: case 0x49: @@ -553,6 +552,17 @@ irq1_end: /* update LEDs on keyboard */ if (leds_orig != leds) KEYBOARD_SetLEDs(leds); + /* update insert cursor */ + extern bool dos_program_running; + if (!dos_program_running) + { + const auto flg = mem_readb(BIOS_KEYBOARD_FLAGS1); + const auto ins = static_cast(flg & BIOS_KEYBOARD_FLAGS1_INSERT_ACTIVE); + const auto ssl = static_cast(ins ? CURSOR_SCAN_LINE_INSERT : CURSOR_SCAN_LINE_NORMAL); + if (CurMode->type == M_TEXT) + INT10_SetCursorShape(ssl, CURSOR_SCAN_LINE_END); + } + /* IO_Write(0x20,0x20); moved out of handler to be virtualizable */ #if 0 /* Signal the keyboard for next code */ @@ -628,6 +638,7 @@ static Bitu IRQ1_Handler_PC98(void) { /* According to Neko Project II, the BIOS maintains a "pressed key" bitmap at 0x50:0x2A. * Without this bitmap many PC-98 games are unplayable. */ + /* ALSO note that byte 0xE of this array is the "shift" state byte. */ { unsigned int o = 0x52Au + ((unsigned int)sc_8251 >> 3u); unsigned char c = mem_readb(o); @@ -1204,12 +1215,23 @@ static Bitu IRQ1_Handler_PC98(void) { } static Bitu PCjr_NMI_Keyboard_Handler(void) { + Bitu save_eax = reg_eax; + while (IO_ReadB(0x64) & 1) { /* while data is available */ + Bitu save_ip = reg_eip; + + /* HACK: IRQ1 handler modifies AX and IP. So do we! + * The NMI handler is registered as CB_IRET which does not save any registers. + * To avoid causing crashes and glitches, save and restore the CPU registers affected NOW. + * + * I don't think the PCjr has the INT 15h hook that AT systems do. */ reg_al=IO_ReadB(0x60); - /* FIXME: Need to execute INT 15h hook */ IRQ1_Handler(); + + reg_eip = save_ip; } + reg_ax = save_eax; return CBRET_NONE; } @@ -1387,7 +1409,7 @@ static void InitBiosSegment(void) { Bit8u flag1 = 0; Bit8u leds = 16; /* Ack received */ -#if SDL_VERSION_ATLEAST(1, 2, 14) +#if 0 /*SDL_VERSION_ATLEAST(1, 2, 14)*/ //Nothing, mapper handles all. #else if (startup_state_capslock) { flag1|=0x40; leds|=0x04;} diff --git a/src/ints/int10.cpp b/src/ints/int10.cpp index 131591852..9150c82ca 100644 --- a/src/ints/int10.cpp +++ b/src/ints/int10.cpp @@ -131,6 +131,7 @@ Bitu INT10_Handler(void) { case 0x10: /* Palette functions */ if (!IS_EGAVGA_ARCH && (reg_al>0x02)) break; else if (!IS_VGA_ARCH && (reg_al>0x03)) break; + else if (machine==MCH_PCJR && (reg_al>0x02)) break; /* "Looking at the PCjr tech ref page A-61, ... the BIOS listing stops at subfunction 2." */ switch (reg_al) { case 0x00: /* SET SINGLE PALETTE REGISTER */ INT10_SetSinglePaletteRegister(reg_bl,reg_bh); @@ -309,8 +310,20 @@ graphics_chars: break; switch (reg_bl) { case 0x10: /* Get EGA Information */ - reg_bh=(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)==0x3B4); - reg_bl=3; //256 kb + reg_bh=(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)==0x3B4); + if (IS_EGA_ARCH) { + if (vga.mem.memsize >= (256*1024)) + reg_bl=3; //256 kb + else if (vga.mem.memsize >= (192*1024)) + reg_bl=2; //192 kb + else if (vga.mem.memsize >= (128*1024)) + reg_bl=1; //128 kb + else + reg_bl=0; //64 kb + } + else { + reg_bl=3; //256 kb + } reg_cl=real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES) & 0x0F; reg_ch=real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES) >> 4; break; @@ -1055,30 +1068,20 @@ void INT10_Startup(Section *sec) { /* number of text rows on the screen. * Touhou Project will not clear/format the text layer properly without this variable. */ - mem_writeb(0x710,25 - 1); /* cursor position Y coordinate */ + mem_writeb(0x710,0); /* cursor position Y coordinate */ mem_writeb(0x711,1); /* function definition display status flag */ - mem_writeb(0x712,25 - 1); /* number of rows - 1 */ + mem_writeb(0x712,25 - 1 - 1); /* scroll range lower limit (usually 23 when function key row is visible) */ mem_writeb(0x713,1); /* normal 25 lines */ mem_writeb(0x714,0xE1); /* content erase attribute */ mem_writeb(0x719,0x20); /* content erase character */ mem_writeb(0x71B,0x01); /* cursor displayed */ - + mem_writeb(0x71C,0x00); /* cursor position X coordinate */ mem_writeb(0x71D,0xE1); /* content display attribute */ - + mem_writeb(0x71E,0x00); /* scroll range upper limit (usually 0) */ mem_writeb(0x71F,0x01); /* scrolling speed is normal */ - /* however, our console functions still use IBM PC values! */ - mem_writeb(0x400+BIOSMEM_CURRENT_MODE,0); - mem_writew(0x400+BIOSMEM_NB_COLS,80); - mem_writew(0x400+BIOSMEM_PAGE_SIZE,0); - mem_writew(0x400+BIOSMEM_CURRENT_START,0); - mem_writew(0x400+BIOSMEM_CURSOR_POS,0); - mem_writew(0x400+BIOSMEM_CURRENT_PAGE,0); - mem_writeb(0x400+BIOSMEM_NB_ROWS,25); - mem_writeb(0x400+BIOSMEM_CHAR_HEIGHT,16); - /* init text RAM */ for (unsigned int i=0;i < 0x2000;i += 2) { mem_writew(0xA0000+i,0); diff --git a/src/ints/int10.h b/src/ints/int10.h index 949318944..06556f25f 100644 --- a/src/ints/int10.h +++ b/src/ints/int10.h @@ -95,8 +95,8 @@ #define VGAMEM_MTEXT 0xB000u /* FIXME: Wait, what?? What the hell kind of preprocessor macro is this??? Kill these macros! --J.C. */ -#define BIOS_NCOLS Bit16u ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); -#define BIOS_NROWS Bit16u nrows=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1u; +#define BIOS_NCOLS Bit16u ncols=IS_PC98_ARCH ? 80 : real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); +#define BIOS_NROWS Bit16u nrows=IS_PC98_ARCH ? (Bit16u)(real_readb(0x60,0x112)+1u) : (Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1u; extern Bit8u int10_font_08[256 * 8]; extern Bit8u int10_font_14[256 * 14]; @@ -155,13 +155,22 @@ typedef struct { extern Int10Data int10; static inline Bit8u CURSOR_POS_COL(Bit8u page) { - return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u); + if (IS_PC98_ARCH) + return real_readb(0x60,0x11C); /* MS-DOS kernel location */ + else + return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u); } static inline Bit8u CURSOR_POS_ROW(Bit8u page) { - return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u+1u); + if (IS_PC98_ARCH) + return real_readb(0x60,0x110); /* MS-DOS kernel location */ + else + return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u+1u); } +//! \brief Gets the state of INS/OVR mode. +bool INT10_GetInsertState(); + bool INT10_SetVideoMode(Bit16u mode); void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page); @@ -172,6 +181,8 @@ void INT10_DisplayCombinationCode(Bit16u * dcc,bool set); void INT10_GetFuncStateInformation(PhysPt save); void INT10_SetCursorShape(Bit8u first,Bit8u last); +void INT10_GetScreenColumns(Bit16u* cols); +void INT10_GetCursorPos(Bit8u *row, Bit8u *col, Bit8u page); void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page); void INT10_TeletypeOutput(Bit8u chr,Bit8u attr); void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr); diff --git a/src/ints/int10_char.cpp b/src/ints/int10_char.cpp index 163f9bfc8..aaddf7814 100644 --- a/src/ints/int10_char.cpp +++ b/src/ints/int10_char.cpp @@ -27,6 +27,8 @@ #include "shiftjis.h" #include "callback.h" +Bit8u DefaultANSIAttr(); + #if defined(_MSC_VER) # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ #endif @@ -199,25 +201,12 @@ static void VGA_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u att } } -static unsigned char VGA_FG_to_PC98(unsigned char vga_attr) { - /* VGA: - * lbbb ffff b=background color (irgb) f=foreground color (irgb) l=blink - * PC-98: - * grb xxxxx g=green r=red b=blue xxxxxx dont care */ - return - ((vga_attr & 0x80 /*blink*/) ? 0x02/*PC-98 blink*/ : 0) + - ((vga_attr & 2/*VGA green*/) ? 0x80/*PC-98 green*/ : 0) + - ((vga_attr & 4/*VGA red */) ? 0x40/*PC-98 red*/ : 0) + - ((vga_attr & 1/*VGA blue */) ? 0x20/*PC-98 blue*/ : 0) + - 1/* ~secret*/; -} - static void PC98_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) { /* Do some filing */ PhysPt dest; dest=base+(row*CurMode->twidth+cleft)*2; Bit16u fill=' '; - Bit16u fattr=VGA_FG_to_PC98(attr ? attr : 7); + Bit16u fattr=attr ? attr : DefaultANSIAttr(); for (Bit8u x=0;x<(Bitu)(cright-cleft);x++) { mem_writew(dest,fill); mem_writew(dest+0x2000,fattr); @@ -429,21 +418,48 @@ dowrite: void vga_pc98_direct_cursor_pos(Bit16u address); +void INT10_GetScreenColumns(Bit16u *cols) +{ + if (IS_PC98_ARCH) + *cols = 80; //TODO + else + *cols = real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS); +} + +void INT10_GetCursorPos(Bit8u *row, Bit8u*col, const Bit8u page) +{ + if (IS_PC98_ARCH) { + *col = real_readb(0x60, 0x11C); + *row = real_readb(0x60, 0x110); + } + else { + *col = real_readb(BIOSMEM_SEG, BIOSMEM_CURSOR_POS + page * 2u); + *row = real_readb(BIOSMEM_SEG, BIOSMEM_CURSOR_POS + page * 2u + 1u); + } +} + void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) { Bit16u address; if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetCursorPos page %d",page); // Bios cursor pos - real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u,col); - real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u+1u,row); + if (IS_PC98_ARCH) { + real_writeb(0x60,0x11C,col); + real_writeb(0x60,0x110,row); + page = 0; + } + else { + real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u,col); + real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u+1u,row); + } // Set the hardware cursor - Bit8u current=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + Bit8u current=IS_PC98_ARCH ? 0 : real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); if(page==current) { // Get the dimensions BIOS_NCOLS; // Calculate the address knowing nbcols nbrows and page num // NOTE: BIOSMEM_CURRENT_START counts in colour/flag pairs - address=(ncols*row)+col+real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START)/2; + address=(ncols*row)+col+(IS_PC98_ARCH ? 0 : (real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START)/2)); if (IS_PC98_ARCH) { vga_pc98_direct_cursor_pos(address); } @@ -534,7 +550,7 @@ void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit16u chr,Bit8u attr,bool useat /* Externally used by the mouse routine */ RealPt fontdata; Bitu x,y; - Bit8u back, cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + Bit8u back, cheight = IS_PC98_ARCH ? 16 : real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); if (CurMode->type != M_PC98) chr &= 0xFF; @@ -556,13 +572,12 @@ void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit16u chr,Bit8u attr,bool useat case M_PC98: { // Compute the address - Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); - address+=(row*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)+col)*2; + Bit16u address=((row*80)+col)*2; // Write the char PhysPt where = CurMode->pstart+address; mem_writew(where,chr); if (useattr) { - mem_writeb(where+0x2000,VGA_FG_to_PC98(attr)); + mem_writeb(where+0x2000,attr); } #if 0 // seems to reenable the cursor, too @@ -728,8 +743,8 @@ static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u pag } // Do we need to scroll ? if(cur_row==nrows) { - //Fill with black on non-text modes and with 0x7 on textmode - Bit8u fill = (CurMode->type == M_TEXT)?0x7:0; + //Fill with black on non-text modes and with the default ANSI attribute on textmode + Bit8u fill = (CurMode->type == M_TEXT)?DefaultANSIAttr():0; INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,fill,page); cur_row--; } @@ -769,3 +784,18 @@ void INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,B INT10_SetCursorPos(cur_row,cur_col,page); } } + +bool pc98_doskey_insertmode = false; + +bool INT10_GetInsertState() +{ + if (IS_PC98_ARCH) { + /* state is internal to DOSKEY */ + return pc98_doskey_insertmode; + } + else { + const auto flags = mem_readb(BIOS_KEYBOARD_FLAGS1); + const auto state =static_cast(flags & BIOS_KEYBOARD_FLAGS1_INSERT_ACTIVE); + return state; + } +} diff --git a/src/ints/int10_memory.cpp b/src/ints/int10_memory.cpp index b8ece75ff..045644fea 100644 --- a/src/ints/int10_memory.cpp +++ b/src/ints/int10_memory.cpp @@ -54,9 +54,16 @@ static Bit16u map_offset[8]={ }; void INT10_LoadFont(PhysPt font,bool reload,Bitu count,Bitu offset,Bitu map,Bitu height) { + unsigned char m64k; + + if (IS_VGA_ARCH || (IS_EGA_ARCH && vga.mem.memsize >= 0x20000)) + m64k=0x02; + else + m64k=0x00; + PhysPt ftwhere=PhysMake(0xa000,map_offset[map & 0x7]+(Bit16u)(offset*32)); IO_Write(0x3c4,0x2);IO_Write(0x3c5,0x4); //Enable plane 2 - IO_Write(0x3c4,0x4);IO_Write(0x3c5,0x6); //disable odd/even memory write + IO_Write(0x3c4,0x4);IO_Write(0x3c5,0x4|m64k); //disable odd/even memory write IO_Write(0x3ce,0x6);Bitu old_6=IO_Read(0x3cf); IO_Write(0x3cf,0x0); //Disable odd/even and a0000 addressing for (Bitu i=0;itype==M_TEXT) { - INT10_SetCursorShape(0x06,07); + INT10_SetCursorShape(CURSOR_SCAN_LINE_NORMAL, CURSOR_SCAN_LINE_END); } // Set cursor pos for page 0..7 for (Bit8u ct=0;ct<8;ct++) INT10_SetCursorPos(0,0,ct); @@ -985,7 +985,14 @@ bool INT10_SetVideoMode(Bit16u mode) { if (CurMode->special & _EGA_HALF_CLOCK) seq_data[1]|=0x08; //Check for half clock if ((machine==MCH_EGA) && (CurMode->special & _EGA_HALF_CLOCK)) seq_data[1]|=0x02; - seq_data[4]|=0x02; //More than 64kb + + if (IS_VGA_ARCH || (IS_EGA_ARCH && vga.mem.memsize >= 0x20000)) + seq_data[4]|=0x02; //More than 64kb + else if (IS_EGA_ARCH && CurMode->vdispend==350) { + seq_data[4] &= ~0x04; // turn on odd/even + seq_data[1] |= 0x04; // half clock + } + switch (CurMode->type) { case M_TEXT: if (CurMode->cwidth==9) seq_data[1] &= ~1; @@ -1239,9 +1246,16 @@ bool INT10_SetVideoMode(Bit16u mode) { case M_LIN32: offset = 4 * CurMode->swidth/8; break; + case M_EGA: + if (IS_EGA_ARCH && vga.mem.memsize < 0x20000 && CurMode->vdispend==350) + offset = CurMode->hdispend/4; + else + offset = CurMode->hdispend/2; + break; default: - offset = CurMode->hdispend/2; - } + offset = CurMode->hdispend/2; + break; + } IO_Write(crtc_base,0x13); IO_Write(crtc_base + 1u,offset & 0xff); @@ -1269,11 +1283,13 @@ bool INT10_SetVideoMode(Bit16u mode) { break; case M_LIN4: case M_EGA: - if (CurMode->mode==0x11) // 0x11 also sets address wrap. thought maybe all 2 color modes did but 0x0f doesn't. - mode_control=0xc3; // so.. 0x11 or 0x0f a one off? - else { - mode_control=0xe3; - } + if (CurMode->mode==0x11) // 0x11 also sets address wrap. thought maybe all 2 color modes did but 0x0f doesn't. + mode_control=0xc3; // so.. 0x11 or 0x0f a one off? + else + mode_control=0xe3; + + if (IS_EGA_ARCH && vga.mem.memsize < 0x20000 && CurMode->vdispend==350) + mode_control &= ~0x40; // word mode break; case M_TEXT: case M_VGA: @@ -1290,6 +1306,9 @@ bool INT10_SetVideoMode(Bit16u mode) { break; } + if (IS_EGA_ARCH && vga.mem.memsize < 0x20000) + mode_control &= ~0x20; // address wrap bit 13 + IO_Write(crtc_base,0x17);IO_Write(crtc_base+1u,mode_control); /* Renable write protection */ IO_Write(crtc_base,0x11); @@ -1349,8 +1368,11 @@ bool INT10_SetVideoMode(Bit16u mode) { break; case M_LIN4: case M_EGA: - if (CurMode->mode == 0x0f) - gfx_data[0x7]=0x05; // only planes 0 and 2 are used + if (IS_EGA_ARCH && vga.mem.memsize < 0x20000 && CurMode->vdispend==350) { + gfx_data[0x5]|=0x10; //Odd-Even Mode + gfx_data[0x6]|=0x02; //Odd-Even Mode + gfx_data[0x7]=0x5; /* Color don't care */ + } gfx_data[0x6]|=0x05; //graphics mode at 0xa000-affff break; case M_CGA4: @@ -1677,9 +1699,9 @@ dac_text16: IO_Write(crtc_base,0x31);IO_Write(crtc_base+1u,reg_31); //Enable banked memory and 256k+ access IO_Write(crtc_base,0x58); - if (vga.vmemsize >= (4*1024*1024)) + if (vga.mem.memsize >= (4*1024*1024)) IO_Write(crtc_base+1u,0x3); // 4+ MB window - else if (vga.vmemsize >= (2*1024*1024)) + else if (vga.mem.memsize >= (2*1024*1024)) IO_Write(crtc_base+1u,0x2); // 2 MB window else IO_Write(crtc_base+1u,0x1); // 1 MB window diff --git a/src/ints/int10_vesa.cpp b/src/ints/int10_vesa.cpp index a0d489f76..f3f42e19f 100644 --- a/src/ints/int10_vesa.cpp +++ b/src/ints/int10_vesa.cpp @@ -155,7 +155,7 @@ Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off) { } mem_writed(buffer+0x0a,0x0); //Capabilities and flags mem_writed(buffer+0x0e,int10.rom.vesa_modes); //VESA Mode list - mem_writew(buffer+0x12,(Bit16u)(vga.vmemsize/(64*1024))); // memory size in 64kb blocks + mem_writew(buffer+0x12,(Bit16u)(vga.mem.memsize/(64*1024))); // memory size in 64kb blocks return VESA_SUCCESS; } @@ -300,11 +300,11 @@ foundit: pageSize &= ~0xFFFFu; } Bitu pages = 0; - if (pageSize > vga.vmemsize) { + if (pageSize > vga.mem.memsize) { // mode not supported by current hardware configuration modeAttributes &= ~0x1; } else if (pageSize) { - pages = (vga.vmemsize / pageSize)-1; + pages = (vga.mem.memsize / pageSize)-1; } var_write(&minfo.NumberOfImagePages, pages); var_write(&minfo.ModeAttributes, modeAttributes); @@ -351,7 +351,7 @@ Bit8u VESA_GetSVGAMode(Bit16u & mode) { Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) { if (window) return VESA_FAIL; - if (((Bit32u)(address)*64*1024sheight; diff --git a/src/ints/int10_vptable.cpp b/src/ints/int10_vptable.cpp index d07e0346b..87ac6c7f1 100644 --- a/src/ints/int10_vptable.cpp +++ b/src/ints/int10_vptable.cpp @@ -564,10 +564,7 @@ void INT10_SetupBasicVideoParameterTable(void) { /* TODO: Free previous block */ BIOS_VIDEO_TABLE_SIZE = (Bitu)copy_sz; - if (mainline_compatible_bios_mapping) - BIOS_VIDEO_TABLE_LOCATION = RealMake(0xf000,0xf0a4); - else - BIOS_VIDEO_TABLE_LOCATION = (Bitu)PhysToReal416(ROMBIOS_GetMemory((Bitu)copy_sz,"BIOS video table (INT 1Dh)")); /* TODO: make option */ + BIOS_VIDEO_TABLE_LOCATION = (Bitu)PhysToReal416(ROMBIOS_GetMemory((Bitu)copy_sz,"BIOS video table (INT 1Dh)")); /* TODO: make option */ /* NTS: Failure to allocate means BIOS_VIDEO_TABLE_LOCATION == 0 */ } diff --git a/src/ints/mouse.cpp b/src/ints/mouse.cpp index 7f8003ae7..5e6baea3b 100644 --- a/src/ints/mouse.cpp +++ b/src/ints/mouse.cpp @@ -611,6 +611,35 @@ void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) { if (mouse.x < mouse.min_x) mouse.x = mouse.min_x; if (mouse.y > mouse.max_y) mouse.y = mouse.max_y; if (mouse.y < mouse.min_y) mouse.y = mouse.min_y; + extern int user_cursor_x, user_cursor_y; + extern int user_cursor_sw, user_cursor_sh; + extern bool user_cursor_locked; + + /*make mouse emulated, eventually*/ + extern MOUSE_EMULATION user_cursor_emulation; + bool emu; + switch (user_cursor_emulation) + { + case MOUSE_EMULATION_ALWAYS: + emu = true; + break; + case MOUSE_EMULATION_INTEGRATION: + emu = !user_cursor_locked; + break; + case MOUSE_EMULATION_LOCKED: + emu = user_cursor_locked; + break; + case MOUSE_EMULATION_NEVER: + default: + emu = false; + } + if (!emu) + { + const auto x1 = 1.0 / static_cast(user_cursor_sw) * user_cursor_x; + const auto y1 = 1.0 / static_cast(user_cursor_sh) * user_cursor_y; + mouse.x = x1 * mouse.max_x; + mouse.y = y1 * mouse.max_y; + } mouse.ps2x += xrel; mouse.ps2y += yrel; @@ -1379,3 +1408,7 @@ void MOUSE_Init() { AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(MOUSE_OnReset)); } +bool MOUSE_IsHidden() +{ + return static_cast(mouse.hidden); +} diff --git a/src/ints/xms.cpp b/src/ints/xms.cpp index 646957129..efafbcb50 100644 --- a/src/ints/xms.cpp +++ b/src/ints/xms.cpp @@ -593,8 +593,6 @@ extern Bitu VGA_BIOS_SEG; extern Bitu VGA_BIOS_SEG_END; extern Bitu VGA_BIOS_Size; -extern bool mainline_compatible_mapping; - bool XMS_IS_ACTIVE() { return (xms_callback != 0); } @@ -697,9 +695,7 @@ public: if (first_umb_seg == 0) { first_umb_seg = DOS_PRIVATE_SEGMENT_END; - if (mainline_compatible_mapping && first_umb_seg < 0xD000) - first_umb_seg = 0xD000; /* Mainline DOSBox assumes a 128KB UMB region starting at 0xD000 */ - else if (first_umb_seg < VGA_BIOS_SEG_END) + if (first_umb_seg < VGA_BIOS_SEG_END) first_umb_seg = VGA_BIOS_SEG_END; } if (first_umb_size == 0) first_umb_size = ROMBIOS_MinAllocatedLoc()>>4; diff --git a/src/misc/setup.cpp b/src/misc/setup.cpp index 17abdf0bf..d42b73f4d 100644 --- a/src/misc/setup.cpp +++ b/src/misc/setup.cpp @@ -289,6 +289,38 @@ bool Prop_double::SetValue(std::string const& input){ return SetVal(val,false,/*warn*/true); } +bool Prop_double::CheckValue(Value const& in, bool warn) +{ + if(suggested_values.empty() && Property::CheckValue(in, warn)) + return true; + + const auto mi = static_cast(min); + const auto ma = static_cast(max); + const auto va = static_cast(Value(in)); + const auto same = [](const double a, const double b, const double epsilon) { + return fabs(a - b < epsilon); + }; + const auto tolerance = 0.0000001; + + if(same(mi, -1.0, tolerance) && same(ma, -1.0, tolerance)) + return true; + + if(va >= mi && va <= ma) + return true; + + if(warn) + LOG_MSG( + "%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s", + in.ToString().c_str(), + min.ToString().c_str(), + max.ToString().c_str(), + propname.c_str(), + default_value.ToString().c_str() + ); + + return false; +} + bool Prop_int::SetValue(std::string const& input){; Value val; if(!val.SetValue(input,Value::V_INT)) return false; diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp index 5f0ab2808..9ab9db788 100644 --- a/src/shell/shell.cpp +++ b/src/shell/shell.cpp @@ -644,7 +644,7 @@ void SHELL_Init() { if (machine == MCH_PC98) { MSG_Add("SHELL_STARTUP_BEGIN", - "\033[44;1m\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" + "\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" diff --git a/src/shell/shell_misc.cpp b/src/shell/shell_misc.cpp index 2c412e22f..9c8651a87 100644 --- a/src/shell/shell_misc.cpp +++ b/src/shell/shell_misc.cpp @@ -28,10 +28,19 @@ #include "regs.h" #include "callback.h" #include "support.h" +#include "../ints/int10.h" #ifdef WIN32 #include "../dos/cdrom.h" #endif +#ifdef _MSC_VER +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#else +# define MIN(a,b) std::min(a,b) +# define MAX(a,b) std::max(a,b) +#endif + void DOS_Shell::ShowPrompt(void) { char dir[DOS_PATHLENGTH]; dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up) @@ -92,6 +101,21 @@ static void outc(Bit8u c) { DOS_WriteFile(STDOUT,&c,&n); } +//! \brief Moves the caret to prev row/last column when column is 0 (video mode 0). +void MoveCaretBackwards() +{ + Bit8u col, row; + const Bit8u page(0); + INT10_GetCursorPos(&row, &col, page); + + if (col != 0) + return; + + Bit16u cols; + INT10_GetScreenColumns(&cols); + INT10_SetCursorPos(row - 1, static_cast(cols), page); +} + /* NTS: buffer pointed to by "line" must be at least CMD_MAXLINE+1 large */ void DOS_Shell::InputCommand(char * line) { Bitu size=CMD_MAXLINE-2; //lastcharacter+0 @@ -124,6 +148,16 @@ void DOS_Shell::InputCommand(char * line) { else if (IS_PC98_ARCH) { extern Bit16u last_int16_code; + /* shift state is needed for some key combinations not directly supported by CON driver. + * bit 4 = CTRL + * bit 3 = GRPH/ALT + * bit 2 = kana + * bit 1 = caps + * bit 0 = SHIFT */ + uint8_t shiftstate = mem_readb(0x52A + 0x0E); + + /* NTS: PC-98 keyboards lack the US layout HOME / END keys, therefore there is no mapping here */ + /* NTS: Since left arrow and backspace map to the same byte value, PC-98 treats it the same at the DOS prompt. * However the PC-98 version of DOSKEY seems to be able to differentiate the two anyway and let the left * arrow move the cursor back (perhaps it's calling INT 18h directly then?) */ @@ -131,44 +165,62 @@ void DOS_Shell::InputCommand(char * line) { cr = 0x4800; /* IBM extended code up arrow */ else if (c == 0x0A) cr = 0x5000; /* IBM extended code down arrow */ - else if (c == 0x0C) - cr = 0x4D00; /* IBM extended code right arrow */ + else if (c == 0x0C) { + if (shiftstate & 0x10/*CTRL*/) + cr = 0x7400; /* IBM extended code CTRL + right arrow */ + else + cr = 0x4D00; /* IBM extended code right arrow */ + } else if (c == 0x08) { /* IBM extended code left arrow OR backspace. use last scancode to tell which as DOSKEY apparently can. */ - if (last_int16_code == 0x3B00) - cr = 0x4B00; /* left arrow */ - else + if (last_int16_code == 0x3B00) { + if (shiftstate & 0x10/*CTRL*/) + cr = 0x7300; /* CTRL + left arrow */ + else + cr = 0x4B00; /* left arrow */ + } + else { cr = 0x08; /* backspace */ + } } else if (c == 0x1B) { /* escape */ - DOS_ReadFile(input_handle,&c,&n); - if (c == 0x44) // DEL - cr = 0x5300; - else if (c == 0x53) // F1 - cr = 0x3B00; - else if (c == 0x54) // F2 - cr = 0x3C00; - else if (c == 0x55) // F3 - cr = 0x3D00; - else if (c == 0x56) // F4 - cr = 0x3E00; - else if (c == 0x57) // F5 - cr = 0x3F00; - else if (c == 0x45) // F6 - cr = 0x4000; - else if (c == 0x4A) // F7 - cr = 0x4100; - else if (c == 0x50) // F8 - cr = 0x4200; - else if (c == 0x51) // F9 - cr = 0x4300; - else if (c == 0x5A) // F10 - cr = 0x4400; - else - cr = 0; + /* Either it really IS the ESC key, or an ANSI code */ + if (last_int16_code != 0x001B) { + DOS_ReadFile(input_handle,&c,&n); + if (c == 0x44) // DEL + cr = 0x5300; + else if (c == 0x50) // INS + cr = 0x5200; + else if (c == 0x53) // F1 + cr = 0x3B00; + else if (c == 0x54) // F2 + cr = 0x3C00; + else if (c == 0x55) // F3 + cr = 0x3D00; + else if (c == 0x56) // F4 + cr = 0x3E00; + else if (c == 0x57) // F5 + cr = 0x3F00; + else if (c == 0x45) // F6 + cr = 0x4000; + else if (c == 0x4A) // F7 + cr = 0x4100; + else if (c == 0x50) // F8 + cr = 0x4200; + else if (c == 0x51) // F9 + cr = 0x4300; + else if (c == 0x5A) // F10 + cr = 0x4400; + else + cr = 0; + } + else { + cr = (Bit16u)c; + } } - else + else { cr = (Bit16u)c; + } } else { if (c == 0) { @@ -200,9 +252,55 @@ void DOS_Shell::InputCommand(char * line) { if (str_index) { outc(8); str_index --; + MoveCaretBackwards(); } break; + case 0x7400: /*CTRL + RIGHT : cmd.exe-like next word*/ + { + auto pos = line + str_index; + auto spc = *pos == ' '; + const auto end = line + str_len; + + while (pos < end) { + if (spc && *pos != ' ') + break; + if (*pos == ' ') + spc = true; + pos++; + } + + const auto lgt = MIN(pos, end) - (line + str_index); + + for (auto i = 0; i < lgt; i++) + outc(static_cast(line[str_index++])); + } + break; + case 0x7300: /*CTRL + LEFT : cmd.exe-like previous word*/ + { + auto pos = line + str_index - 1; + const auto beg = line; + const auto spc = *pos == ' '; + + if (spc) { + while(*pos == ' ') pos--; + while(*pos != ' ') pos--; + pos++; + } + else { + while(*pos != ' ') pos--; + pos++; + } + + const auto lgt = abs(MAX(pos, beg) - (line + str_index)); + + for (auto i = 0; i < lgt; i++) { + outc(8); + str_index--; + MoveCaretBackwards(); + } + } + break; case 0x4D00: /* RIGHT */ if (str_index < str_len) { outc((Bit8u)line[str_index++]); @@ -216,6 +314,15 @@ void DOS_Shell::InputCommand(char * line) { } break; + case 0x5200: /* INS */ + if (IS_PC98_ARCH) { // INS state handled by IBM PC/AT BIOS, faked for PC-98 mode + extern bool pc98_doskey_insertmode; + + // NTS: No visible change to the cursor, just like DOSKEY on PC-98 MS-DOS + pc98_doskey_insertmode = !pc98_doskey_insertmode; + } + break; + case 0x4F00: /* END */ while (str_index < str_len) { outc((Bit8u)line[str_index++]); @@ -437,24 +544,47 @@ void DOS_Shell::InputCommand(char * line) { } break; case 0x1b: /* ESC */ - if (IS_PC98_ARCH) { - //TODO: Either different behavior or none at all + // NTS: According to real PC-98 DOS: + // If DOSKEY is loaded, ESC clears the prompt + // If DOSKEY is NOT loaded, ESC does nothing. In fact, after ESC, + // the next character input is thrown away before resuming normal keyboard input. + // + // DOSBox / DOSBox-X have always acted as if DOSKEY is loaded in a fashion, so + // we'll emulate the PC-98 DOSKEY behavior here. + // + // DOSKEY on PC-98 is able to clear the whole prompt and even bring the cursor + // back up to the first line if the input crosses multiple lines. + + // NTS: According to real IBM/Microsoft PC/AT DOS: + // If DOSKEY is loaded, ESC clears the prompt + // If DOSKEY is NOT loaded, ESC prints a backslash and goes to the next line. + // The Windows 95 version of DOSKEY puts the cursor at a horizontal position + // that matches the DOS prompt (not emulated here). + // + // DOSBox / DOSBox-X have always acted as if DOSKEY is loaded in a fashion, so + // we'll emulate DOSKEY behavior here. + + while (str_index < str_len) { + outc(' '); + str_index++; } - else { - //write a backslash and return to the next line - outc('\\'); - outc('\n'); - *line = 0; // reset the line. - if (l_completion.size()) l_completion.clear(); //reset the completion list. - this->InputCommand(line); //Get the NEW line. - size = 0; // stop the next loop - str_len = 0; // prevent multiple adds of the same line + while (str_index > 0) { + outc(8); + outc(' '); + outc(8); + MoveCaretBackwards(); + str_index--; } + + *line = 0; // reset the line. + if (l_completion.size()) l_completion.clear(); //reset the completion list. + str_index = 0; + str_len = 0; break; default: if (cr >= 0x100) break; if (l_completion.size()) l_completion.clear(); - if(str_index < str_len && true) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ? + if(str_index < str_len && !INT10_GetInsertState()) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ? outc(' ');//move cursor one to the right. Bit16u a = str_len - str_index; Bit8u* text=reinterpret_cast(&line[str_index]); diff --git a/src/xBRZ/Changelog.txt b/src/xBRZ/Changelog.txt new file mode 100644 index 000000000..7eea4c0c8 --- /dev/null +++ b/src/xBRZ/Changelog.txt @@ -0,0 +1,58 @@ +=========== +|Changelog| +=========== + +xBRZ 1.6 [2018-02-27] +--------------------- +Added bilinear scaling +Updated license info +Option to skip color buffer creation + + +xBRZ 1.5 [2017-08-07] +--------------------- +Added RGB conversion routines + + +xBRZ 1.4 [2015-07-25] +--------------------- +Added 6xBRZ scaler +Create color distance buffer lazily + + +xBRZ 1.3 [2015-04-03] +--------------------- +Improved ARGB performance by 15% +Fixed alpha channel gradient bug + + +xBRZ 1.2 [2014-11-21] +--------------------- +Further improved performance by over 30% + + +xBRZ 1.1 [2014-11-02] +--------------------- +Support images with alpha channel +Improved color analysis + + +xBRZ 1.0 [2013-02-11] +--------------------- +Fixed xBRZ scaler compiler issues for GCC + + +xBRZ 0.2 [2012-12-11] +--------------------- +Added 5xBRZ scaler +Optimized xBRZ scaler performance by factor 3 +Further improved image quality of xBRZ scaler + + +xBRZ 0.1 [2012-09-26] +--------------------- +Initial release: +- scale while preserving small image features +- support multithreading +- support 64-bit architectures +- support processing image slices diff --git a/src/xBRZ/License.txt b/src/xBRZ/License.txt new file mode 100644 index 000000000..94a045322 --- /dev/null +++ b/src/xBRZ/License.txt @@ -0,0 +1,621 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/src/xBRZ/xbrz.cpp b/src/xBRZ/xbrz.cpp new file mode 100644 index 000000000..8c7cf7d65 --- /dev/null +++ b/src/xBRZ/xbrz.cpp @@ -0,0 +1,1270 @@ +// **************************************************************************** +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +#include "xbrz.h" +#include +#include +#include +#include //std::sqrt +#include "xbrz_tools.h" + +using namespace xbrz; + + +namespace +{ +template inline +uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending +{ + static_assert(0 < M && M < N && N <= 1000, ""); + + auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; }; + + return makePixel(calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); +} + + +template inline +uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!) +{ + static_assert(0 < M && M < N && N <= 1000, ""); + + const unsigned int weightFront = getAlpha(pixFront) * M; + const unsigned int weightBack = getAlpha(pixBack) * (N - M); + const unsigned int weightSum = weightFront + weightBack; + if (weightSum == 0) + return 0; + + auto calcColor = [=](unsigned char colFront, unsigned char colBack) + { + return static_cast((colFront * weightFront + colBack * weightBack) / weightSum); + }; + + return makePixel(static_cast(weightSum / N), + calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff" +// { +// fld n +// fsqrt +// } +//} +// + + +#ifdef _MSC_VER + #define FORCE_INLINE __forceinline +#elif defined __GNUC__ + #define FORCE_INLINE __attribute__((always_inline)) inline +#else + #define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + +#if 0 +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} +#endif + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + //const double k_b = 0.0722; //ITU-R BT.709 conversion + //const double k_r = 0.2126; // + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +inline +double distYCbCrBuffered(uint32_t pix1, uint32_t pix2) +{ + //30% perf boost compared to plain distYCbCr()! + //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB + static const std::vector diffToDist = [] + { + std::vector tmp; + + for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores) + { + const int r_diff = getByte<2>(i) * 2 - 0xFF; + const int g_diff = getByte<1>(i) * 2 - 0xFF; + const int b_diff = getByte<0>(i) * 2 - 0xFF; + + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + tmp.push_back(static_cast(std::sqrt(square(y) + square(c_b) + square(c_r)))); + } + return tmp; + }(); + + //if (pix1 == pix2) -> 8% perf degradation! + // return 0; + //if (pix1 < pix2) + // std::swap(pix1, pix2); -> 30% perf degradation!!! +#if 1 + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + return diffToDist[(((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte + (((g_diff + 0xFF) / 2) << 8) | + (( b_diff + 0xFF) / 2)]; +#else //not noticeably faster: + const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2; + const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte + const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2; + + return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)]; +#endif +} + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evaluate the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +template +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + + auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); }; + + const int weight = 4; + double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); + double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +//inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +#ifndef NDEBUG + int debugPixelX = -1; + int debugPixelY = 12; + __declspec(thread) bool breakIntoDebugger = false; +#endif + + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void blendPixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz::ScalerCfg& cfg) +{ +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + +#ifndef NDEBUG + if (breakIntoDebugger) + __debugbreak(); //__asm int 3; +#endif + + (void)a; //silence Clang's -Wunused-function + + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + { + auto eq = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight) < cfg.equalColorTolerance; }; + auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); }; + + const bool doLineBlend = [&]() -> bool + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (!eq(e, i) && eq(g, h) && eq(h, i) && eq(i, f) && eq(f, c)) + return false; + + return true; + }(); + + const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + + if (doLineBlend) + { + const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = dist(h, c); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px, out); + } + } + else + Scaler::blendCorner(px, out); + } + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0'); + static_assert(BLEND_NONE == 0, ""); + + //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligible + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < bufferSize) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { +#ifndef NDEBUG + breakIntoDebugger = debugPixelX == x && debugPixelY == y; +#endif + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker4 = {}; //perf: initialization is negligible + + ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker4.b = s_m1[x]; + ker4.c = s_m1[x_p1]; + ker4.d = s_m1[x_p2]; + + ker4.e = s_0[x_m1]; + ker4.f = s_0[x]; + ker4.g = s_0[x_p1]; + ker4.h = s_0[x_p2]; + + ker4.i = s_p1[x_m1]; + ker4.j = s_p1[x]; + ker4.k = s_p1[x_p1]; + ker4.l = s_p1[x_p2]; + + ker4.m = s_p2[x_m1]; + ker4.n = s_p2[x]; + ker4.o = s_p2[x_p1]; + ker4.p = s_p2[x_p2]; + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + const BlendResult res = preProcessCorners(ker4, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale); + //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 5% perf-improvement + { + Kernel_3x3 ker3 = {}; //perf: initialization is negligible + + ker3.a = ker4.a; + ker3.b = ker4.b; + ker3.c = ker4.c; + + ker3.d = ker4.e; + ker3.e = ker4.f; + ker3.f = ker4.g; + + ker3.g = ker4.i; + ker3.h = ker4.j; + ker3.i = ker4.k; + + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + } + } + } +} + +//------------------------------------------------------------------------------------ + +template +struct Scaler2x : public ColorGradient +{ + static const int scale = 2; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<1, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 1>(), col); + alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +template +struct Scaler3x : public ColorGradient +{ + static const int scale = 3; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<2, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 2>(), col); + alphaGrad<3, 4>(out.template ref<2, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref<2, 1>(), col); + alphaGrad<7, 8>(out.template ref<2, 2>(), col); // + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254 + } +}; + + +template +struct Scaler4x : public ColorGradient +{ + static const int scale = 4; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<3, 4>(out.template ref<3, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 3>(), col); + alphaGrad<1, 4>(out.template ref<3, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 3>(), col); + + alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + + out.template ref<3, 3>() = col; + out.template ref<3, 2>() = col; + out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +template +struct Scaler5x : public ColorGradient +{ + static const int scale = 5; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + alphaGrad<2, 3>(out.template ref<3, 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref(), col); + alphaGrad<1, 8>(out.template ref(), col); // + + alphaGrad<7, 8>(out.template ref<4, 3>(), col); + alphaGrad<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367 + } +}; + + +template +struct Scaler6x : public ColorGradient +{ + static const int scale = 6; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910 + alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243 + alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243 + alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508 + alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508 + } +}; + +//------------------------------------------------------------------------------------ + +struct ColorDistanceRGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + return distYCbCrBuffered(pix1, pix2); + + //if (pix1 == pix2) //about 4% perf boost + // return 0; + //return distYCbCr(pix1, pix2, luminanceWeight); + } +}; + +struct ColorDistanceARGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + const double a1 = getAlpha(pix1) / 255.0 ; + const double a2 = getAlpha(pix2) / 255.0 ; + /* + Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1] + + 1. if a1 = a2, distance should be: a1 * distYCbCr() + 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255 + 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr() + */ + + //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2); + //=> following code is 15% faster: + const double d = distYCbCrBuffered(pix1, pix2); + if (a1 < a2) + return a1 * d + 255 * (a2 - a1); + else + return a2 * d + 255 * (a1 - a2); + + //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2))); + } +}; + + +struct ColorDistanceUnbufferedARGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + const double a1 = getAlpha(pix1) / 255.0 ; + const double a2 = getAlpha(pix2) / 255.0 ; + + const double d = distYCbCr(pix1, pix2, luminanceWeight); + if (a1 < a2) + return a1 * d + 255 * (a2 - a1); + else + return a2 * d + 255 * (a1 - a2); + } +}; + + +struct ColorGradientRGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientRGB(pixFront, pixBack); + } +}; + +struct ColorGradientARGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientARGB(pixFront, pixBack); + } +}; +} + + +void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + static_assert(SCALE_FACTOR_MAX == 6, ""); + switch (colFmt) + { + case ColorFormat::RGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + + case ColorFormat::ARGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + + case ColorFormat::ARGB_UNBUFFERED: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + } + assert(false); +} + + +bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance) +{ + switch (colFmt) + { + case ColorFormat::RGB: + return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + case ColorFormat::ARGB: + return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + case ColorFormat::ARGB_UNBUFFERED: + return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + } + assert(false); + return false; +} + + +void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + 0, trgHeight, [](uint32_t pix) { return pix; }); +} + + +void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + 0, trgHeight, [](uint32_t pix) { return pix; }); +} + + +#if 0 +//#include +void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + const int TASK_GRANULARITY = 16; + + concurrency::task_group tg; + + for (int i = 0; i < trgHeight; i += TASK_GRANULARITY) + tg.run([=] + { + const int iLast = std::min(i + TASK_GRANULARITY, trgHeight); + xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + i, iLast, [](uint32_t pix) { return pix; }); + }); + tg.wait(); +} + + +//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080) +//#include +void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + //C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx + //introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx + using namespace concurrency; + //TODO: pitch + + if (srcHeight <= 0 || srcWidth <= 0) return; + + const float scaleX = static_cast(trgWidth ) / srcWidth; + const float scaleY = static_cast(trgHeight) / srcHeight; + + array_view srcView(srcHeight, srcWidth, src); + array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg); + trgView.discard_data(); + + parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ? + { + const int y = idx[0]; + const int x = idx[1]; + //Perf notes: + // -> float-based calculation is (almost 2x) faster than double! + // -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx + // -> no noticeable improvement with restrict(amp,cpu) + // -> iterating over y-axis only is significantly slower! + // -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower! + const int y1 = srcHeight * y / trgHeight; + int y2 = y1 + 1; + if (y2 == srcHeight) --y2; + + const float yy1 = y / scaleY - y1; + const float y2y = 1 - yy1; + //------------------------------------- + const int x1 = srcWidth * x / trgWidth; + int x2 = x1 + 1; + if (x2 == srcWidth) --x2; + + const float xx1 = x / scaleX - x1; + const float x2x = 1 - xx1; + //------------------------------------- + const float x2xy2y = x2x * y2y; + const float xx1y2y = xx1 * y2y; + const float x2xyy1 = x2x * yy1; + const float xx1yy1 = xx1 * yy1; + + auto interpolate = [=](int offset) + { + /* + https://en.wikipedia.org/wiki/Bilinear_interpolation + (c11(x2 - x) + c21(x - x1)) * (y2 - y ) + + (c12(x2 - x) + c22(x - x1)) * (y - y1) + */ + const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff; + const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff; + const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff; + const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff; + + return c11 * x2xy2y + c21 * xx1y2y + + c12 * x2xyy1 + c22 * xx1yy1; + }; + + const float bi = interpolate(0); + const float gi = interpolate(1); + const float ri = interpolate(2); + const float ai = interpolate(3); + + const auto b = static_cast(bi + 0.5f); + const auto g = static_cast(gi + 0.5f); + const auto r = static_cast(ri + 0.5f); + const auto a = static_cast(ai + 0.5f); + + trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b; + }); + trgView.synchronize(); //throw ? +} +#endif \ No newline at end of file diff --git a/src/xBRZ/xbrz.h b/src/xBRZ/xbrz.h new file mode 100644 index 000000000..ac0c792bc --- /dev/null +++ b/src/xBRZ/xbrz.h @@ -0,0 +1,79 @@ +// **************************************************************************** +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +#ifndef XBRZ_HEADER_3847894708239054 +#define XBRZ_HEADER_3847894708239054 + +#include //size_t +#include //uint32_t +#include +#include "xbrz_config.h" + +namespace xbrz +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- highly optimized for performance +- support alpha channel +- support multithreading +- support 64-bit architectures +- support processing image slices +- support scaling up to 6xBRZ +*/ + +enum class ColorFormat //from high bits -> low bits, 8 bit per channel +{ + RGB, //8 bit for each red, green, blue, upper 8 bits unused + ARGB, //including alpha channel, BGRA byte order on little-endian machines + ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time +}; + +const int SCALE_FACTOR_MAX = 6; + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + in the target image data if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows +*/ +void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + ColorFormat colFmt, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = (std::numeric_limits::max)()); //slice of source image + +void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight); + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight); + + +//parameter tuning +bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance); +} + +#endif diff --git a/src/xBRZ/xbrz_config.h b/src/xBRZ/xbrz_config.h new file mode 100644 index 000000000..dc5e5aec8 --- /dev/null +++ b/src/xBRZ/xbrz_config.h @@ -0,0 +1,34 @@ +// **************************************************************************** +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +#ifndef XBRZ_CONFIG_HEADER_284578425345 +#define XBRZ_CONFIG_HEADER_284578425345 + +//do NOT include any headers here! used by xBRZ_dll!!! + +namespace xbrz +{ +struct ScalerCfg +{ + double luminanceWeight = 1; + double equalColorTolerance = 30; + double dominantDirectionThreshold = 3.6; + double steepDirectionThreshold = 2.2; + double newTestAttribute = 0; //unused; test new parameters +}; +} + +#endif \ No newline at end of file diff --git a/src/xBRZ/xbrz_tools.h b/src/xBRZ/xbrz_tools.h new file mode 100644 index 000000000..20a3bb5ae --- /dev/null +++ b/src/xBRZ/xbrz_tools.h @@ -0,0 +1,297 @@ +// **************************************************************************** +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +#ifndef XBRZ_TOOLS_H_825480175091875 +#define XBRZ_TOOLS_H_825480175091875 + +#include +#include +#include + + +namespace xbrz +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } +inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } +inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } +inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } + +inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; } +inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } + +inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); } +inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); } + +inline uint16_t rgb888to555(uint32_t pix) { return static_cast(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); } +inline uint16_t rgb888to565(uint32_t pix) { return static_cast(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); } + + +template inline +Pix* byteAdvance(Pix* ptr, int bytes) +{ + using PixNonConst = typename std::remove_cv::type; + using PixByte = typename std::conditional::value, char, const char>::type; + + static_assert(std::is_integral::value, "Pix* is expected to be cast-able to char*"); + + return reinterpret_cast(reinterpret_cast(ptr) + bytes); +} + + +//fill block with the given color +template inline +void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +// pitch change (use to change image pitch without any scaling, useful for fitting scaled image into D3D texture) +template +void pitchChange(const PixSrc* src, PixTrg* trg, int width, int height, int srcPitch, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixSrc* is expected to be cast-able to char*"); + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < width * static_cast(sizeof(PixSrc)) || + trgPitch < width * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = (std::max)(yFirst, 0); + yLast = (std::min)(yLast, height); + if (yFirst >= yLast || height <= 0 || width <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + const PixSrc* const srcLine = byteAdvance(src, y * srcPitch); + PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); + for (int x = 0; x < width; ++x) + trgLine[x] = pixCvrt(srcLine[x]); + } +} + +//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) +template +void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixSrc* is expected to be cast-able to char*"); + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(PixSrc)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = (std::max)(yFirst, 0); + yLast = (std::min)(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + const int ySrc = srcHeight * y / trgHeight; + const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch); + PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); + + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = pixCvrt(srcLine[xSrc]); + } + } +} + + +//nearest-neighbor (going over source image - fast for upscaling, since source is read only once +template +void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixSrc* is expected to be cast-able to char*"); + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(PixSrc)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrgLast - yTrgFirst; + + if (blockHeight > 0) + { + const PixSrc* srcLine = byteAdvance(src, y * srcPitch); + /**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch); + int xTrgFirst = 0; + + for (int x = 0; x < srcWidth; ++x) + { + const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrgLast - xTrgFirst; + if (blockWidth > 0) + { + xTrgFirst = xTrgLast; + + const auto trgPix = pixCvrt(srcLine[x]); + fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } +} + + +template +void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = (std::max)(yFirst, 0); + yLast = (std::min)(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + const double scaleX = static_cast(trgWidth ) / srcWidth; + const double scaleY = static_cast(trgHeight) / srcHeight; + + //perf notes: + // -> double-based calculation is (slightly) faster than float + // -> precalculation gives significant boost; std::vector<> memory allocation is negligible! + struct CoeffsX + { + int x1 = 0; + int x2 = 0; + double xx1 = 0; + double x2x = 0; + }; + std::vector buf(trgWidth); + for (int x = 0; x < trgWidth; ++x) + { + const int x1 = srcWidth * x / trgWidth; + int x2 = x1 + 1; + if (x2 == srcWidth) --x2; + + const double xx1 = x / scaleX - x1; + const double x2x = 1 - xx1; + + buf[x] = { x1, x2, xx1, x2x }; + } + + for (int y = yFirst; y < yLast; ++y) + { + const int y1 = srcHeight * y / trgHeight; + int y2 = y1 + 1; + if (y2 == srcHeight) --y2; + + const double yy1 = y / scaleY - y1; + const double y2y = 1 - yy1; + + const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch); + const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch); + PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); + + for (int x = 0; x < trgWidth; ++x) + { + //perf: do NOT "simplify" the variable layout without measurement! + const int x1 = buf[x].x1; + const int x2 = buf[x].x2; + const double xx1 = buf[x].xx1; + const double x2x = buf[x].x2x; + + const double x2xy2y = x2x * y2y; + const double xx1y2y = xx1 * y2y; + const double x2xyy1 = x2x * yy1; + const double xx1yy1 = xx1 * yy1; + + auto interpolate = [=](int offset) + { + /* + https://en.wikipedia.org/wiki/Bilinear_interpolation + (c11(x2 - x) + c21(x - x1)) * (y2 - y ) + + (c12(x2 - x) + c22(x - x1)) * (y - y1) + */ + const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff; + const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff; + const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff; + const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff; + + return c11 * x2xy2y + c21 * xx1y2y + + c12 * x2xyy1 + c22 * xx1yy1; + }; + + const double bi = interpolate(0); + const double gi = interpolate(1); + const double ri = interpolate(2); + const double ai = interpolate(3); + + const auto b = static_cast(bi + 0.5); + const auto g = static_cast(gi + 0.5); + const auto r = static_cast(ri + 0.5); + const auto a = static_cast(ai + 0.5); + + const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b; + + trgLine[x] = pixCvrt(trgPix); + } + } +} +} + +#endif //XBRZ_TOOLS_H_825480175091875 diff --git a/vs2015/config.h b/vs2015/config.h index cdd99a3bc..e4ba088dd 100644 --- a/vs2015/config.h +++ b/vs2015/config.h @@ -114,6 +114,9 @@ /* Set to 1 to enable SDL 2.x support */ /* #undef C_SDL2 */ +/* Set to 1 to enable XBRZ support */ +#define C_XBRZ 1 + /* Define to 1 if you have setpriority support */ #undef C_SET_PRIORITY diff --git a/vs2015/dosbox-x.vcxproj b/vs2015/dosbox-x.vcxproj index 38c5738de..974d25a12 100644 --- a/vs2015/dosbox-x.vcxproj +++ b/vs2015/dosbox-x.vcxproj @@ -120,57 +120,57 @@ $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib; + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib; $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib; + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib; $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib; + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib; $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib; + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib; $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ false - $(IncludePath);%DXSDK_DIR%\Include; - $(LibraryPath);%DXSDK_DIR%\Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ + $(IncludePath);$(DXSDK_DIR)Include; + $(LibraryPath);$(DXSDK_DIR)Lib;$(SolutionDir)..\bin\$(Platform)\$(Configuration)\ @@ -904,6 +904,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" + @@ -1117,9 +1118,12 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" + + + - \ No newline at end of file + diff --git a/vs2015/dosbox-x.vcxproj.filters b/vs2015/dosbox-x.vcxproj.filters index 2d6df5335..04a5cdd13 100644 --- a/vs2015/dosbox-x.vcxproj.filters +++ b/vs2015/dosbox-x.vcxproj.filters @@ -82,6 +82,9 @@ {77588c09-bef2-4f95-a219-8ac9d696a7ff} + + {6212d2c1-4c3f-4ec6-ad0e-88413d9f1b6c} + @@ -822,6 +825,9 @@ Sources\gui + + Sources\xBRZ + Sources\gui @@ -1451,6 +1457,15 @@ Includes + + Sources\xBRZ + + + Sources\xBRZ + + + Sources\xBRZ + Includes