Repository: lenticularis39/axpbox Branch: main Commit: e81dce564bea Files: 180 Total size: 2.0 MB Directory structure: gitextract_fd_kwrgy/ ├── .clang-format ├── .github/ │ └── workflows/ │ └── build-test-and-artifact.yml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake/ │ ├── CheckLargeFiles.cmake │ ├── FindPCAP.cmake │ ├── TestFileOffsetBits.c │ ├── TestLargeFiles.c.cmake.in │ └── TestWindowsFSeek.c ├── es40.cfg ├── sonar-project.properties ├── src/ │ ├── AliM1543C.cpp │ ├── AliM1543C.hpp │ ├── AliM1543C_ide.cpp │ ├── AliM1543C_ide.hpp │ ├── AliM1543C_usb.cpp │ ├── AliM1543C_usb.hpp │ ├── AlphaCPU.cpp │ ├── AlphaCPU.hpp │ ├── AlphaCPU_ieeefloat.cpp │ ├── AlphaCPU_vaxfloat.cpp │ ├── AlphaCPU_vmspal.cpp │ ├── AlphaSim.cpp │ ├── Cirrus.cpp │ ├── Cirrus.hpp │ ├── Configurator.cpp │ ├── Configurator.hpp │ ├── DEC21143.cpp │ ├── DEC21143.hpp │ ├── DEC21143_mii.hpp │ ├── DEC21143_tulipreg.hpp │ ├── DMA.cpp │ ├── DMA.hpp │ ├── DPR.cpp │ ├── DPR.hpp │ ├── Disk.cpp │ ├── Disk.hpp │ ├── DiskController.cpp │ ├── DiskController.hpp │ ├── DiskDevice.cpp │ ├── DiskDevice.hpp │ ├── DiskFile.cpp │ ├── DiskFile.hpp │ ├── DiskRam.cpp │ ├── DiskRam.hpp │ ├── Ethernet.cpp │ ├── Ethernet.hpp │ ├── Flash.cpp │ ├── Flash.hpp │ ├── FloppyController.cpp │ ├── FloppyController.hpp │ ├── FreeTextQuestion.hpp │ ├── Keyboard.cpp │ ├── Keyboard.hpp │ ├── Main.cpp │ ├── MultipleChoiceQuestion.hpp │ ├── NumberQuestion.hpp │ ├── PCIDevice.cpp │ ├── PCIDevice.hpp │ ├── Port80.cpp │ ├── Port80.hpp │ ├── Question.hpp │ ├── S3Trio64.cpp │ ├── S3Trio64.hpp │ ├── SCSIBus.cpp │ ├── SCSIBus.hpp │ ├── SCSIDevice.cpp │ ├── SCSIDevice.hpp │ ├── Serial.cpp │ ├── Serial.hpp │ ├── ShrinkingChoiceQuestion.hpp │ ├── StdAfx.hpp │ ├── Sym53C810.cpp │ ├── Sym53C810.hpp │ ├── Sym53C895.cpp │ ├── Sym53C895.hpp │ ├── System.cpp │ ├── System.hpp │ ├── SystemComponent.cpp │ ├── SystemComponent.hpp │ ├── TraceEngine.cpp │ ├── TraceEngine.hpp │ ├── VGA.cpp │ ├── VGA.hpp │ ├── base/ │ │ ├── Bugcheck.cpp │ │ ├── Bugcheck.hpp │ │ ├── ErrorHandler.cpp │ │ ├── ErrorHandler.hpp │ │ ├── Event.cpp │ │ ├── Event.hpp │ │ ├── Event_POSIX.cpp │ │ ├── Event_POSIX.hpp │ │ ├── Event_WIN32.cpp │ │ ├── Event_WIN32.hpp │ │ ├── Exception.cpp │ │ ├── Exception.hpp │ │ ├── Foundation.hpp │ │ ├── Mutex.cpp │ │ ├── Mutex.hpp │ │ ├── Mutex_POSIX.cpp │ │ ├── Mutex_POSIX.hpp │ │ ├── Mutex_WIN32.cpp │ │ ├── Mutex_WIN32.hpp │ │ ├── NumberFormatter.cpp │ │ ├── NumberFormatter.hpp │ │ ├── Platform.hpp │ │ ├── Platform_POSIX.hpp │ │ ├── Platform_VMS.hpp │ │ ├── Platform_WIN32.hpp │ │ ├── Poco.hpp │ │ ├── RWLock.cpp │ │ ├── RWLock.hpp │ │ ├── RWLock_POSIX.cpp │ │ ├── RWLock_POSIX.hpp │ │ ├── RWLock_WIN32.cpp │ │ ├── RWLock_WIN32.hpp │ │ ├── RefCountedObject.cpp │ │ ├── RefCountedObject.hpp │ │ ├── ScopedLock.hpp │ │ ├── Semaphore.cpp │ │ ├── Semaphore.hpp │ │ ├── Semaphore_POSIX.cpp │ │ ├── Semaphore_POSIX.hpp │ │ ├── Semaphore_WIN32.cpp │ │ ├── Semaphore_WIN32.hpp │ │ ├── SingletonHolder.hpp │ │ ├── Timestamp.cpp │ │ ├── Timestamp.hpp │ │ ├── Types.hpp │ │ └── UnWindows.hpp │ ├── config.hpp.in │ ├── config_debug.hpp │ ├── cpu_arith.hpp │ ├── cpu_bwx.hpp │ ├── cpu_control.hpp │ ├── cpu_debug.hpp │ ├── cpu_defs.hpp │ ├── cpu_fp_branch.hpp │ ├── cpu_fp_memory.hpp │ ├── cpu_fp_operate.hpp │ ├── cpu_logical.hpp │ ├── cpu_memory.hpp │ ├── cpu_misc.hpp │ ├── cpu_mvi.hpp │ ├── cpu_pal.hpp │ ├── cpu_vax.hpp │ ├── datatypes.hpp │ ├── es40-cfg.cpp │ ├── es40_debug.cpp │ ├── es40_debug.hpp │ ├── es40_endian.hpp │ ├── es40_float.hpp │ ├── gui/ │ │ ├── gui.cpp │ │ ├── gui.hpp │ │ ├── gui_win32_font.hpp │ │ ├── gui_x11.cpp │ │ ├── keymap.cpp │ │ ├── keymap.hpp │ │ ├── plugin.hpp │ │ ├── scancodes.cpp │ │ ├── scancodes.hpp │ │ ├── sdl.cpp │ │ ├── sdl_fonts.hpp │ │ ├── sdlkeys.hpp │ │ └── vga.hpp │ ├── lockstep.cpp │ ├── lockstep.hpp │ ├── make_unique.hpp │ └── telnet.hpp └── test/ ├── disk/ │ └── unwritable/ │ ├── axp_correct.log │ ├── es40.cfg │ └── test.sh ├── rom/ │ ├── axp_correct.log │ ├── es40.cfg │ ├── test.ps1 │ └── test.sh └── run ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 2 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseTab: Never ... ================================================ FILE: .github/workflows/build-test-and-artifact.yml ================================================ name: Build, run test and upload binaries on: [push, pull_request] jobs: linux-x86-clang: runs-on: "ubuntu-24.04" steps: - uses: actions/checkout@v4 - name: Get current date id: date run: echo "name=date::$(date +'%Y-%m-%dT%H%M')" >> $GITHUB_OUTPUT - name: Install build dependencies run: sudo apt -y update && sudo apt -y install libpcap-dev libsdl-dev netcat-openbsd - name: Create build environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_C_COMPILER="clang" -DCMAKE_CXX_COMPILER="clang++" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Wall" - name: Build working-directory: ${{runner.workspace}}/build shell: bash run: cmake --build . --config Release env: MAKEFLAGS: "-j2" - name: Run test scripts working-directory: ${{runner.workspace}}/axpbox shell: bash run: ${{runner.workspace}}/axpbox/test/run - name: Upload AXPbox binary uses: actions/upload-artifact@v4 with: name: AXPbox-linux-x86-clang-${{ env.BUILD_DATE }} path: ${{runner.workspace}}/build/axpbox env: BUILD_DATE: ${{ steps.date.outputs.date }} linux-x86-gcc: runs-on: "ubuntu-24.04" steps: - uses: actions/checkout@v4 - name: Get current date id: date run: echo "name=date::$(date +'%Y-%m-%dT%H%M')" >> $GITHUB_OUTPUT - name: Install gcovr run: pip install gcovr - name: Install build dependencies run: sudo apt -y update && sudo apt -y install libpcap-dev libsdl1.2-dev netcat-openbsd - name: Create build environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Wall" -DCMAKE_EXPORT_COMPILE_COMMANDS=1 - name: Build working-directory: ${{runner.workspace}}/build shell: bash run: cmake --build . --config Release env: MAKEFLAGS: "-j2" - name: Run test scripts working-directory: ${{runner.workspace}}/axpbox shell: bash run: ${{runner.workspace}}/axpbox/test/run - name: Upload AXPbox binary uses: actions/upload-artifact@v4 with: name: AXPbox-linux-x86-gcc-${{ env.BUILD_DATE }} path: ${{runner.workspace}}/build/axpbox env: BUILD_DATE: ${{ steps.date.outputs.date }} linux-arm-gcc-crosscompile: runs-on: "ubuntu-24.04" continue-on-error: true steps: - uses: actions/checkout@v4 - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H%M')" - name: Install build dependencies run: sudo apt -y update && sudo apt -y install netcat-openbsd qemu-user qemu-user-binfmt gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-dev-armhf-cross - name: Create build environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DSTATIC_COMPILE=yes -DDISABLE_PCAP=yes -DDISABLE_SDL=yes -DDISABLE_X11=yes -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Wall" -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_CXX_FLAGS="-O3 -mcpu=cortex-a7 -mfpu=neon -mfloat-abi=hard -mno-unaligned-access" - name: Build working-directory: ${{runner.workspace}}/build shell: bash run: cmake --build . --config Release env: MAKEFLAGS: "-j2" - name: Run binary working-directory: ${{runner.workspace}}/axpbox shell: bash run: ${{runner.workspace}}/build/axpbox - name: Binary info working-directory: ${{runner.workspace}}/axpbox shell: bash run: file ${{runner.workspace}}/build/axpbox - name: Upload AXPbox binary uses: actions/upload-artifact@v4 with: name: AXPbox-linux-ARM-gcc-${{ env.BUILD_DATE }} path: ${{runner.workspace}}/build/axpbox env: BUILD_DATE: ${{ steps.date.outputs.date }} windows-x86-msvc: runs-on: windows-2025 steps: - uses: actions/checkout@v4 - name: Create npcap sdk folder working-directory: ${{runner.workspace}}\ run: mkdir pcap - name: Download npcap sdk working-directory: ${{runner.workspace}}\pcap run: curl -o ./npcap-sdk-1.13.zip https://npcap.com/dist/npcap-sdk-1.13.zip - name: unzip npcap sdk working-directory: ${{runner.workspace}}\pcap run: unzip npcap-sdk-1.13.zip - name: Get current date id: date run: echo "name=date::$(date +'%Y-%m-%dT%H%M')" >> $GITHUB_OUTPUT - name: Create build environment run: cmake -E make_directory ${{runner.workspace}}\build - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Release -DPCAP_INCLUDE_DIR="${{runner.workspace}}/pcap/Include/" -DPCAP_LIBRARY="${{runner.workspace}}/pcap/Lib/x64/wpcap.lib" - name: Build working-directory: ${{runner.workspace}}\build shell: bash run: cmake --build . --config Release env: MAKEFLAGS: "-j2" - name: Test run working-directory: ${{runner.workspace}}\build run: ${{runner.workspace}}\build\Release\axpbox.exe - name: Upload AXPbox Binary uses: actions/upload-artifact@v4 with: name: AXPbox-windows-x86-msvc-${{ env.BUILD_DATE }}.exe path: ${{runner.workspace}}\build\Release\axpbox.exe env: BUILD_DATE: ${{ steps.date.outputs.date }} osx-x86-appleclang: runs-on: macos-15" steps: - uses: actions/checkout@v4 - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H%M')" - name: Create build environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Install dependencies run: brew install libpcap netcat gnu-sed # sdl2 libx11 / sdl doesnt work see #44 - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Wall -std=c++11 -stdlib=libc++ -I/usr/local/opt/libpcap/include" - name: Build working-directory: ${{runner.workspace}}/build shell: bash run: cmake --build . --config Release env: MAKEFLAGS: "-j2" - name: Upload AXPbox Binary uses: actions/upload-artifact@v4 with: name: AXPbox-osx-x86-12-appleclang-${{ env.BUILD_DATE }} path: ${{runner.workspace}}/build/axpbox env: BUILD_DATE: ${{ steps.date.outputs.date }} ================================================ FILE: .gitignore ================================================ cmake-build-debug/ cmake-build-release/ build**/ .idea/ .vs/ CMakeSettings.json run/ img/ ================================================ FILE: .travis.yml ================================================ language: cpp dist: focal compiler: clang before_install: sudo apt-get install cmake libpcap-dev libsdl-dev netcat-openbsd install: - mkdir build - cd build - cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Wall -Werror" - make - cd .. script: - test/run ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(AXPBox VERSION 1.1.3) # Source files file(GLOB srcs src/*.cpp src/base/*.cpp src/gui/*.cpp) file(GLOB include_sources src/base/*WIN32.cpp src/base/*POSIX.cpp) list(REMOVE_ITEM srcs ${include_sources}) add_executable(axpbox ${srcs} src/Main.cpp) target_include_directories(axpbox PRIVATE src src/base src/gui ${CMAKE_BINARY_DIR}/src) # Path to additional CMake modules set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) if (CMAKE_GENERATOR MATCHES "Visual Studio") add_definitions(-DNOMINMAX -D_WIN32_WINNT=0x0A00 -DLANG_CXX11 -DCOMPILER_MSVC -D__VERSION__=\"MSVC\") add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DWIN32_LEAN_AND_MEAN -DNOGDI -DPLATFORM_WINDOWS) add_definitions(/bigobj /nologo /EHsc /GF /FC /MP /Gm-) # Suppress warnings to reduce build log size. add_definitions(/wd4267 /wd4244 /wd4800 /wd4503 /wd4554 /wd4996 /wd4348 /wd4018) add_definitions(/wd4099 /wd4146 /wd4267 /wd4305 /wd4307) add_definitions(/wd4715 /wd4722 /wd4723 /wd4838 /wd4309 /wd4334) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") endif() find_package(Threads REQUIRED) target_link_libraries(axpbox Threads::Threads) # Configuration options include(CheckSymbolExists) include(CheckIncludeFile) include(CheckLibraryExists) include(CheckTypeSize) include(CheckIncludeFiles) include(TestBigEndian) include(CheckLargeFiles) TEST_BIG_ENDIAN(IS_BIG_ENDIAN) if(IS_BIG_ENDIAN) set(ES40_BIG_ENDIAN 1) else() set(ES40_LITTLE_ENDIAN 1) endif() check_symbol_exists(alarm "unistd.h" HAVE_ALARM) check_include_file("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file("arpa/telnet.h" HAVE_ARPA_TELNET_H) check_symbol_exists(atexit "stdlib.h" HAVE_ATEXIT) check_include_file("ctype.h" HAVE_CTYPE_H) check_include_file("errno.h" HAVE_ERRNO_H) check_include_file("fcntl.h" HAVE_FCNTL_H) check_symbol_exists(fopen "stdio.h" HAVE_FOPEN) check_symbol_exists(fopen64 "stdio.h" HAVE_FOPEN64) check_symbol_exists(fork "unistd.h" HAVE_FORK) check_symbol_exists(fseek "stdio.h" HAVE_FSEEK) check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO) check_symbol_exists(fseeko64 "stdio.h" HAVE_FSEEKO64) check_symbol_exists(ftell "stdio.h" HAVE_FTELL) check_symbol_exists(ftello "stdio.h" HAVE_FTELLO) check_symbol_exists(ftello64 "stdio.h" HAVE_FTELLO64) check_symbol_exists(gmtime_s "time.h" HAVE_GMTIME_S) check_symbol_exists(inet_aton "arpa/inet.h" HAVE_INET_ATON) check_include_file("inet.h" HAVE_INET_H) check_include_file("inttypes.h" HAVE_INTTYPES_H) check_include_file("in.h" HAVE_IN_H) check_symbol_exists(isblank "ctype.h" HAVE_ISBLANK) check_symbol_exists(localtime_s "time.h" HAVE_LOCALTIME_S) check_symbol_exists(malloc "stdlib.h" HAVE_MALLOC) check_include_file("malloc.h" HAVE_MALLOC_H) check_include_file("memory.h" HAVE_MEMORY_H) check_symbol_exists(memset "string.h" HAVE_MEMSET) check_include_file("netinet/in.h" HAVE_NETINET_IN_H) check_symbol_exists(pow "math.h" HAVE_POW) check_include_file("process.h" HAVE_PROCESS_H) check_library_exists(pthread pthread_self "" HAVE_PTHREAD) check_include_file("pthread.h" HAVE_PTHREAD_H) check_symbol_exists(realloc "stdlib.h" HAVE_REALLOC) check_symbol_exists(select "sys/select.h" HAVE_SELECT) check_include_file("signal.h" HAVE_SIGNAL_H) check_symbol_exists(socket "sys/socket.h" HAVE_SOCKET) check_include_file("socket.h" HAVE_SOCKET_H) check_symbol_exists(sqrt "math.h" HAVE_SQRT) check_include_file("stdbool.h" HAVE_STDBOOL_H) check_include_file("stdint.h" HAVE_STDINT_H) check_include_file("stdlib.h" HAVE_STDLIB_H) check_symbol_exists(strcasecmp "string.h" HAVE_STRCASECMP) check_symbol_exists(strchr "string.h" HAVE_STRCHR) check_symbol_exists(strdup "string.h" HAVE_STRDUP) check_include_file("stddef.h" HAVE_STDDEF_H) check_include_file("strings.h" HAVE_STRINGS_H) check_include_file("string.h" HAVE_STRING_H) check_symbol_exists(strncasecmp "string.h" HAVE_STRNCASECMP) check_symbol_exists(strspn "string.h" HAVE_STRSPN) check_include_file("sys/param.h" HAVE_SYS_PARAM_H) check_include_file("sys/select.h" HAVE_SYS_SELECT_H) check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H) check_include_file("sys/stat.h" HAVE_SYS_STAT_H) check_include_file("sys/time.h" HAVE_SYS_TIME_H) check_include_file("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("sys/wait.h" HAVE_SYS_WAIT_H) check_include_file("unistd.h" HAVE_UNISTD_H) check_symbol_exists(vfork "unistd.h" HAVE_VFORK) check_include_file("vfork.h" HAVE_VFORK_H) check_include_file("windows.h" HAVE_WINDOWS_H) check_include_file("winsock2.h" HAVE_WINSOCK2_H) set(HAVE_WORKING_FORK ${HAVE_FORK}) set(HAVE_WORKING_VFORK ${HAVE_VFORK}) check_include_file("ws2tcpip.h" HAVE_WS2TCPIP_H) check_type_size(_Bool _BOOL) check_symbol_exists(_fseeki64 "stdio.h" HAVE__FSEEKI64) check_symbol_exists(_ftelli64 "stdio.h" HAVE__FTELLI64) check_symbol_exists(_strdup "string.h" HAVE__STRDUP) check_symbol_exists(_stricasecmp "string.h" HAVE__STRICASECMP) check_symbol_exists(_stricmp "string.h" HAVE__STRICMP) # Features check_include_file("SDL/SDL.h" HAVE_SDL) check_include_file("X11/X.h" HAVE_X11) # Large file support (fopen64 / disk files > 2 GB) AXPBOX_TEST_LARGE_FILES(HAVE_LARGE_FILES) if (DISABLE_PCAP STREQUAL "yes") set(WANT_PCAP 0) else() set(WANT_PCAP 1) endif() if(WANT_PCAP) find_package(PCAP) if(PCAP_FOUND) message(STATUS "pcap found. Networking support enabled") set(HAVE_PCAP 1) include_directories(${PCAP_INCLUDE_DIR}) target_link_libraries(axpbox ${PCAP_LIBRARY}) else() message(STATUS "pcap not found. Networking support disabled") endif() else() message(STATUS "pcap disabled. Networking support disabled") endif() if (DISABLE_SDL STREQUAL "yes") set(HAVE_SDL 0) endif() if (DISABLE_X11 STREQUAL "yes") set(HAVE_X11 0) endif() if(HAVE_SDL) target_link_libraries(axpbox SDL) message(STATUS "sdl found. SDL graphics support enabled") else() message(WARNING "sdl not found. Building without SDL graphics support") endif() if(HAVE_X11) target_link_libraries(axpbox X11) message(STATUS "x11 found. x11 graphics support enabled") else() message(WARNING "x11 not found. Building without x11 graphics support") endif() if (STATIC_COMPILE STREQUAL "yes") message(STATUS "Static compilation") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -lpthread -lrt -Wl,--whole-archive -lpthread -Wl,--no-whole-archive") set(CMAKE_EXE_LINKER_FLAGS "-static") set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") set(BUILD_SHARED_LIBS OFF) endif() if (NOT CMAKE_GENERATOR MATCHES "Visual Studio") if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -ggdb") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og -ggdb") elseif (CMAKE_BUILD_TYPE STREQUAL "Release") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG") endif() endif() if(HAVE_LARGE_FILES) message(STATUS "Large file support enabled") else() message(WARNING "Building without large file graphics support") endif() find_package(Git) if (GIT_FOUND) execute_process(COMMAND "${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=40 --dirty WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) set(PACKAGE_GITSHA "\"${GIT_SHA}\"") endif() set(PACKAGE ${PROJECT_NAME}) set(PACKAGE_BUGREPORT "tglozar@gmail.com") set(PACKAGE_NAME string(TOLOWER ${PROJECT_NAME})) set(PACKAGE_VERSION ${PROJECT_VERSION}) set(PACKAGE_STRING ${PACKAGE_NAME}-${PACKAGE_VERSION}) set(PACKAGE_TARNAME ${PACKAGE_STRING}.tar.gz) set(PACKAGE_URL "http://github.com/lenticularis39/axpbox") set(PTHREAD_CREATE_JOINABLE "PTHREAD_CREATE_JOINABLE") set(SELECT_TYPE_ARG1 "int") set(SELECT_TYPE_ARG234 "fd_set *") set(SELECT_TYPE_ARG5 "struct timeval *") set(RETSIGTYPE "void") set(STDC_HEADERS 1) check_include_files("time.h;sys/time.h" TIME_WITH_SYS_TIME) configure_file(src/config.hpp.in src/config.hpp) install(TARGETS axpbox DESTINATION bin) message(STATUS "C++ compiler flags : ${CMAKE_CXX_FLAGS}") message(STATUS "C compiler flags : ${CMAKE_C_FLAGS}") message(STATUS "Linker flags : ${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_STATIC_LINKER_FLAGS}") ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: README.md ================================================ # AXPbox Alpha emulator AXPbox is a fork of the discontinued es40 emulator. It could theoretically used for running any operating system that runs on the OpenVMS or Tru64 PALcode (e.g. OpenVMS, Tru64 UNIX, Linux, NetBSD), however as of now only OpenVMS and some versions of NetBSD can be installed (for more details see [Guest support](https://github.com/lenticularis39/axpbox/wiki/Guest-support)). The emulator supports SCSI, IDE, serial ports, Ethernet (using PCAP) and [VGA graphics](https://github.com/lenticularis39/axpbox/wiki/VGA) (using SDL). ![OpenVMS 8.4 desktop](https://i.ibb.co/zQh35hm/Sn-mek-z-2021-01-24-14-18-41.png) OpenVMS 8.4 desktop in AXPbox. [Here is a wiki page showing you how to get this CDE desktop running](https://github.com/lenticularis39/axpbox/wiki/GUI-Desktop-Environment-(CDE)) ## Getting AXPbox Pre-built binaries for generic Linux amd64, Windows 10 amd64 and macOS amd64 are available for each release, and also as artifacts produced for each commit in CI. T2 SDE has an [official package](http://t2sde.org/packages/axpbox) for AXPbox, and openSUSE's Emulators project has an [AXPbox package](https://build.opensuse.org/package/show/Emulators/axpbox), too. The former gets updated the same day when a release happens, while requests are submitted now the latter that undergo approval of Emulators maintainers. You can also build from source using CMake; you need a C++ 11 compiler, optional dependencies are PCAP for networking and SDL or X11 for graphics support. ## Usage First invoke the interactive configuration file generator: ``` axpbox configure ``` This creates a file named es40.cfg, which you can now modify (the generator UI doesn't allow to set all options). After the configuration file and the required ROM image are ready, you can start the emulation: ``` axpbox run ``` Please read the [Installation Guide](https://github.com/lenticularis39/axpbox/wiki/OpenVMS-installation-guide) for information to get OpenVMS installed in the emulator. A guide for NetBSD is [also available on the Wiki](https://github.com/lenticularis39/axpbox/wiki/NetBSD-9.2-install-guide) ## Changes in comparison with es40 - Renamed from es40 to AXPbox to avoid confusion with the physical machine (AlphaServer ES40) - CMake is used for compilation instead of autotools - OpenVMS host support was dropped - es40 and es40_cfg were merged into one executable (axpbox) - The code was cleaned to compile without warnings on most compilers - Code modernizing, replacing POCO framework parts by native C++11 counterparts not available in 2008 (std::threads, etc) - Incorporate various patches from other es40 forks, for example, added MC146818 periodic interrupt to allow netbsd to boot and install, skip_memtest for faster booting. - Bug fixes, less segfaults, overall less crashes. - [More](https://github.com/lenticularis39/axpbox/wiki/) documentation and usage information on the various features and operating systems ## What doesn't work (also see issues) - Some guest operating systems (see [Guest support](https://github.com/lenticularis39/axpbox/wiki/Guest-support)) - ARC - VGA in OpenVMS - SDL keyboard (partly works, but easily breaks) - Multiple CPU system emulation - Running on big endian platforms - Some SCSI and IDE commands - Copying large files between IDE CD-ROM to IDE hard drive (this usually doesn't affect OpenVMS installation) ================================================ FILE: cmake/CheckLargeFiles.cmake ================================================ # Via: https://github.com/uclouvain/openjpeg/blob/master/cmake/TestLargeFiles.cmake # - Define macro to check large file support # # AXPBOX_TEST_LARGE_FILES(VARIABLE) # # VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present. # This macro will also defines the necessary variable enable large file support, for instance # _LARGE_FILES # _LARGEFILE_SOURCE # _FILE_OFFSET_BITS 64 # AXPBOX_HAVE_FSEEKO # # Adapted from Gromacs project (http://www.gromacs.org/) # by Julien Malik # macro(AXPBOX_TEST_LARGE_FILES VARIABLE) if(NOT DEFINED ${VARIABLE}) # On most platforms it is probably overkill to first test the flags for 64-bit off_t, # and then separately fseeko. However, in the future we might have 128-bit filesystems # (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64. message(STATUS "Checking for 64-bit off_t") # First check without any special flags try_compile(FILE64_OK "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/TestFileOffsetBits.c") if(FILE64_OK) message(STATUS "Checking for 64-bit off_t - present") endif() if(NOT FILE64_OK) # Test with _FILE_OFFSET_BITS=64 try_compile(FILE64_OK "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" ) if(FILE64_OK) message(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64") set(_FILE_OFFSET_BITS 64) endif() endif() if(NOT FILE64_OK) # Test with _LARGE_FILES try_compile(FILE64_OK "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_LARGE_FILES" ) if(FILE64_OK) message(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES") set(_LARGE_FILES 1) endif() endif() if(NOT FILE64_OK) # Test with _LARGEFILE_SOURCE try_compile(FILE64_OK "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" ) if(FILE64_OK) message(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE") set(_LARGEFILE_SOURCE 1) endif() endif() #if(NOT FILE64_OK) # # now check for Windows stuff # try_compile(FILE64_OK "${PROJECT_BINARY_DIR}" # "${PROJECT_SOURCE_DIR}/cmake/TestWindowsFSeek.c") # if(FILE64_OK) # message(STATUS "Checking for 64-bit off_t - present with _fseeki64") # set(HAVE__FSEEKI64 1) # endif() #endif() if(NOT FILE64_OK) message(STATUS "Checking for 64-bit off_t - not present") endif() set(_FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} CACHE INTERNAL "Result of test for needed _FILE_OFFSET_BITS=64") set(_LARGE_FILES ${_LARGE_FILES} CACHE INTERNAL "Result of test for needed _LARGE_FILES") set(_LARGEFILE_SOURCE ${_LARGEFILE_SOURCE} CACHE INTERNAL "Result of test for needed _LARGEFILE_SOURCE") # Set the flags we might have determined to be required above configure_file("${PROJECT_SOURCE_DIR}/cmake/TestLargeFiles.c.cmake.in" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c") message(STATUS "Checking for fseeko/ftello") # Test if ftello/fseeko are available try_compile(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c") if(FSEEKO_COMPILE_OK) message(STATUS "Checking for fseeko/ftello - present") endif() if(NOT FSEEKO_COMPILE_OK) # glibc 2.2 needs _LARGEFILE_SOURCE for fseeko (but not for 64-bit off_t...) try_compile(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c" COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" ) if(FSEEKO_COMPILE_OK) message(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE") set(_LARGEFILE_SOURCE ${_LARGEFILE_SOURCE} CACHE INTERNAL "Result of test for needed _LARGEFILE_SOURCE") endif() endif() if(FSEEKO_COMPILE_OK) set(AXPBOX_HAVE_FSEEKO ON CACHE INTERNAL "Result of test for fseeko/ftello") else() message(STATUS "Checking for fseeko/ftello - not found") set(AXPBOX_HAVE_FSEEKO OFF CACHE INTERNAL "Result of test for fseeko/ftello") endif() if(FILE64_OK AND FSEEKO_COMPILE_OK) message(STATUS "Large File support - found") set(${VARIABLE} ON CACHE INTERNAL "Result of test for large file support") else() message(STATUS "Large File support - not found") set(${VARIABLE} OFF CACHE INTERNAL "Result of test for large file support") endif() endif() endmacro() ================================================ FILE: cmake/FindPCAP.cmake ================================================ # ~~~ # - Try to find libpcap include dirs and libraries # # Usage of this module as follows: # # find_package(PCAP) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # Imported Targets: # PCAP::PCAP The libpcap library, if found # # Variables defined by this module: # # PCAP_FOUND System has libpcap, include and library dirs found # PCAP_INCLUDE_DIR The libpcap include directories. # PCAP_LIBRARY The libpcap library (possibly includes a thread # library e.g. required by pf_ring's libpcap) # HAVE_PCAP_IMMEDIATE_MODE If the version of libpcap found supports immediate mode # HAVE_PCAP_DIRECTION If the version of libpcap found support for setting direction # # Hints and Backward Compatibility # ================================ # # To tell this module where to look, a user may set the environment variable # PCAP_ROOT to point cmake to the *root* of a directory with include and lib # subdirectories for packet.dll (e.g WpdPack or npcap-sdk). Alternatively, # PCAP_ROOT may also be set from cmake command line or GUI (e.g cmake # -DPCAP_ROOT=C:\path\to\packet [...]) # ~~~ find_path( PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h PATH_SUFFIXES include Include) # The 64-bit Wpcap.lib is located under /x64 if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) # # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level directory contains 32-bit libraries. The # 64-bit libraries are in the Lib/x64 directory. # # The only way to *FORCE* CMake to look in the Lib/x64 directory without searching in the Lib directory first appears # to be to set CMAKE_LIBRARY_ARCHITECTURE to "x64". # set(CMAKE_LIBRARY_ARCHITECTURE "x64") endif() find_library(PCAP_LIBRARY NAMES pcap wpcap) # If Pcap is not found as this level no need to continue if(NOT PCAP_LIBRARY OR NOT PCAP_INCLUDE_DIR) return() endif() include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) check_cxx_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO) set(CMAKE_REQUIRED_LIBRARIES) # check if linking against libpcap also needs to link against a thread library if(NOT PCAP_LINKS_SOLO) find_package(Threads) if(THREADS_FOUND) set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) check_cxx_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS) set(CMAKE_REQUIRED_LIBRARIES) endif(THREADS_FOUND) if(THREADS_FOUND AND PCAP_NEEDS_THREADS) set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) list(REMOVE_DUPLICATES _tmp) set(PCAP_LIBRARY ${_tmp} CACHE STRING "Libraries needed to link against libpcap" FORCE) else(THREADS_FOUND AND PCAP_NEEDS_THREADS) message(FATAL_ERROR "Couldn't determine how to link against libpcap") endif(THREADS_FOUND AND PCAP_NEEDS_THREADS) endif(NOT PCAP_LINKS_SOLO) include(CheckFunctionExists) set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) check_function_exists(pcap_set_immediate_mode HAVE_PCAP_IMMEDIATE_MODE) check_function_exists(pcap_setdirection HAVE_PCAP_DIRECTION) check_function_exists(pcap_lib_version HAVE_PCAP_LIB_VERSION) set(CMAKE_REQUIRED_LIBRARIES) # Check libPCAP version if(HAVE_PCAP_LIB_VERSION AND NOT CMAKE_CROSSCOMPILING) # Simple C code to extract the libpcap version set(PCAP_VERSION_CODE " #include #include #include int main() { const char* version = pcap_lib_version(); const char* prefix = \"libpcap version \"; if (strncmp(version, prefix, strlen(prefix)) == 0) { version += strlen(prefix); } printf(\"%s\", version); return 0; } ") # Write the code to a temporary file set(detect_pcap_version_file "${PROJECT_BINARY_DIR}/detect_pcap_version.c") file(WRITE "${detect_pcap_version_file}" "${PCAP_VERSION_CODE}") # Try to compile and run the program try_run( RUN_RESULT_VAR COMPILE_RESULT_VAR "${CMAKE_BINARY_DIR}" "${detect_pcap_version_file}" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PCAP_INCLUDE_DIR}" LINK_LIBRARIES ${PCAP_LIBRARY} RUN_OUTPUT_VARIABLE PCAP_VERSION_OUTPUT) # If successful, parse the output to get the version string if(COMPILE_RESULT_VAR AND RUN_RESULT_VAR EQUAL 0) set(PCAP_VERSION ${PCAP_VERSION_OUTPUT}) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( PCAP REQUIRED_VARS PCAP_LIBRARY PCAP_INCLUDE_DIR VERSION_VAR PCAP_VERSION) # create IMPORTED target for libpcap dependency if(NOT TARGET PCAP::PCAP) add_library(PCAP::PCAP IMPORTED SHARED) set_target_properties( PCAP::PCAP PROPERTIES IMPORTED_LOCATION ${PCAP_LIBRARY} IMPORTED_IMPLIB ${PCAP_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${PCAP_INCLUDE_DIR}) endif() mark_as_advanced(PCAP_INCLUDE_DIR PCAP_LIBRARY) ================================================ FILE: cmake/TestFileOffsetBits.c ================================================ #include /* Cause a compile-time error if off_t is smaller than 64 bits */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ]; int main(int argc, char **argv) { return 0; } ================================================ FILE: cmake/TestLargeFiles.c.cmake.in ================================================ #cmakedefine _LARGEFILE_SOURCE #cmakedefine _LARGE_FILES #cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ #include #include #include int main(int argc, char **argv) { /* Cause a compile-time error if off_t is smaller than 64 bits, * and make sure we have ftello / fseeko. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ]; FILE *fp = fopen(argv[0],"r"); off_t offset = ftello( fp ); fseeko( fp, offset, SEEK_CUR ); fclose(fp); return 0; } ================================================ FILE: cmake/TestWindowsFSeek.c ================================================ #include int main() { __int64 off=0; _fseeki64(NULL, off, SEEK_SET); return 0; } ================================================ FILE: es40.cfg ================================================ /** * AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * \file * Sample configuration file. * * $Id: es40.cfg,v 1.22 2008/03/05 14:41:46 iamcamiel Exp $ * * X-1.22 Camiel Vanderhoeven 05-MAR-2008 * Multi-threading version. * * X-1.21 Camiel Vanderhoeven 02-MAR-2008 * Natural way to specify large numeric values ("10M"). * * X-1.20 Camiel Vanderhoeven 16-FEB-2008 * Added Symbios 53C810 controller. * * X-1.19 Camiel Vanderhoeven 13-FEB-2008 Put SCSI controller on a *different PCI ID, and clarified PCI configuration rules. Thanks to Eduardo *Marcelo Serrat. * * X-1.18 Camiel Vanderhoeven 25-JAN-2008 Added autocreate_size *option for disk-images. * * X-1.17 Camiel Vanderhoeven 25-JAN-2008 Added option to disable *the icache. * * X-1.16 Camiel Vanderhoeven 23-JAN-2008 Added comments for win32 *and X11 gui's. * * X-1.15 Camiel Vanderhoeven 05-JAN-2008 Added device as a disk. * * X-1.14 David Hittner 04-JAN-2008 Replaced decnet variable with *mac variable. * * X-1.13 Camiel Vanderhoeven 12-DEC-2007 Changed the way disks *work. * * X-1.12 Camiel Vanderhoeven 10-DEC-2007 Added vga_console *parameter. * * X-1.11 Camiel Vanderhoeven 10-DEC-2007 New structure for *configuration file. * * X-1.10 Camiel Vanderhoeven 10-DEC-2007 Unintended version # *increase. * * X-1.9 Brian Wheeler 22-NOV-2007 Added nic0.disabled *configuration option. * * X-1.8 Camiel Vanderhoeven 17-NOV-2007 Clarified nic0.adapter *syntax. * * X-1.7 Camiel Vanderhoeven 17-NOV-2007 Added configuration *lines for the network interface. * * X-1.6 Camiel Vanderhoeven 16-APR-2007 Added configuration *lines to start PuTTy for each serial port. * * X-1.5 Camiel Vanderhoeven 10-APR-2007 a) Added *rom.decompressed variable. b) Fixed some of the documentation mistakes. * * X-1.4 Camiel Vanderhoeven 1-APR-2007 Documented, and added old *changelog comments. * * X-1.3 Brian wheeler 7-FEB-2007 Added memory.bits variable. * * X-1.2 Brian Wheeler 3-FEB-2007 Added serial.base variable. * * X-1.1 Brian Wheeler 3-FEB-2007 Created this file. **/ // Specifying values // // Numeric values can be specified as a number ("500"), as a number with a // suffix ("100K" = 102400), or as a fancy combination ("2G512M" = 2.5 G). // // Boolean values can be specified as "true"/"false", "yes"/"no", or "1"/"0". // // String-constants are best specified in double quotes ("string"). If the // string needs to contain double quotes, double the double quotes. // ("One ""word"" in this string is quoted") // GUI // // If you want to use an emulated graphics card, the emulator needs to interface // with the OS'es user interface. There are three ways to do this: // // On systems that have the SDL (simple directmedia layer) run-time libraries // installed, you can use SDL. (gui=sdl) The emulator needs to be compiled with // -DHAVE_SDL. // // On MS Windows-systems, you can use Win32 API calls. (gui=win32) // // On many Linux, BSD and UNIX systems, you can use X11. (gui=X11) gui = sdl { keyboard.use_mapping = false; keyboard.map = "keys.map"; } sys0 = tsunami { // VARIABLE: rom.srm // // Specify the filename of the original (compressed) ROM image. This file is // essential to the functioning of the emulator. This file an be obtained from // HP (it's on the firmware-update CD-ROM for Alpha ES40 systems). // rom.srm = "rom\cl67srmrom.exe"; // VARIABLE: rom.decompressed // // Specify the filename of the decompressed ROM image. If possible, it will be // created the first time the emulator is run. When it exists, it allows the // emulator to start quicker by skipping the ROM decompression. // rom.decompressed = "rom\decompressed.rom"; // VARIABLES: rom.flash and rom.dpr // // Specify the filenames of Flash and DPR ROM images. These files are not // required, but will be created he first time the emulator runs. Contents of // Flash and DPR ROM will be put in these files after successful termination // of the emulator. This allows setting SRM variables such as auto_action and // boot_osflags. // rom.flash = "rom\flash.rom"; rom.dpr = "rom\dpr.rom"; // VARIABLE: memory.bits // // Only amounts of memory that are a power of 2 are supported. This number // determines the amount of memory, by setting the number of bits in the // address (and thus which power of 2). // // 26 = 64 MB // 27 = 128MB // 28 = 256 MB // 29 = 512 MB // 30 = 1GB // 31 = 2GB // memory.bits = 30; // VARIABLE: time // // Override the guest clock with a fixed date/time. // Format: "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS" // //time = "2017-05-01"; // fake date (YYYY-MM-DD) //time = "2017-05-01 12:00:00"; // fake date+time (YYYY-MM-DD HH:MM:SS) cpu0 = ev68cb { // VARIABLE: icache // // enables or disables the onchip-cache. The emulator runs faster // when this is disabled, but that might lead to problems with some // OS'es, so here is the option to enable it. icache = false; speed = 800M; } cpu1 = ev68cb { icache = false; speed = 800M; } // System Internal PCI Devices: ali, ali_ide, ali_usb // // The following PCI-devices are built into the system, and should // always be at the PCI-id's they are on in this sample configuration // file for compatibility. pci0 .7 = ali { mouse.enabled = true; lpt.outfile = "lpt.out"; vga_console = true; timezone = "utc"; // Sets timezone to UTC timezone = "local"; // Sets timezone to host timezone (default) timezone = "utc+1h"; // Sets timezone to UTC plus 1 hour timezone = "local-20M"; // Sets timezone to localtime minus 20 months timezone = "utc+2y"; // Sets timezone to UTC + two years } pci0 .15 = ali_ide { // sub-components: disk. // // Here, up to 4 IDE disks can be defined (0.0, 0.1, 1.0 and 1.1). // // file: create a disk using a file on the host filesystem as a disk image disk0 .0 = file { file = "img\disk0.img"; serial_number = "VMS"; rev_number = "8.3"; model_number = "OpenVMS8.3"; read_only = false; cdrom = false; // if the file does not exist, it will be created if autocreate_size is // set to the desired size of the disk. autocreate_size = 600M; } disk1 .0 = file { file = "img\vms83.iso"; read_only = true; cdrom = true; } // device: create a disk using a physical device // // WARNING: making a physical disk device writeable here may // seriously jeopardize the contents of that disk. // // Windows syntax for device: \\.\CDRom0, \\.\PhysicalDrive0 // // UNIX-like syntax for device: /dev/sda disk1 .0 = device { device = "\\.\CDRom0"; read_only = true; cdrom = true; } // ramdisk: create a disk using a portion of host RAM disk1 .1 = ramdisk { size = 10M; } } pci0 .19 = ali_usb {} // "Free" PCI Devices // // These can occupy pci0.1 .. pci0.4 and pci1.1 .. pci1.6 // // AFAIK, VGA should always be on pci0.x. pci0 .2 = cirrus { rom = "rom\vgabios-0.6a.debug.bin"; } // pci0.2 = s3 //{ // rom = "rom\vgabios-0.6a.debug.bin"; //} // Symbios SCSI controller // // There are two flavors of SCSI controllers on the emulator; 53c810 // and 53c895. The 53c810 supports 7 disks (0.0..0.6), the 53c895 // supports 15 disks (0.0..0.6 and 0.8..0.15). // // Right now, the 53c810 is the only controller that works with OpenVMS. pci0 .3 = sym53c810 { disk0 .0 = file { file = "img\dka0.img"; read_only = false; cdrom = false; } disk0 .4 = file { file = "img\scsi_cd.iso"; read_only = true; cdrom = true; } disk0 .5 = ramdisk { size = 10M; } } pci0 .4 = dec21143 { // VARIABLE: adapter // // Defines the host computer's adapter to use for the emulated NIC. If // you're unsure of this, start the emulator without this variable set, and // you will be presented with a list of adapters to choose from. You can // enter the name indicated on this line. // // Windows syntax: // adapter = "\Device\NPF_{F266CDC2-6BA2-43D8-8B00-1C468F737ED7}"; // // Linux syntax: // adapter = "eth0"; // adapter = "\Device\NPF_{F266CDC2-6BA2-43D8-8B00-1C468F737ED7}"; // VARIABLE: mac // // Defines the ethernet MAC address to be used by the virtual nic. // The variable format is: xx-xx-xx-xx-xx-xx, where all values are in hex. // This value should be unique on your network, or Bad Things Will Happen. // // The default value is: 08-00-2B-E5-40- // mac = "08-00-2B-E5-40-00"; } serial0 = serial { // VARIABLE: port // // Determines which Telnet port is opened to receive connections for the // emulated serial port. The default is 8000 + the port number. port = 21264; // VARIABLE: action // // Defines the action to take for each serial port (= a telnet client). If // you want to connect manually, leave this out. action = "" "c:\Program Files\PuTTY\putty.exe" " telnet://localhost:21264"; } serial1 = serial { port = 21265; action = "" "c:\Program Files\PuTTY\putty.exe" " telnet://localhost:21265"; } } ================================================ FILE: sonar-project.properties ================================================ sonar.projectKey=rve_axpbox sonar.organization=rve # This is the name and version displayed in the SonarCloud UI. sonar.projectName=AXPBox sonar.projectVersion=1.1.3 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=src/ # use complation database sonar.cfamily.compile-commands=../build/compile_commands.json # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 ================================================ FILE: src/AliM1543C.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2020 Martin Vorländer * Copyright (C) 2012 Dmitry Kalinkin * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "AliM1543C.hpp" #include "StdAfx.hpp" #include "System.hpp" #include "VGA.hpp" #ifdef DEBUG_PIC bool pic_messages = false; #endif /* Timer Calibration: Instructions per Microsecond (assuming 1 clock = 1 * instruction) */ #define IPus 847 u32 ali_cfg_data[64] = { /*00*/ 0x153310b9, // CFID: vendor + device /*04*/ 0x0200000f, // CFCS: command + status /*08*/ 0x060100c3, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000000, // BAR0: /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x00000000, // CFIT: interrupt configuration 0, 0, 0, 0, 0, /*54*/ 0x00000200, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; u32 ali_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x00000000, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000000, // BAR0 /*14*/ 0x00000000, // BAR1: CBMA /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x00000000, // CFIT: interrupt configuration /*40*/ 0xffcfff7f, /*44*/ 0xff00cbdf, /*48*/ 0xffffffff, /*4c*/ 0x000000ff, /*50*/ 0xffff8fff, /*54*/ 0xf0ffff00, /*58*/ 0x030f0d7f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Constructor. **/ CAliM1543C::CAliM1543C(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev) { if (theAli != 0) FAILURE(Configuration, "More than one Ali"); theAli = this; } /** * Initialize the Ali device. **/ void CAliM1543C::init() { add_function(0, ali_cfg_data, ali_cfg_mask); int i; char *filename; add_legacy_io(1, 0x61, 1); state.reg_61 = 0; add_legacy_io(2, 0x70, 4); cSystem->RegisterMemory(this, 2, U64(0x00000801fc000070), 4); for (i = 0; i < 4; i++) state.toy_access_ports[i] = 0; for (i = 0; i < 256; i++) state.toy_stored_data[i] = 0; state.toy_stored_data[0x17] = myCfg->get_bool_value("vga_console") ? 1 : 0; if (state.toy_stored_data[0x17] && !theVGA) { printf("! CONFIGURATION WARNING ! vga_console set to true, but no VGA card " "installed.\n"); state.toy_stored_data[0x17] = 0; } state.toy_pi_last_fire = 0; ResetPCI(); // PIT Setup add_legacy_io(6, 0x40, 4); for (i = 0; i < 3; i++) state.pit_status[i] = 0x40; // invalid/null counter for (i = 0; i < 9; i++) state.pit_counter[i] = 0; add_legacy_io(7, 0x20, 2); add_legacy_io(8, 0xa0, 2); add_legacy_io(30, 0x4d0, 2); // odd one, byte read in PCI IACK (interrupt acknowledge) cycle. Interrupt // vector. cSystem->RegisterMemory(this, 20, U64(0x00000801f8000000), 1); for (i = 0; i < 2; i++) { state.pic_mode[i] = 0; state.pic_intvec[i] = 0; state.pic_mask[i] = 0; state.pic_asserted[i] = 0; } // Initialize parallel port add_legacy_io(27, 0x3bc, 4); filename = myCfg->get_text_value("lpt.outfile"); if (filename) { lpt = fopen(filename, "ab"); } else { lpt = NULL; } lpt_reset(); myRegLock = new CMutex("ali-reg"); printf("%s: $Id: AliM1543C.cpp,v 1.66 2008/05/31 15:47:07 iamcamiel Exp $\n", devid_string); } void CAliM1543C::start_threads() { if (!myThread) { printf(" ali"); StopThread = false; myThread = std::make_unique([this]() { this->run(); }); } } void CAliM1543C::stop_threads() { StopThread = true; if (myThread) { printf(" ali"); myThread->join(); myThread = nullptr; } } /** * Destructor. **/ CAliM1543C::~CAliM1543C() { stop_threads(); if (lpt) fclose(lpt); } /** * Calculates VM time based on the configuration specified in the configuration * file. * @return The time. */ struct tm CAliM1543C::get_time() { struct tm time_out; time_t time_raw; // Timezone setting from configuration file std::string timezone{myCfg->get_text_value("timezone", "local")}; // Time base (local or utc) std::string timebase; // Time offset bool offset_present = false; long offset; // Check for absolute time override: "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS" // Read from system-level config (sys0 block) char *faketime = myCfg->get_myParent()->get_text_value("time"); if (faketime) { struct tm ft; memset(&ft, 0, sizeof(ft)); ft.tm_isdst = -1; // let mktime figure it out int n = sscanf(faketime, "%d-%d-%d %d:%d:%d", &ft.tm_year, &ft.tm_mon, &ft.tm_mday, &ft.tm_hour, &ft.tm_min, &ft.tm_sec); if (n >= 3) { ft.tm_year -= 1900; ft.tm_mon -= 1; time_raw = mktime(&ft); if (time_raw == (time_t)-1) { FAILURE_1(Configuration, "Invalid time value: %s", faketime); } // Apply timezone conversion and return if (timezone.rfind("utc") == 0) { gmtime_s(&time_out, &time_raw); } else { #ifdef _WIN32 localtime_s(&time_out, &time_raw); #else localtime_s(&time_raw, &time_out); #endif } return time_out; } else { FAILURE_1(Configuration, "Invalid time format: %s (use YYYY-MM-DD or " "YYYY-MM-DD HH:MM:SS)", faketime); } } // Get raw time time(&time_raw); // Set time base if (timezone.rfind("local") == 0) { timebase = "local"; } else if (timezone.rfind("utc") == 0) { timebase = "utc"; } else { FAILURE_1(Configuration, "Invalid timezone %s", timezone.c_str()); } // Characters remaining after time base int remaining_chars = timezone.length() - timebase.length(); if (remaining_chars > 0 && timezone.at(timezone.length() - remaining_chars) == '+') { // An offset is included in the timezone offset_present = true; sscanf(timezone.c_str() + timezone.length() - remaining_chars + 1, "%ld", &offset); remaining_chars -= std::to_string(offset).length() + 1; if (remaining_chars != 1) { // Offset type always has one character FAILURE_1(Configuration, "Invalid timezone %s", timezone.c_str()); } } else if (remaining_chars > 0) { FAILURE_1(Configuration, "Invalid timezone %s", timezone.c_str()); } if (offset_present) { // Apply POSIX time offset (seconds, minutes, hours, days) switch (timezone.at(timezone.length() - 1)) { case 's': time_raw += offset; break; case 'm': time_raw += offset * 60; break; case 'h': time_raw += offset * 3600; break; case 'd': time_raw += offset * 86400; break; case 'M': case 'y': break; default: FAILURE_1(Configuration, "Invalid timezone offset type %c", timezone.at(timezone.length() - 1)); } } // Convert POSIX time to date if (timebase == "local") { #ifdef _WIN32 localtime_s(&time_out, &time_raw); #else localtime_s(&time_raw, &time_out); #endif } else if (timebase == "utc") { gmtime_s(&time_out, &time_raw); } else { // This shouldn't happen FAILURE_1(Configuration, "Invalid timezone %s", timezone.c_str()); } if (offset_present) { // Apply date offset (months, years) switch (timezone.at(timezone.length() - 1)) { case 'M': time_out.tm_year += offset / 12; time_out.tm_mon += offset % 12; break; case 'y': time_out.tm_year += offset; break; } // Fix day of week mktime(&time_out); } return time_out; } /** * Read (byte,word,longword) from one of the legacy ranges. Only byte-accesses *are supported. * * Ranges are: * - 1. I/O port 61h * - 2. I/O ports 70h-73h (time-of-year clock) * - 6. I/O ports 40h-43h (programmable interrupt timer) * - 7. I/O ports 20h-21h (primary programmable interrupt controller) * - 8. I/O ports a0h-a1h (secondary (cascaded) programmable interrupt *controller) * - 20. PCI IACK address (interrupt vector) * - 27. I/O ports 3bch-3bfh (parallel port) * - 30. I/O ports 4d0h-4d1h (edge/level register of programmable interrupt *controller) * . **/ u32 CAliM1543C::ReadMem_Legacy(int index, u32 address, int dsize) { if (dsize != 8 && index != 20) // when interrupt vector is read, dsize doesn't matter. { FAILURE_4( InvalidArgument, "%s: DSize %d reading from legacy memory range # %d at address %02x\n", devid_string, dsize, index, address); } int channel = 0; switch (index) { case 1: return reg_61_read(); case 2: return toy_read(address); case 6: return pit_read(address); case 8: channel = 1; case 7: return pic_read(channel, address); case 20: return pic_read_vector(); case 30: return pic_read_edge_level(address); case 27: return lpt_read(address); } return 0; } /** * Write (byte,word,longword) to one of the legacy ranges. Only byte-accesses *are supported. * * Ranges are: * - 1. I/O port 61h * - 2. I/O ports 70h-73h (time-of-year clock) * - 6. I/O ports 40h-43h (programmable interrupt timer) * - 7. I/O ports 20h-21h (primary programmable interrupt controller) * - 8. I/O ports a0h-a1h (secondary (cascaded) programmable interrupt *controller) * - 12. I/O ports 00h-0fh (primary DMA controller) * - 13. I/O ports c0h-dfh (secondary DMA controller) * - 20. PCI IACK address (interrupt vector) * - 27. I/O ports 3bch-3bfh (parallel port) * - 30. I/O ports 4d0h-4d1h (edge/level register of programmable interrupt *controller) * - 33. I/O ports 80h-8fh (DMA controller memory base low page register) * - 34. I/O ports 480h-48fh (DMA controller memory base high page register) * . **/ void CAliM1543C::WriteMem_Legacy(int index, u32 address, int dsize, u32 data) { if (dsize != 8) { FAILURE_4( InvalidArgument, "%s: DSize %d writing to legacy memory range # %d at address %02x\n", devid_string, dsize, index, address); } int channel = 0; switch (index) { case 1: reg_61_write((u8)data); return; case 2: toy_write(address, (u8)data); return; case 6: pit_write(address, (u8)data); return; case 8: channel = 1; case 7: pic_write(channel, address, (u8)data); return; case 30: pic_write_edge_level(address, (u8)data); return; case 27: lpt_write(address, (u8)data); return; } } /** * Read port 61h (speaker/ miscellaneous). * * BDW: * This may need some expansion to help with timer delays. It looks like * the 8254 flips bits on occasion, and the linux kernel (at least) uses * do { * count++; * } while ((inb(0x61) & 0x20) == 0 && count < TIMEOUT_COUNT); * to calibrate the cpu clock. * * Every 1500 reads the bit gets flipped so maybe the timing will * seem reasonable to the OS. */ u8 CAliM1543C::reg_61_read() { #if 0 static long read_count = 0; if(!(state.reg_61 & 0x20)) { if(read_count % 1500 == 0) state.reg_61 |= 0x20; } else { state.reg_61 &= ~0x20; } read_count++; #else state.reg_61 &= ~0x20; state.reg_61 |= (state.pit_status[2] & 0x80) >> 2; #endif return state.reg_61; } /** * Write port 61h (speaker/ miscellaneous). **/ void CAliM1543C::reg_61_write(u8 data) { state.reg_61 = (state.reg_61 & 0xf0) | (((u8)data) & 0x0f); } /** * Read time-of-year clock ports (70h-73h). **/ u8 CAliM1543C::toy_read(u32 address) { // printf("%%ALI-I-READTOY: read port %02x: 0x%02x\n", (u32)(0x70 + address), // state.toy_access_ports[address]); return (u8)state.toy_access_ports[address]; } /** * Write time-of-year clock ports (70h-73h). On a write to port 0, recalculate * clock values. **/ void CAliM1543C::toy_write(u32 address, u8 data) { struct tm stime; static long read_count = 0; static long hold_count = 0; // printf("%%ALI-I-WRITETOY: write port %02x: 0x%02x\n", (u32)(0x70 + // address), data); state.toy_access_ports[address] = (u8)data; switch (address) { case 0: if ((data & 0x7f) < 14) { // Assign VRT (valid RAM and time) bit state.toy_stored_data[RTC_REG_D] = RTC_VRT; // Update time stime = get_time(); if (state.toy_stored_data[RTC_REG_B] & RTC_DM) { // binary state.toy_stored_data[0] = (u8)(stime.tm_sec); state.toy_stored_data[2] = (u8)(stime.tm_min); if (state.toy_stored_data[RTC_REG_B] & RTC_2412) // 24-hour state.toy_stored_data[4] = (u8)(stime.tm_hour); else // 12-hour state.toy_stored_data[4] = (u8)(((stime.tm_hour / 12) ? 0x80 : 0) | (stime.tm_hour % 12)); state.toy_stored_data[6] = (u8)(stime.tm_wday + 1); state.toy_stored_data[7] = (u8)(stime.tm_mday); state.toy_stored_data[8] = (u8)(stime.tm_mon + 1); state.toy_stored_data[9] = (u8)(stime.tm_year % 100); } else { // BCD state.toy_stored_data[0] = (u8)(((stime.tm_sec / 10) << 4) | (stime.tm_sec % 10)); state.toy_stored_data[2] = (u8)(((stime.tm_min / 10) << 4) | (stime.tm_min % 10)); if (state.toy_stored_data[0x0b] & 2) // 24-hour state.toy_stored_data[4] = (u8)(((stime.tm_hour / 10) << 4) | (stime.tm_hour % 10)); else { // 12-hour state.toy_stored_data[4] = (u8)(((stime.tm_hour / 12) ? 0x80 : 0) | (((stime.tm_hour % 12) / 10) << 4) | ((stime.tm_hour % 12) % 10)); } state.toy_stored_data[6] = (u8)(stime.tm_wday + 1); state.toy_stored_data[7] = (u8)(((stime.tm_mday / 10) << 4) | (stime.tm_mday % 10)); state.toy_stored_data[8] = (u8)((((stime.tm_mon + 1) / 10) << 4) | ((stime.tm_mon + 1) % 10)); state.toy_stored_data[9] = (u8)((((stime.tm_year % 100) / 10) << 4) | ((stime.tm_year % 100) % 10)); } // SRM initializes the value of A register to 0x26. This means: // xtal speed is set to MC_BASE_32_KHz 32.768KHz (standard) // periodic interrupt rate divisor of 32 = interrupt every 976.562 ms // (1024Hz clock) if (state.toy_stored_data[RTC_REG_A] & RTC_UIP) { // Once the UIP line goes high, we have to stay high for 2228us. hold_count--; if (hold_count == 0 || (state.toy_stored_data[RTC_REG_B] & RTC_SET)) { // Set UIP low and trigger the related interrupt. state.toy_stored_data[RTC_REG_A] &= ~RTC_UIP; state.toy_stored_data[RTC_REG_C] |= RTC_UF; toy_update_irqf(); read_count = 0; } } else { // UIP isn't high, so if we're looping and waiting for it to go, it // will take 1,000,000/(IPus*3) reads for a 3 instruction loop. // If it happens to be a one time read, it'll only throw our // calculations off a tiny bit, and they'll be re-synced on the next // read-loop. read_count++; if (read_count > 1000000 / (IPus * 3)) // 3541 @ 847IPus { state.toy_stored_data[RTC_REG_A] |= RTC_UIP; hold_count = (2228 / (IPus * 3)) + 1; // .876 @ 847IPus, so we add one. } } } toy_handle_periodic_interrupt(data); toy_update_irqf(); // Assign specified data to port so it can be read by the program state.toy_access_ports[1] = state.toy_stored_data[data & 0x7f]; // Register C is cleared after a read, and we don't care if it's a write if (data == RTC_REG_C) state.toy_stored_data[data & 0x7f] = 0; break; case 1: if (state.toy_access_ports[0] == RTC_REG_B && data & 0x040) // If we're writing to register B, we make register C look // like it fired. // TODO: Do actual interrupt implementation instead of // a workaround. state.toy_stored_data[RTC_REG_C] = 0xf0; state.toy_stored_data[state.toy_access_ports[0] & 0x7f] = (u8)data; break; case 2: state.toy_access_ports[3] = state.toy_stored_data[0x80 + (data & 0x7f)]; break; case 3: state.toy_stored_data[0x80 + (state.toy_access_ports[2] & 0x7f)] = (u8)data; break; } } /** * Handle RTC periodic interrupt. **/ void CAliM1543C::toy_handle_periodic_interrupt(u8 data) { /* See sys/dev/ic/mc146818reg.h and sys/arch/alpha/alpha/mcclock.c in NetBSD and the RTC datasheet: https://www.nxp.com/docs/en/data-sheet/MC146818.pdf. */ clock_t now = clock(); double timedelta = (now - state.toy_pi_last_fire) / (double)CLOCKS_PER_SEC; // For the meaning of the period calculation see the table on page 14 of the // aforementioned datasheet int rate_pow = state.toy_stored_data[RTC_REG_A] & 0x0f; double period = (1 << rate_pow) / 65536.0; if (state.toy_stored_data[RTC_REG_A] & MC_BASE_32_KHz) { if (rate_pow == 0x1) { period = 1 / 256.0; } else if (rate_pow == 0x2) { period = 1 / 128.0; } } if (rate_pow && (timedelta >= period)) { // Elapsed time since last check is equal or greater than the specified // period - fire the interrupt by setting the PF flag in register C // (see page 16 in the datasheet). state.toy_stored_data[RTC_REG_C] |= RTC_PF; state.toy_pi_last_fire = now; } } /** * Update RTC interrupt request flag **/ void CAliM1543C::toy_update_irqf() { if ((state.toy_stored_data[RTC_REG_B] & RTC_PIE && state.toy_stored_data[RTC_REG_C] & RTC_PF) || (state.toy_stored_data[RTC_REG_B] & RTC_UIE && state.toy_stored_data[RTC_REG_C] & RTC_UF) || (state.toy_stored_data[RTC_REG_B] & RTC_AIE && state.toy_stored_data[RTC_REG_C] & RTC_AF)) state.toy_stored_data[RTC_REG_C] |= RTC_IRQF; else state.toy_stored_data[RTC_REG_C] &= ~RTC_IRQF; } /** * Read from the programmable interrupt timer ports (40h-43h) * * BDW: * Here's the PIT Traffic during SRM and Linux Startup: * * SRM * PIT Write: 3, 36 = counter 0, load lsb + msb, mode 3 * PIT Write: 0, 00 * PIT Write: 0, 00 = 65536 = 18.2 Hz = timer interrupt. * PIT Write: 3, 54 = counter 1, msb only, mode 2 * PIT Write: 1, 12 = 0x1200 = memory refresh? * PIT Write: 3, b6 = counter 2, load lsb + msb, mode 3 * PIT Write: 3, 00 * PIT Write: 0, 00 * PIT Write: 0, 00 * * Linux Startup * PIT Write: 3, b0 = counter 2, load lsb+msb, mode 0 * PIT Write: 2, a4 * PIT Write: 2, ec = eca4 * PIT Write: 3, 36 = counter 0, load lsb+msb, mode 3 * PIT Write: 0, 00 * PIT Write: 0, 00 = 65536 * PIT Write: 3, b6 = counter 2, load lsb+msb, mode 3 * PIT Write: 2, 31 * PIT Write: 2, 13 = 1331 **/ u8 CAliM1543C::pit_read(u32 address) { // printf("PIT Read: %02" PRIx64 " \n",address); u8 data; data = 0; return data; } /** * Write to the programmable interrupt timer ports (40h-43h) **/ void CAliM1543C::pit_write(u32 address, u8 data) { // printf("PIT Write: %02" PRIx64 ", %02x \n",address,data); if (address == 3) { // control if (data != 0) { state.pit_status[address] = data; // last command seen. if ((data & 0xc0) >> 6 != 3) { state.pit_status[(data & 0xc0) >> 6] = data & 0x3f; state.pit_mode[(data & 0xc0) >> 6] = (data & 0x30) >> 4; } else { // readback command 8254 only state.pit_status[address] = 0xc0; // bogus :) } } } else { // a counter switch (state.pit_mode[address]) { case 0: break; case 1: case 3: state.pit_counter[address] = (state.pit_counter[address] & 0xff) | data << 8; state.pit_counter[address + PIT_OFFSET_MAX] = state.pit_counter[address]; if (state.pit_mode[address] == 3) { state.pit_mode[address] = 2; } else state.pit_status[address] &= ~0xc0; // no longer high, counter valid. break; case 2: state.pit_counter[address] = (state.pit_counter[address] & 0xff00) | data; // two bytes were written with 0x00, so its really 0x10000 if ((state.pit_status[address] & 0x30) >> 4 == 3 && state.pit_counter[address] == 0) { state.pit_counter[address] = 65536; } state.pit_counter[address + PIT_OFFSET_MAX] = state.pit_counter[address]; state.pit_status[address] &= ~0xc0; // no longer high, counter valid. break; } } } #define PIT_FACTOR 5000 #define PIT_DEC(p) p = (p < PIT_FACTOR ? 0 : p - PIT_FACTOR); /** * Handle the PIT interrupt. * * - counter 0 is the 18.2Hz time counter. * - counter 1 is the ram refresh, we don't care. * - counter 2 is the speaker and/or generic timer * . **/ void CAliM1543C::pit_clock() { int i; for (i = 0; i < 3; i++) { // decrement the counter. if (state.pit_status[i] & 0x40) continue; PIT_DEC(state.pit_counter[i]); switch ((state.pit_status[i] & 0x0e) >> 1) { case 0: // interrupt at terminal if (!state.pit_counter[i]) { state.pit_status[i] |= 0xc0; // out pin high, no count set. } break; case 3: // square wave generator if (!state.pit_counter[i]) { if (state.pit_status[i] & 0x80) { state.pit_status[i] &= ~0x80; // lower output; } else { state.pit_status[i] |= 0x80; // raise output if (i == 0) { pic_interrupt(0, 0); // counter 0 is tied to irq 0. // printf("Generating timer interrupt.\n"); } } state.pit_counter[i] = state.pit_counter[i + PIT_OFFSET_MAX]; } // decrement again, since we want a half-wide square wave. PIT_DEC(state.pit_counter[i]); break; default: break; // we don't care to handle it. } } } /** * Thread entry point. **/ void CAliM1543C::run() { try { for (;;) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (StopThread) return; do_pit_clock(); } } catch (CException &e) { printf("Exception in Ali thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } #define PIT_RATIO 1 /** * Handle all events that need to be handled on a clock-driven basis. * * This is a slow-clocked device, which means this DoClock isn't called as often *as the CPU's DoClock. Do the following: * - Handle PIT clock. * . **/ void CAliM1543C::do_pit_clock() { static int pit_counter = 0; if (pit_counter++ >= PIT_RATIO) { pit_counter = 0; pit_clock(); } } #define PIC_STD 0 #define PIC_INIT_0 1 #define PIC_INIT_1 2 #define PIC_INIT_2 3 /** * Read a byte from one of the programmable interrupt controller's registers. **/ u8 CAliM1543C::pic_read(int index, u32 address) { u8 data; data = 0; if (address == 1) data = state.pic_mask[index]; #ifdef DEBUG_PIC if (pic_messages) printf("%%PIC-I-READ: read %02x from port %" PRId64 " on PIC %d\n", data, address, index); #endif return data; } /** * Read a byte from the edge/level register of one of the programmable interrupt *controllers. **/ u8 CAliM1543C::pic_read_edge_level(int index) { return state.pic_edge_level[index]; } /** * Read the interrupt vector during a PCI IACK cycle. **/ u8 CAliM1543C::pic_read_vector() { if (state.pic_asserted[0] & 1) return state.pic_intvec[0]; if (state.pic_asserted[0] & 2) return state.pic_intvec[0] + 1; if (state.pic_asserted[0] & 4) { if (state.pic_asserted[1] & 1) return state.pic_intvec[1]; if (state.pic_asserted[1] & 2) return state.pic_intvec[1] + 1; if (state.pic_asserted[1] & 4) return state.pic_intvec[1] + 2; if (state.pic_asserted[1] & 8) return state.pic_intvec[1] + 3; if (state.pic_asserted[1] & 16) return state.pic_intvec[1] + 4; if (state.pic_asserted[1] & 32) return state.pic_intvec[1] + 5; if (state.pic_asserted[1] & 64) return state.pic_intvec[1] + 6; if (state.pic_asserted[1] & 128) return state.pic_intvec[1] + 7; } if (state.pic_asserted[0] & 8) return state.pic_intvec[0] + 3; if (state.pic_asserted[0] & 16) return state.pic_intvec[0] + 4; if (state.pic_asserted[0] & 32) return state.pic_intvec[0] + 5; if (state.pic_asserted[0] & 64) return state.pic_intvec[0] + 6; if (state.pic_asserted[0] & 128) return state.pic_intvec[0] + 7; return 0; } /** * Write a byte to one of the programmable interrupt controller's registers. **/ void CAliM1543C::pic_write(int index, u32 address, u8 data) { int level; int op; #ifdef DEBUG_PIC if (pic_messages) printf("%%PIC-I-WRITE: write %02x to port %" PRId64 " on PIC %d\n", data, address, index); #endif switch (address) { case 0: if (data & 0x10) state.pic_mode[index] = PIC_INIT_0; else state.pic_mode[index] = PIC_STD; if (data & 0x08) { // OCW3 } else { // OCW2 op = (data >> 5) & 7; level = data & 7; switch (op) { case 1: // non-specific EOI state.pic_asserted[index] = 0; // if (index == 1) state.pic_asserted[0] &= ~(1 << 2); // if (!state.pic_asserted[0]) cSystem->interrupt(55, false); #ifdef DEBUG_PIC pic_messages = false; #endif break; case 3: // specific EOI state.pic_asserted[index] &= ~(1 << level); // if ((index == 1) && (!state.pic_asserted[1])) state.pic_asserted[0] &= ~(1 << 2); // if (!state.pic_asserted[0]) cSystem->interrupt(55, false); #ifdef DEBUG_PIC pic_messages = false; #endif break; } } return; case 1: switch (state.pic_mode[index]) { case PIC_INIT_0: state.pic_intvec[index] = (u8)data & 0xf8; state.pic_mode[index] = PIC_INIT_1; return; case PIC_INIT_1: state.pic_mode[index] = PIC_INIT_2; return; case PIC_INIT_2: state.pic_mode[index] = PIC_STD; return; case PIC_STD: state.pic_mask[index] = data; state.pic_asserted[index] &= ~data; return; } } } /** * Write a byte to the edge/level register of one of the programmable interrupt *controllers. **/ void CAliM1543C::pic_write_edge_level(int index, u8 data) { state.pic_edge_level[index] = data; } #define DEBUG_EXPR (index != 0 || (intno != 0 && intno > 4)) /** * Assert an interrupt on one of the programmable interrupt controllers. **/ void CAliM1543C::pic_interrupt(int index, int intno) { #ifdef DEBUG_PIC if (DEBUG_EXPR) { printf("%%PIC-I-INCOMING: Interrupt %d incomming on PIC %d", intno, index); pic_messages = true; } #endif // do we have this interrupt enabled? if (state.pic_mask[index] & (1 << intno)) { #ifdef DEBUG_PIC if (DEBUG_EXPR) printf(" (masked)\n"); pic_messages = false; #endif return; } if (state.pic_asserted[index] & (1 << intno)) { #ifdef DEBUG_PIC if (DEBUG_EXPR) printf(" (already asserted)\n"); #endif return; } #ifdef DEBUG_PIC if (DEBUG_EXPR) printf("\n"); #endif state.pic_asserted[index] |= (1 << intno); if (index == 1) pic_interrupt(0, 2); // cascade if (index == 0) cSystem->interrupt(55, true); } /** * De-assert an interrupt on one of the programmable interrupt controllers. **/ void CAliM1543C::pic_deassert(int index, int intno) { if (!(state.pic_asserted[index] & (1 << intno))) return; // printf("De-asserting %d,%d\n",index,intno); state.pic_asserted[index] &= ~(1 << intno); if (index == 1 && state.pic_asserted[1] == 0) pic_deassert(0, 2); // cascade if (index == 0 && state.pic_asserted[0] == 0) cSystem->interrupt(55, false); } static u32 ali_magic1 = 0xA111543C; static u32 ali_magic2 = 0xC345111A; /** * Save state to a Virtual Machine State file. **/ int CAliM1543C::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&ali_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&ali_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CAliM1543C::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != ali_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != ali_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * Parallel Port information: * address 0 (R/W): data pins. On read, the last byte written is returned. * * * address 1 (R): status register * \code * 1 0 0 0 0 0 00 <-- default * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +- Undefined * | | | | | +--- IRQ (undefined?) * | | | | +----- printer has error condition * | | | +------- printer is not selected. * | | +--------- printer has paper (online) * | +----------- printer is asserting 'ack' * +------------- printer busy (active low). * \endcode * * address 2 (R/W): control register. * \code * 00 0 0 1 0 1 1 <-- default * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +-- Strobe (active low) * | | | | | +---- Auto feed (active low) * | | | | +------ Initialize * | | | +-------- Select (active low) * | | +---------- Interrupt Control * | +------------ Bidirectional control (unimplemented) * +--------------- Unused * \endcode **/ void CAliM1543C::lpt_reset() { state.lpt_data = ~0; state.lpt_status = 0xd8; // busy, ack, online, error state.lpt_control = 0x0c; // select, init state.lpt_init = false; } /** * Read a byte from one of the parallel port controller's registers. **/ u8 CAliM1543C::lpt_read(u32 address) { u8 data = 0; switch (address) { case 0: data = state.lpt_data; break; case 1: data = state.lpt_status; if ((state.lpt_status & 0x80) == 0 && (state.lpt_control & 0x01) == 0) { if (state.lpt_status & 0x40) { // test ack state.lpt_status &= ~0x40; // turn off ack } else { state.lpt_status |= 0x40; // set ack. state.lpt_status |= 0x80; // set (not) busy. } } break; case 2: data = state.lpt_control; } #ifdef DEBUG_LPT printf("%%LPT-I-READ: port %d = %x\n", address, data); #endif return data; } /** * Write a byte to one of the parallel port controller's registers. **/ void CAliM1543C::lpt_write(u32 address, u8 data) { #ifdef DEBUG_LPT printf("%%LPT-I-WRITE: port %d = %x\n", address, data); #endif switch (address) { case 0: state.lpt_data = data; break; case 1: break; case 2: if ((data & 0x04) == 0) { state.lpt_init = true; state.lpt_status = 0xd8; } else { if (data & 0x08) { // select bit if (data & 0x01) { // strobe? state.lpt_status &= ~0x80; // we're busy // do the write! if (lpt && state.lpt_init) fputc(state.lpt_data, lpt); if (state.lpt_control & 0x10) { pic_interrupt(0, 7); } } else { // ? } } } state.lpt_control = data; } } /** * Check if threads are still running. **/ void CAliM1543C::check_state() { if (myThreadDead.load()) FAILURE(Thread, "ALi thread has died"); } CAliM1543C *theAli = 0; ================================================ FILE: src/AliM1543C.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_ALIM1543C_H_) #define INCLUDED_ALIM1543C_H_ #include "PCIDevice.hpp" #define PIT_OFFSET_MAX 6 // RTC register A (MC_BASE_32_KHz is a divider bits configuration) #define RTC_REG_A 0x0a #define RTC_UIP 0x80 #define MC_BASE_32_KHz 0x20 // RTC register B (here most options reside; 0x08 is the unused square wave // enable pin) #define RTC_REG_B 0x0b #define RTC_SET 0x80 #define RTC_PIE 0x40 #define RTC_AIE 0x20 #define RTC_UIE 0x10 #define RTC_DM 0x04 #define RTC_2412 0x02 #define RTC_DSE 0x01 // RTC register C (rest of register is always zero) #define RTC_REG_C 0x0c #define RTC_IRQF 0x80 #define RTC_PF 0x40 #define RTC_AF 0x20 #define RTC_UF 0x10 // RTC register D (rest of register is always zero) #define RTC_REG_D 0x0d #define RTC_VRT 0x80 /** * \brief Emulated ISA part of the ALi M1543C chipset. * * The ALi M1543C device provides i/o and glue logic support to the system: * ISA, DMA, Interrupt, Timer, TOY Clock. * * Documentation consulted: * - Ali M1543C B1 South Bridge Version 1.20 *(http://mds.gotdns.com/sensors/docs/ali/1543dScb1-120.pdf) * - MC146818 RTC *(https://www.nxp.com/docs/en/data-sheet/MC146818.pdf) * - Keyboard Scancodes, by Andries Brouwer *(http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html) * . **/ class CAliM1543C : public CPCIDevice { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); virtual void WriteMem_Legacy(int index, u32 address, int dsize, u32 data); virtual u32 ReadMem_Legacy(int index, u32 address, int dsize); void do_pit_clock(); CAliM1543C(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CAliM1543C(); void pic_interrupt(int index, int intno); void pic_deassert(int index, int intno); void init(); void start_threads(); void stop_threads(); void run(); private: std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; CMutex *myRegLock; bool StopThread; struct tm get_time(); // REGISTER 61 (NMI) u8 reg_61_read(); void reg_61_write(u8 data); // REGISTERS 70 - 73: TOY u8 toy_read(u32 address); void toy_write(u32 address, u8 data); void toy_handle_periodic_interrupt(u8 data); void toy_update_irqf(); // Timer/Counter u8 pit_read(u32 address); void pit_write(u32 address, u8 data); void pit_clock(); // interrupt controller u8 pic_read(int index, u32 address); void pic_write(int index, u32 address, u8 data); u8 pic_read_vector(); u8 pic_read_edge_level(int index); void pic_write_edge_level(int index, u8 data); // LPT controller u8 lpt_read(u32 address); void lpt_write(u32 address, u8 data); void lpt_reset(); /// The state structure contains all elements that need to be saved to the /// statefile. struct SAli_state { // REGISTER 61 (NMI) u8 reg_61; // REGISTERS 70 - 73: TOY u8 toy_stored_data[256]; u8 toy_access_ports[4]; // TOY periodic interrupt last fire clock_t toy_pi_last_fire; // Timer/Counter u32 pit_counter[9]; u8 pit_status[4]; u8 pit_mode[4]; // interrupt controller int pic_mode[2]; u8 pic_intvec[2]; u8 pic_mask[2]; u8 pic_asserted[2]; u8 pic_edge_level[2]; u8 lpt_data; u8 lpt_control; u8 lpt_status; bool lpt_init; } state; FILE *lpt; }; extern CAliM1543C *theAli; #endif // !defined(INCLUDED_ALIM1543C_H_) ================================================ FILE: src/AliM1543C_ide.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Although this is not required, the author would appreciate being * notified of, and receiving any modifications you may make to the * source code that might serve the general public. */ #ifdef DEBUG_IDE_LOCKS #define DEBUG_LOCKS #endif #include "AliM1543C_ide.hpp" #include "StdAfx.hpp" #include "System.hpp" #include #include "gui/keymap.hpp" #include "gui/scancodes.hpp" #include "AliM1543C.hpp" #include "Disk.hpp" #define PAUSE(msg) \ do { \ printf("Debug Pause: "); \ printf(msg); \ getc(stdin); \ } while (0); u32 AliM1543C_ide_cfg_data[64] = { /*00*/ 0x522910b9, // CFID: vendor + device /*04*/ 0x02800000, // CFCS: command + status /*08*/ 0x0101fac1, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x000001f1, // BAR0: /*14*/ 0x000003f5, // BAR1: /*18*/ 0x00000171, // BAR2: /*1c*/ 0x00000375, // BAR3: /*20*/ 0x0000f001, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x040201ff, // CFIT: interrupt configuration 0, 0, /*48*/ 0x4a000000, // UDMA test /*4c*/ 0x1aba0000, // reserved 0, /*54*/ 0x44445555, // udma setting + fifo treshold 0, 0, 0, 0, 0, 0, 0, 0, /*78*/ 0x00000021, // ide clock 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; u32 AliM1543C_ide_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x00000105, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xfffffff8, // BAR0 /*14*/ 0xfffffffc, // BAR1: CBMA /*18*/ 0xfffffff8, // BAR2: /*1c*/ 0xfffffffc, // BAR3: /*20*/ 0xfffffff0, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Constructor. **/ CAliM1543C_ide::CAliM1543C_ide(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev), CDiskController(2, 2) { if (theIDE != 0) FAILURE(Configuration, "More than one IDE controller"); theIDE = this; // create scsi busses CSCSIBus *a = new CSCSIBus(cfg, c); CSCSIBus *b = new CSCSIBus(cfg, c); scsi_register(0, a, 7); // scsi id 7 by default scsi_register(1, b, 7); // scsi id 7 by default } /** * Initialize the IDE device. **/ void CAliM1543C_ide::init() { add_function(0, AliM1543C_ide_cfg_data, AliM1543C_ide_cfg_mask); add_legacy_io(PRI_COMMAND, 0x1f0, 8); add_legacy_io(PRI_CONTROL, 0x3f6, 2); add_legacy_io(SEC_COMMAND, 0x170, 8); add_legacy_io(SEC_CONTROL, 0x376, 2); add_legacy_io(PRI_BUSMASTER, 0xf000, 8); add_legacy_io(SEC_BUSMASTER, 0xf008, 8); usedma = myCfg->get_bool_value("dma", true); if (!usedma) { printf("IDE: DMA Transfers turned off.\n"); } ResetPCI(); // start controller threads StopThread = false; mtRegisters[0] = new CRWLock("ide0-registers"); mtRegisters[1] = new CRWLock("ide1-registers"); mtBusMaster[0] = new CRWLock("ide0-busmaster"); mtBusMaster[1] = new CRWLock("ide1-busmaster"); for (int i = 0; i < 2; i++) { semController[i] = new CSemaphore(0, 1); // disk controller semControllerReady[i] = new CSemaphore(0, 1); // disk controller ready semBusMaster[i] = new CSemaphore(0, 1); // bus master semBusMasterReady[i] = new CSemaphore(0, 1); // bus master ready semControllerReady[i]->set(); semBusMasterReady[i]->set(); thrController[i] = 0; } printf("%%IDE-I-INIT: New IDE emulator initialized.\n"); } void CAliM1543C_ide::start_threads() { char buffer[5]; for (int i = 0; i < 2; i++) { if (!thrController[i]) { sprintf(buffer, "ide%d", i); thrController[i] = std::make_unique([this, i]() { this->run(i); }); printf(" %s", buffer); StopThread = false; } } } void CAliM1543C_ide::stop_threads() { char buffer[5]; StopThread = true; for (int i = 0; i < 2; i++) { if (thrController[i]) { sprintf(buffer, "ide%d", i); printf(" %s", buffer); semController[i]->set(); thrController[i]->join(); thrController[i] = nullptr; } } } CAliM1543C_ide::~CAliM1543C_ide() { stop_threads(); } void CAliM1543C_ide::ResetPCI() { int i; int j; CPCIDevice::ResetPCI(); for (i = 0; i < 2; i++) { CONTROLLER(i).bm_status = 0; CONTROLLER(i).selected = 0; for (j = 0; j < 2; j++) { REGISTERS(i, j).error = 0; COMMAND(i, j).command_in_progress = 0; COMMAND(i, j).command_cycle = 0; STATUS(i, j).busy = false; STATUS(i, j).drive_ready = false; STATUS(i, j).drq = false; STATUS(i, j).err = false; STATUS(i, j).index_pulse = false; STATUS(i, j).index_pulse_count = 0; STATUS(i, j).seek_complete = false; PER_DRIVE(i, j).multiple_size = 1; set_signature(i, j); } } } void CAliM1543C_ide::register_disk(class CDisk *dsk, int bus, int dev) { CDiskController::register_disk(dsk, bus, dev); if (dsk->cdrom()) { dsk->scsi_register(0, scsi_bus[bus], dev); dsk->set_atapi_mode(); } } static u32 ide_magic1 = 0xB222654D; static u32 ide_magic2 = 0xD456222C; /** * Save state to a Virtual Machine State file. **/ int CAliM1543C_ide::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&ide_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&ide_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CAliM1543C_ide::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != ide_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != ide_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /* * Region read/write redirection */ u32 CAliM1543C_ide::ReadMem_Legacy(int index, u32 address, int dsize) { int channel = 0; switch (index) { case SEC_COMMAND: channel = 1; case PRI_COMMAND: return ide_command_read(channel, address, dsize); case SEC_CONTROL: channel = 1; case PRI_CONTROL: return ide_control_read(channel, address); case SEC_BUSMASTER: channel = 1; case PRI_BUSMASTER: return ide_busmaster_read(channel, address, dsize); } return 0; } void CAliM1543C_ide::WriteMem_Legacy(int index, u32 address, int dsize, u32 data) { int channel = 0; switch (index) { case SEC_COMMAND: channel = 1; case PRI_COMMAND: ide_command_write(channel, address, dsize, data); break; case SEC_CONTROL: channel = 1; case PRI_CONTROL: ide_control_write(channel, address, data); break; case SEC_BUSMASTER: channel = 1; case PRI_BUSMASTER: ide_busmaster_write(channel, address, dsize, data); break; } } u32 CAliM1543C_ide::ReadMem_Bar(int func, int bar, u32 address, int dsize) { int channel = 0; switch (bar) { case BAR_SEC_COMMAND: channel = 1; case BAR_PRI_COMMAND: return ide_command_read(channel, address, dsize); case BAR_SEC_CONTROL: channel = 1; case BAR_PRI_CONTROL: // we have to offset by two because the BAR starts at 3f4 vs 3f6 return ide_control_read(channel, address - 2); case BAR_BUSMASTER: if (address < 8) return ide_busmaster_read(0, address, dsize); else return ide_busmaster_read(1, address - 8, dsize); } return 0; } void CAliM1543C_ide::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { int channel = 0; switch (bar) { case BAR_SEC_COMMAND: channel = 1; case BAR_PRI_COMMAND: ide_command_write(channel, address, dsize, data); return; case BAR_SEC_CONTROL: channel = 1; case BAR_PRI_CONTROL: // we have to offset by two because the BAR starts at 3f4 vs 3f6 ide_control_write(channel, address - 2, data); return; case BAR_BUSMASTER: if (address < 8) return ide_busmaster_write(0, address, data, dsize); else return ide_busmaster_write(1, address - 8, data, dsize); } } /* * Register read/write handlers */ u32 CAliM1543C_ide::ide_command_read(int index, u32 address, int dsize) { u32 data = 0; if (!get_disk(index, 0) && !get_disk(index, 1)) { // no disks are present, so the data lines actually float to // a high state, which is logical 1. return 0xffffffff; } switch (address) { case REG_COMMAND_DATA: if (!SEL_STATUS(index).drq) { #ifdef DEBUG_IDE_REG_COMMAND printf("Reading from data buffer when data is not ready.\n"); ide_status(index); PAUSE("WTF"); #endif break; } data = 0; switch (dsize) { case 32: data = CONTROLLER(index).data[CONTROLLER(index).data_ptr++]; data |= CONTROLLER(index).data[CONTROLLER(index).data_ptr++] << 16; break; case 16: data = CONTROLLER(index).data[CONTROLLER(index).data_ptr++]; } if (CONTROLLER(index).data_ptr >= CONTROLLER(index).data_size) { // there's no more to take. SEL_STATUS(index).drq = false; if (SEL_COMMAND(index).command_in_progress) { SEL_STATUS(index).busy = true; SEL_STATUS(index).drive_ready = false; UPDATE_ALT_STATUS(index); semControllerReady[index]->wait(); semController[index]->set(); // wake up the controller. #if defined(DEBUG_IDE_MULTIPLE) || defined(DEBUG_IDE_PACKET) printf("Command still in progress, waking up controller.\n"); printf("-- Packet Phase: %d\n", SEL_COMMAND(index).packet_phase); ide_status(index); #endif } } if (CONTROLLER(index).data_ptr > IDE_BUFFER_SIZE) { printf("%%IDE-W-OVERFLOW: data pointer past end of buffer, setting to " "0.\n"); CONTROLLER(index).data_ptr = 0; SEL_STATUS(index).drq = false; UPDATE_ALT_STATUS(index); } break; case REG_COMMAND_ERROR: data = SEL_REGISTERS(index).error; break; case REG_COMMAND_SECTOR_COUNT: data = SEL_REGISTERS(index).sector_count; break; case REG_COMMAND_SECTOR_NO: data = SEL_REGISTERS(index).sector_no; break; case REG_COMMAND_CYL_LOW: data = SEL_REGISTERS(index).cylinder_no & 0xff; break; case REG_COMMAND_CYL_HI: data = (SEL_REGISTERS(index).cylinder_no >> 8) & 0xff; break; case REG_COMMAND_DRIVE: data = 0x80 | (SEL_REGISTERS(index).lba_mode ? 0x40 : 0x00) | 0x20 // 512 byte sector size | (CONTROLLER(index).selected ? 0x10 : 0x00) | (SEL_REGISTERS(index).head_no & 0x0f); break; case REG_COMMAND_STATUS: // get the status and clear the interrupt. data = get_status(index); theAli->pic_deassert(1, 6 + index); #ifdef DEBUG_IDE_INTERRUPT printf("%%IDE-I-INTERRUPT: Interrupt Acknowledged on %d.\n", index); #endif break; } #ifdef DEBUG_IDE_REG_COMMAND if (address != 0) { printf("%%IDE-I-REGCMD: Read from command register %d (%s) on controller " "%d, value: %x\n", address, register_names[address], index, data); } #endif return data; } void CAliM1543C_ide::ide_command_write(int index, u32 address, int dsize, u32 data) { #ifdef DEBUG_IDE_REG_COMMAND if (address != 0) { printf("%%IDE-I-REGCMD: Write to command register %d (%s) on controller " "%d, value: %x\n", address, register_names[address], index, data); } SEL_STATUS(index).debug_status_update = true; #endif switch (address) { case REG_COMMAND_DATA: if (!SEL_STATUS(index).drq) { #ifdef DEBUG_IDE_REG_COMMAND printf("%%IDE-I-DATA: Unrequested data written to data port: %x\n", data); ide_status(index); #endif break; } switch (dsize) { case 32: CONTROLLER(index).data[CONTROLLER(index).data_ptr++] = data & 0xffff; CONTROLLER(index).data[CONTROLLER(index).data_ptr++] = (data >> 16) & 0xffff; break; case 16: CONTROLLER(index).data[CONTROLLER(index).data_ptr++] = data & 0xffff; } if (CONTROLLER(index).data_ptr >= CONTROLLER(index).data_size) { // we don't want any more. SEL_STATUS(index).drq = false; SEL_STATUS(index).busy = true; UPDATE_ALT_STATUS(index); semControllerReady[index]->wait(); semController[index]->set(); // wake the controller up. } if (CONTROLLER(index).data_ptr > IDE_BUFFER_SIZE) { printf("%%IDE-W-OVERFLOW: data pointer overflow, setting to 0.\n"); CONTROLLER(index).data_ptr = 0; SEL_STATUS(index).drq = false; UPDATE_ALT_STATUS(index); } break; case REG_COMMAND_FEATURES: REGISTERS(index, 0).features = data; REGISTERS(index, 1).features = data; break; case REG_COMMAND_SECTOR_COUNT: REGISTERS(index, 0).sector_count = REGISTERS(index, 1).sector_count = data & 0xff; break; case REG_COMMAND_SECTOR_NO: REGISTERS(index, 0).sector_no = REGISTERS(index, 1).sector_no = data & 0xff; break; case REG_COMMAND_CYL_LOW: REGISTERS(index, 0).cylinder_no = REGISTERS(index, 1).cylinder_no = (REGISTERS(index, 1).cylinder_no & 0xff00) | (data & 0xff); break; case REG_COMMAND_CYL_HI: REGISTERS(index, 0).cylinder_no = REGISTERS(index, 1).cylinder_no = (REGISTERS(index, 1).cylinder_no & 0xff) | ((data << 8) & 0xff00); break; case REG_COMMAND_DRIVE: if (((data >> 4) & 1) != CONTROLLER(index).selected) #ifdef DEBUG_IDE printf("Setting selected device on %d to: %d. val=%02x\n", index, (data >> 4) & 1, data); #endif CONTROLLER(index).selected = (data >> 4) & 1; REGISTERS(index, 0).head_no = REGISTERS(index, 1).head_no = data & 0x0f; REGISTERS(index, 0).lba_mode = REGISTERS(index, 1).lba_mode = (data >> 6) & 1; break; case REG_COMMAND_COMMAND: theAli->pic_deassert(1, 6 + index); // interrupt is cleared on write. if (!SEL_DISK(index)) { #ifdef DEBUG_IDE printf("%%IDE-I-NODEV: Command to non-existing device %d.%d. cmd=%x\n", index, CONTROLLER(index).selected, data); #endif } if (SEL_COMMAND(index).command_in_progress == true) { // we're already working, why is another command being issued? #ifdef DEBUG_IDE printf("%%IDE-W-CIP: Command is already in progress.\n"); PAUSE("dang it!"); #endif } if ((data & 0xf0) == 0x10) data = 0x10; SEL_COMMAND(index).command_in_progress = false; SEL_COMMAND(index).current_command = data; #ifdef DEBUG_IDE_CMD printf("%%IDE-I-CMD: Command %02x issued on controller %d, disk %d.\n", data, index, CONTROLLER(index).selected); #endif SEL_COMMAND(index).command_cycle = 0; SEL_STATUS(index).drq = false; UPDATE_ALT_STATUS(index); CONTROLLER(index).data_ptr = 0; if (data != 0x00) { SEL_STATUS(index).busy = true; UPDATE_ALT_STATUS(index); SEL_COMMAND(index).command_in_progress = true; SEL_COMMAND(index).packet_phase = PACKET_NONE; semControllerReady[index]->wait(); semController[index]->set(); // wake up the controller. } else { // this is a nop, so we cancel everything that's pending and // pretend that this operation got done super fast! if (SEL_DISK(index)) command_aborted(index, data); } break; } } u32 CAliM1543C_ide::ide_control_read(int index, u32 address) { u32 data = 0; switch (address) { case 0: // Compute status live from current state rather than using cached // alt_status, to avoid stale values when the controller thread has // updated status fields but not yet called UPDATE_ALT_STATUS. // Unlike get_status(), this does not affect index_pulse or clear interrupts. if (SEL_DISK(index)) { data = (SEL_STATUS(index).busy ? 0x80 : 0x00) | (SEL_STATUS(index).drive_ready ? 0x40 : 0x00) | (SEL_STATUS(index).fault ? 0x20 : 0x00) | (SEL_STATUS(index).seek_complete ? 0x10 : 0x00) | (SEL_STATUS(index).drq ? 0x08 : 0x00) | (SEL_STATUS(index).index_pulse ? 0x02 : 0x00) | (SEL_STATUS(index).err ? 0x01 : 0x00); } #ifdef DEBUG_IDE_REG_CONTROL static u32 last_data = 0; if (last_data != data) { printf("%%IDE-I-READCTRL: alternate status on IDE control %d: 0x%02x\n", index, data); } last_data = data; #endif break; case 1: // 3x7h drive address register. (floppy?) data |= (CONTROLLER(index).selected == 0) ? 1 : 2; data |= (SEL_REGISTERS(index).head_no) << 2; data = (~data) & 0xff; // negate everything #ifdef DEBUG_IDE_REG_CONTROL printf("%%IDE-I-READCTRL: drive address port on IDE control %d: 0x%02x\n", index, data); #endif break; } return data; } /** * Write to the IDE controller control interface. **/ void CAliM1543C_ide::ide_control_write(int index, u32 address, u32 data) { bool prev_reset; #ifdef DEBUG_IDE_REG_CONTROL printf("%%IDE-I-WRITCTRL: write port %d on IDE control %d: 0x%02x\n", (u32)(address), index, data); #endif switch (address) { case 0: prev_reset = CONTROLLER(index).reset; CONTROLLER(index).reset = (data >> 2) & 1; CONTROLLER(index).disable_irq = (data >> 1) & 1; if (!prev_reset && CONTROLLER(index).reset) { #ifdef DEBUG_IDE_REG_CONTROL printf("IDE reset on index %d started.\n", index); #endif STATUS(index, 0).busy = true; STATUS(index, 0).drive_ready = false; STATUS(index, 0).seek_complete = true; STATUS(index, 0).drq = false; STATUS(index, 0).err = false; COMMAND(index, 0).current_command = 0; COMMAND(index, 0).command_in_progress = false; STATUS(index, 1).busy = true; STATUS(index, 1).drive_ready = false; STATUS(index, 1).seek_complete = true; STATUS(index, 1).drq = false; STATUS(index, 1).err = false; COMMAND(index, 1).current_command = 0; COMMAND(index, 1).command_in_progress = false; CONTROLLER(index).reset_in_progress = true; SEL_REGISTERS(index).error = 0x01; // no error COMMAND(index, 0).current_command = 0; CONTROLLER(index).disable_irq = false; } else if (prev_reset && !CONTROLLER(index).reset) { #ifdef DEBUG_IDE_REG_CONTROL printf("IDE reset on index %d ended.\n", index); #endif STATUS(index, 0).busy = false; STATUS(index, 0).drive_ready = true; STATUS(index, 1).busy = false; STATUS(index, 1).drive_ready = true; CONTROLLER(index).reset_in_progress = false; set_signature(index, 0); set_signature(index, 1); } break; case 1: // floppy? break; } } /** * Read from the IDE controller busmaster interface. **/ u32 CAliM1543C_ide::ide_busmaster_read(int index, u32 address, int dsize) { u32 data = 0; switch (dsize) { case 8: data = CONTROLLER(index).busmaster[address]; break; case 32: data = *(u32 *)(&CONTROLLER(index).busmaster[address]); break; default: FAILURE(InvalidArgument, "16-bit read from busmaster"); break; } #ifdef DEBUG_IDE_BUSMASTER printf( "%%IDE-I-READBUSM: read port %d on IDE bus master %d: 0x%02x, %d bytes\n", (u32)(address), index, data, dsize / 8); #endif return data; } /** * Write to the IDE controller busmaster interface. **/ void CAliM1543C_ide::ide_busmaster_write(int index, u32 address, u32 data, int dsize) { #ifdef DEBUG_IDE_BUSMASTER if (!(dsize == 8 && (address >= 4 && address <= 7))) { printf("%%IDE-I-WRITBUSM: write port %d on IDE bus master %d: 0x%02x, %d " "bytes\n", (u32)(address), index, data, dsize / 8); } #endif u32 prd_address; // u32 base, control; switch (dsize) { case 32: ide_busmaster_write(index, address, data & 0xff, 8); ide_busmaster_write(index, address + 1, (data >> 8) & 0xff, 8); ide_busmaster_write(index, address + 2, (data >> 16) & 0xff, 8); ide_busmaster_write(index, address + 3, (data >> 24) & 0xff, 8); return; case 16: ide_busmaster_write(index, address, data & 0xff, 8); ide_busmaster_write(index, address + 1, (data >> 8) & 0xff, 8); return; } switch (address) { case 0: // command register #ifdef DEBUG_IDE_BUSMASTER printf("%%IDE-I-BUSM: Bus master command got data: %x (%s,%s)\n", data, (data & 0x08 ? "write" : "read"), (data & 0x01 ? "start" : "stop")); #endif // bits 7:4 & 2:1 are reserved and must return zero on read. CONTROLLER(index).busmaster[0] = data & 0x09; if (data & 0x01) { // set the status register CONTROLLER(index).busmaster[2] |= 0x01; semBusMasterReady[index]->wait(); semBusMaster[index]->set(); // wake up the controller for busmastering } else { // clear the status register CONTROLLER(index).busmaster[2] &= 0xfe; } break; case 2: // status // bit 7 = simplex only (0=both channels are independent) // bit 6 = drive 1 dma capable. // bit 5 = drive 0 dma capable. // bit 4,3 = reserved // bit 2 = interrupt (write 1 to reset) // bit 1 = error (write 1 to reset) // bit 0 = busmaster active. CONTROLLER(index).busmaster[2] = data & 0x67; if (data & 0x04) // interrupt CONTROLLER(index).busmaster[2] &= ~0x04; if (data & 0x02) // error CONTROLLER(index).busmaster[2] &= ~0x02; if (data & 0x01) // busy CONTROLLER(index).busmaster[2] &= ~0x01; break; case 4: // descriptor table pointer register(s) case 5: case 6: CONTROLLER(index).busmaster[address] = data; break; case 7: CONTROLLER(index).busmaster[address] = data; prd_address = endian_32(*(u32 *)(&CONTROLLER(index).busmaster[4])); #ifdef DEBUG_IDE_BUSMASTER printf("%%IDE-I-PRD: Virtual address: %" PRIx64 " \n", endian_32(*(u32 *)(&CONTROLLER(index).busmaster[4]))); printf("-IDE-I-PRD: Physical address: %" PRIx64 " \n", prd_address); u32 base, control; do { do_pci_read(prd_address, &base, 4, 1); do_pci_read(prd_address + 4, &control, 4, 1); printf("-IDE-I-PRD: base: %x, control: %x \n", base, control); prd_address += 8; } while (base & 0x80 == 0); #endif break; default: break; } } void CAliM1543C_ide::set_signature(int index, int id) { // Device signature REGISTERS(index, id).head_no = 0; REGISTERS(index, id).sector_count = 1; REGISTERS(index, id).sector_no = 1; if (get_disk(index, id)) { if (!get_disk(index, id)->cdrom()) { REGISTERS(index, id).cylinder_no = 0; CONTROLLER(index).selected = 0; // XXX: This may not be correct. } else { REGISTERS(index, id).cylinder_no = 0xeb14; } } else { REGISTERS(index, id).cylinder_no = 0xffff; } } void CAliM1543C_ide::raise_interrupt(int index) { if (!CONTROLLER(index).disable_irq) { #ifdef DEBUG_IDE_INTERRUPT printf("%%IDE-I-INTERRUPT: Interrupt raised on controller %d.\n", index); #endif #if !defined(IDE_YIELD_INTERRUPTS) { SCOPED_WRITE_LOCK(mtBusMaster[index]); CONTROLLER(index).busmaster[2] |= 0x04; } UPDATE_ALT_STATUS(index); theAli->pic_interrupt(1, 6 + index); #else CONTROLLER(index).interrupt_pending = true; #endif } } u8 CAliM1543C_ide::get_status(int index) { u8 data; if (!SEL_DISK(index)) { #ifdef DEBUG_IDE_REG_COMMAND printf("%%IDE-I-STATUS: Read status for nonexiting device %d.%d\n", index, CONTROLLER(index).selected); #endif return 0; } data = (SEL_STATUS(index).busy ? 0x80 : 0x00) | (SEL_STATUS(index).drive_ready ? 0x40 : 0x00) | (SEL_STATUS(index).fault ? 0x20 : 0x00) | (SEL_STATUS(index).seek_complete ? 0x10 : 0x00) | (SEL_STATUS(index).drq ? 0x08 : 0x00) | (SEL_STATUS(index).index_pulse ? 0x02 : 0x00) | (SEL_STATUS(index).err ? 0x01 : 0x00); SEL_STATUS(index).index_pulse_count++; SEL_STATUS(index).index_pulse = false; if (SEL_STATUS(index).index_pulse_count >= 10) { SEL_STATUS(index).index_pulse_count = 0; SEL_STATUS(index).index_pulse = true; } #ifdef DEBUG_IDE_REG_COMMAND if ((SEL_STATUS(index).debug_last_status & 0xfd) != (data & 0xfd) || SEL_STATUS(index).debug_status_update) { printf("%%IDE-I-STATUS: Controller %d status: %x = %s %s %s %s %s %s %s\n", index, data, SEL_STATUS(index).busy ? "busy" : "", SEL_STATUS(index).drive_ready ? "drdy" : "", SEL_STATUS(index).fault ? "fault" : "", SEL_STATUS(index).seek_complete ? "seek_complete" : "", SEL_STATUS(index).drq ? "drq" : "", SEL_STATUS(index).index_pulse ? "pulse" : "", SEL_STATUS(index).err ? "error" : ""); SEL_STATUS(index).debug_status_update = false; } SEL_STATUS(index).debug_last_status = data; #endif return data; } void CAliM1543C_ide::identify_drive(int index, bool packet) { char serial_number[21]; char model_number[41]; char rev_number[9]; size_t i; // clear the block. for (i = 0; i < 256; i++) CONTROLLER(index).data[i] = 0; CONTROLLER(index).data_ptr = 0; CONTROLLER(index).data_size = 256; // The data here was taken from T13/1153D revision 18 if (!packet) { // flags: 0x0080 = removable, 0x0040 = fixed. CONTROLLER(index).data[0] = SEL_DISK(index)->cdrom() ? 0x0080 : 0x0040; } else { // flags: 15-14: 10=atapi, 11=reserved; 12-8: packet set; 7: // removable 6-5: timing info, 4-2: command, 1-0: 00= 12 byte packet CONTROLLER(index).data[0] = 0x8580; } // logical cylinders if (SEL_DISK(index)->get_cylinders() > 16383) CONTROLLER(index).data[1] = 16383; else CONTROLLER(index).data[1] = (u16)(SEL_DISK(index)->get_cylinders()); // logical heads CONTROLLER(index).data[3] = (u16)(SEL_DISK(index)->get_heads()); // logical sectors per logical track CONTROLLER(index).data[6] = (u16)(SEL_DISK(index)->get_sectors()); // serial number strcpy(serial_number, " "); i = strlen(SEL_DISK(index)->get_serial()); i = (i > 20) ? 20 : i; memcpy(model_number, SEL_DISK(index)->get_serial(), i); for (i = 0; i < 10; i++) CONTROLLER(index).data[10 + i] = (serial_number[i * 2] << 8) | serial_number[i * 2 + 1]; // firmware revision strcpy(rev_number, " "); i = strlen(SEL_DISK(index)->get_rev()); i = (i > 8) ? 8 : i; memcpy(model_number, SEL_DISK(index)->get_rev(), i); for (i = 0; i < 4; i++) CONTROLLER(index).data[23 + i] = (rev_number[i * 2] << 8) | rev_number[i * 2 + 1]; // model number strcpy(model_number, " "); i = strlen(SEL_DISK(index)->get_model()); i = (i > 40) ? 40 : i; memcpy(model_number, SEL_DISK(index)->get_model(), i); for (i = 0; i < 20; i++) CONTROLLER(index).data[i + 27] = (model_number[i * 2] << 8) | model_number[i * 2 + 1]; // read/write multiple (15-8 = 0x80, 7-0 = # sectors) CONTROLLER(index).data[47] = 0x8000 | MAX_MULTIPLE_SECTORS; // capabilities if (!packet) { CONTROLLER(index).data[49] = 0x0300; } else { CONTROLLER(index).data[49] = 0x0b00; // dma, iordy } // capabilities (2) CONTROLLER(index).data[50] = 0x4000; // pio data transfer number (bits 15-8) CONTROLLER(index).data[51] = 0x0300; // validity: bit 2 = #88 valid, 1 = 64-70 valid, 0 = 54-58 valid CONTROLLER(index).data[53] = 7; // geometry CONTROLLER(index).data[54] = (u16)(SEL_DISK(index)->get_cylinders()); CONTROLLER(index).data[55] = (u16)(SEL_DISK(index)->get_heads()); CONTROLLER(index).data[56] = (u16)(SEL_DISK(index)->get_sectors()); CONTROLLER(index).data[57] = (u16)(SEL_DISK(index)->get_chs_size() >> 0) & 0xFFFF; CONTROLLER(index).data[58] = (u16)(SEL_DISK(index)->get_chs_size() >> 16) & 0xFFFF; // multiple sector setting (valid, 1 sector per interrupt) if (SEL_PER_DRIVE(index).multiple_size != 0) { CONTROLLER(index).data[59] = 0x0100 | SEL_PER_DRIVE(index).multiple_size; } else { CONTROLLER(index).data[59] = 0x0000; } // lba capacity CONTROLLER(index).data[60] = (u16)(SEL_DISK(index)->get_lba_size() >> 0) & 0xFFFF; CONTROLLER(index).data[61] = (u16)(SEL_DISK(index)->get_lba_size() >> 16) & 0xFFFF; // multiword dma capability (10-8: modes selected, 2-0, modes // supported) if (usedma) CONTROLLER(index).data[63] = CONTROLLER(index).dma_mode << 8 | 0x01; // dma 0 supported else CONTROLLER(index).data[63] = CONTROLLER(index).dma_mode << 8 | 0x00; // dma not supported // pio modes supported (bit 0 = mode 3, bit 1 = mode 4) CONTROLLER(index).data[64] = 0x0002; // minimum cycle times CONTROLLER(index).data[65] = 480; // mode 0 CONTROLLER(index).data[66] = 480; // mode 0 CONTROLLER(index).data[67] = 120; // pio4 CONTROLLER(index).data[68] = 120; // pio4 if (packet) { // packet to bus release time CONTROLLER(index).data[71] = 120; // service to bus release time CONTROLLER(index).data[72] = 120; } // queue depth (we don't do queing) CONTROLLER(index).data[75] = 0; // ata version supported (bits/version: 1,2,3,4) CONTROLLER(index).data[80] = 0x001e; // atapi revision supported (ata/atapi-4 T13 1153D revision 17) CONTROLLER(index).data[81] = 0x0017; // command set supported (cdrom = nop,packet,removable; disk=nop) CONTROLLER(index).data[82] = SEL_DISK(index)->cdrom() ? 0x4014 : 0x4000; // command sets supported(no additional command sets) CONTROLLER(index).data[83] = 0x4000; CONTROLLER(index).data[84] = 0x4000; // command sets enabled. CONTROLLER(index).data[85] = SEL_DISK(index)->cdrom() ? 0x4014 : 0x4000; CONTROLLER(index).data[86] = 0x4000; CONTROLLER(index).data[87] = 0x4000; // ultra dma modes supported (10-8: modes selected, 2-0, modes // supported) CONTROLLER(index).data[88] = 0x0000; } void CAliM1543C_ide::command_aborted(int index, u8 command) { printf("ide%d.%d aborting on command 0x%02x \n", index, CONTROLLER(index).selected, command); SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).err = true; SEL_STATUS(index).drq = false; SEL_REGISTERS(index).error |= 0x04; // command ABORTED CONTROLLER(index).data_ptr = 0; SEL_COMMAND(index).command_in_progress = false; UPDATE_ALT_STATUS(index); raise_interrupt(index); } CAliM1543C_ide *theIDE = 0; void CAliM1543C_ide::ide_status(int index) { printf("IDE %d.%d: [busy: %d, drdy: %d, flt: %d, drq: %d, err: %d]\n" " [c: %d, h: %d, s: %d, #: %d, f: %x, lba: %d]\n" " [ptr: %d, size: %d, error: %d, cmd: %x, in progress: %d]\n" " [cycle: %d, pkt phase: %d, pkt cmd: %x, dma: %d]\n" " [bm-cmd: %x bm-stat: %x]\n", index, CONTROLLER(index).selected, SEL_STATUS(index).busy, SEL_STATUS(index).drive_ready, SEL_STATUS(index).fault, SEL_STATUS(index).drq, SEL_STATUS(index).err, SEL_REGISTERS(index).cylinder_no, SEL_REGISTERS(index).head_no, SEL_REGISTERS(index).sector_no, SEL_REGISTERS(index).sector_count, SEL_REGISTERS(index).features, (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no, CONTROLLER(index).data_ptr, CONTROLLER(index).data_size, SEL_REGISTERS(index).error, SEL_COMMAND(index).current_command & 0xff, SEL_COMMAND(index).command_in_progress, SEL_COMMAND(index).command_cycle, SEL_COMMAND(index).packet_phase, SEL_COMMAND(index).packet_command[0], SEL_COMMAND(index).packet_dma, CONTROLLER(index).busmaster[0], CONTROLLER(index).busmaster[2]); } /** * Check if threads are still running. **/ void CAliM1543C_ide::check_state() { if (thrController[0] && thrControllerDead[0].load()) FAILURE(Thread, "IDE 0 thread has died"); if (thrController[1] && thrControllerDead[1].load()) FAILURE(Thread, "IDE 1 thread has died"); } void CAliM1543C_ide::execute(int index) { if (SEL_DISK(index) == NULL && SEL_COMMAND(index).current_command != 0x90) { // this device doesn't exist (and its not execute device // diagnostic) // so we'll just timeout SEL_COMMAND(index).command_in_progress = false; } else { #ifdef DEBUG_IDE_COMMAND printf("%%IDE-I-COMMAND: Processing command on controller %d.\n", index); ide_status(index); #endif switch (SEL_COMMAND(index).current_command) { case 0x00: // nop SEL_REGISTERS(index).error = 0x04; SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = true; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = true; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); printf("got nop on %d.%d\n", index, CONTROLLER(index).selected); // FAILURE("This isn't possible, but you're seeing it."); break; case 0x08: // device reset if (SEL_DISK(index)->cdrom() || 1) { // the spec says that non-packet devices must not respond // to device reset. However, by allowing it, Tru64 // recognizes the device properly. SEL_COMMAND(index).command_in_progress = false; if (CONTROLLER(index).selected == 0) { REGISTERS(index, 0).error = 0x01; // device passed. REGISTERS(index, 1).error = 0x01; // slave not present or passed } else { REGISTERS(index, 1).error = 0x01; // slave passed } set_signature(index, CONTROLLER(index).selected); // step "k", page 216 (232) SEL_STATUS(index).drq = false; // bit 3 SEL_STATUS(index).bit_2 = false; // bit 2 SEL_STATUS(index).err = false; // bit 0 if (SEL_DISK(index)->cdrom()) { SEL_STATUS(index).fault = false; // bit 5 SEL_STATUS(index).drive_ready = false; // per step "m2" } else { SEL_STATUS(index).drive_ready = true; // step "m1" } SEL_STATUS(index).busy = false; // some sources say there's no reset on device reset. // raise_interrupt(index); } else { command_aborted(index, SEL_COMMAND(index).current_command); } break; case 0x10: // calibrate drive SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_REGISTERS(index).cylinder_no = 0; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); break; case 0x20: // read with retries case 0x21: // read without retries if (SEL_COMMAND(index).command_cycle == 0) { // fixup the 0=256 case. if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; } if (!SEL_STATUS(index).drq) { // buffer is empty, so lets fill it. if (!SEL_REGISTERS(index).lba_mode) { FAILURE(NotImplemented, "Non-LBA disk read"); } else { u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->read_blocks(&(CONTROLLER(index).data[0]), 1); #if defined(ES40_BIG_ENDIAN) for (int i = 0; i < SEL_DISK(index)->get_block_size() / sizeof(u16); i++) CONTROLLER(index).data[i] = endian_16(CONTROLLER(index).data[i]); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; CONTROLLER(index).data_ptr = 0; CONTROLLER(index).data_size = 256; // prepare for next sector SEL_REGISTERS(index).sector_count--; if (SEL_REGISTERS(index).sector_count == 0) { SEL_COMMAND(index).command_in_progress = false; if (SEL_DISK(index)->cdrom()) set_signature(index, CONTROLLER(index).selected); // per 9.1 } else { // set the next block to read. // increment the lba. SEL_REGISTERS(index).sector_no++; if (SEL_REGISTERS(index).sector_no > 255) { SEL_REGISTERS(index).sector_no = 0; SEL_REGISTERS(index).cylinder_no++; if (SEL_REGISTERS(index).cylinder_no > 65535) { SEL_REGISTERS(index).cylinder_no = 0; SEL_REGISTERS(index).head_no++; } } } } UPDATE_ALT_STATUS(index); raise_interrupt(index); } break; case 0x30: // write with retries case 0x31: // write without retries if (SEL_COMMAND(index).command_cycle == 0) { // this is our first time through if (SEL_DISK(index)->cdrom() || SEL_DISK(index)->ro()) { printf("%%IDE-W-RO: Write attempt to read-only disk %d.%d.\n", index, CONTROLLER(index).selected); command_aborted(index, SEL_COMMAND(index).current_command); } else { SEL_STATUS(index).drq = true; SEL_STATUS(index).busy = false; CONTROLLER(index).data_size = 256; if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; } } else { // now we should be getting data. if (!SEL_STATUS(index).drq) { // the buffer is full. Do something with the data. if (!SEL_REGISTERS(index).lba_mode) { FAILURE(NotImplemented, "Non-LBA disk write"); } else { u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; #if defined(ES40_BIG_ENDIAN) { u16 data[IDE_BUFFER_SIZE]; SEL_DISK(index)->seek_block(lba); for (int i = 0; i < SEL_DISK(index)->get_block_size() / sizeof(u16); i++) data[i] = endian_16(CONTROLLER(index).data[i]); SEL_DISK(index)->write_blocks(&(data[0]), 1); } #else SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->write_blocks(&(CONTROLLER(index).data[0]), 1); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; CONTROLLER(index).data_ptr = 0; // prepare for next sector SEL_REGISTERS(index).sector_count--; if (SEL_REGISTERS(index).sector_count == 0) { // we're done SEL_STATUS(index).drq = false; SEL_COMMAND(index).command_in_progress = false; } else { // set the next block to read. // increment the lba. SEL_REGISTERS(index).sector_no++; if (SEL_REGISTERS(index).sector_no > 255) { SEL_REGISTERS(index).sector_no = 0; SEL_REGISTERS(index).cylinder_no++; if (SEL_REGISTERS(index).cylinder_no > 65535) { SEL_REGISTERS(index).cylinder_no = 0; SEL_REGISTERS(index).head_no++; } } } } raise_interrupt(index); } } break; /* * case 0x40, 0x41: read verify sector(s) is mandatory for * non-packet (no w/packet */ case 0x70: // seek if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); } break; /* * 0x90: execute device diagnostic: mandatory */ case 0x91: // initialize device parameters SEL_COMMAND(index).command_in_progress = false; if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { #ifdef DEBUG_IDE printf("Original c: %d, h: %d, s: %d\n", SEL_DISK(index)->get_cylinders(), SEL_DISK(index)->get_heads(), SEL_DISK(index)->get_sectors()); printf("Requested c: %d, h: %d, s: %d\n", SEL_REGISTERS(index).cylinder_no, SEL_REGISTERS(index).head_no + 1, SEL_REGISTERS(index).sector_count); #endif if (SEL_DISK(index)->get_heads() == (SEL_REGISTERS(index).head_no + 1) && SEL_DISK(index)->get_sectors() == SEL_REGISTERS(index).sector_count) { // use the default translation -- ok! SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; raise_interrupt(index); } else { #ifdef DEBUG_IDE PAUSE("INIT DEV PARAMS -- geometry not supported!"); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = true; SEL_REGISTERS(index).error = 0x04; // ABORT. raise_interrupt(index); } } break; case 0xa0: // packet send /* * The state machine and protocol used here was actually * derived from ATA/ATAPI-5 (D1321R3) instead of the -4 * documenation. State names were taken from that document. */ if (!SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { if (SEL_REGISTERS(index).features & 0x02) { // overlap not supported PAUSE("overlapping not supported"); command_aborted(index, SEL_COMMAND(index).current_command); } else { if (SEL_COMMAND(index).packet_phase == PACKET_NONE) { // this must be the first time through. if (!scsi_arbitrate(index)) FAILURE(IllegalState, "ATAPI SCSI bus busy"); if (!scsi_select(index, CONTROLLER(index).selected)) FAILURE(IllegalState, "ATAPI device not responding to selection"); SEL_REGISTERS(index).REASON = IR_CD; SEL_STATUS(index).busy = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).DMRD = false; SEL_STATUS(index).SERV = false; CONTROLLER(index).data_ptr = 0; CONTROLLER(index).data_size = 6; SEL_COMMAND(index).packet_dma = (SEL_REGISTERS(index).features & 0x01) ? true : false; SEL_COMMAND(index).packet_phase = PACKET_DP1; // we drop out of the thread and shut down the // controller. we will be awakened when the drq flag // is false, and the process will start in the state machine. break; } /* * This is the Packet I/O state machine. The gist of * it is this: we loop until yield==true, so we can * move from state to state in the same DoClock(). * By the time we get here, we're in DP1 (Receive * Packet) and we're waiting for an actual packet to * arrive. */ #ifdef DEBUG_IDE_PACKET printf("Entering Packet state machine %d\n", index); ide_status(index); #endif bool yield = false; do { #ifdef DEBUG_IDE_PACKET printf("PACKET STATE: %s (%d)\n", packet_states[SEL_COMMAND(index).packet_phase], SEL_COMMAND(index).packet_phase); #endif switch (SEL_COMMAND(index).packet_phase) { case PACKET_DP1: // receive packet // we now have a full command packet. if (scsi_get_phase(index) != SCSI_PHASE_COMMAND) FAILURE(IllegalState, "SCSI command phase expected"); memcpy(scsi_xfer_ptr(index, 12), CONTROLLER(index).data, 12); memcpy(SEL_COMMAND(index).packet_command, CONTROLLER(index).data, 12); scsi_xfer_done(index); SEL_COMMAND(index).packet_phase = PACKET_DP2; SEL_COMMAND(index).packet_buffersize = SEL_REGISTERS(index).cylinder_no; SEL_STATUS(index).busy = true; break; case PACKET_DP2: // prepare b SEL_STATUS(index).busy = true; SEL_STATUS(index).drq = false; if (SEL_COMMAND(index).command_in_progress) { switch (scsi_get_phase(index)) { case SCSI_PHASE_DATA_IN: { size_t num_bytes = scsi_expected_xfer(index); void *data_ptr = scsi_xfer_ptr(index, num_bytes); memcpy(CONTROLLER(index).data, data_ptr, num_bytes); scsi_xfer_done(index); SEL_COMMAND(index).packet_phase = PACKET_DP34; SEL_REGISTERS(index).BYTE_COUNT = (int)num_bytes; CONTROLLER(index).data_size = (int)num_bytes / 2; // word count. CONTROLLER(index).data_ptr = 0; } break; case SCSI_PHASE_DATA_OUT: FAILURE(NotImplemented, "ATAPI for now does not support write operations"); break; case SCSI_PHASE_STATUS: scsi_xfer_ptr(index, scsi_expected_xfer(index)); scsi_xfer_done(index); if (scsi_get_phase(index) != SCSI_PHASE_FREE) FAILURE(IllegalState, "SCSI bus free phase expected"); SEL_COMMAND(index).packet_phase = PACKET_DI; break; default: FAILURE(IllegalState, "Unexpected SCSI phase"); } } else { // transition to an idle state #if defined(DEBUG_IDE_PACKET) printf("Transition into idle state from DP2.\n"); #endif SEL_COMMAND(index).packet_phase = PACKET_DI; } break; case PACKET_DP34: if (SEL_COMMAND(index).packet_dma) { // send back via dma #ifdef DEBUG_IDE_PACKET printf("Sending ATAPI data back via DMA.\n"); #endif do_dma_transfer(index, (u8 *)(&CONTROLLER(index).data[0]), SEL_REGISTERS(index).BYTE_COUNT, false); if (scsi_get_phase(index) != SCSI_PHASE_STATUS) FAILURE(IllegalState, "SCSI status phase expected"); scsi_xfer_ptr(index, scsi_expected_xfer(index)); scsi_xfer_done(index); if (scsi_get_phase(index) != SCSI_PHASE_FREE) FAILURE(IllegalState, "SCSI bus free phase expected"); SEL_STATUS(index).drq = true; SEL_STATUS(index).busy = false; SEL_COMMAND(index).packet_phase = PACKET_DI; } else { // send back via pio #ifdef DEBUG_IDE_PACKET printf("Sending ATAPI data back via PIO.\n"); #endif #if 0 if((!SEL_STATUS(index).drq) && (CONTROLLER(index).data_ptr == 0)) { // first time through: no data // transferred, and drq=0 SEL_STATUS(index).drq = true; SEL_STATUS(index).busy = false; SEL_REGISTERS(index).REASON = IR_IO; raise_interrupt(index); yield = true; } else { if(!SEL_STATUS(index).drq) { // all of the data has been read // from the buffer. // for now I assume that it is // everything. if(scsi_get_phase(index) != SCSI_PHASE_STATUS) FAILURE(IllegalState, "SCSI status phase expected"); scsi_xfer_ptr(index, scsi_expected_xfer(index)); scsi_xfer_done(index); if(scsi_get_phase(index) != SCSI_PHASE_FREE) FAILURE(IllegalState, "SCSI bus free phase expected"); #ifdef DEBUG_IDE_PACKET printf("Finished transferring!\n"); #endif SEL_COMMAND(index).packet_phase = PACKET_DI; yield = false; } } #else // do the transfer SEL_STATUS(index).drq = true; SEL_STATUS(index).busy = false; SEL_REGISTERS(index).REASON = IR_IO; if (scsi_get_phase(index) != SCSI_PHASE_STATUS) FAILURE(IllegalState, "SCSI status phase expected"); scsi_xfer_ptr(index, scsi_expected_xfer(index)); scsi_xfer_done(index); if (scsi_get_phase(index) != SCSI_PHASE_FREE) FAILURE(IllegalState, "SCSI Bus free phase expected"); #ifdef DEBUG_IDE_PACKET printf("Finished Transferring\n"); #endif raise_interrupt(index); SEL_COMMAND(index).packet_phase = PACKET_DI; yield = true; #endif } break; case PACKET_DI: // this is either DI0 or DI1 SEL_REGISTERS(index).REASON = IR_CD | IR_IO; SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).SERV = false; SEL_STATUS(index).CHK = false; SEL_STATUS(index).drq = false; raise_interrupt(index); SEL_COMMAND(index).command_in_progress = false; yield = true; break; default: FAILURE(InvalidArgument, "Unknown packet phase"); } } while (!yield); #ifdef DEBUG_IDE_PACKET printf("Drop out of packet state machine %d\n", index); ide_status(index); #endif } } break; case 0xa1: // identify packet device if (SEL_DISK(index)->cdrom()) { identify_drive(index, true); SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); } else { command_aborted(index, SEL_COMMAND(index).current_command); } break; case 0xc4: // read multiple if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { if (SEL_COMMAND(index).command_cycle == 0) { // fixup the 0=256 case. if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; SEL_STATUS(index).drq = false; } if (!SEL_STATUS(index).drq) { // buffer is empty, so lets fill it. if (!SEL_REGISTERS(index).lba_mode) { FAILURE(NotImplemented, "Non-LBA disk read"); } else { u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; if (SEL_REGISTERS(index).sector_count >= SEL_PER_DRIVE(index).multiple_size) { // easy, its a full block CONTROLLER(index).data_size = 256 * SEL_PER_DRIVE(index).multiple_size; SEL_REGISTERS(index).sector_count -= SEL_PER_DRIVE(index).multiple_size; } else { // partial block. CONTROLLER(index).data_size = 256 * SEL_REGISTERS(index).sector_count; SEL_REGISTERS(index).sector_count = 0; } #ifdef DEBUG_IDE_MULTIPLE printf("IDE %d.%d: Reading %d sectors, %d sectors left.\n", index, CONTROLLER(index).selected, CONTROLLER(index).data_size / 256, SEL_REGISTERS(index).sector_count); #endif SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->read_blocks( &(CONTROLLER(index).data[0]), CONTROLLER(index).data_size / 256); // actual number of blocks we want. #if defined(ES40_BIG_ENDIAN) for (int i = 0; i < 256; i++) CONTROLLER(index).data[i] = endian_16(CONTROLLER(index).data[i]); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; CONTROLLER(index).data_ptr = 0; // prepare for next sector if (SEL_REGISTERS(index).sector_count == 0) { SEL_COMMAND(index).command_in_progress = false; if (SEL_DISK(index)->cdrom()) set_signature(index, CONTROLLER(index).selected); // per 9.1 } else { // set the next block to read. // increment the lba. SEL_REGISTERS(index).sector_no += CONTROLLER(index).data_size / 256; // # sectors read. if (SEL_REGISTERS(index).sector_no > 255) { SEL_REGISTERS(index).sector_no = 0; SEL_REGISTERS(index).cylinder_no++; if (SEL_REGISTERS(index).cylinder_no > 65535) { SEL_REGISTERS(index).cylinder_no = 0; SEL_REGISTERS(index).head_no++; } } } } raise_interrupt(index); } } break; case 0xc5: // write multiple if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { if (SEL_COMMAND(index).command_cycle == 0) { // this is our first time through if (SEL_DISK(index)->ro()) { printf("%%IDE-W-RO: Write attempt to read-only disk %d.%d.\n", index, CONTROLLER(index).selected); command_aborted(index, SEL_COMMAND(index).current_command); } else { SEL_STATUS(index).drq = true; SEL_STATUS(index).busy = false; if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; if (SEL_REGISTERS(index).sector_count >= SEL_PER_DRIVE(index).multiple_size) { CONTROLLER(index).data_size = 256 * SEL_PER_DRIVE(index).multiple_size; SEL_REGISTERS(index).sector_count -= SEL_PER_DRIVE(index).multiple_size; } else { CONTROLLER(index).data_size = 256 * SEL_REGISTERS(index).sector_count; SEL_REGISTERS(index).sector_count = 0; } } } else { // now we should be getting data. if (!SEL_STATUS(index).drq) { // the buffer is full. Do something with the data. if (!SEL_REGISTERS(index).lba_mode) { FAILURE(NotImplemented, "Non-LBA disk write"); } else { u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; #if defined(ES40_BIG_ENDIAN) { u16 data[IDE_BUFFER_SIZE]; SEL_DISK(index)->seek_block(lba); for (int i = 0; i < CONTROLLER(index).data_size; i++) data[i] = endian_16(CONTROLLER(index).data[i]); SEL_DISK(index)->write_blocks( &(data[0]), CONTROLLER(index).data_size / 256); } #else SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->write_blocks(&(CONTROLLER(index).data[0]), CONTROLLER(index).data_size / 256); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; CONTROLLER(index).data_ptr = 0; if (SEL_REGISTERS(index).sector_count == 0) { // we're done SEL_STATUS(index).drq = false; SEL_COMMAND(index).command_in_progress = false; } else { // prepare for next block if (SEL_REGISTERS(index).sector_count >= SEL_PER_DRIVE(index).multiple_size) { CONTROLLER(index).data_size = 256 * SEL_PER_DRIVE(index).multiple_size; SEL_REGISTERS(index).sector_count -= SEL_PER_DRIVE(index).multiple_size; } else { CONTROLLER(index).data_size = 256 * SEL_REGISTERS(index).sector_count; SEL_REGISTERS(index).sector_count = 0; } // set the next block to read. // increment the lba. SEL_REGISTERS(index).sector_no++; if (SEL_REGISTERS(index).sector_no > 255) { SEL_REGISTERS(index).sector_no = 0; SEL_REGISTERS(index).cylinder_no++; if (SEL_REGISTERS(index).cylinder_no > 65535) { SEL_REGISTERS(index).cylinder_no = 0; SEL_REGISTERS(index).head_no++; } } } } raise_interrupt(index); } } } break; case 0xc6: // set multiple mode if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); } else { SEL_PER_DRIVE(index).multiple_size = SEL_REGISTERS(index).sector_count; #ifdef DEBUG_IDE_MULTIPLE printf("Set multiple mode: sector_count = %d\n", SEL_REGISTERS(index).sector_count); #endif SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); } break; case 0xc8: // read dma case 0xc9: // read dma (old) if (SEL_DISK(index)->cdrom()) { command_aborted(index, SEL_COMMAND(index).current_command); SEL_COMMAND(index).command_in_progress = false; } else { if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; #ifdef DEBUG_IDE_DMA printf("%%IDE-I-DMA: Read %d sectors = %d bytes.\n", SEL_REGISTERS(index).sector_count, SEL_REGISTERS(index).sector_count * 512); #endif u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->read_blocks(&(CONTROLLER(index).data[0]), SEL_REGISTERS(index).sector_count); u8 *ptr = (u8 *)(&CONTROLLER(index).data[0]); do_dma_transfer(index, ptr, SEL_REGISTERS(index).sector_count * 512, false); SEL_COMMAND(index).command_in_progress = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_STATUS(index).busy = false; } break; case 0xca: // write dma case 0xcb: // write dma (old) if (SEL_DISK(index)->cdrom() || SEL_DISK(index)->ro()) { command_aborted(index, SEL_COMMAND(index).current_command); SEL_COMMAND(index).command_in_progress = false; } else { if (SEL_DISK(index)->ro()) { printf("%%IDE-W-RO: DMA Write attempt to read-only disk %d.%d.\n", index, CONTROLLER(index).selected); command_aborted(index, SEL_COMMAND(index).current_command); } else { if (SEL_REGISTERS(index).sector_count == 0) SEL_REGISTERS(index).sector_count = 256; #ifdef DEBUG_IDE_DMA printf("%%IDE-I-DMA: Write %d sectors = %d bytes.\n", SEL_REGISTERS(index).sector_count, SEL_REGISTERS(index).sector_count * 512); #endif u8 *ptr = (u8 *)(&CONTROLLER(index).data[0]); do_dma_transfer(index, ptr, SEL_REGISTERS(index).sector_count * 512, true); u32 lba = (SEL_REGISTERS(index).head_no << 24) | (SEL_REGISTERS(index).cylinder_no << 8) | SEL_REGISTERS(index).sector_no; SEL_DISK(index)->seek_block(lba); SEL_DISK(index)->write_blocks(&(CONTROLLER(index).data[0]), SEL_REGISTERS(index).sector_count); SEL_COMMAND(index).command_in_progress = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_STATUS(index).busy = false; } } break; #if 0 case 0xe5: // check power mode ide_status(index); command_aborted(index, SEL_COMMAND(index).current_command); SEL_COMMAND(index).command_in_progress = false; // raise_interrupt(index); break; #endif case 0xec: // identify if (!SEL_DISK(index)->cdrom()) { identify_drive(index, false); SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = true; SEL_STATUS(index).err = false; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); } else { set_signature(index, CONTROLLER(index).selected); // per // // 9.1 command_aborted(index, 0xec); } break; case 0xef: // set features SEL_COMMAND(index).command_in_progress = false; switch (SEL_REGISTERS(index).features) { case 0x03: // set transfer mode if (SEL_REGISTERS(index).sector_count < 16) { // allow all PIO modes. SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; raise_interrupt(index); break; } else { // a DMA mode. switch (SEL_REGISTERS(index).sector_count) { case 0x20: case 0x21: case 0x22: // multiword dma CONTROLLER(index).dma_mode = SEL_REGISTERS(index).sector_count & 0x03; SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).seek_complete = true; SEL_STATUS(index).fault = false; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; raise_interrupt(index); break; case 0x40: case 0x41: case 0x42: // ultra dma command_aborted(index, SEL_COMMAND(index).current_command); break; } break; } default: printf("%%IDE-I-FEAT: Unhandled set feature subcommand %x\n", SEL_REGISTERS(index).features); command_aborted(index, SEL_COMMAND(index).current_command); break; } break; /*** * Special cases: commands we don't support, but return success. ***/ case 0xe0: // standby now case 0xe1: // idle immediate case 0xe2: // standby case 0xe3: // idle case 0xe6: // sleep case 0xe7: // flush cache case 0xea: // flush cache ext SEL_STATUS(index).busy = false; SEL_STATUS(index).drive_ready = true; SEL_STATUS(index).drq = false; SEL_STATUS(index).err = false; SEL_COMMAND(index).command_in_progress = false; raise_interrupt(index); break; default: // unknown/unhandled ATA command ide_status(index); FAILURE_1(NotImplemented, "Unknown IDE command %x", SEL_COMMAND(index).current_command); break; } #ifdef DEBUG_IDE_COMMAND if (SEL_COMMAND(index).command_in_progress == false) { printf("%%IDE-I-COMMAND: Command has completed on controller %d.\n", index); ide_status(index); printf("==================================================\n"); } else { printf("%%IDE-I-COMMAND: controller %d is yielding to host.\n", index); ide_status(index); printf("--------------------------------------------------\n"); } #endif } SEL_COMMAND(index).command_cycle++; } int CAliM1543C_ide::do_dma_transfer(int index, u8 *buffer, u32 buffersize, bool direction) { u8 xfer; size_t xfersize = 0; u8 status = 0; u8 count = 0; u32 prd; semBusMaster[index]->wait(); // wait until the start bit is set. { SCOPED_READ_LOCK(mtBusMaster[index]); prd = endian_32(*(u32 *)(&CONTROLLER(index).busmaster[4])); } do { u32 base; do_pci_read(prd, &base, 4, 1); u16 size_16; do_pci_read(prd + 4, &size_16, 2, 1); size_t size = size_16 ? size_16 : 65536; do_pci_read(prd + 7, &xfer, 1, 1); #ifdef DEBUG_IDE_DMA printf("-IDE-I-DMA: Transfer %d bytes to/from %lx (%x)\n", size, base, xfer); #endif if (xfersize + size > buffersize) { // only copy as much data as we have from the disk. size = buffersize - xfersize; status = 2; #ifdef DEBUG_IDE_DMA printf("-IDE-I-DMA: Actual transfer size: %d bytes\n", size); #endif } // copy it to/from ram. if (!direction) { do_pci_write(base, buffer, 1, size); buffer += size; } else { do_pci_read(base, buffer, 1, size); buffer += size; } xfersize += size; prd += 8; // go to next entry. if (xfer == 0x80 && xfersize < buffersize) { // we still have disk data left over! status = 1; } if (count++ > 32) { FAILURE(InvalidArgument, "Too many PRD nodes?"); } if (buffersize == xfersize && xfer != 0x80) { // we're done, but there's more prd nodes. status = 2; } } while (xfer != 0x80 && status == 0); switch (status) { case 0: // normal completion. { SCOPED_WRITE_LOCK(mtBusMaster[index]); CONTROLLER(index).busmaster[2] &= 0xfe; // clear active. } raise_interrupt(index); break; case 1: // PRD is smaller than the data we have. { SCOPED_WRITE_LOCK(mtBusMaster[index]); CONTROLLER(index).busmaster[2] &= 0xfe; // clear active. } // do not raise an interrupt break; case 2: // PRD is larger than the data we have. // leave active set. raise_interrupt(index); break; } semBusMasterReady[index]->set(); return status; } /** * Thread entry point. **/ void CAliM1543C_ide::run(int index) { try { for (;;) { semController[index]->wait(); if (StopThread) return; { #ifdef DEBUG_IDE_THREADS printf("Thread %d: \n", index); ide_status(index); #endif if (SEL_COMMAND(index).command_in_progress) execute(index); UPDATE_ALT_STATUS(index); #ifdef IDE_YIELD_INTERRUPTS if (CONTROLLER(index).interrupt_pending) { { SCOPED_WRITE_LOCK(mtBusMaster[index]); CONTROLLER(index).busmaster[2] |= 0x04; } theAli->pic_interrupt(1, 6 + index); } #endif } semControllerReady[index]->set(); } } catch (CException &e) { printf("Exception in IDE thread: %s.\n", e.displayText().c_str()); thrControllerDead[index].store(true); // Let the thread die... } } ================================================ FILE: src/AliM1543C_ide.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_ALIM1543C_IDE_H_) #define INCLUDED_ALIM1543C_IDE_H_ // If DEBUG_IDE is defined, define all IDE debugging flags. #ifdef DEBUG_IDE #define DEBUG_IDE_BUSMASTER #define DEBUG_IDE_COMMAND #define DEBUG_IDE_DMA #define DEBUG_IDE_INTERRUPT #define DEBUG_IDE_REG_COMMAND #define DEBUG_IDE_REG_CONTROL #define DEBUG_IDE_PACKET #define DEBUG_IDE_MULTIPLE #endif #include "Configurator.hpp" #include "DiskController.hpp" #include "PCIDevice.hpp" #include "SCSIBus.hpp" #include "SCSIDevice.hpp" #define MAX_MULTIPLE_SECTORS 128 /** * \brief Emulated IDE part of ALi M1543C multi-function device. * * Documentation consulted: * - Ali M1543C B1 South Bridge Version 1.20 *(http://mds.gotdns.com/sensors/docs/ali/1543dScb1-120.pdf) * - AT Attachment with Packet Interface - 5 (ATA/ATAPI-5) *(http://www.t13.org/Documents/UploadedDocuments/project/d1321r3-ATA-ATAPI-5.pdf) * - Programming Interface for Bus Master IDE COntroller *(http://suif.stanford.edu/%7Ecsapuntz/specs/idems100.ps) * - T13-1153Dr18 ATA/ATAPI-4 * - Mt. Fuji Commands for Multimedia Devices Version 7 INF-8090i v7 * . **/ class CAliM1543C_ide : public CPCIDevice, public CDiskController, public CSCSIDevice { public: CAliM1543C_ide(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CAliM1543C_ide(); virtual void register_disk(class CDisk *dsk, int bus, int dev); virtual void WriteMem_Legacy(int index, u32 address, int dsize, u32 data); virtual u32 ReadMem_Legacy(int index, u32 address, int dsize); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); virtual void ResetPCI(); void run(int index); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: // IDE controller u32 ide_command_read(int channel, u32 address, int dsize); void ide_command_write(int channel, u32 address, int dsize, u32 data); u32 ide_control_read(int channel, u32 address); void ide_control_write(int channel, u32 address, u32 data); u32 ide_busmaster_read(int channel, u32 address, int dsize); void ide_busmaster_write(int channel, u32 address, u32 data, int dsize); int do_dma_transfer(int index, u8 *buffer, u32 size, bool direction); void raise_interrupt(int channel); void set_signature(int channel, int id); u8 get_status(int index); void command_aborted(int index, u8 command); void identify_drive(int index, bool packet); void ide_status(int index); void execute(int index); std::unique_ptr thrController[2]; std::atomic_bool thrControllerDead[2] = {{false}, {false}}; CSemaphore *semController[2]; // controller start/stop CSemaphore *semControllerReady[2]; // controller ready CSemaphore *semBusMaster[2]; // bus master start/stop CSemaphore *semBusMasterReady[2]; // bus master ready CRWLock *mtRegisters[2]; // main registers CRWLock *mtBusMaster[2]; // busmaster registers bool StopThread; bool usedma; // The state structure contains all elements that need to be saved to the // statefile. struct SAliM1543C_ideState { struct SDriveState { struct { bool busy; bool drive_ready; bool fault; bool seek_complete; bool drq; bool bit_2; bool index_pulse; bool err; int index_pulse_count; // debugging u8 debug_last_status; bool debug_status_update; u8 alt_status; // this is the latched status. } status; struct { bool lba_mode; int features; int error; int sector_count; int sector_no; int cylinder_no; int head_no; int command; } registers; struct { bool command_in_progress; int current_command; int command_cycle; bool packet_dma; int packet_phase; u8 packet_command[12]; int packet_buffersize; u8 packet_sense; u8 packet_asc; u8 packet_ascq; } command; u8 multiple_size; }; struct SControllerState { // the attached devices struct SDriveState drive[2]; // control data. bool disable_irq; bool reset; // internal state bool reset_in_progress; int selected; // dma stuff u8 busmaster[8]; u8 dma_mode; u8 bm_status; // pio stuff #define IDE_BUFFER_SIZE 65536 // 64K words = 128K = 256 sectors @ 512 bytes u16 data[IDE_BUFFER_SIZE]; int data_ptr; int data_size; bool interrupt_pending; } controller[2]; } state; }; /// Status for selected drive on controller a #define SEL_STATUS(a) \ state.controller[a].drive[state.controller[a].selected].status /// Command for selected drive on controller a #define SEL_COMMAND(a) \ state.controller[a].drive[state.controller[a].selected].command /// Registers for selected drive on controller a #define SEL_REGISTERS(a) \ state.controller[a].drive[state.controller[a].selected].registers /// Selected drive on controller a #define SEL_DISK(a) get_disk(a, state.controller[a].selected) /// Per-drive data for selected drive on controller a #define SEL_PER_DRIVE(a) state.controller[a].drive[state.controller[a].selected] // Status for drive b on controller a #define STATUS(a, b) state.controller[a].drive[b].status // Command for drive b on controller a #define COMMAND(a, b) state.controller[a].drive[b].command // Registers for drive b on controller a #define REGISTERS(a, b) state.controller[a].drive[b].registers // Per-drive data for drive b on controller a #define PER_DRIVE(a, b) state.controller[a].drive[b] // Data for controller a #define CONTROLLER(a) state.controller[a] // Update alt-status for controller a with locking #define UPDATE_ALT_STATUS(a) \ { \ SCOPED_WRITE_LOCK(mtRegisters[a]); \ SEL_STATUS(a).alt_status = get_status(a); \ } /* memory region ids */ #define PRI_COMMAND 1 #define PRI_CONTROL 2 #define SEC_COMMAND 3 #define SEC_CONTROL 4 #define PRI_BUSMASTER 5 #define SEC_BUSMASTER 6 /* bar IDs */ #define BAR_PRI_COMMAND 0 #define BAR_PRI_CONTROL 1 #define BAR_SEC_COMMAND 2 #define BAR_SEC_CONTROL 3 #define BAR_BUSMASTER 4 /* device registers */ #define REG_COMMAND_DATA 0 #define REG_COMMAND_ERROR 1 #define REG_COMMAND_FEATURES 1 #define REG_COMMAND_SECTOR_COUNT 2 #define REG_COMMAND_SECTOR_NO 3 #define REG_COMMAND_CYL_LOW 4 #define REG_COMMAND_CYL_HI 5 #define REG_COMMAND_DRIVE 6 #define REG_COMMAND_STATUS 7 #define REG_COMMAND_COMMAND 7 static const char *register_names[] = { "DATA", "ERROR/FEATURES", "SECTOR_COUNT/PKT REASON", "SECTOR_NO", "CYL_LOW/PKT BYTE LOW", "CYL_HI/PKT BYTE HI", "DRIVE", "STATUS/COMMAND", }; /* misc constants */ /* Packet Protocol Aliases */ #define DMRD fault #define SERV seek_complete #define CHK err #define BYTE_COUNT cylinder_no #define REASON sector_count #define IR_CD 0x01 #define IR_IO 0x02 #define IR_REL 0x04 /* Packet protocol states */ static const char *packet_states[] = { "DP0: Prepare A", "DP1: Receive Packet", "DP2: Prepare B", "DP3/4: Ready INITRQ/Transfer Data", "DIx: Device Interrupt ", }; #define PACKET_NONE 0 #define PACKET_DP0 0 #define PACKET_DP1 1 #define PACKET_DP2 2 #define PACKET_DP34 3 #define PACKET_DI 4 /* SCSI SENSE Constants */ #define SENSE_NONE 0x00 #define SENSE_RECOVERED_ERROR 0x01 #define SENSE_NOT_READY 0x02 #define SENSE_MEDIUM_ERROR 0x03 #define SENSE_HARDWARE_ERROR 0x04 #define SENSE_ILLEGAL_REQUEST 0x05 #define SENSE_UNIT_ATTENTION 0x06 #define SENSE_DATA_PROTECT 0x07 #define SENSE_BLANK_CHECK 0x08 #define SENSE_ABORT_COMMAND 0x0b #define SENSE_MISCOMPARE 0x0e extern CAliM1543C_ide *theIDE; #endif ================================================ FILE: src/AliM1543C_usb.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "AliM1543C_usb.hpp" #include "StdAfx.hpp" #include "System.hpp" u32 usb_cfg_data[64] = { /*00*/ 0x523710b9, // CFID: vendor + device /*04*/ 0x02800000, // CFCS: command + status /*08*/ 0x0c031003, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000000, // BAR0: /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x500001ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; u32 usb_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x00000157, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xfffff000, // BAR0 /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration /*40*/ 0x04100000, // TM - test mode register 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Constructor. **/ CAliM1543C_usb::CAliM1543C_usb(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev) { add_function(0, usb_cfg_data, usb_cfg_mask); ResetPCI(); state.usb_data[0x34 / 4] = 0x2edf; state.usb_data[0x48 / 4] = 0x01000003; printf( "%s: $Id: AliM1543C_usb.cpp,v 1.6 2008/03/14 15:30:50 iamcamiel Exp $\n", devid_string); } CAliM1543C_usb::~CAliM1543C_usb() {} u32 CAliM1543C_usb::ReadMem_Bar(int func, int bar, u32 address, int dsize) { u32 data = 0; switch (bar) { case 0: data = usb_hci_read(address, dsize); break; default: printf("%%USB-W-READBAR: Bad BAR %d selected.\n", bar); } return data; } void CAliM1543C_usb::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { switch (bar) { case 0: usb_hci_write(address, dsize, data); break; default: printf("%%USB-W-WRITEBAR: Bad BAR %d selected.\n", bar); } return; } u64 CAliM1543C_usb::usb_hci_read(u64 address, int dsize) { u64 data = 0; if (dsize != 32) printf("%%USB-W-HCIREAD: Non dword read, returning 32 bits anyway.\n"); switch (address) { case 0: // HcRevision data = 0x00000110; break; case 4: // HcControl case 8: // HcCommandStatus case 0x0c: // HcInterruptStatus case 0x10: // HcInterrupt Enable case 0x14: // HcInterruptDisable case 0x18: // HcHCCA (datasheet says 0x17, but that's wrong) case 0x1c: // HcPeriodCurrentED case 0x20: // HcControlHeadED case 0x24: // HcControlCurrentED case 0x28: // HcBulkHeadED case 0x2c: // HcBulkCurrentED case 0x30: // HcDoneHead case 0x34: // HcFmInterval case 0x38: // HcFrameRemaining case 0x3c: // HcFmNumber case 0x40: // HcPeriodicStart case 0x44: // HcLSThreshold case 0x48: // HcRhDescriptorA case 0x4c: // HcRhDescriptorB case 0x50: // HcRhStatus case 0x54: // HcRhPortStatus1 case 0x58: // HcRhPortStatus1 case 0x5c: // HcRhPortStatus1 case 0x100: // HceControlRegister case 0x104: // HceInputRegister case 0x108: // HceOutputRegister case 0x10c: // HceStatusRegister data = state.usb_data[address / 4]; break; default: printf("%%USB-W-HCIREAD: Reading from unknown address %x. Ignoring.\n", (int)address); } return data; } void CAliM1543C_usb::usb_hci_write(u64 address, int dsize, u64 data) { if (dsize != 32) printf("%%USB-W-HCIWRITE: Non dword write, writing 32 bits anyway.\n"); switch (address) { case 4: // HcControl case 8: // HcCommandStatus case 0x0c: // HcInterruptStatus case 0x10: // HcInterrupt Enable case 0x14: // HcInterruptDisable case 0x18: // HcHCCA (datasheet says 0x17, but that's wrong) case 0x1c: // HcPeriodCurrentED case 0x20: // HcControlHeadED case 0x24: // HcControlCurrentED case 0x28: // HcBulkHeadED case 0x2c: // HcBulkCurrentED case 0x30: // HcDoneHead case 0x34: // HcFmInterval case 0x38: // HcFrameRemaining case 0x3c: // HcFmNumber case 0x40: // HcPeriodicStart case 0x44: // HcLSThreshold case 0x48: // HcRhDescriptorA case 0x4c: // HcRhDescriptorB case 0x50: // HcRhStatus case 0x54: // HcRhPortStatus1 case 0x58: // HcRhPortStatus1 case 0x5c: // HcRhPortStatus1 case 0x100: // HceControlRegister case 0x104: // HceInputRegister case 0x108: // HceOutputRegister case 0x10c: // HceStatusRegister state.usb_data[address / 4] = data; break; default: printf("%%USB-W-HCIWRITE: Writing to unknown address %x. Ignoring.\n", (int)address); } } static u32 usb_magic1 = 0x9000432B; static u32 usb_magic2 = 0xB2340009; /** * Save state to a Virtual Machine State file. **/ int CAliM1543C_usb::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&usb_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&usb_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CAliM1543C_usb::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != usb_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != usb_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } ================================================ FILE: src/AliM1543C_usb.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_ALIM1543C_USB_H_) #define INCLUDED_ALIM1543C_USB_H_ #include "PCIDevice.hpp" /** * \brief Emulated USB part of ALi M1543C multi-function device. * * \todo This device is just a stub. Not functional yet. * * Documentation consulted: * - Ali M1543C B1 South Bridge Version 1.20 * (http://mds.gotdns.com/sensors/docs/ali/1543dScb1-120.pdf) * . **/ class CAliM1543C_usb : public CPCIDevice { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); CAliM1543C_usb(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CAliM1543C_usb(); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); private: u64 usb_hci_read(u64 address, int dsize); void usb_hci_write(u64 address, int dsize, u64 data); /// The state structure contains all elements that need to be saved to the /// statefile. struct SUSB_state { u32 usb_data[0x110 / 4]; } state; }; #endif // !defined(INCLUDED_ALIM1543C_USB_H) ================================================ FILE: src/AlphaCPU.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2012 Dmitry Kalinkin * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "AlphaCPU.hpp" #include "StdAfx.hpp" #include "TraceEngine.hpp" #include "cpu_arith.hpp" #include "cpu_bwx.hpp" #include "cpu_control.hpp" #include "cpu_debug.hpp" #include "cpu_fp_branch.hpp" #include "cpu_fp_memory.hpp" #include "cpu_fp_operate.hpp" #include "cpu_logical.hpp" #include "cpu_memory.hpp" #include "cpu_misc.hpp" #include "cpu_mvi.hpp" #include "cpu_pal.hpp" #include "cpu_vax.hpp" #include "lockstep.hpp" #if !defined(HAVE_NEW_FP) #include "es40_float.hpp" #endif void CAlphaCPU::release_threads() { mySemaphore.set(); } void CAlphaCPU::run() { try { mySemaphore.wait(); while (state.wait_for_start) { if (StopThread) return; std::this_thread::sleep_for(std::chrono::milliseconds(1)); } printf("*** CPU%d *** STARTING ***\n", get_cpuid()); for (;;) { if (StopThread) return; for (int i = 0; i < 1000000; i++) execute(); } } catch (CException &e) { printf("Exception in CPU thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** * Constructor. **/ CAlphaCPU::CAlphaCPU(CConfigurator *cfg, CSystem *system) : CSystemComponent(cfg, system), mySemaphore(0, 1) {} /** * Initialize the CPU. **/ void CAlphaCPU::init() { memset(&state, 0, sizeof(state)); cpu_hz = myCfg->get_num_value("speed", true, 500000000); state.iProcNum = cSystem->RegisterCPU(this); state.wait_for_start = (state.iProcNum == 0) ? false : true; icache_enabled = true; flush_icache(); icache_enabled = myCfg->get_bool_value("icache", false); skip_memtest_hack = myCfg->get_bool_value("skip_memtest_hack", false); skip_memtest_counter = 0; tbia(ACCESS_READ); tbia(ACCESS_EXEC); // state.fpcr = U64(0x8ff0000000000000); state.fpen = true; state.i_ctl_other = U64(0x502086); state.smc = 1; // SROM imitation... add_tb(0, 0, U64(0xff61), ACCESS_READ); #if defined(IDB) bListing = false; #endif cc_large = 0; prev_cc = 0; start_cc = 0; prev_time = 0; prev_icount = 0; start_icount = 0; #if defined(CONSTANT_TIME_FACTOR) cc_per_instruction = CONSTANT_TIME_FACTOR; #else cc_per_instruction = 70; #endif ins_per_timer_int = cpu_hz / 1024; next_timer_int = state.iProcNum ? U64(0xFFFFFFFFFFFFFFFF) : ins_per_timer_int; /* only on CPU 0 */ state.r[22] = state.r[22 + 32] = state.iProcNum; printf( "%s(%d): $Id: AlphaCPU.cpp,v 1.82 2009/03/16 01:33:27 iamcamiel Exp $\n", devid_string, state.iProcNum); } void CAlphaCPU::start_threads() { char buffer[5]; mySemaphore.tryWait(1); if (!myThread) { sprintf(buffer, "cpu%d", state.iProcNum); myThread = std::make_unique([this](){ this->run(); }); printf(" %s", buffer); StopThread = false; } } void CAlphaCPU::stop_threads() { char buffer[5]; StopThread = true; if (myThread) { sprintf(buffer, "cpu%d", state.iProcNum); mySemaphore.set(); printf(" %s", buffer); myThread->join(); myThread = nullptr; } mySemaphore.tryWait(1); } /** * Destructor. **/ CAlphaCPU::~CAlphaCPU() { stop_threads(); } #if defined(IDB) char dbg_string[1000]; #if !defined(LS_MASTER) && !defined(LS_SLAVE) char *dbg_strptr; #endif /** * \brief Do whatever needs to be done to a debug-string. * * Used in IDB-mode to handle the disassembly- string. In es40_idb, it is * written to the standard output. * * \param s Pointer to the debug string. **/ void handle_debug_string(char *s) { #if defined(LS_SLAVE) || defined(LS_MASTER) // lockstep_compare(s); *dbg_strptr++ = '\n'; *dbg_strptr = '\0'; #else if (*s) printf("%s\n", s); #endif } #endif #if defined(MIPS_ESTIMATE) // MIPS_INTERVAL must take longer than 1 second to execute // or estimate will generate a divide-by-zero error #define MIPS_INTERVAL 0xfffffff static time_t saved = 0; static u64 count; static double min_mips = 999999999999999.0; static double max_mips = 0.0; #endif /** * Check if threads are still running. * * Calibrate the CPU timing loop. **/ void CAlphaCPU::check_state() { if (myThreadDead.load()) FAILURE(Thread, "CPU thread has died"); #if !defined(CONSTANT_TIME_FACTOR) if (state.instruction_count > 0) { // correct CPU timing loop... u64 icount = state.instruction_count; u64 cc = cc_large; u64 time = start_time.elapsed(); s64 ce = cc_per_instruction; u64 cc_aim = time * cpu_hz / 1000000; // microsecond resolution u64 ce_aim = cc_aim / icount; s64 icount_lapse = icount - prev_icount; s64 cc_diff = cc_aim - cc; s64 ce_diff = (u64)((float)cc_diff / (float)icount_lapse); s64 ce_new = ce_aim + ce_diff; if (ce_new < 0) ce_new = 0; if (ce_new > 200) ce_new = 200; if (ce_new != ce) { // printf(" time %12" PRId64 " | prev // %12" PRId64 " \n",time,prev_time); printf(" count lapse %12" // LL "d | curr %12" PRId64 " | prev %12" PRId64 " // \n",icount_lapse,icount,prev_icount); printf("cc %12" PRId64 " | aim // %12" PRId64 " | diff %12" PRId64 " | prev %12" PRId64 " // \n",cc,cc_aim,cc_diff,prev_cc); printf("ce %12" PRId64 " | aim %12" LL // "d | diff %12" PRId64 " | new %12" PRId64 " // \n",ce,ce_aim,ce_diff,ce_new); // printf("========================================================================== // \n"); cc_per_instruction = ce_new; // printf("cpu %d speed factor: %d\n",get_cpuid(),ce_new); } prev_cc = cc; prev_icount = icount; prev_time = time; } #endif return; } /** * Skip SRM memtest. * * Hack that skips memory check in SRM. **/ inline void CAlphaCPU::skip_memtest() { const char *wrong_memskip = "warning: wrong memory check skip\n"; const char *counter_mismatch = "warning: memory check skip counter mismatch"; if (!(state.current_pc & U64(0x8b000)) || (skip_memtest_counter >= 5)) { return; } if (state.current_pc == U64(0x8bb90)) { if (state.r[5] != U64(0xaaaaaaaaaaaaaaaa)) { printf("%s", wrong_memskip); } else { if (skip_memtest_counter != 0) printf("%s", counter_mismatch); ++skip_memtest_counter; state.r[0] = state.r[4]; } } if (state.current_pc == U64(0x8bbe0)) { if (state.r[5] != U64(0xaaaaaaaaaaaaaaaa)) { printf("%s", wrong_memskip); } else { if (skip_memtest_counter != 1) printf("%s", counter_mismatch); ++skip_memtest_counter; state.r[16] = 0; } } if (state.current_pc == U64(0x8bc28)) { if (state.r[5] != U64(0xaaaaaaaaaaaaaaaa)) { printf("%s", wrong_memskip); } else { if (skip_memtest_counter != 2) printf("%s", counter_mismatch); ++skip_memtest_counter; state.r[8] = state.r[4]; } } if (state.current_pc == U64(0x8bc70)) { if (state.r[7] != U64(0x5555555555555555)) { printf("%s", wrong_memskip); } else { if (skip_memtest_counter != 3) printf("%s", counter_mismatch); ++skip_memtest_counter; state.r[0] = 0; } } if (state.current_pc == U64(0x8bcb0)) { if (state.r[7] != U64(0x5555555555555555)) { if (skip_memtest_counter != 4) printf("%s", counter_mismatch); printf("%s", wrong_memskip); } else { ++skip_memtest_counter; state.r[3] = state.r[4]; } } } /** * \brief Called each clock-cycle. * * This is where the actual CPU emulation takes place. Each clocktick, one *instruction is processed by the processor. The instruction pipeline is not *emulated, things are complicated enough as it is. The one exception is the *instruction cache, which is implemented, to accomodate self-modifying code. *The instruction cache can be disabled if self-modifying code is not expected. **/ void CAlphaCPU::execute() { u32 ins; int i; u64 phys_address; u64 temp_64; u64 temp_64_1; u64 temp_64_2; bool pbc; int opcode; int function; #if defined(MIPS_ESTIMATE) // Calculate simulated performance statistics if (++count >= MIPS_INTERVAL) { time_t current; time(¤t); if (saved > 0) { double secs = difftime(current, saved); double ips = MIPS_INTERVAL / secs; double mips = ips / 1000000.0; if (max_mips < mips) max_mips = mips; if (min_mips > mips) min_mips = mips; printf("ES40 MIPS (%3.1f sec):: current: %5.3f, min: %5.3f, max: %5.3f\n", secs, mips, min_mips, max_mips); } saved = current; count = 0; } #endif #if defined(IDB) char *funcname = 0; dbg_string[0] = '\0'; #if !defined(LS_MASTER) && !defined(LS_SLAVE) dbg_strptr = dbg_string; #endif #endif state.current_pc = state.pc; if (skip_memtest_hack) skip_memtest(); // Service interrupts if (DO_ACTION) { // We're actually executing code. Cycle counter should be updated, interrupt // and interrupt timer status needs to be checked, and the next instruction // should be fetched from the instruction cache. Increase the cycle counter // if it is currently enabled. state.instruction_count++; cc_large += cc_per_instruction; if (cc_large > next_timer_int) { next_timer_int += ins_per_timer_int; cSystem->interrupt(-1, true); } if (state.cc_ena) { state.cc += cc_per_instruction; } if (state.check_timers) { // There are one or more active delayed irq_h interrupts. Go through the 6 // irq_h timers, decrease them as needed, and set the interrupt if the // timer reaches 0. state.check_timers = false; for (int i = 0; i < 6; i++) { if (state.irq_h_timer[i]) { // This timer is active. Decrease it, and check if it reached 0. state.irq_h_timer[i]--; if (state.irq_h_timer[i]) { // The timer hasn't reached 0 yet; check on the timers again next // clock tick. state.check_timers = true; } else { // The timer has reached 0. Set the interrupt status, and set the // flag that we need to check the interrupt status state.eir |= (U64(0x1) << i); state.check_int = true; } } } } if (state.check_int && !(state.pc & 1)) { // One or more of the variables that affect interrupt status have changed, // and we are not currently inside PALmode. It is not certain that this // means we hava an interrupt to service, but we might have. This needs to // be checked. /* if (state.pal_vms) { // PALcode base is set to 0x8000; meaning OpenVMS PALcode is currently active. In this // case, our VMS PALcode replacement routines are valid, and should be used as it is // faster than using the original PALcode. if (state.eir & state.eien & 6) if (vmspal_ent_ext_int(state.eir&state.eien & 6)) return; if (state.sir & state.sien & 0xfffc) if (vmspal_ent_sw_int(state.sir&state.sien)) return; if (state.asten && (state.aster & state.astrr & ((1<<(state.cm+1))-1) )) if (vmspal_ent_ast_int(state.aster & state.astrr & ((1<<(state.cm+1))-1) )) return; if (state.sir & state.sien) if (vmspal_ent_sw_int(state.sir&state.sien)) return; } else */ { // PALcode base is set to an unsupported value. We have no choice but to // transfer control to PALmode at the PALcode interrupt entry point. // if (state.eir & 8) // { // printf("%s: IP interrupt received%s...\n",devid_string, // (state.eien&8)?"(enabled)":"(masked)"); // } if ((state.eien & state.eir) || (state.sien & state.sir) || (state.asten && (state.aster & state.astrr & ((1 << (state.cm + 1)) - 1)))) { GO_PAL(INTERRUPT); return; } } // This point is reached only if there are no more active interrupts. We // can safely set check_int to false now to save time on the next CPU // clock ticks. state.check_int = false; } // If profiling is enabled, increase the profiling counter for the current // block of addresses. #if defined(PROFILE) PROFILE_DO(state.pc); #endif // Get the next instruction from the instruction cache. if (get_icache(state.pc, &ins)) return; #if defined(IDB) current_pc_physical = state.pc_phys; #endif } // if (DO_ACTION) else { // We're not really executing any code (DO_ACTION is false); that means that // we're in a debugging session, and just listing instructions at a // particular address. In this case, we treat the program counter as a // physical address. ins = (u32)(cSystem->ReadMem(state.pc, 32, this)); } // Increase the program counter. The current value is retained in // state.current_pc. next_pc(); // Clear "always zero" registers. The last instruction might have written // something to one of these registers. state.r[31] = 0; state.f[31] = 0; // Decode and dispatch opcode. This is kept very compact using the OP-macro // defined in cpu_debug.h. For the normal emulator, this simply calls the // DO_ macro defined in one of the other cpu_*.h files; but for the // interactive debugger, it will also do disassembly, where the second // parameter to the macro (e.g. R12_R3) determines the formatting applied to // the operands. The macro ends with "return 0;". #if defined(IDB) last_instruction = ins; #endif opcode = ins >> 26; switch (opcode) { case 0x00: // CALL_PAL function = ins & 0x1fffffff; OP(CALL_PAL, PAL); // switch (function) // { // case 0x123401: OP_FNC(vmspal_int_read_ide, NOP); // default: OP(CALL_PAL,PAL); // } case 0x08: OP(LDA, MEM); case 0x09: OP(LDAH, MEM); case 0x0a: OP(LDBU, MEM); case 0x0b: OP(LDQ_U, MEM); case 0x0c: OP(LDWU, MEM); case 0x0d: OP(STW, MEM); case 0x0e: OP(STB, MEM); case 0x0f: OP(STQ_U, MEM); case 0x10: // INTA* instructions function = (ins >> 5) & 0x7f; switch (function) { case 0x40: OP(ADDL_V, R12_R3); case 0x00: OP(ADDL, R12_R3); case 0x02: OP(S4ADDL, R12_R3); case 0x49: OP(SUBL_V, R12_R3); case 0x09: OP(SUBL, R12_R3); case 0x0b: OP(S4SUBL, R12_R3); case 0x0f: OP(CMPBGE, R12_R3); case 0x12: OP(S8ADDL, R12_R3); case 0x1b: OP(S8SUBL, R12_R3); case 0x1d: OP(CMPULT, R12_R3); case 0x60: OP(ADDQ_V, R12_R3); case 0x20: OP(ADDQ, R12_R3); case 0x22: OP(S4ADDQ, R12_R3); case 0x69: OP(SUBQ_V, R12_R3); case 0x29: OP(SUBQ, R12_R3); case 0x2b: OP(S4SUBQ, R12_R3); case 0x2d: OP(CMPEQ, R12_R3); case 0x32: OP(S8ADDQ, R12_R3); case 0x3b: OP(S8SUBQ, R12_R3); case 0x3d: OP(CMPULE, R12_R3); case 0x4d: OP(CMPLT, R12_R3); case 0x6d: OP(CMPLE, R12_R3); default: UNKNOWN2; } break; case 0x11: // INTL* instructions function = (ins >> 5) & 0x7f; switch (function) { case 0x00: OP(AND, R12_R3); case 0x08: OP(BIC, R12_R3); case 0x14: OP(CMOVLBS, R12_R3); case 0x16: OP(CMOVLBC, R12_R3); case 0x20: OP(BIS, R12_R3); case 0x24: OP(CMOVEQ, R12_R3); case 0x26: OP(CMOVNE, R12_R3); case 0x28: OP(ORNOT, R12_R3); case 0x40: OP(XOR, R12_R3); case 0x44: OP(CMOVLT, R12_R3); case 0x46: OP(CMOVGE, R12_R3); case 0x48: OP(EQV, R12_R3); case 0x61: OP(AMASK, R2_R3); case 0x64: OP(CMOVLE, R12_R3); case 0x66: OP(CMOVGT, R12_R3); case 0x6c: OP(IMPLVER, X_R3); default: UNKNOWN2; } break; case 0x12: // INTS* instructions function = (ins >> 5) & 0x7f; switch (function) { case 0x02: OP(MSKBL, R12_R3); case 0x06: OP(EXTBL, R12_R3); case 0x0b: OP(INSBL, R12_R3); case 0x12: OP(MSKWL, R12_R3); case 0x16: OP(EXTWL, R12_R3); case 0x1b: OP(INSWL, R12_R3); case 0x22: OP(MSKLL, R12_R3); case 0x26: OP(EXTLL, R12_R3); case 0x2b: OP(INSLL, R12_R3); case 0x30: OP(ZAP, R12_R3); case 0x31: OP(ZAPNOT, R12_R3); case 0x32: OP(MSKQL, R12_R3); case 0x34: OP(SRL, R12_R3); case 0x36: OP(EXTQL, R12_R3); case 0x39: OP(SLL, R12_R3); case 0x3b: OP(INSQL, R12_R3); case 0x3c: OP(SRA, R12_R3); case 0x52: OP(MSKWH, R12_R3); case 0x57: OP(INSWH, R12_R3); case 0x5a: OP(EXTWH, R12_R3); case 0x62: OP(MSKLH, R12_R3); case 0x67: OP(INSLH, R12_R3); case 0x6a: OP(EXTLH, R12_R3); case 0x72: OP(MSKQH, R12_R3); case 0x77: OP(INSQH, R12_R3); case 0x7a: OP(EXTQH, R12_R3); default: UNKNOWN2; } break; case 0x13: // INTM* instructions function = (ins >> 5) & 0x7f; switch (function) // ignore /V for now { case 0x40: OP(MULL_V, R12_R3); case 0x00: OP(MULL, R12_R3); case 0x60: OP(MULQ_V, R12_R3); case 0x20: OP(MULQ, R12_R3); case 0x30: OP(UMULH, R12_R3); default: UNKNOWN2; } break; case 0x14: // ITFP* instructions function = (ins >> 5) & 0x7ff; switch (function) { case 0x004: OP(ITOFS, R1_F3); case 0x00a: case 0x08a: case 0x10a: case 0x18a: case 0x40a: case 0x48a: case 0x50a: case 0x58a: OP(SQRTF, F2_F3); case 0x00b: case 0x04b: case 0x08b: case 0x0cb: case 0x10b: case 0x14b: case 0x18b: case 0x1cb: case 0x50b: case 0x54b: case 0x58b: case 0x5cb: case 0x70b: case 0x74b: case 0x78b: case 0x7cb: OP(SQRTS, F2_F3); case 0x014: OP(ITOFF, R1_F3); case 0x024: OP(ITOFT, R1_F3); case 0x02a: case 0x0aa: case 0x12a: case 0x1aa: case 0x42a: case 0x4aa: case 0x52a: case 0x5aa: OP(SQRTG, F2_F3); case 0x02b: case 0x06b: case 0x0ab: case 0x0eb: case 0x12b: case 0x16b: case 0x1ab: case 0x1eb: case 0x52b: case 0x56b: case 0x5ab: case 0x5eb: case 0x72b: case 0x76b: case 0x7ab: case 0x7eb: OP(SQRTT, F2_F3); default: UNKNOWN2; } break; case 0x15: // FLTV* instructions function = (ins >> 5) & 0x7ff; switch (function) { case 0x0a5: case 0x4a5: OP(CMPGEQ, F12_F3); case 0x0a6: case 0x4a6: OP(CMPGLT, F12_F3); case 0x0a7: case 0x4a7: OP(CMPGLE, F12_F3); case 0x03c: case 0x0bc: OP(CVTQF, F2_F3); case 0x03e: case 0x0be: OP(CVTQG, F2_F3); default: if (function & 0x200) { UNKNOWN2; } switch (function & 0x7f) { case 0x000: OP(ADDF, F12_F3); case 0x001: OP(SUBF, F12_F3); case 0x002: OP(MULF, F12_F3); case 0x003: OP(DIVF, F12_F3); case 0x01e: OP(CVTDG, F2_F3); case 0x020: OP(ADDG, F12_F3); case 0x021: OP(SUBG, F12_F3); case 0x022: OP(MULG, F12_F3); case 0x023: OP(DIVG, F12_F3); case 0x02c: OP(CVTGF, F12_F3); case 0x02d: OP(CVTGD, F2_F3); case 0x02f: OP(CVTGQ, F2_F3); default: UNKNOWN2; } break; } break; case 0x16: // FLTI* instructions function = (ins >> 5) & 0x7ff; switch (function) { case 0x0a4: case 0x5a4: OP(CMPTUN, F12_F3); case 0x0a5: case 0x5a5: OP(CMPTEQ, F12_F3); case 0x0a6: case 0x5a6: OP(CMPTLT, F12_F3); case 0x0a7: case 0x5a7: OP(CMPTLE, F12_F3); case 0x2ac: case 0x6ac: OP(CVTST, F2_F3); default: if (((function & 0x600) == 0x200) || ((function & 0x500) == 0x400)) { UNKNOWN2; } switch (function & 0x3f) { case 0x00: OP(ADDS, F12_F3); case 0x01: OP(SUBS, F12_F3); case 0x02: OP(MULS, F12_F3); case 0x03: OP(DIVS, F12_F3); case 0x20: OP(ADDT, F12_F3); case 0x21: OP(SUBT, F12_F3); case 0x22: OP(MULT, F12_F3); case 0x23: OP(DIVT, F12_F3); case 0x2c: OP(CVTTS, F2_F3); case 0x2f: OP(CVTTQ, F2_F3); case 0x3c: if ((function & 0x300) == 0x100) { UNKNOWN2; } OP(CVTQS, F2_F3); case 0x3e: if ((function & 0x300) == 0x100) { UNKNOWN2; } OP(CVTQT, F2_F3); default: UNKNOWN2; } break; } break; case 0x17: // FLTL* instructions function = (ins >> 5) & 0x7ff; switch (function) { case 0x010: OP(CVTLQ, F2_F3); case 0x020: OP(CPYS, F12_F3); case 0x021: OP(CPYSN, F12_F3); case 0x022: OP(CPYSE, F12_F3); case 0x024: OP(MT_FPCR, X_F1); case 0x025: OP(MF_FPCR, X_F1); case 0x02a: OP(FCMOVEQ, F12_F3); case 0x02b: OP(FCMOVNE, F12_F3); case 0x02c: OP(FCMOVLT, F12_F3); case 0x02d: OP(FCMOVGE, F12_F3); case 0x02e: OP(FCMOVLE, F12_F3); case 0x02f: OP(FCMOVGT, F12_F3); case 0x030: case 0x130: case 0x530: OP(CVTQL, F12_F3); default: UNKNOWN2; } break; case 0x18: // MISC* instructions function = (ins & 0xffff); switch (function) { case 0x0000: OP(TRAPB, NOP); case 0x0400: OP(EXCB, NOP); case 0x4000: OP(MB, NOP); case 0x4400: OP(WMB, NOP); case 0x8000: OP(FETCH, NOP); case 0xA000: OP(FETCH_M, NOP); case 0xC000: OP(RPCC, X_R1); case 0xE000: OP(RC, X_R1); case 0xE800: OP(ECB, NOP); case 0xF000: OP(RS, X_R1); case 0xF800: OP(WH64, NOP); case 0xFC00: OP(WH64EN, NOP); default: UNKNOWN2; } break; case 0x19: // HW_MFPR function = (ins >> 8) & 0xff; OP(HW_MFPR, MFPR); case 0x1a: // JSR* instructions OP(JMP, JMP); case 0x1b: // PAL reserved - HW_LD function = (ins >> 12) & 0xf; if (function & 1) { OP(HW_LDQ, HW_LD); } else { OP(HW_LDL, HW_LD); } case 0x1c: // FPTI* instructions function = (ins >> 5) & 0x7f; switch (function) { case 0x00: OP(SEXTB, R2_R3); case 0x01: OP(SEXTW, R2_R3); case 0x30: OP(CTPOP, R2_R3); case 0x31: OP(PERR, R2_R3); case 0x32: OP(CTLZ, R2_R3); case 0x33: OP(CTTZ, R2_R3); case 0x34: OP(UNPKBW, R2_R3); case 0x35: OP(UNPKBL, R2_R3); case 0x36: OP(PKWB, R2_R3); case 0x37: OP(PKLB, R2_R3); case 0x38: OP(MINSB8, R12_R3); case 0x39: OP(MINSW4, R12_R3); case 0x3a: OP(MINUB8, R12_R3); case 0x3b: OP(MINUW4, R12_R3); case 0x3c: OP(MAXUB8, R12_R3); case 0x3d: OP(MAXUW4, R12_R3); case 0x3e: OP(MAXSB8, R12_R3); case 0x3f: OP(MAXSW4, R12_R3); case 0x70: OP(FTOIT, F1_R3); case 0x78: OP(FTOIS, F1_R3); default: UNKNOWN2; } break; case 0x1d: // HW_MTPR function = (ins >> 8) & 0xff; OP(HW_MTPR, MTPR); case 0x1e: OP(HW_RET, RET); case 0x1f: // HW_ST function = (ins >> 12) & 0xf; if (function & 1) { OP(HW_STQ, HW_ST); } else { OP(HW_STL, HW_ST); } case 0x20: OP(LDF, FMEM); case 0x21: OP(LDG, FMEM); case 0x22: OP(LDS, FMEM); case 0x23: OP(LDT, FMEM); case 0x24: OP(STF, FMEM); case 0x25: OP(STG, FMEM); case 0x26: OP(STS, FMEM); case 0x27: OP(STT, FMEM); case 0x28: OP(LDL, MEM); case 0x29: OP(LDQ, MEM); case 0x2a: OP(LDL_L, MEM); case 0x2b: OP(LDQ_L, MEM); case 0x2c: OP(STL, MEM); case 0x2d: OP(STQ, MEM); case 0x2e: OP(STL_C, MEM); case 0x2f: OP(STQ_C, MEM); case 0x30: OP(BR, BR); case 0x31: OP(FBEQ, FCOND); case 0x32: OP(FBLT, FCOND); case 0x33: OP(FBLE, FCOND); case 0x34: OP(BSR, BSR); case 0x35: OP(FBNE, FCOND); case 0x36: OP(FBGE, FCOND); case 0x37: OP(FBGT, FCOND); case 0x38: OP(BLBC, COND); case 0x39: OP(BEQ, COND); case 0x3a: OP(BLT, COND); case 0x3b: OP(BLE, COND); case 0x3c: OP(BLBS, COND); case 0x3d: OP(BNE, COND); case 0x3e: OP(BGE, COND); case 0x3f: OP(BGT, COND); default: UNKNOWN1; } return; } #if defined(IDB) /** * \brief Produce disassembly-listing without marker * * \param from Address of first instruction to be disassembled. * \param to Address of instruction following the last instruction to * be disassembled. **/ void CAlphaCPU::listing(u64 from, u64 to) { listing(from, to, 0); } /** * \brief Produce disassembly-listing with marker * * \param from Address of first instruction to be disassembled. * \param to Address of instruction following the last instruction to * be disassembled. * \param mark Address of instruction to be underlined with a marker line. **/ void CAlphaCPU::listing(u64 from, u64 to, u64 mark) { printf("%%CPU-I-LISTNG: Listing from %016" PRIx64 " to %016" PRIx64 "\n", from, to); u64 iSavedPC; bool bSavedDebug; iSavedPC = state.pc; bSavedDebug = bDisassemble; bDisassemble = true; bListing = true; for (state.pc = from; state.pc <= to;) { execute(); if (state.pc == mark) printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } bListing = false; state.pc = iSavedPC; bDisassemble = bSavedDebug; } u64 CAlphaCPU::get_instruction_count() { return state.instruction_count; } #endif static u32 cpu_magic1 = 0x2126468C; static u32 cpu_magic2 = 0xC8646212; /** * Save state to a Virtual Machine State file. **/ int CAlphaCPU::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&cpu_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&cpu_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CAlphaCPU::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != cpu_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != cpu_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /***************************************************************************/ /** * \name TB * Translation Buffer related functions ******************************************************************************/ //\{ /** * \brief Find translation-buffer entry * * Try to find a translation-buffer entry that maps the page inside which * the specified virtual address lies. * * \param virt Virtual address to find in translation buffer. * \param flags ACCESS_EXEC determines which translation buffer to use. * \return Number of matching entry, or -1 if no match found. **/ int CAlphaCPU::FindTBEntry(u64 virt, int flags) { // Use ITB (tb[1]) if ACCESS_EXEC is set, otherwise use DTB (tb[0]) int t = (flags & ACCESS_EXEC) ? 1 : 0; int asn = (flags & ACCESS_EXEC) ? state.asn : state.asn0; int rw = (flags & ACCESS_WRITE) ? 1 : 0; // Try last match first; this is a good quess, especially in the ITB int i = state.last_found_tb[t][rw]; if (state.tb[t][i].valid && !((state.tb[t][i].virt ^ virt) & state.tb[t][i].match_mask) && (state.tb[t][i].asm_bit || (state.tb[t][i].asn == asn))) return i; // Otherwise, loop through the TB entries to find a match. for (i = 0; i < TB_ENTRIES; i++) { if (state.tb[t][i].valid && !((state.tb[t][i].virt ^ virt) & state.tb[t][i].match_mask) && (state.tb[t][i].asm_bit || (state.tb[t][i].asn == asn))) { state.last_found_tb[t][rw] = i; return i; } } return -1; } /** * \brief Translate a virtual address to a physical address. * * Translate a 64-bit virtual address into a 64-bit physical address, using * the page table buffers. * * The following steps are taken to resolve the address: * - See if the address can be found in the translation buffer. * - If not, try to load the right page table entry into the translation * buffer, if this is not possible, trap to the OS. * - Check access privileges. * - Check fault bits. * . * * \param virt Virtual address to be translated. * \param phys Pointer to where the physical address is to be returned. * \param flags Set of flags that determine the exact functioning of the * function. A combination of the following flags: * - ACCESS_READ Data-read-access. * - ACCESS_WRITE Data-write-access. * - ACCESS_EXEC Code-read-access. * - NO_CHECK Do not perform access checks. * - VPTE VPTE access; if this misses, it's a double *miss. * - FAKE Access is not initiated by executing code, *but by the debugger. If a translation can't be found through the translation *buffer, don't bother. * - ALT Use alt_cm for access checks instead of cm. * - RECUR Recursive try. We tried to find this address * before, added a TB entry, and now it should *sail through. * - PROBE Access is for a PROBER or PROBEW access; *Don't swap in the page if it is outswapped. * - PROBEW Access is for a PROBEW access. * . * \param asm_bit Status of the ASM (address space match) bit in the *page-table-entry. \param ins Instruction currently being executed. *Important for the correct handling of traps. * * \return 0 on success, -1 if address could not be converted without * help (in this case state.pc contains the address of the * next instruction to execute (PALcode or OS entry point). **/ int CAlphaCPU::virt2phys(u64 virt, u64 *phys, int flags, bool *asm_bit, u32 ins) { int t = (flags & ACCESS_EXEC) ? 1 : 0; int i; int res; int spe = (flags & ACCESS_EXEC) ? state.i_ctl_spe : state.m_ctl_spe; int cm = (flags & ALT) ? state.alt_cm : state.cm; bool forreal = !(flags & FAKE); #if defined IDB if (bListing) { *phys = virt; return 0; } #endif #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("TB %" PRIx64 ",%x: ", virt, flags); #endif // try superpage first. if (spe && !cm) { #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("try spe..."); #endif // HRM 5.3.9: SPE[2], when set, enables superpage mapping when VA[47:46] // = 2. In this mode, VA[43:13] are mapped directly to PA[43:13] and // VA[45:44] are ignored. if (((virt & SPE_2_MASK) == SPE_2_MATCH) && (spe & 4)) { *phys = virt & SPE_2_MAP; if (asm_bit) *asm_bit = false; #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("SPE\n"); #endif return 0; } // SPE[1], when set, enables superpage mapping when VA[47:41] = 7E. In // this mode, VA[40:13] are mapped directly to PA[40:13] and PA[43:41] are // copies of PA[40] (sign extension). else if (((virt & SPE_1_MASK) == SPE_1_MATCH) && (spe & 2)) { *phys = (virt & SPE_1_MAP) | ((virt & SPE_1_TEST) ? SPE_1_ADD : 0); if (asm_bit) *asm_bit = false; #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("SPE\n"); #endif return 0; } // SPE[0], when set, enables superpage mapping when VA[47:30] = 3FFFE. // In this mode, VA[29:13] are mapped directly to PA[29:13] and PA[43:30] // are cleared. else if (((virt & SPE_0_MASK) == SPE_0_MATCH) && (spe & 1)) { *phys = virt & SPE_0_MAP; if (asm_bit) *asm_bit = false; #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("SPE\n"); #endif return 0; } } // try to find it in the translation buffer i = FindTBEntry(virt, flags); if (i < 0) // not found, either trap to PALcode, or try to load the TB entry // and try again. { if (!forreal) // debugger-lookup of the address return -1; // report failure, and don't look any further if (!state.pal_vms) // unknown PALcode { // transfer execution to PALcode state.exc_addr = state.current_pc; if (flags & VPTE) { state.fault_va = virt; state.exc_sum = (u64)REG_1 << 8; set_pc(state.pal_base + DTBM_DOUBLE_3 + 1); } else if (flags & ACCESS_EXEC) { set_pc(state.pal_base + ITB_MISS + 1); } else { state.fault_va = virt; state.exc_sum = (u64)REG_1 << 8; u32 opcode = I_GETOP(ins); state.mm_stat = ((opcode == 0x1b || opcode == 0x1f) ? opcode - 0x18 : opcode) << 4 | (flags & ACCESS_WRITE); set_pc(state.pal_base + DTBM_SINGLE + 1); } return -1; } else // VMS PALcode { if (flags & RECUR) // we already tried this { printf("Translationbuffer RECUR lookup failed!\n"); return -1; } state.exc_addr = state.current_pc; if (flags & VPTE) { // try to handle the double miss. If this needs to transfer control // to the OS, it will return non-zero value. if ((res = vmspal_ent_dtbm_double_3(flags))) return res; // Double miss succesfully handled. Try to get the physical address // again. return virt2phys(virt, phys, flags | RECUR, asm_bit, ins); } else if (flags & ACCESS_EXEC) { // try to handle the ITB miss. If this needs to transfer control // to the OS, it will return non-zero value. if ((res = vmspal_ent_itbm(flags))) return res; // ITB miss succesfully handled. Try to get the physical address again. return virt2phys(virt, phys, flags | RECUR, asm_bit, ins); } else { state.fault_va = virt; state.exc_sum = (u64)REG_1 << 8; u32 opcode = I_GETOP(ins); state.mm_stat = ((opcode == 0x1b || opcode == 0x1f) ? opcode - 0x18 : opcode) << 4 | (flags & ACCESS_WRITE); // try to handle the single miss. If this needs to transfer control // to the OS, it will return non-zero value. if ((res = vmspal_ent_dtbm_single(flags))) return res; // Single miss succesfully handled. Try to get the physical address // again. return virt2phys(virt, phys, flags | RECUR, asm_bit, ins); } } } // If we get here, the number of the matching TB entry is in i. #if defined(DEBUG_TB) else { if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("entry %d - ", i); } #endif if (!(flags & NO_CHECK)) { // check if requested access is allowed if (!state.tb[t][i].access[flags & ACCESS_WRITE][cm]) { #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("acv\n"); #endif if (flags & ACCESS_EXEC) { // handle I-stream access violation state.exc_addr = state.current_pc; state.exc_sum = 0; if (state.pal_vms) { if ((res = vmspal_ent_iacv(flags))) return res; } else { set_pc(state.pal_base + IACV + 1); return -1; } } else { // Handle D-stream access violation state.exc_addr = state.current_pc; state.fault_va = virt; state.exc_sum = (u64)REG_1 << 8; u32 opcode = I_GETOP(ins); state.mm_stat = ((opcode == 0x1b || opcode == 0x1f) ? opcode - 0x18 : opcode) << 4 | (flags & ACCESS_WRITE) | 2; if (state.pal_vms) { if ((res = vmspal_ent_dfault(flags))) return res; } else { set_pc(state.pal_base + DFAULT + 1); return -1; } } } // check if requested access doesn't fault if (state.tb[t][i].fault[flags & ACCESS_MODE]) { #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("fault\n"); #endif if (flags & ACCESS_EXEC) { // handle I-stream access fault state.exc_addr = state.current_pc; state.exc_sum = 0; if (state.pal_vms) { if ((res = vmspal_ent_iacv(flags))) return res; } else { set_pc(state.pal_base + IACV + 1); return -1; } } else { // handle D-stream access fault state.exc_addr = state.current_pc; state.fault_va = virt; state.exc_sum = (u64)REG_1 << 8; u32 opcode = I_GETOP(ins); state.mm_stat = ((opcode == 0x1b || opcode == 0x1f) ? opcode - 0x18 : opcode) << 4 | (flags & ACCESS_WRITE) | ((flags & ACCESS_WRITE) ? 8 : 4); if (state.pal_vms) { if ((res = vmspal_ent_dfault(flags))) return res; } else { set_pc(state.pal_base + DFAULT + 1); return -1; } } } } // No access violations or faults // Return the converted address *phys = state.tb[t][i].phys | (virt & state.tb[t][i].keep_mask); if (asm_bit) *asm_bit = state.tb[t][i].asm_bit ? true : false; #if defined(DEBUG_TB) if (forreal) #if defined(IDB) if (bTB_Debug) #endif printf("phys: %" PRIx64 " - OK\n", *phys); #endif return 0; } #define GH_0_MATCH U64(0x000007ffffffe000) /* <42:13> */ #define GH_0_PHYS U64(0x00000fffffffe000) /* <43:13> */ #define GH_0_KEEP U64(0x0000000000001fff) /* <12:0> */ #define GH_1_MATCH U64(0x000007ffffff0000) #define GH_1_PHYS U64(0x00000fffffff0000) #define GH_1_KEEP U64(0x000000000000ffff) #define GH_2_MATCH U64(0x000007fffff80000) #define GH_2_PHYS U64(0x00000ffffff80000) #define GH_2_KEEP U64(0x000000000007ffff) #define GH_3_MATCH U64(0x000007ffffc00000) #define GH_3_PHYS U64(0x00000fffffc00000) #define GH_3_KEEP U64(0x00000000003fffff) /** * \brief Add translation-buffer entry * * Add a translation-buffer entry to one of the translation buffers. * * \param virt Virtual address. * \param pte Translation in DTB_PTE format (see add_tb_d). * \param flags ACCESS_EXEC determines which translation buffer to use. **/ void CAlphaCPU::add_tb(u64 virt, u64 pte_phys, u64 pte_flags, int flags) { int t = (flags & ACCESS_EXEC) ? 1 : 0; int rw = (flags & ACCESS_WRITE) ? 1 : 0; u64 match_mask = 0; u64 keep_mask = 0; u64 phys_mask = 0; int i; int asn = (flags & ACCESS_EXEC) ? state.asn : state.asn0; switch (pte_flags & 0x60) // granularity hint { case 0: match_mask = GH_0_MATCH; phys_mask = GH_0_PHYS; keep_mask = GH_0_KEEP; break; case 0x20: match_mask = GH_1_MATCH; phys_mask = GH_1_PHYS; keep_mask = GH_1_KEEP; break; case 0x40: match_mask = GH_2_MATCH; phys_mask = GH_2_PHYS; keep_mask = GH_2_KEEP; break; case 0x60: match_mask = GH_3_MATCH; phys_mask = GH_3_PHYS; keep_mask = GH_3_KEEP; break; } i = FindTBEntry(virt, flags); if (i < 0) { i = state.next_tb[t]; state.next_tb[t]++; if (state.next_tb[t] == TB_ENTRIES) state.next_tb[t] = 0; } state.tb[t][i].match_mask = match_mask; state.tb[t][i].keep_mask = keep_mask; state.tb[t][i].virt = virt & match_mask; state.tb[t][i].phys = pte_phys & phys_mask; state.tb[t][i].fault[0] = (int)pte_flags & 2; state.tb[t][i].fault[1] = (int)pte_flags & 4; state.tb[t][i].fault[2] = (int)pte_flags & 8; state.tb[t][i].access[0][0] = (int)pte_flags & 0x100; state.tb[t][i].access[1][0] = (int)pte_flags & 0x1000; state.tb[t][i].access[0][1] = (int)pte_flags & 0x200; state.tb[t][i].access[1][1] = (int)pte_flags & 0x2000; state.tb[t][i].access[0][2] = (int)pte_flags & 0x400; state.tb[t][i].access[1][2] = (int)pte_flags & 0x4000; state.tb[t][i].access[0][3] = (int)pte_flags & 0x800; state.tb[t][i].access[1][3] = (int)pte_flags & 0x8000; state.tb[t][i].asm_bit = (int)pte_flags & 0x10; state.tb[t][i].asn = asn; state.tb[t][i].valid = true; state.last_found_tb[t][rw] = i; #if defined(DEBUG_TB_) #if defined(IDB) if (bTB_Debug) #endif { printf("Add TB---------------------------------------\n"); printf("Map VIRT %016" PRIx64 "\n", state.tb[i].virt); printf("Matching %016" PRIx64 "\n", state.tb[i].match_mask); printf("And keeping %016" PRIx64 "\n", state.tb[i].keep_mask); printf("To PHYS %016" PRIx64 "\n", state.tb[i].phys); printf("Read : %c%c%c%c %c\n", state.tb[i].access[0][0] ? 'K' : '-', state.tb[i].access[0][1] ? 'E' : '-', state.tb[i].access[0][2] ? 'S' : '-', state.tb[i].access[0][3] ? 'U' : '-', state.tb[i].fault[0] ? 'F' : '-'); printf("Write: %c%c%c%c %c\n", state.tb[i].access[1][0] ? 'K' : '-', state.tb[i].access[1][1] ? 'E' : '-', state.tb[i].access[1][2] ? 'S' : '-', state.tb[i].access[1][3] ? 'U' : '-', state.tb[i].fault[1] ? 'F' : '-'); printf("Exec : %c%c%c%c %c\n", state.tb[i].access[1][0] ? 'K' : '-', state.tb[i].access[1][1] ? 'E' : '-', state.tb[i].access[1][2] ? 'S' : '-', state.tb[i].access[1][3] ? 'U' : '-', state.tb[i].fault[1] ? 'F' : '-'); } #endif } /** * \brief Add translation-buffer entry to the DTB * * The format of the PTE field is: * \code * 63 62 32 31 16 15 14 13 12 11 10 9 8 7 6 5 4 3 *2 1 0 * +--+---------------+---------+---+---+---+---+---+---+---+---+-+----+---+-+---+---+-+ * | | PA <43:13> | |UWE|SWE|EWE|KWE|URE|SRE|ERE|KRE| | GH |ASM| *|FOW|FOR| | * +--+---------------+---------+---+---+---+---+---+---+---+---+-+----+---+-+---+---+-+ * +-------------------------------+ | | *+-------+ | | | | * (user,supervisor,executive,kernel)(read,write)enable ----+ | | | * granularity hint -----+ | | * address space match -----+ | * fault-on-(read,write) *----+ \endcode * * \param virt Virtual address. * \param pte Translation in DTB_PTE format. **/ void CAlphaCPU::add_tb_d(u64 virt, u64 pte) { add_tb(virt, pte >> (32 - 13), pte, ACCESS_READ); } /** * \brief Add translation-buffer entry to the ITB * * The format of the PTE field is: * \code * 63 44 43 13 12 11 10 9 8 7 6 5 4 3 0 * +------------------+---------------+--+---+---+---+---+-+----+---+-----+ * | | PA <43:13> | |URE|SRE|ERE|KRE| | GH |ASM| | * +------------------+---------------+--+---+---+---+---+-+----+---+-----+ * +---------------+ | | * | | | * (user,supervisor,executive,kernel)read enable ----+ | | * granularity hint -----+ | * address space match -----+ * * \endcode * * \param virt Virtual address. * \param pte Translation in ITB_PTE format. **/ void CAlphaCPU::add_tb_i(u64 virt, u64 pte) { add_tb(virt, pte, pte & 0xf70, ACCESS_EXEC); } /** * \brief Invalidate all translation-buffer entries * * Invalidate all translation-buffer entries in one of the translation buffers. * * \param flags ACCESS_EXEC determines which translation buffer to use. **/ void CAlphaCPU::tbia(int flags) { int t = (flags & ACCESS_EXEC) ? 1 : 0; int i; for (i = 0; i < TB_ENTRIES; i++) state.tb[t][i].valid = false; state.last_found_tb[t][0] = 0; state.last_found_tb[t][1] = 0; state.next_tb[t] = 0; } /** * \brief Invalidate all process-specific translation-buffer entries * * Invalidate all translation-buffer entries that do not have the ASM bit * set in one of the translation buffers. * * \param flags ACCESS_EXEC determines which translation buffer to use. **/ void CAlphaCPU::tbiap(int flags) { int t = (flags & ACCESS_EXEC) ? 1 : 0; int i; for (i = 0; i < TB_ENTRIES; i++) if (!state.tb[t][i].asm_bit) state.tb[t][i].valid = false; } /** * \brief Invalidate single translation-buffer entry * * \param virt Virtual address for which the entry should be invalidated. * \param flags ACCESS_EXEC determines which translation buffer to use. **/ void CAlphaCPU::tbis(u64 virt, int flags) { int t = (flags & ACCESS_EXEC) ? 1 : 0; int i = FindTBEntry(virt, flags); if (i >= 0) state.tb[t][i].valid = false; } //\} /** * \brief Enable i-cache regardles of config file. * * Required for SRM-ROM decompression. **/ void CAlphaCPU::enable_icache() { icache_enabled = true; } /** * \brief Enable or disable i-cache depending on config file. **/ void CAlphaCPU::restore_icache() { bool newval; newval = myCfg->get_bool_value("icache", false); if (!newval) flush_icache(); icache_enabled = newval; } #if defined(IDB) const char *PAL_NAME[] = { "HALT", "CFLUSH", "DRAINA", "LDQP", "STQP", "SWPCTX", "MFPR_ASN", "MTPR_ASTEN", "MTPR_ASTSR", "CSERVE", "SWPPAL", "MFPR_FEN", "MTPR_FEN", "MTPR_IPIR", "MFPR_IPL", "MTPR_IPL", "MFPR_MCES", "MTPR_MCES", "MFPR_PCBB", "MFPR_PRBR", "MTPR_PRBR", "MFPR_PTBR", "MFPR_SCBB", "MTPR_SCBB", "MTPR_SIRR", "MFPR_SISR", "MFPR_TBCHK", "MTPR_TBIA", "MTPR_TBIAP", "MTPR_TBIS", "MFPR_ESP", "MTPR_ESP", "MFPR_SSP", "MTPR_SSP", "MFPR_USP", "MTPR_USP", "MTPR_TBISD", "MTPR_TBISI", "MFPR_ASTEN", "MFPR_ASTSR", "28", "MFPR_VPTB", "MTPR_VPTB", "MTPR_PERFMON", "2C", "2D", "MTPR_DATFX", "2F", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "WTINT", "MFPR_WHAMI", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "BPT", "BUGCHK", "CHME", "CHMK", "CHMS", "CHMU", "IMB", "INSQHIL", "INSQTIL", "INSQHIQ", "INSQTIQ", "INSQUEL", "INSQUEQ", "INSQUEL/D", "INSQUEQ/D", "PROBER", "PROBEW", "RD_PS", "REI", "REMQHIL", "REMQTIL", "REMQHIQ", "REMQTIQ", "REMQUEL", "REMQUEQ", "REMQUEL/D", "REMQUEQ/D", "SWASTEN", "WR_PS_SW", "RSCC", "READ_UNQ", "WRITE_UNQ", "AMOVRR", "AMOVRM", "INSQHILR", "INSQTILR", "INSQHIQR", "INSQTIQR", "REMQHILR", "REMQTILR", "REMQHIQR", "REMQTIQR", "GENTRAP", "AB", "AC", "AD", "CLRFEN", "AF", "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF"}; const char *IPR_NAME[] = { "ITB_TAG", "ITB_PTE", "ITB_IAP", "ITB_IA", "ITB_IS", "PMPC", "EXC_ADDR", "IVA_FORM", "IER_CM", "CM", "IER", "IER_CM", "SIRR", "ISUM", "HW_INT_CLR", "EXC_SUM", "PAL_BASE", "I_CTL", "IC_FLUSH_ASM", "IC_FLUSH", "PCTR_CTL", "CLR_MAP", "I_STAT", "SLEEP", "?0001.1000?", "?0001.1001?", "?0001.1010?", "?0001.1011?", "?0001.1100?", "?0001.1101?", "?0001.1110?", "?0001.1111?", "DTB_TAG0", "DTB_PTE0", "?0010.0010?", "?0010.0011?", "DTB_IS0", "DTB_ASN0", "DTB_ALTMODE", "MM_STAT", "M_CTL", "DC_CTL", "DC_STAT", "C_DATA", "C_SHFT", "M_FIX", "?0010.1110?", "?0010.1111?", "?0011.0000?", "?0011.0001?", "?0011.0010?", "?0011.0011?", "?0011.0100?", "?0010.0101?", "?0010.0110?", "?0010.0111?", "?0011.1000?", "?0011.1001?", "?0011.1010?", "?0011.1011?", "?0011.1100?", "?0010.1101?", "?0010.1110?", "?0010.1111?", "PCTX.00000", "PCTX.00001", "PCTX.00010", "PCTX.00011", "PCTX.00100", "PCTX.00101", "PCTX.00110", "PCTX.00111", "PCTX.01000", "PCTX.01001", "PCTX.01010", "PCTX.01011", "PCTX.01100", "PCTX.01101", "PCTX.01110", "PCTX.01111", "PCTX.10000", "PCTX.10001", "PCTX.10010", "PCTX.10011", "PCTX.10100", "PCTX.10101", "PCTX.10110", "PCTX.10111", "PCTX.11000", "PCTX.11001", "PCTX.11010", "PCTX.11011", "PCTX.11100", "PCTX.11101", "PCTX.11110", "PCTX.11111", "PCTX.00000", "PCTX.00001", "PCTX.00010", "PCTX.00011", "PCTX.00100", "PCTX.00101", "PCTX.00110", "PCTX.00111", "PCTX.01000", "PCTX.01001", "PCTX.01010", "PCTX.01011", "PCTX.01100", "PCTX.01101", "PCTX.01110", "PCTX.01111", "PCTX.10000", "PCTX.10001", "PCTX.10010", "PCTX.10011", "PCTX.10100", "PCTX.10101", "PCTX.10110", "PCTX.10111", "PCTX.11000", "PCTX.11001", "PCTX.11010", "PCTX.11011", "PCTX.11100", "PCTX.11101", "PCTX.11110", "PCTX.11111", "?1000.0000?", "?1000.0001?", "?1000.0010?", "?1000.0011?", "?1000.0100?", "?1000.0101?", "?1000.0110?", "?1000.0111?", "?1000.1000?", "?1000.1001?", "?1000.1010?", "?1000.1011?", "?1000.1100?", "?1000.1101?", "?1000.1110?", "?1000.1111?", "?1001.0000?", "?1001.0001?", "?1001.0010?", "?1001.0011?", "?1001.0100?", "?1001.0101?", "?1001.0110?", "?1001.0111?", "?1001.1000?", "?1001.1001?", "?1001.1010?", "?1001.1011?", "?1001.1100?", "?1001.1101?", "?1001.1110?", "?1001.1111?", "DTB_TAG1", "DTB_PTE1", "DTB_IAP", "DTB_IA", "DTB_IS1", "DTB_ASN1", "?1010.0110?", "?1010.0111?", "?1010.1000?", "?1010.1001?", "?1010.1010?", "?1010.1011?", "?1010.1100?", "?1010.1101?", "?1010.1110?", "?1010.1111?", "?1011.0000?", "?1011.0001?", "?1011.0010?", "?1011.0011?", "?1011.0100?", "?1011.0101?", "?1011.0110?", "?1011.0111?", "?1011.1000?", "?1011.1001?", "?1011.1010?", "?1011.1011?", "?1011.1100?", "?1011.1101?", "?1011.1110?", "?1011.1111?", "CC", "CC_CTL", "VA", "VA_FORM", "VA_CTL", "?1100.0101?", "?1100.0110?", "?1100.0111?", "?1100.1000?", "?1100.1001?", "?1100.1010?", "?1100.1011?", "?1100.1100?", "?1100.1101?", "?1100.1110?", "?1100.1111?", "?1101.0000?", "?1101.0001?", "?1101.0010?", "?1101.0011?", "?1101.0100?", "?1101.0101?", "?1101.0110?", "?1101.0111?", "?1101.1000?", "?1101.1001?", "?1101.1010?", "?1101.1011?", "?1101.1100?", "?1101.1101?", "?1101.1110?", "?1101.1111?", "?1110.0000?", "?1110.0001?", "?1110.0010?", "?1110.0011?", "?1110.0100?", "?1110.0101?", "?1110.0110?", "?1110.0111?", "?1110.1000?", "?1110.1001?", "?1110.1010?", "?1110.1011?", "?1110.1100?", "?1110.1101?", "?1110.1110?", "?1110.1111?", "?1111.0000?", "?1111.0001?", "?1111.0010?", "?1111.0011?", "?1111.0100?", "?1111.0101?", "?1111.0110?", "?1111.0111?", "?1111.1000?", "?1111.1001?", "?1111.1010?", "?1111.1011?", "?1111.1100?", "?1111.1101?", "?1111.1110?", "?1111.1111?", }; #endif ================================================ FILE: src/AlphaCPU.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified * of, and receiving any modifications you may make to the source code that * might serve the general public. */ #if !defined(INCLUDED_ALPHACPU_H) #define INCLUDED_ALPHACPU_H #include "System.hpp" #include "SystemComponent.hpp" #include "cpu_defs.hpp" /// Number of entries in the Instruction Cache #define ICACHE_ENTRIES 1024 // Size of Instruction Cache entries in DWORDS (instructions) #define ICACHE_LINE_SIZE 512 /** These bits should match to have an Instruction Cache hit. This includes bit 0, because it indicates PALmode . */ #define ICACHE_MATCH_MASK (u64)(U64(0x1) - (ICACHE_LINE_SIZE * 4)) /// DWORD (instruction) number of an address in an ICache entry. #define ICACHE_INDEX_MASK (u64)(ICACHE_LINE_SIZE - U64(0x1)) /// Byte numer of an address in an ICache entry. #define ICACHE_BYTE_MASK (u64)(ICACHE_INDEX_MASK << 2) /// Number of entries in each Translation Buffer #define TB_ENTRIES 16 /** * \brief Emulated CPU. * * The CPU emulated is the DECchip 21264CB Alpha Processor (EV68). * * Documentation consulted: * - Alpha 21264/EV68CB and 21264/EV68DC Microprocessor Hardware Reference *Manual [HRM] (http://download.majix.org/dec/21264ev68cb_ev68dc_hrm.pdf) * - DS-0026A-TE: Alpha 21264B Microprocessor Hardware Reference Manual [HRM] *(http://ftp.digital.com/pub/Digital/info/semiconductor/literature/21264hrm.pdf) * - Alpha Architecture Reference Manual, fourth edition [ARM] *(http://download.majix.org/dec/alpha_arch_ref.pdf) * . **/ class CAlphaCPU : public CSystemComponent { public: void flush_icache_asm(); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void irq_h(int number, bool assert, int delay); int get_cpuid(); void flush_icache(); void run(); void execute(); void release_threads(); void set_PAL_BASE(u64 pb); virtual void check_state(); CAlphaCPU(CConfigurator *cfg, CSystem *system); virtual ~CAlphaCPU(); u64 get_r(int i, bool translate); u64 get_f(int i); void set_r(int reg, u64 val); void set_f(int reg, u64 val); u64 get_prbr(void); u64 get_hwpcb(void); u64 get_pc(); u64 get_pal_base(); void enable_icache(); void restore_icache(); bool get_waiting() { return state.wait_for_start; }; void stop_waiting() { state.wait_for_start = false; }; #ifdef IDB u64 get_current_pc_physical(); u64 get_instruction_count(); u32 get_last_instruction(); u64 get_last_read_loc() { return last_read_loc; } u64 get_last_write_loc() { return last_write_loc; } #endif u64 get_clean_pc(); void next_pc(); void set_pc(u64 p_pc); void add_pc(u64 a_pc); u64 get_speed() { return cpu_hz; }; u64 va_form(u64 address, bool bIBOX); #if defined(IDB) void listing(u64 from, u64 to); void listing(u64 from, u64 to, u64 mark); #endif int virt2phys(u64 virt, u64 *phys, int flags, bool *asm_bit, u32 instruction); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; CSemaphore mySemaphore; bool StopThread; int get_icache(u64 address, u32 *data); int FindTBEntry(u64 virt, int flags); void add_tb(u64 virt, u64 pte_phys, u64 pte_flags, int flags); void add_tb_i(u64 virt, u64 pte); void add_tb_d(u64 virt, u64 pte); void tbia(int flags); void tbiap(int flags); void tbis(u64 virt, int flags); /* Floating Point routines */ u64 ieee_lds(u32 op); u32 ieee_sts(u64 op); u64 ieee_cvtst(u64 op, u32 ins); u64 ieee_cvtts(u64 op, u32 ins); s32 ieee_fcmp(u64 s1, u64 s2, u32 ins, u32 trap_nan); u64 ieee_cvtif(u64 val, u32 ins, u32 dp); u64 ieee_cvtfi(u64 op, u32 ins); u64 ieee_fadd(u64 s1, u64 s2, u32 ins, u32 dp, bool sub); u64 ieee_fmul(u64 s1, u64 s2, u32 ins, u32 dp); u64 ieee_fdiv(u64 s1, u64 s2, u32 ins, u32 dp); u64 ieee_sqrt(u64 op, u32 ins, u32 dp); int ieee_unpack(u64 op, UFP *r, u32 ins); void ieee_norm(UFP *r); u64 ieee_rpack(UFP *r, u32 ins, u32 dp); void ieee_trap(u64 trap, u32 instenb, u64 fpcrdsb, u32 ins); u64 vax_ldf(u32 op); u64 vax_ldg(u64 op); u32 vax_stf(u64 op); u64 vax_stg(u64 op); void vax_trap(u64 mask, u32 ins); void vax_unpack(u64 op, UFP *r, u32 ins); void vax_unpack_d(u64 op, UFP *r, u32 ins); void vax_norm(UFP *r); u64 vax_rpack(UFP *r, u32 ins, u32 dp); u64 vax_rpack_d(UFP *r, u32 ins); int vax_fcmp(u64 s1, u64 s2, u32 ins); u64 vax_cvtif(u64 val, u32 ins, u32 dp); u64 vax_cvtfi(u64 op, u32 ins); u64 vax_fadd(u64 s1, u64 s2, u32 ins, u32 dp, bool sub); u64 vax_fmul(u64 s1, u64 s2, u32 ins, u32 dp); u64 vax_fdiv(u64 s1, u64 s2, u32 ins, u32 dp); u64 vax_sqrt(u64 op, u32 ins, u32 dp); /* VMS PALcode call: */ void vmspal_call_cflush(); void vmspal_call_draina(); void vmspal_call_ldqp(); void vmspal_call_stqp(); void vmspal_call_swpctx(); void vmspal_call_mfpr_asn(); void vmspal_call_mtpr_asten(); void vmspal_call_mtpr_astsr(); void vmspal_call_cserve(); void vmspal_call_mfpr_fen(); void vmspal_call_mtpr_fen(); void vmspal_call_mfpr_ipl(); void vmspal_call_mtpr_ipl(); void vmspal_call_mfpr_mces(); void vmspal_call_mtpr_mces(); void vmspal_call_mfpr_pcbb(); void vmspal_call_mfpr_prbr(); void vmspal_call_mtpr_prbr(); void vmspal_call_mfpr_ptbr(); void vmspal_call_mfpr_scbb(); void vmspal_call_mtpr_scbb(); void vmspal_call_mtpr_sirr(); void vmspal_call_mfpr_sisr(); void vmspal_call_mfpr_tbchk(); void vmspal_call_mtpr_tbia(); void vmspal_call_mtpr_tbiap(); void vmspal_call_mtpr_tbis(); void vmspal_call_mfpr_esp(); void vmspal_call_mtpr_esp(); void vmspal_call_mfpr_ssp(); void vmspal_call_mtpr_ssp(); void vmspal_call_mfpr_usp(); void vmspal_call_mtpr_usp(); void vmspal_call_mtpr_tbisd(); void vmspal_call_mtpr_tbisi(); void vmspal_call_mfpr_asten(); void vmspal_call_mfpr_astsr(); void vmspal_call_mfpr_vptb(); void vmspal_call_mtpr_datfx(); void vmspal_call_mfpr_whami(); void vmspal_call_imb(); void vmspal_call_prober(); void vmspal_call_probew(); void vmspal_call_rd_ps(); int vmspal_call_rei(); void vmspal_call_swasten(); void vmspal_call_wr_ps_sw(); void vmspal_call_rscc(); void vmspal_call_read_unq(); void vmspal_call_write_unq(); /* VMS PALcode entry: */ int vmspal_ent_dtbm_double_3(int flags); int vmspal_ent_dtbm_single(int flags); int vmspal_ent_itbm(int flags); int vmspal_ent_iacv(int flags); int vmspal_ent_dfault(int flags); int vmspal_ent_ext_int(int ei); int vmspal_ent_sw_int(int si); int vmspal_ent_ast_int(int ast); /* VMS PALcode internal: */ int vmspal_int_initiate_exception(); int vmspal_int_initiate_interrupt(); bool icache_enabled; bool skip_memtest_hack; int skip_memtest_counter; // ... ... ... u64 cc_large; u64 start_icount; u64 start_cc; CTimestamp start_time; u64 prev_icount; u64 prev_cc; u64 prev_time; u64 cc_per_instruction; u64 ins_per_timer_int; u64 next_timer_int; u64 cpu_hz; /// The state structure contains all elements that need to be saved to the /// statefile struct SCPU_state { bool wait_for_start; u64 pal_base; /**< IPR PAL_BASE [HRM: p 5-15] */ u64 pc; /**< Program counter */ u64 cc; /**< IPR CC: Cycle counter [HRM p 5-3] */ u64 r[64]; /**< Integer registers (0-31 normal, 32-63 shadow) */ u64 dc_stat; /**< IPR DC_STAT: Dcache status [HRM p 5-31..32] */ bool ppcen; /**< IPR PCTX: ppce (proc perf counting enable) [HRM p 5-21..23] */ u64 i_stat; /**< IPR I_STAT: Ibox status [HRM p 5-18..20] */ u64 pctr_ctl; /**< IPR PCTR_CTL [HRM p 5-23..25] */ bool cc_ena; /**< IPR CC_CTL: Cycle counter enabled [HRM p 5-3] */ u32 cc_offset; /**< IPR CC: Cycle counter offset [HRM p 5-3] */ u64 dc_ctl; /**< IPR DC_CTL: Dcache control [HRM p 5-30..31] */ int alt_cm; /**< IPR DTB_ALTMODE: alternative cm for HW_LD/HW_ST [HRM p 5-26..27] */ int smc; /**< IPR M_CTL: smc (speculative miss control) [HRM p 5-29..30] */ bool fpen; /**< IPR PCTX: fpe (floating point enable) [HRM p 5-21..23] */ bool sde; /**< IPR I_CTL: sde[1] (PALshadow enable) [HRM p 5-15..18] */ u64 fault_va; /**< IPR VA: virtual address of last Dstream miss or fault [HRM p 5-4] */ u64 exc_sum; /**< IPR EXC_SUM: exception summary [HRM p 5-13..15] */ int i_ctl_va_mode; /**< IPR I_CTL: (va_form_32 + va_48) [HRM p 5-15..17] */ int va_ctl_va_mode; /**< IPR VA_CTL: (va_form_32 + va_48) [HRM p 5-4] */ u64 i_ctl_vptb; /**< IPR I_CTL: vptb (virtual page table base) [HRM p 5-15..16] */ u64 va_ctl_vptb; /**< IPR VA_CTL: vptb (virtual page table base) [HRM p 5-4] */ int cm; /**< IPR IER_CM: cm (current mode) [HRM p 5-9..10] */ int asn; /**< IPR PCTX: asn (address space number) [HRM p 5-21..22] */ int asn0; /**< IPR DTB_ASN0: asn (address space number) [HRM p 5-28] */ int asn1; /**< IPR DTB_ASN1: asn (address space number) [HRM p 5-28] */ int eien; /**< IPR IER_CM: eien (external interrupt enable) [HRM p 5-9..10] */ int slen; /**< IPR IER_CM: slen (serial line interrupt enable) [HRM p 5-9..10] */ int cren; /**< IPR IER_CM: cren (corrected read error int enable) [HRM p 5-9..10] */ int pcen; /**< IPR IER_CM: pcen (perf counter interrupt enable) [HRM p 5-9..10] */ int sien; /**< IPR IER_CM: sien (software interrupt enable) [HRM p 5-9..10] */ int asten; /**< IPR IER_CM: asten (AST interrupt enable) [HRM p 5-9..10] */ int sir; /**< IPR SIRR: sir (software interrupt request) [HRM p 5-10..11] */ int eir; /**< external interrupt request */ int slr; /**< serial line interrupt request */ int crr; /**< corrected read error interrupt */ int pcr; /**< perf counter interrupt */ int astrr; /**< IPR PCTX: astrr (AST request) [HRM p 5-21..22] */ int aster; /**< IPR PCTX: aster (AST enable) [HRM p 5-21..22] */ u64 i_ctl_other; /**< various bits in IPR I_CTL that have no meaning to the emulator */ u64 mm_stat; /**< IPR MM_STAT: memory management status [HRM p 5-28..29] */ bool hwe; /**< IPR I_CLT: hwe (allow palmode ins in kernel mode) [HRM p 5-15..17] */ int m_ctl_spe; /**< IPR M_CTL: spe (Super Page mode enabled) [HRM p 5-29..30] */ int i_ctl_spe; /**< IPR I_CTL: spe (Super Page mode enabled) [HRM p 5-15..18] */ u64 exc_addr; /**< IPR EXC_ADDR: address of last exception [HRM p 5-8] */ u64 pmpc; u64 fpcr; /**< Floating-Point Control Register [HRM p 2-36] */ bool bIntrFlag; u64 current_pc; /**< Virtual address of current instruction */ /** * \brief Instruction cache entry. * * An instruction cache entry contains the address and address space number * (ASN) + 16 32-bit instructions. [HRM 2-11] **/ struct SICache { int asn; /**< Address Space Number */ u32 data[ICACHE_LINE_SIZE]; /**< Actual cached instructions */ u64 address; /**< Address of first instruction */ u64 p_address; /**< Physical address of first instruction */ bool asm_bit; /**< Address Space Match bit */ bool valid; /**< Valid cache entry */ } icache[ICACHE_ENTRIES]; /**< Instruction cache entries [HRM p 2-11] */ int next_icache; /**< Number of next cache entry to use */ int last_found_icache; /**< Number of last cache entry found */ /** * \brief Translation Buffer Entry. * * A translation buffer entry provides the mapping from a page of virtual *memory to a page of physical memory. **/ struct STBEntry { u64 virt; /**< Virtual address of page*/ u64 phys; /**< Physical address of page*/ u64 match_mask; /**< The virtual address has to match for these bits to be a hit*/ u64 keep_mask; /**< This part of the virtual address is OR-ed with the phys address*/ int asn; /**< Address Space Number*/ int asm_bit; /**< Address Space Match bit*/ int access[2][4]; /**< Access permitted [read/write][current mode]*/ int fault[3]; /**< Fault on access [read/write/execute]*/ bool valid; /**< Valid entry*/ } tb[2][TB_ENTRIES]; /**< Translation buffer entries */ int next_tb[2]; /**< Number of next translation buffer entry to use */ int last_found_tb[2] [2]; /**< Number of last translation buffer entry found */ u32 rem_ins_in_page; /**< Number of instructions remaining in current page */ u64 pc_phys; u64 f[64]; /**< Floating point registers (0-31 normal, 32-63 shadow) */ int iProcNum; /**< number of the current processor (0 in a 1-processor system) */ u64 instruction_count; /**< Number of times doclock has been called */ u64 last_tb_virt; bool pal_vms; /**< True if the PALcode base is 0x8000 (=VMS PALcode base) */ bool check_int; /**< True if an interrupt may be pending */ int irq_h_timer[6]; /**< Timers for delayed IRQ_H[0:5] assertion */ bool check_timers; } state; /**< Determines CPU state that needs to be saved to the state file */ #ifdef IDB u64 current_pc_physical; /**< Physical address of current instruction */ u32 last_instruction; u64 last_read_loc; u64 last_write_loc; #endif void skip_memtest(); }; /** Translate raw register (0..31) number to a number that takes PALshadow registers into consideration (0..63). Considers the program counter (to determine if we're in PALmode), and the SDE (Shadow Enable) bit. */ #define RREG(a) \ (((a)&0x1f) + (((state.pc & 1) && (((a)&0xc) == 0x4) && state.sde) ? 32 : 0)) /** * Empty the instruction cache. **/ inline void CAlphaCPU::flush_icache() { if (icache_enabled) { // memset(state.icache,0,sizeof(state.icache)); int i; for (i = 0; i < ICACHE_ENTRIES; i++) { state.icache[i].valid = false; // state.icache[i].asm_bit = true; } state.next_icache = 0; state.last_found_icache = 0; } } /** * Empty the instruction cache of lines with the ASM bit clear. **/ inline void CAlphaCPU::flush_icache_asm() { if (icache_enabled) { int i; for (i = 0; i < ICACHE_ENTRIES; i++) if (!state.icache[i].asm_bit) state.icache[i].valid = false; } } /** * Set the PALcode BASE register, and determine whether we're running VMS *PALcode. **/ inline void CAlphaCPU::set_PAL_BASE(u64 pb) { state.pal_base = pb; state.pal_vms = (pb == U64(0x8000)); } /** * Get an instruction from the instruction cache. * If necessary, fill a new cache block from memory. * * get_icache checks all cache entries, to see if there is a * cache entry that matches the current address space number, * and that contains the address we're looking for. If it * exists, the instruction is fetched from this cache, * otherwise, the physical address for the instruction is * calculated, and the cache block is filled. * * The last cache entry that was a hit is remembered, so that * cache entry is checked first on the next instruction. (very * likely to be the same cache block) * * It would be easiest to do without the instruction cache * altogether, but unfortunately SRM uses self-modifying * code, that relies on the correct instruction stream to * remain in the cache. **/ inline int CAlphaCPU::get_icache(u64 address, u32 *data) { int i = state.last_found_icache; u64 v_a; u64 p_a; int result; bool asm_bit; if (icache_enabled) { if (state.icache[i].valid && (state.icache[i].asn == state.asn || state.icache[i].asm_bit) && state.icache[i].address == (address & ICACHE_MATCH_MASK)) { *data = endian_32(state.icache[i].data[(address >> 2) & ICACHE_INDEX_MASK]); #ifdef IDB current_pc_physical = state.icache[i].p_address + (address & ICACHE_BYTE_MASK); #endif return 0; } for (i = 0; i < ICACHE_ENTRIES; i++) { if (state.icache[i].valid && (state.icache[i].asn == state.asn || state.icache[i].asm_bit) && state.icache[i].address == (address & ICACHE_MATCH_MASK)) { state.last_found_icache = i; *data = endian_32(state.icache[i].data[(address >> 2) & ICACHE_INDEX_MASK]); #ifdef IDB current_pc_physical = state.icache[i].p_address + (address & ICACHE_BYTE_MASK); #endif return 0; } } v_a = address & ICACHE_MATCH_MASK; if (address & 1) { p_a = v_a & ~U64(0x1); asm_bit = true; } else { result = virt2phys(v_a, &p_a, ACCESS_EXEC, &asm_bit, 0); if (result) return result; } memcpy(state.icache[state.next_icache].data, cSystem->PtrToMem(p_a), ICACHE_LINE_SIZE * 4); state.icache[state.next_icache].valid = true; state.icache[state.next_icache].asn = state.asn; state.icache[state.next_icache].asm_bit = asm_bit; state.icache[state.next_icache].address = address & ICACHE_MATCH_MASK; state.icache[state.next_icache].p_address = p_a; *data = endian_32(state.icache[state.next_icache] .data[(address >> 2) & ICACHE_INDEX_MASK]); #ifdef IDB current_pc_physical = state.icache[state.next_icache].p_address + (address & ICACHE_BYTE_MASK); #endif state.last_found_icache = state.next_icache; state.next_icache++; if (state.next_icache == ICACHE_ENTRIES) state.next_icache = 0; return 0; } // icache disabled if (address & 1) { state.pc_phys = address & ~U64(0x3); state.rem_ins_in_page = 1; } else { if (!state.rem_ins_in_page) { result = virt2phys(address, &state.pc_phys, ACCESS_EXEC, &asm_bit, 0); if (result) return result; state.rem_ins_in_page = 2048 - ((((u32)address) >> 2) & 2047); } } *data = (u32)cSystem->ReadMem(state.pc_phys, 32, this); return 0; } /** * Convert a virtual address to va_form format. * Used for IPR VA_FORM [HRM 5-5..6] and IPR IVA_FORM [HRM 5-9]. **/ inline u64 CAlphaCPU::va_form(u64 address, bool bIBOX) { switch (bIBOX ? state.i_ctl_va_mode : state.va_ctl_va_mode) { case 0: return ((bIBOX ? state.i_ctl_vptb : state.va_ctl_vptb) & U64(0xfffffffe00000000)) | ((address >> 10) & U64(0x00000001fffffff8)); case 1: return ((bIBOX ? state.i_ctl_vptb : state.va_ctl_vptb) & U64(0xfffff80000000000)) | ((address >> 10) & U64(0x0000003ffffffff8)) | (((address >> 10) & U64(0x0000002000000000)) * U64(0x3e)); case 2: return ((bIBOX ? state.i_ctl_vptb : state.va_ctl_vptb) & U64(0xffffffffc0000000)) | ((address >> 10) & U64(0x00000000003ffff8)); } return 0; } /** * Return processor number. **/ inline int CAlphaCPU::get_cpuid() { return state.iProcNum; } /** * Assert or release an external interrupt line to the cpu. **/ inline void CAlphaCPU::irq_h(int number, bool assert, int delay) { bool active = (state.eir & (U64(0x1) << number)) || state.irq_h_timer[number]; if (assert && !active) { if (delay) { state.irq_h_timer[number] = delay; state.check_timers = true; } else { state.eir |= (U64(0x1) << number); state.check_int = true; } return; } if (!assert && active) { state.eir &= ~(U64(0x1) << number); state.irq_h_timer[number] = 0; state.check_timers = false; for (int i = 0; i < 6; i++) { if (state.irq_h_timer[i]) state.check_timers = true; } } } /** * Return program counter value. **/ inline u64 CAlphaCPU::get_pc() { return state.pc; } #ifdef IDB /** * Return the physical address the program counter refers to. **/ inline u64 CAlphaCPU::get_current_pc_physical() { return state.pc_phys; } #endif /** * Return program counter value without PALmode bit. **/ inline u64 CAlphaCPU::get_clean_pc() { return state.pc & ~U64(0x3); } /** * Jump to next instruction **/ inline void CAlphaCPU::next_pc() { state.pc += 4; state.pc_phys += 4; if (state.rem_ins_in_page) state.rem_ins_in_page--; } /** * Set program counter to a certain value. **/ inline void CAlphaCPU::set_pc(u64 p_pc) { state.pc = p_pc; state.rem_ins_in_page = 0; } /** * Add value to the program counter. **/ inline void CAlphaCPU::add_pc(u64 a_pc) { state.pc += a_pc; state.rem_ins_in_page = 0; } /** * Get a register value. * If \a translate is true, use shadow registers if currently enabled. **/ inline u64 CAlphaCPU::get_r(int i, bool translate) { if (translate) return state.r[RREG(i)]; else return state.r[i]; } /** * Get a fp register value. **/ inline u64 CAlphaCPU::get_f(int i) { return state.f[i]; } /** * Set a register value **/ inline void CAlphaCPU::set_r(int reg, u64 value) { state.r[reg] = value; } /** * Set a fp register value **/ inline void CAlphaCPU::set_f(int reg, u64 value) { state.f[reg] = value; } /** * Get the PALcode base register. **/ inline u64 CAlphaCPU::get_pal_base() { return state.pal_base; } /** * Get the processor base register. * A bit fuzzy... **/ inline u64 CAlphaCPU::get_prbr(void) { u64 v_prbr; // virtual u64 p_prbr; // physical bool b; if (state.r[21 + 32] && ((u64)(state.r[21 + 32] + 0xaf) < (u64)((U64(0x1) << cSystem->get_memory_bits())))) v_prbr = cSystem->ReadMem(state.r[21 + 32] + 0xa8, 64, this); else v_prbr = cSystem->ReadMem(0x70a8 + (0x200 * get_cpuid()), 64, this); if (virt2phys(v_prbr, &p_prbr, ACCESS_READ | FAKE | NO_CHECK, &b, 0)) p_prbr = v_prbr; if ((u64)p_prbr > (u64)(U64(0x1) << cSystem->get_memory_bits())) p_prbr = 0; return p_prbr; } /** * Get the hardware process control block address. **/ inline u64 CAlphaCPU::get_hwpcb(void) { u64 v_pcb; // virtual u64 p_pcb; // physical bool b; if (state.r[21 + 32] && ((u64)(state.r[21 + 32] + 0x17) < (u64)((U64(0x1) << cSystem->get_memory_bits())))) v_pcb = cSystem->ReadMem(state.r[21 + 32] + 0x10, 64, this); else v_pcb = cSystem->ReadMem(0x7010 + (0x200 * get_cpuid()), 64, this); if (virt2phys(v_pcb, &p_pcb, ACCESS_READ | NO_CHECK | FAKE, &b, 0)) p_pcb = v_pcb; if (p_pcb > (u64)(U64(0x1) << cSystem->get_memory_bits())) p_pcb = 0; return p_pcb; } #if defined(IDB) /** * Return the last instruction executed. **/ inline u32 CAlphaCPU::get_last_instruction(void) { return last_instruction; } #endif extern bool bTB_Debug; #endif // !defined(INCLUDED_ALPHACPU_H) ================================================ FILE: src/AlphaCPU_ieeefloat.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /* Copyright notice from SimH/alpha/alpha_fpi.c: Copyright (c) 2003-2006, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Portions of this module (specifically, the convert floating to integer routine and the square root routine) are a derivative work from SoftFloat, written by John Hauser. SoftFloat includes the following license terms: Written by John R. Hauser. This work was made possible in part by the International Computer Science Institute, located at Suite 600, 1947 Center Street, Berkeley, California 94704. Funding was partially provided by the National Science Foundation under grant MIP-9311980. The original version of this code was written as part of a project to build a fixed-point vector processor in collaboration with the University of California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. More information is available through the Web page 'http://www.cs.berkeley.edu/~jhauser/ arithmetic/SoftFloat.html'. THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. Derivative works are acceptable, even for commercial purposes, so long as (1) the source code for the derivative work includes prominent notice that the work is derivative, and (2) the source code includes prominent notice with these four paragraphs for those parts of this code that are retained. */ #include "AlphaCPU.hpp" #include "StdAfx.hpp" #include "cpu_debug.hpp" /***************************************************************************/ /** * \page IEEE IEEE floating point arithmetic * * \section IEEE_fmt IEEE Floating-point formats * The Alpha processor supports two different floating-point formats; S- and T- * floating formats. * * \subsection IEEE_S S-Floating * An IEEE single precision, or S-Floating, is a 32-bit floating point value. * In memory, it's layout is as follows: * \code * 31 30 23 22 0 * +---+----------+-------------------------+ * | S | Exponent | Fraction | * +---+----------+-------------------------+ * \endcode * * In the floating point registers, the S-Floating is left-justified to occupy * 64 bits: * \code * 63 62 52 51 29 28 0 * +---+--------------+-------------------------+----------------------+ * | S | Exponent | Fraction | 0 | * +---+--------------+-------------------------+----------------------+ * \endcode * * The exponent is mapped from the 8-bit memory-format exponent to the 11-bit * register-format as follows: * \code * +----------------+------------------+--------------------------------+ * | Memory <30:23> | Register <62:52> | Meaning | * +----------------+------------------+--------------------------------+ * | 1 1111111 | 1 111 1111111 | frac <> 0: NaN (not-a-number) | * | | | frac == 0: +/- Infinity | * +----------------+------------------+--------------------------------+ * | 1 xxxxxxx | 1 000 xxxxxxx | Finite number | * | 0 xxxxxxx | 0 111 xxxxxxx | Finite number | * +----------------+------------------+--------------------------------+ * | 0 0000000 | 0 000 0000000 | frac <> 0: Subnormal finite | * | | | frac == 0: +/- Zero | * +----------------+------------------+--------------------------------+ * \endcode * * \subsection IEEE_T T-Floating * An IEEE double precision, or T-Floating, is a 64-bit floating point value. * Both in memory, and in the flowting point registers it's layout is as * follows: * \code * 63 62 52 51 0 * +---+--------------+------------------------------------------------+ * | S | Exponent | Fraction | * +---+--------------+------------------------------------------------+ * \endcode * * The value (V) of a T-Floating value can be determined from the Sign (S), * Exponent (E) and Fraction (F) as follows: * \code * +--------------+--------+--------+--------------------------+ * | E == 2047 | F <> 0 | | V = NaN | * +--------------+--------+--------+--------------------------+ * | E == 2047 | F == 0 | S == 0 | V = + Infinity | * +--------------+--------+--------+--------------------------+ * | E == 2047 | F == 0 | S == 1 | V = - Infinity | * +--------------+--------+--------+--------------------------+ * | 0 < E < 2047 | | S == 0 | V = + (1.F) * 2^(E-1023) | * +--------------+--------+--------+--------------------------+ * | 0 < E < 2047 | | S == 1 | V = - (1.F) * 2^(E-1023) | * +--------------+--------+--------+--------------------------+ * | E == 0 | F <> 0 | S == 0 | V = + (0.F) * 2^(-1022) | * +--------------+--------+--------+--------------------------+ * | E == 0 | F <> 0 | S == 1 | V = - (0.F) * 2^(-1022) | * +--------------+--------+--------+--------------------------+ * | E == 0 | F == 0 | S == 0 | V = + 0 | * +--------------+--------+--------+--------------------------+ * | E == 0 | F == 0 | S == 1 | V = - 0 | * +--------------+--------+--------+--------------------------+ * \endcode * ******************************************************************************/ /* Register format constants */ #define QNAN U64(0x0008000000000000) /* quiet NaN flag */ #define CQNAN U64(0xFFF8000000000000) /* canonical quiet NaN */ #define FPZERO U64(0x0000000000000000) /* plus zero (fp) */ #define FMZERO U64(0x8000000000000000) /* minus zero (fp) */ #define FPINF U64(0x7FF0000000000000) /* plus infinity (fp) */ #define FMINF U64(0xFFF0000000000000) /* minus infinity (fp) */ #define FPMAX U64(0x7FFFFFFFFFFFFFFF) /* plus MAX (fp) */ #define FMMAX U64(0xFFFFFFFFFFFFFFFF) /* minus MAX (fp) */ #define IPMAX U64(0x7FFFFFFFFFFFFFFF) /* plus MAX (int) */ #define IMMAX U64(0x8000000000000000) /* minus MAX (int) */ /* Unpacked rounding constants */ #define UF_SRND U64(0x0000008000000000) /* S normal round */ #define UF_SINF U64(0x000000FFFFFFFFFF) /* S infinity round */ #define UF_TRND U64(0x0000000000000400) /* T normal round */ #define UF_TINF U64(0x00000000000007FF) /* T infinity round */ /***************************************************************************/ /** * \name IEEE_fp_load_store * IEEE floating point load and store functions. ******************************************************************************/ //\{ /** * \brief Convert an IEEE S-floating from memory format to register format. * * Adjust the exponent base, and widen the exponent and fraction fields. * * \param op IEEE S-floating value in memory format. * \return IEEE S-floating value in register format. **/ u64 CAlphaCPU::ieee_lds(u32 op) { u32 exp = S_GETEXP(op); /* get exponent */ if (exp == S_NAN) exp = FPR_NAN; /* inf or NaN? */ else if (exp != 0) exp = exp + T_BIAS - S_BIAS; /* zero or denorm? */ return (((u64)(op & S_SIGN)) ? FPR_SIGN : 0) | /* reg format */ (((u64)exp) << FPR_V_EXP) | (((u64)(op & ~(S_SIGN | S_EXP))) << S_V_FRAC); } /** * \brief Convert an IEEE S-floating from register format to memory format. * * Adjust the exponent base, and make the exponent and fraction fields smaller. * * \param op IEEE S-floating value in register format. * \return IEEE S-floating value in memory format. **/ u32 CAlphaCPU::ieee_sts(u64 op) { u32 sign = FPR_GETSIGN(op) ? S_SIGN : 0; u32 exp = FPR_GETEXP(op); if (exp == FPR_NAN) exp = S_NAN; /* inf or NaN? */ else if (exp != 0) exp = exp + S_BIAS - T_BIAS; /* zero or denorm? */ exp = (exp << S_V_EXP) & S_EXP; u32 frac = ((u32)(op >> S_V_FRAC)) & X64_LONG; return sign | exp | (frac & ~(S_SIGN | S_EXP)); } //\} /***************************************************************************/ /** * \name IEEE_fp_conversion * IEEE floating point conversion routines ******************************************************************************/ //\{ /** * \brief Convert an IEEE S-floating to an IEEE T-floating. * * LDS doesn't handle denorms correctly. * * \param op IEEE S-floating value. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \return IEEE T-floating value. **/ u64 CAlphaCPU::ieee_cvtst(u64 op, u32 ins) { UFP b; u32 ftpb; ftpb = ieee_unpack(op, &b, ins); /* unpack; norm dnorm */ if (ftpb == UFT_DENORM) /* denormal? */ { // i'm not completely sure this is correct... b.exp = b.exp + T_BIAS - S_BIAS; /* change 0 exp to T */ return ieee_rpack(&b, ins, DT_T); /* round, pack */ } else return op; /* identity */ } /** * \brief Convert an IEEE T-floating to an IEEE S-floating. * * \param op IEEE T-floating value. * \param ins The instruction currently being executed. Used to properly * handle exceptions and to determine the rounding mode. * \return IEEE S-floating. **/ u64 CAlphaCPU::ieee_cvtts(u64 op, u32 ins) { UFP b; u32 ftpb; ftpb = ieee_unpack(op, &b, ins); /* unpack */ if (Q_FINITE(ftpb)) return ieee_rpack(&b, ins, DT_S); /* finite? round, pack */ if (ftpb == UFT_NAN) return (op | QNAN); /* nan? cvt to quiet */ if (ftpb == UFT_INF) return op; /* inf? unchanged */ return 0; /* denorm? 0 */ } //\} /***************************************************************************/ /** * \name IEEE_fp_operations * IEEE floating point operations ******************************************************************************/ //\{ /** * \brief Compare 2 IEEE floating-point values. * * The following steps are taken: * - Take care of NaNs * - Force -0 to +0 * - Then normal compare will work (even on inf and denorms) * . * * \param s1 First IEEE floating to be compared. * \param s1 Second IEEE floating to be compared. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \return 0 if s1==s2, 1 if s1>s2, -1 if s1> (UF_V_NM - ubexp); /* result */ } else { if ((ubexp - UF_V_NM) > 63) a.frac = 0; /* out of range */ else a.frac = (a.frac << (ubexp - UF_V_NM)) & X64_QUAD; ovf = 1; /* overflow */ sticky = 0; /* no rounding */ } rndm = I_GETFRND(ins); /* get round mode */ if (((rndm == I_FRND_N) && (sticky & Q_SIGN)) /* nearest? */ || ((rndm == I_FRND_P) && !a.sign && sticky) /* +inf and +? */ || ((rndm == I_FRND_M) && a.sign && sticky)) /* -inf and -? */ { a.frac = (a.frac + 1) & X64_QUAD; if (a.frac == 0) ovf = 1; /* overflow? */ if ((rndm == I_FRND_N) && (sticky == Q_SIGN)) /* round nearest hack */ a.frac = a.frac & ~1; } if (a.frac > (a.sign ? IMMAX : IPMAX)) ovf = 1; /* overflow? */ if (ovf) ieee_trap(TRAP_IOV, ins & I_FTRP_V, 0, 0); /* overflow trap */ if (ovf || sticky) /* ovflo or round? */ ieee_trap(TRAP_INE, Q_SUI(ins), FPCR_INED, ins); return (a.sign ? NEG_Q(a.frac) : a.frac); } /** * \brief Add or subtract 2 IEEE floating-point values. * * The following steps are taken: * - Take care of NaNs and infinites * - Test for zero (fast exit) * - Sticky logic for floating add * - If result normalized, sticky in right place * - If result carries out, renormalize, retain sticky * . * - Sticky logic for floating subtract * - If shift < guard, no sticky bits; 64b result is exact * - If shift <= 1, result may require extensive normalization, * but there are no sticky bits to worry about * - If shift >= guard, there is a sticky bit, * but normalization is at most 1 place, sticky bit is retained * for rounding purposes (but not in low order bit) * . * . * * \param s1 Augend or minuend. * \param s2 Addend or subtrahend. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_S for S-floating or DT_T for T-floating. * \param sub subtract if true, add if false. * \return IEEE floating. **/ u64 CAlphaCPU::ieee_fadd(u64 s1, u64 s2, u32 ins, u32 dp, bool sub) { UFP a; UFP b; UFP t; u32 ftpa; u32 ftpb; u32 sticky; s32 ediff; ftpa = ieee_unpack(s1, &a, ins); /* unpack operands */ ftpb = ieee_unpack(s2, &b, ins); if (ftpb == UFT_NAN) return s2 | QNAN; /* B = NaN? quiet B */ if (ftpa == UFT_NAN) return s1 | QNAN; /* A = NaN? quiet A */ if (sub) b.sign = b.sign ^ 1; /* sign of B */ if (ftpb == UFT_INF) { /* B = inf? */ if ((ftpa == UFT_INF) && (a.sign ^ b.sign)) { /* eff sub of inf? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* inv op trap */ return CQNAN; } /* canonical NaN */ return (sub ? (s2 ^ FPR_SIGN) : s2); } /* return B */ if (ftpa == UFT_INF) return s1; /* A = inf? ret A */ if (ftpa == UFT_ZERO) a = b; /* s1 = 0? */ else if (ftpb != UFT_ZERO) { /* s2 != 0? */ if ((a.exp < b.exp) /* s1 < s2? swap */ || ((a.exp == b.exp) && (a.frac < b.frac))) { t = a; a = b; b = t; } ediff = a.exp - b.exp; /* exp diff */ if (ediff > 63) b.frac = 1; /* >63? retain sticky */ else if (ediff) { /* [1,63]? shift */ sticky = ((b.frac << (64 - ediff)) & X64_QUAD) ? 1 : 0; /* lost bits */ b.frac = ((b.frac >> ediff) & X64_QUAD) | sticky; } if (a.sign ^ b.sign) { /* eff sub? */ a.frac = (a.frac - b.frac) & X64_QUAD; /* subtract fractions */ ieee_norm(&a); } /* normalize */ else { /* eff add */ a.frac = (a.frac + b.frac) & X64_QUAD; /* add frac */ if (a.frac < b.frac) { /* chk for carry */ a.frac = UF_NM | (a.frac >> 1) | /* shift in carry */ (a.frac & 1); /* retain sticky */ a.exp = a.exp + 1; } } /* skip norm */ } /* end else if */ return ieee_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Multiply 2 IEEE floating-point values. * * The following steps are taken: * - Take care of NaNs and infinites * - Test for zero operands (fast exit) * - 64b x 64b fraction multiply, yielding 128b result * - Normalize (at most 1 bit) * - Insert "sticky" bit in low order fraction, for rounding * . * * Because IEEE fractions have a range of [1,2), the result can have a range * of [1,4). Results in the range of [1,2) appear to be denormalized by one * place, when in fact they are correct. Results in the range of [2,4) appear * to be in correct, when in fact they are 2X larger. This problem is taken * care of in the result exponent calculation. * * \param s1 Multiplicand. * \param s2 Multiplier. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_S for S-floating or DT_T for T-floating. * \return IEEE floating. **/ u64 CAlphaCPU::ieee_fmul(u64 s1, u64 s2, u32 ins, u32 dp) { UFP a; UFP b; u32 ftpa; u32 ftpb; u64 resl; ftpa = ieee_unpack(s1, &a, ins); /* unpack operands */ ftpb = ieee_unpack(s2, &b, ins); if (ftpb == UFT_NAN) return s2 | QNAN; /* B = NaN? quiet B */ if (ftpa == UFT_NAN) return s1 | QNAN; /* A = NaN? quiet A */ a.sign = a.sign ^ b.sign; /* sign of result */ if ((ftpa == UFT_ZERO) || (ftpb == UFT_ZERO)) { /* zero operand? */ if ((ftpa == UFT_INF) || (ftpb == UFT_INF)) { /* 0 * inf? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* inv op trap */ return CQNAN; } /* canonical NaN */ return (a.sign ? FMZERO : FPZERO); } /* return signed 0 */ if (ftpb == UFT_INF) return (a.sign ? FMINF : FPINF); /* B = inf? */ if (ftpa == UFT_INF) return (a.sign ? FMINF : FPINF); /* A = inf? */ a.exp = a.exp + b.exp + 1 - T_BIAS; /* add exponents */ resl = uemul64(a.frac, b.frac, &a.frac); /* multiply fracs */ ieee_norm(&a); /* normalize */ a.frac = a.frac | (resl ? 1 : 0); /* sticky bit */ return ieee_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Divide 2 IEEE floating-point values. * * The following steps are taken: * - Take care of NaNs and infinites * - Check for zero cases * - Divide fractions (55b to develop a rounding bit) * - Set sticky bit if remainder non-zero * . * * Because IEEE fractions have a range of [1,2), the result can have a range * of (.5,2). Results in the range of [1,2) are correct. Results in the * range of (.5,1) need to be normalized by one place. * * \param s1 Dividend. * \param s2 Divisor. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_S for S-floating or DT_T for T-floating. * \return IEEE floating. **/ u64 CAlphaCPU::ieee_fdiv(u64 s1, u64 s2, u32 ins, u32 dp) { UFP a; UFP b; u32 ftpa; u32 ftpb; u32 sticky; ftpa = ieee_unpack(s1, &a, ins); ftpb = ieee_unpack(s2, &b, ins); if (ftpb == UFT_NAN) return s2 | QNAN; /* B = NaN? quiet B */ if (ftpa == UFT_NAN) return s1 | QNAN; /* A = NaN? quiet A */ a.sign = a.sign ^ b.sign; /* sign of result */ if (ftpb == UFT_INF) { /* B = inf? */ if (ftpa == UFT_INF) { /* inf/inf? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* inv op trap */ return CQNAN; } /* canonical NaN */ return (a.sign ? FMZERO : FPZERO); } /* !inf/inf, ret 0 */ if (ftpa == UFT_INF) { /* A = inf? */ if (ftpb == UFT_ZERO) /* inf/0? */ ieee_trap(TRAP_DZE, 1, FPCR_DZED, ins); /* div by 0 trap */ return (a.sign ? FMINF : FPINF); } /* return inf */ if (ftpb == UFT_ZERO) { /* B = 0? */ if (ftpa == UFT_ZERO) { /* 0/0? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* inv op trap */ return CQNAN; } /* canonical NaN */ ieee_trap(TRAP_DZE, 1, FPCR_DZED, ins); /* div by 0 trap */ return (a.sign ? FMINF : FPINF); } /* return inf */ if (ftpa == UFT_ZERO) return (a.sign ? FMZERO : FPZERO); /* A = 0? */ a.exp = a.exp - b.exp + T_BIAS; /* unbiased exp */ a.frac = a.frac >> 1; /* allow 1 bit left */ b.frac = b.frac >> 1; a.frac = ufdiv64(a.frac, b.frac, 55, &sticky); /* divide */ ieee_norm(&a); /* normalize */ a.frac = a.frac | sticky; /* insert sticky */ return ieee_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Determine principal square root of a IEEE floating-point value. * * The following steps are taken: * - Take care of NaNs, +infinite, zero * - Check for negative operand * - Compute result exponent * - Compute sqrt of fraction * . * * \param op IEEE floating. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_S for S-floating or DT_T for T-floating. * \return IEEE floating. **/ u64 CAlphaCPU::ieee_sqrt(u64 op, u32 ins, u32 dp) { u32 ftpb; UFP b; ftpb = ieee_unpack(op, &b, ins); /* unpack */ if (ftpb == UFT_NAN) return op | QNAN; /* NaN? */ if ((ftpb == UFT_ZERO) || /* zero? */ ((ftpb == UFT_INF) && !b.sign)) return op; /* +infinity? */ if (b.sign) { /* minus? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* signal inv op */ return CQNAN; } b.exp = ((b.exp - T_BIAS) >> 1) + T_BIAS; /* result exponent */ b.frac = fsqrt64(b.frac, b.exp); /* result fraction */ return ieee_rpack(&b, ins, dp); /* round and pack */ } //\} /***************************************************************************/ /** * \name IEEE_fp_support * IEEE floating point support functions ******************************************************************************/ //\{ /** * \brief Unpack IEEE floating-point value * * Converts a IEEE floating-point value to it's sign, exponent and fraction * components. * * \param op IEEE floating. * \param r Pointer to the unpacked-floating-point UFP structure * where the results are to be returned. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \return Returns the type of value (UFT_ZERO, UFT_FIN, etc.). **/ int CAlphaCPU::ieee_unpack(u64 op, UFP *r, u32 ins) { r->sign = FPR_GETSIGN(op); /* get sign */ r->exp = FPR_GETEXP(op); /* get exponent */ r->frac = FPR_GETFRAC(op); /* get fraction */ if (r->exp == 0) /* exponent = 0? */ { if (r->frac == 0) return UFT_ZERO; /* frac = 0? then true 0 */ if (state.fpcr & FPCR_DNZ) /* denorms to 0? */ { r->frac = 0; /* clear fraction */ return UFT_ZERO; } r->frac = r->frac << FPR_GUARD; /* guard fraction */ ieee_norm(r); /* normalize dnorm */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* signal inv op */ return UFT_DENORM; } if (r->exp == FPR_NAN) /* exponent = max? */ { if (r->frac == 0) return UFT_INF; /* frac = 0? then inf */ if (!(r->frac & QNAN)) /* signaling NaN? */ ieee_trap(TRAP_INV, 1, FPCR_INVD, ins); /* signal inv op */ return UFT_NAN; } r->frac = (r->frac | FPR_HB) << FPR_GUARD; /* ins hidden bit, guard */ return UFT_FIN; /* finite */ } /** * \brief Normalize IEEE floating-point value * * Normalize exponent and fraction components. Input must be zero, finite, or *denorm. * * \param r Pointer to the unpacked-floating-point UFP structure * containing the value to be normalized, and where the * results are to be returned. **/ void CAlphaCPU::ieee_norm(UFP *r) { s32 i; static u64 normmask[5] = {U64(0xc000000000000000), U64(0xf000000000000000), U64(0xff00000000000000), U64(0xffff000000000000), U64(0xffffffff00000000)}; static s32 normtab[6] = {1, 2, 4, 8, 16, 32}; r->frac = r->frac & X64_QUAD; if (r->frac == 0) { /* if fraction = 0 */ r->exp = 0; /* result is signed 0 */ return; } while ((r->frac & UF_NM) == 0) { /* normalized? */ for (i = 0; i < 5; i++) { /* find first 1 */ if (r->frac & normmask[i]) break; } r->frac = r->frac << normtab[i]; /* shift frac */ r->exp = r->exp - normtab[i]; } /* decr exp */ return; } /** * \brief Round and pack IEEE floating-point value * * Converts sign, exponent and fraction components to an IEEE floating * point value. * * Much of the treachery of the IEEE standard is buried here: * - Rounding modes (chopped, +infinity, nearest, -infinity). * - Inexact (set if there are any rounding bits, regardless of rounding). * - Overflow (result is infinite if rounded, max if not). * - Underflow (no denorms!). * . * * Underflow handling is particularly complicated: * - Result is always 0. * - UNF and INE are always set in FPCR. * - If /U is set, * - If /S is clear, trap. * - If /S is set, UNFD is set, but UNFZ is clear, ignore UNFD and * trap, because the hardware cannot produce denormals. * - If /S is set, UNFD is set, and UNFZ is set, do not trap. * . * - If /SUI is set, and INED is clear, trap * . * * \param r Pointer to the unpacked-floating-point UFP structure * to be packed. * \param ins The instruction currently being executed. Used to properly * handle exceptions and to determine the rounding mode. * \param dp DT_S for S-floating or DT_T for T-floating. * \return IEEE floating. **/ u64 CAlphaCPU::ieee_rpack(UFP *r, u32 ins, u32 dp) { static const u64 stdrnd[2] = {UF_SRND, UF_TRND}; static const u64 infrnd[2] = {UF_SINF, UF_TINF}; static const s32 expmax[2] = {T_BIAS - S_BIAS + S_M_EXP - 1, T_M_EXP - 1}; static const s32 expmin[2] = {T_BIAS - S_BIAS, 0}; u64 rndadd; u64 rndbits; u64 res; u64 rndm; if (r->frac == 0) return (((u64)r->sign) << FPR_V_SIGN); /* result 0? */ rndm = I_GETFRND(ins); /* inst round mode */ if (rndm == I_FRND_D) rndm = FPCR_GETFRND(state.fpcr); /* dynamic? use FPCR */ rndbits = r->frac & infrnd[dp]; /* isolate round bits */ if (rndm == I_FRND_N) rndadd = stdrnd[dp]; /* round to nearest? */ else if (((rndm == I_FRND_P) && !r->sign) /* round to inf and */ || ((rndm == I_FRND_M) && r->sign)) /* right sign? */ rndadd = infrnd[dp]; else rndadd = 0; r->frac = (r->frac + rndadd) & X64_QUAD; /* round */ if ((r->frac & UF_NM) == 0) { /* carry out? */ r->frac = (r->frac >> 1) | UF_NM; /* renormalize */ r->exp = r->exp + 1; } if (rndbits) /* inexact? */ ieee_trap(TRAP_INE, Q_SUI(ins), FPCR_INED, ins); /* set inexact */ if (r->exp > expmax[dp]) { /* ovflo? */ ieee_trap(TRAP_OVF, 1, FPCR_OVFD, ins); /* set overflow trap */ ieee_trap(TRAP_INE, Q_SUI(ins), FPCR_INED, ins); /* set inexact */ if (rndadd) /* did we round? */ return (r->sign ? FMINF : FPINF); /* return infinity */ return (r->sign ? FMMAX : FPMAX); } /* no, return max */ if (r->exp <= expmin[dp]) { /* underflow? */ ieee_trap(TRAP_UNF, ins & I_FTRP_U, /* set underflow trap */ (state.fpcr & FPCR_UNDZ) ? FPCR_UNFD : 0, ins); /* (dsbl only if UNFZ set) */ ieee_trap(TRAP_INE, Q_SUI(ins), FPCR_INED, ins); /* set inexact */ return 0; } /* underflow to +0 */ res = (((u64)r->sign) << FPR_V_SIGN) | /* form result */ (((u64)r->exp) << FPR_V_EXP) | ((r->frac >> FPR_GUARD) & FPR_FRAC); if ((rndm == I_FRND_N) && (rndbits == stdrnd[dp])) /* nearest and halfway? */ res = res & ~1; /* clear lo bit */ return res; } /** * \brief Set IEEE floating-point trap * * Called when a IEEE floating-point operation detects an exception. * * \param trap A bitmask in which the bits are set that correspond to * the exception that occurred. * \param instenb True if the exception is enabled in the instruction. * \param fpcrdsb A bitmask containing the bits that, if set in the floating- * point control register (fpcr), disable the trap. * \param ins The instruction currently being executed. Used to properly * set some registers for the trap to be handled. **/ void CAlphaCPU::ieee_trap(u64 trap, u32 instenb, u64 fpcrdsb, u32 ins) { u64 real_trap = U64(0x0); if (~(state.fpcr & (trap << 51))) // trap bit not set in FPCR real_trap |= trap << 41; // SET trap bit in EXC_SUM if ((instenb != 0) /* not enabled in inst? ignore */ && !((ins & I_FTRP_S) && (state.fpcr & fpcrdsb))) /* /S and disabled? ignore */ real_trap |= trap; // trap bit in EXC_SUM if (real_trap) ARITH_TRAP(real_trap | ((ins & I_FTRP_S) ? TRAP_SWC : 0), I_GETRC(ins)); return; } //\} ================================================ FILE: src/AlphaCPU_vaxfloat.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /* Copyright notice from Simh/alpha/alpha_fpv.c: Copyright (c) 2003-2006, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include "AlphaCPU.hpp" #include "StdAfx.hpp" #include "cpu_debug.hpp" #define IPMAX U64(0x7FFFFFFFFFFFFFFF) /* plus MAX (int) */ #define IMMAX U64(0x8000000000000000) /* minus MAX (int) */ /* Unpacked rounding constants */ #define UF_FRND U64(0x0000008000000000) /* F round */ #define UF_DRND U64(0x0000000000000080) /* D round */ #define UF_GRND U64(0x0000000000000400) /* G round */ /***************************************************************************/ /** * \name VAX_fp_load_store * VAX floating point load and store functions ******************************************************************************/ //\{ /** * \brief Convert the VAX F-floating memory format to the VAX floating register * format. * * Adjust the exponent base, reorder the bytes of the fraction to compensate for * the VAX byte-order, and widen the exponent and fraction fields. * * \param op 32-bit VAX F-floating value in memory format. * \return The value op converted to 64-bit VAX floating in *register format. **/ u64 CAlphaCPU::vax_ldf(u32 op) { u32 exp = F_GETEXP(op); if (exp != 0) exp = exp + G_BIAS - F_BIAS; /* zero? */ u64 res = (((u64)(op & F_SIGN)) ? FPR_SIGN : 0) | /* finite non-zero */ (((u64)exp) << FPR_V_EXP) | (((u64)SWAP_VAXF(op & ~(F_SIGN | F_EXP))) << F_V_FRAC); // printf("vax_ldf: %08x -> %016" PRIx64 ".\n", op, res); return res; } /** * \brief Convert the VAX G-floating memory format to the VAX floating register * format. * * Reorder the bytes to compensate for the VAX byte-order. * * \param op 64-bit VAX G-floating value in memory format. * \return The value op converted to 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_ldg(u64 op) { return SWAP_VAXG(op); /* swizzle bits */ } /** * \brief Convert the VAX floating register format to the VAX F-floating memory * format. * * Adjust the exponent base, make the exponent and fraction fields smaller, and * reorder the bytes of the fraction to compensate for the VAX byte-order. * * \param op 64-bit VAX floating in register format. * \return The value op converted to 32-bit VAX F-floating value in memory *format. **/ u32 CAlphaCPU::vax_stf(u64 op) { u32 sign = FPR_GETSIGN(op) ? F_SIGN : 0; // u32 exp = ((u32) (op >> (FPR_V_EXP - F_V_EXP))) & F_EXP; u32 exp = FPR_GETEXP(op); if (exp != 0) exp = exp + F_BIAS - G_BIAS; /* zero? */ exp = (exp << F_V_EXP) & F_EXP; u32 frac = (u32)(op >> F_V_FRAC); u32 res = sign | exp | (SWAP_VAXF(frac) & ~(F_SIGN | F_EXP)); // printf("vax_stf: %016" PRIx64 " -> %08x.\n", op, res); return res; } /** * \brief Convert the VAX floating register format to the VAX G-floating memory * format. * * Reorder the bytes to compensate for the VAX byte-order. * * \param op 64-bit VAX floating in register format. * \return The value op converted to 64-bit VAX G-floating value in memory *format. **/ u64 CAlphaCPU::vax_stg(u64 op) { return SWAP_VAXG(op); /* swizzle bits */ } //\} /***************************************************************************/ /** * \name VAX_fp_operations * VAX floating point operations ******************************************************************************/ //\{ /** * \brief Compare 2 VAX floating-point values. * * \param s1 First 64-bit VAX floating in register format to be compared. * \param s1 Second 64-bit VAX floating in register format to be compared. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \return 0 if s1==s2, 1 if s1>s2, -1 if s1> (UF_V_NM - ubexp); /* leave rnd bit */ if (rndm) a.frac = a.frac + 1; /* not chopped, round */ a.frac = a.frac >> 1; /* now justified */ if ((a.frac > (a.sign ? IMMAX : IPMAX)) /* out of range? */ && (ins & I_FTRP_V)) /* trap enabled? */ vax_trap(TRAP_IOV, ins); } /* set overflow */ else { if (ubexp > (UF_V_NM + 64)) a.frac = 0; /* out of range */ else a.frac = (a.frac << (ubexp - UF_V_NM - 1)) & X64_QUAD; /* no rnd bit */ if (ins & I_FTRP_V) /* trap enabled? */ vax_trap(TRAP_IOV, ins); } /* set overflow */ return (a.sign ? NEG_Q(a.frac) : a.frac); } /** * \brief Add or subtract 2 VAX floating-point values. * * \param s1 Augend or minuend in 64-bit VAX floating register format. * \param s2 Addend or subtrahend in 64-bit VAX floating register format. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_F for F-floating or DT_G for G-floating. * \param sub subtract if true, add if false. * \return 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_fadd(u64 s1, u64 s2, u32 ins, u32 dp, bool sub) { UFP a; UFP b; UFP t; u32 sticky; s32 ediff; vax_unpack(s1, &a, ins); vax_unpack(s2, &b, ins); if (sub) b.sign = b.sign ^ 1; /* sub? invert b sign */ if (a.exp == 0) a = b; /* s1 = 0? */ else if (b.exp) /* s2 != 0? */ { if ((a.exp < b.exp) /* |s1| < |s2|? swap */ || ((a.exp == b.exp) && (a.frac < b.frac))) { t = a; a = b; b = t; } ediff = a.exp - b.exp; /* exp diff */ if (a.sign ^ b.sign) /* eff sub? */ { if (ediff > 63) b.frac = 1; /* >63? retain sticky */ else if (ediff) /* [1,63]? shift */ { sticky = ((b.frac << (64 - ediff)) & X64_QUAD) ? 1 : 0; /* lost bits */ b.frac = (b.frac >> ediff) | sticky; } a.frac = (a.frac - b.frac) & X64_QUAD; /* subtract fractions */ vax_norm(&a); /* normalize */ } else /* eff add */ { if (ediff > 63) b.frac = 0; /* >63? b disappears */ else if (ediff) b.frac = b.frac >> ediff; /* denormalize */ a.frac = (a.frac + b.frac) & X64_QUAD; /* add frac */ if (a.frac < b.frac) /* chk for carry */ { a.frac = UF_NM | (a.frac >> 1); /* shift in carry */ a.exp = a.exp + 1; /* skip norm */ } } } /* end else if */ return vax_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Multiply 2 VAX floating-point values. * * \param s1 Multiplicand in 64-bit VAX floating register format. * \param s2 Multiplier in 64-bit VAX floating register format. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_F for F-floating or DT_G for G-floating. * \return 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_fmul(u64 s1, u64 s2, u32 ins, u32 dp) { UFP a; UFP b; vax_unpack(s1, &a, ins); vax_unpack(s2, &b, ins); if ((a.exp == 0) || (b.exp == 0)) return 0; /* zero argument? */ a.sign = a.sign ^ b.sign; /* sign of result */ a.exp = a.exp + b.exp - G_BIAS; /* add exponents */ uemul64(a.frac, b.frac, &a.frac); /* mpy fractions */ vax_norm(&a); /* normalize */ return vax_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Divide 2 VAX floating-point values. * * Needs to develop at least one rounding bit. Since the first * divide step can fail, develop 2 more bits than the precision of * the fraction. * * \param s1 Dividend in 64-bit VAX floating register format. * \param s2 Divisor in 64-bit VAX floating register format. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_F for F-floating or DT_G for G-floating. * \return 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_fdiv(u64 s1, u64 s2, u32 ins, u32 dp) { UFP a; UFP b; vax_unpack(s1, &a, ins); vax_unpack(s2, &b, ins); if (b.exp == 0) { /* divr = 0? */ vax_trap(TRAP_DZE, ins); /* dze trap */ return 0; } if (a.exp == 0) return 0; /* divd = 0? */ a.sign = a.sign ^ b.sign; /* result sign */ a.exp = a.exp - b.exp + G_BIAS + 1; /* unbiased exp */ a.frac = a.frac >> 1; /* allow 1 bit left */ b.frac = b.frac >> 1; a.frac = ufdiv64(a.frac, b.frac, 55, NULL); /* divide */ vax_norm(&a); /* normalize */ return vax_rpack(&a, ins, dp); /* round and pack */ } /** * \brief Determine principal square root of a VAX floating-point value. * * \param op 64-bit VAX floating in register format. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \param dp DT_F for F-floating or DT_G for G-floating. * \return 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_sqrt(u64 op, u32 ins, u32 dp) { UFP b; vax_unpack(op, &b, ins); if (b.exp == 0) return 0; /* zero? */ if (b.sign) { /* minus? */ vax_trap(TRAP_INV, ins); /* invalid operand */ return 0; } b.exp = ((b.exp + 1 - G_BIAS) >> 1) + G_BIAS; /* result exponent */ b.frac = fsqrt64(b.frac, b.exp); /* result fraction */ return vax_rpack(&b, ins, dp); /* round and pack */ } //\} /***************************************************************************/ /** * \name VAX_fp_support * VAX floating point support functions ******************************************************************************/ //\{ /** * \brief Set VAX floating-point trap * * Called when a VAX floating-point operation detects an exception. * * \param mask A bitmask in which the bits are set that correspond to * the exception that occurred. * \param ins The instruction currently being executed. Used to properly * set some registers for the trap to be handled. **/ void CAlphaCPU::vax_trap(u64 mask, u32 ins) { ARITH_TRAP(mask | ((ins & I_FTRP_S) ? TRAP_SWC : 0), I_GETRC(ins)); } /** * \brief Unpack VAX floating-point value * * Converts a VAX floating-point value to it's sign, exponent and fraction * components. * * \param op 64-bit VAX floating in register format. * \param r Pointer to the unpacked-floating-point UFP structure * where the results are to be returned. * \param ins The instruction currently being executed. Used to properly * handle exceptions. **/ void CAlphaCPU::vax_unpack(u64 op, UFP *r, u32 ins) { r->sign = FPR_GETSIGN(op); /* get sign */ r->exp = FPR_GETEXP(op); /* get exponent */ r->frac = FPR_GETFRAC(op); /* get fraction */ if (r->exp == 0) /* exp = 0? */ { if (r->sign != 0) /* rsvd op? */ vax_trap(TRAP_INV, ins); // zero r->frac = r->sign = 0; return; } r->frac = (r->frac | FPR_HB) << FPR_GUARD; /* ins hidden bit, guard */ return; } /** * \brief Unpack VAX D-floating-point value * * Converts a VAX D-floating-point value to it's sign, exponent and fraction * components. * * \param op 64-bit VAX D-floating in register format. * \param r Pointer to the unpacked-floating-point UFP structure * where the results are to be returned. * \param ins The instruction currently being executed. Used to properly * handle exceptions. **/ void CAlphaCPU::vax_unpack_d(u64 op, UFP *r, u32 ins) { r->sign = FDR_GETSIGN(op); /* get sign */ r->exp = FDR_GETEXP(op); /* get exponent */ r->frac = FDR_GETFRAC(op); /* get fraction */ if (r->exp == 0) /* exp = 0? */ { if (op != 0) /* rsvd op? */ vax_trap(TRAP_INV, ins); r->frac = r->sign = 0; return; } r->exp = r->exp + G_BIAS - D_BIAS; /* change to G bias */ r->frac = (r->frac | FDR_HB) << FDR_GUARD; /* ins hidden bit, guard */ return; } /** * \brief Normalize VAX floating-point value * * Normalize exponent and fraction components. * * \param r Pointer to the unpacked-floating-point UFP structure * containing the value to be normalized, and where the * results are to be returned. **/ void CAlphaCPU::vax_norm(UFP *r) { s32 i; static u64 normmask[5] = {U64(0xc000000000000000), U64(0xf000000000000000), U64(0xff00000000000000), U64(0xffff000000000000), U64(0xffffffff00000000)}; static s32 normtab[6] = {1, 2, 4, 8, 16, 32}; r->frac = r->frac & X64_QUAD; if (r->frac == 0) { /* if fraction = 0 */ r->sign = r->exp = 0; /* result is 0 */ return; } while ((r->frac & UF_NM) == 0) { /* normalized? */ for (i = 0; i < 5; i++) { /* find first 1 */ if (r->frac & normmask[i]) break; } r->frac = r->frac << normtab[i]; /* shift frac */ r->exp = r->exp - normtab[i]; } /* decr exp */ return; } /** * \brief Round and pack VAX floating-point value * * Converts sign, exponent and fraction components to a register-format VAX * floating point value. * * \param r Pointer to the unpacked-floating-point UFP structure * to be packed. * \param ins The instruction currently being executed. Used to properly * handle exceptions and to determine the rounding mode. * \param dp DT_F for F-floating or DT_G for G-floating. * \return 64-bit VAX floating in register format. **/ u64 CAlphaCPU::vax_rpack(UFP *r, u32 ins, u32 dp) { u32 rndm = I_GETFRND(ins); static const u64 roundbit[2] = {UF_FRND, UF_GRND}; static const s32 expmax[2] = {G_BIAS - F_BIAS + F_M_EXP, G_M_EXP}; static const s32 expmin[2] = {G_BIAS - F_BIAS, 0}; if (r->frac == 0) return 0; /* result 0? */ if (rndm) { /* round? */ r->frac = (r->frac + roundbit[dp]) & X64_QUAD; /* add round bit */ if ((r->frac & UF_NM) == 0) { /* carry out? */ r->frac = (r->frac >> 1) | UF_NM; /* renormalize */ r->exp = r->exp + 1; } } if (r->exp > expmax[dp]) { /* ovflo? */ vax_trap(TRAP_OVF, ins); /* set trap */ r->exp = expmax[dp]; } /* return max */ if (r->exp <= expmin[dp]) { /* underflow? */ if (ins & I_FTRP_V) vax_trap(TRAP_UNF, ins); /* enabled? set trap */ return 0; } /* underflow to 0 */ return (((u64)r->sign) << FPR_V_SIGN) | (((u64)r->exp) << FPR_V_EXP) | ((r->frac >> FPR_GUARD) & FPR_FRAC); } /** * \brief Round and pack VAX D-floating-point value * * Converts sign, exponent and fraction components to a register-format VAX * D-floating-point value. * * \param r Pointer to the unpacked-floating-point UFP structure * to be packed. * \param ins The instruction currently being executed. Used to properly * handle exceptions. * \return 64-bit VAX D-floating in register format. **/ u64 CAlphaCPU::vax_rpack_d(UFP *r, u32 ins) { if (r->frac == 0) return 0; /* result 0? */ r->exp = r->exp + D_BIAS - G_BIAS; /* rebias */ if (r->exp > FDR_M_EXP) { /* ovflo? */ vax_trap(TRAP_OVF, ins); /* set trap */ r->exp = FDR_M_EXP; } /* return max */ if (r->exp <= 0) { /* underflow? */ if (ins & I_FTRP_V) vax_trap(TRAP_UNF, ins); /* enabled? set trap */ return 0; } /* underflow to 0 */ return (((u64)r->sign) << FDR_V_SIGN) | (((u64)r->exp) << FDR_V_EXP) | ((r->frac >> FDR_GUARD) & FDR_FRAC); } //\} ================================================ FILE: src/AlphaCPU_vmspal.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "AlphaCPU.hpp" #include "StdAfx.hpp" #include "AliM1543C_ide.hpp" #include "Disk.hpp" #include "Serial.hpp" /*********************************************************** * * * VMS PALcode * * * ***********************************************************/ #define p4 state.r[32 + 4] #define p5 state.r[32 + 5] #define p6 state.r[32 + 6] #define p7 state.r[32 + 7] #define p20 state.r[32 + 20] #define p21 state.r[32 + 21] #define p22 state.r[32 + 22] #define p23 state.r[32 + 23] #define r0 state.r[0] #define r1 state.r[1] #define r2 state.r[2] #define r3 state.r[3] //#define r4 state.r[4] //#define r5 state.r[5] //#define r6 state.r[6] //#define r7 state.r[7] #define r8 state.r[8] #define r9 state.r[9] #define r10 state.r[10] #define r11 state.r[11] #define r12 state.r[12] #define r13 state.r[13] #define r14 state.r[14] #define r15 state.r[15] #define r16 state.r[16] #define r17 state.r[17] #define r18 state.r[18] #define r19 state.r[19] //#define r20 state.r[20] //#define r21 state.r[21] //#define r22 state.r[22] //#define r23 state.r[23] #define r24 state.r[24] //#define r25 state.r[25] //#define r26 state.r[26] #define r27 state.r[27] #define r28 state.r[28] #define r29 state.r[29] #define r30 state.r[30] #define r31 state.r[31] #define hw_stq(a, b) cSystem->WriteMem(a & ~U64(0x7), 64, b, this) #define hw_stl(a, b) cSystem->WriteMem(a & ~U64(0x3), 32, b, this) #define stq(a, b) \ if (virt2phys(a, &phys_address, ACCESS_WRITE, NULL, 0)) \ return -1; \ cSystem->WriteMem(phys_address, 64, b, this); #define ldq(a, b) \ if (virt2phys(a, &phys_address, ACCESS_READ, NULL, 0)) \ return -1; \ b = cSystem->ReadMem(phys_address, 64, this); #define stl(a, b) \ if (virt2phys(a, &phys_address, ACCESS_WRITE, NULL, 0)) \ return -1; \ cSystem->WriteMem(phys_address, 32, b, this); #define ldl(a, b) \ if (virt2phys(a, &phys_address, ACCESS_READ, NULL, 0)) \ return -1; \ b = sext_u64_32(cSystem->ReadMem(phys_address, 32, this)); #define ldb(a, b) \ if (virt2phys(a, &phys_address, ACCESS_READ, NULL, 0)) \ return -1; \ b = (char)(cSystem->ReadMem(phys_address, 8, this)); #define hw_ldq(a, b) b = cSystem->ReadMem(a & ~U64(0x7), 64, this) #define hw_ldl(a, b) b = sext_u64_32(cSystem->ReadMem(a & ~U64(0x3), 32, this)); #define hw_ldbu(a, b) b = cSystem->ReadMem(a, 8, this) /** * Mask for interrupt enabling at IPL's. * * For each of the 32 IPL's gives the values for eien, slen, cren, pcen, * sien and asten. * * Source: table of IER masks in PALcode at offset 0d00H. **/ static int ipl_ier_mask[32][6] = { /* ei, sl, cr, pc, si, ast */ {0x3f, 0, 1, 3, 0xfffe, 1}, {0x3f, 0, 1, 3, 0xfffc, 1}, {0x3f, 0, 1, 3, 0xfff8, 0}, {0x3f, 0, 1, 3, 0xfff0, 0}, {0x3f, 0, 1, 3, 0xffe0, 0}, {0x3f, 0, 1, 3, 0xffc0, 0}, {0x3f, 0, 1, 3, 0xff80, 0}, {0x3f, 0, 1, 3, 0xff00, 0}, {0x3f, 0, 1, 3, 0xfe00, 0}, {0x3f, 0, 1, 3, 0xfc00, 0}, {0x3f, 0, 1, 3, 0xf800, 0}, {0x3f, 0, 1, 3, 0xf000, 0}, {0x3f, 0, 1, 3, 0xe000, 0}, {0x3f, 0, 1, 3, 0xc000, 0}, {0x3f, 0, 1, 3, 0x8000, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3f, 0, 1, 3, 0, 0}, {0x3d, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x31, 0, 1, 0, 0, 0}, {0x31, 0, 1, 3, 0, 0}, {0x10, 0, 1, 3, 0, 0}}; /***************************************************************************/ /** * \name VMS_pal_call * VMS PALcode CALL replacement routines. ******************************************************************************/ //\{ /** * Implementation of CALL_PAL CFLUSH opcode. **/ void CAlphaCPU::vmspal_call_cflush() { // don't do anything... } /** * Implementation of CALL_PAL DRAINA opcode. **/ void CAlphaCPU::vmspal_call_draina() { // don't do anything... } /** * Implementation of CALL_PAL LDQP opcode. **/ void CAlphaCPU::vmspal_call_ldqp() { hw_ldq(r16, r0); } /** * Implementation of CALL_PAL STQP opcode. **/ void CAlphaCPU::vmspal_call_stqp() { hw_stq(r16, r17); } /** * Implementation of CALL_PAL SWPCTX opcode. **/ void CAlphaCPU::vmspal_call_swpctx() { p23 = state.pc; if (r16 & 0x7f) { // context pointer not properly aligned... p4 = 0x430; hw_stq(p21 + 0x150, p23); hw_stq(p21 + 0x158, p4); vmspal_int_initiate_exception(); return; } hw_ldq(r16 + 0x30, p4); hw_ldq(r16 + 0x38, p5); hw_ldq(r16 + 0x28, p6); p20 = state.aster | (state.astrr << 4); p6 &= 0xff; state.asn0 = (int)p6; state.asn1 = (int)p6; state.asn = (int)p6; state.aster = (int)p4 & 0xf; state.astrr = (int)(p4 >> 4) & 0xf; state.fpen = (int)p5 & 1; state.ppcen = (int)(p5 >> 0x3e) & 1; state.check_int = true; hw_ldq(r16 + 0x40, p7); hw_ldq(r16 + 0x20, p6); p4 = (state.cc & U64(0xffffffff)) + state.cc_offset; state.cc_offset = ((u32)p7 & 0xffffffff) - (state.cc & U64(0xffffffff)); p6 <<= 0x0d; hw_stq(p21 + 8, p6); hw_ldq(p21 + 0x10, p5); hw_ldq(r16, p6); hw_stl(p5 + 0x40, p4); hw_stq(p5 + 0x30, p20); hw_stq(p5, r30); r30 = p6; hw_stq(p21 + 0x10, r16); } /** * Implementation of CALL_PAL MFPR_ASN opcode. **/ void CAlphaCPU::vmspal_call_mfpr_asn() { r0 = state.asn; } /** * Implementation of CALL_PAL MTPR_ASTEN opcode. **/ void CAlphaCPU::vmspal_call_mtpr_asten() { r0 = state.aster; state.aster &= r16; state.aster |= (r16 >> 4) & 0xf; state.check_int = true; } /** * Implementation of CALL_PAL MTPR_ASTSR opcode. **/ void CAlphaCPU::vmspal_call_mtpr_astsr() { r0 = state.astrr; state.astrr &= r16; state.astrr |= (r16 >> 4) & 0xf; state.check_int = true; } /** * Implementation of CALL_PAL CSERVE opcode. **/ void CAlphaCPU::vmspal_call_cserve() { p23 = state.pc; switch (r16) { case 0x10: hw_ldl(r17, r0); break; case 0x11: hw_stl(r17, r18); break; case 0x12: set_pc(0x12e21); break; case 0x13: set_pc(0x12f95); break; case 0x14: set_pc(0x13115); break; case 0x15: set_pc(0x131c1); break; case 0x40: set_pc(0x13249); break; case 0x41: hw_ldq(p21 + 0x98, r0); break; case 0x42: set_pc(0x13781); break; case 0x43: set_pc(0x13261); break; case 0x44: set_pc(r17); break; case 0x45: set_pc(0x13289); break; case 0x65: set_pc(0x132bd); break; case 0x3e: set_pc(0x1344d); break; case 0x66: set_pc(0x133e9); break; } } /** * Implementation of CALL_PAL MFPR_FEN opcode. **/ void CAlphaCPU::vmspal_call_mfpr_fen() { r0 = state.fpen ? 1 : 0; } /** * Implementation of CALL_PAL MTPR_FEN opcode. **/ void CAlphaCPU::vmspal_call_mtpr_fen() { hw_ldq(p21 + 0x10, p4); state.fpen = (r16 & 1); hw_stl(p4 + 0x38, r16); } /** * Implementation of CALL_PAL MFPR_IPL opcode. **/ void CAlphaCPU::vmspal_call_mfpr_ipl() { r0 = (p22 >> 8) & 0x1f; } /** * Implementation of CALL_PAL MTPR_IPL opcode. **/ void CAlphaCPU::vmspal_call_mtpr_ipl() { r0 = (p22 >> 8) & 0xff; p22 &= ~U64(0xff00); p22 |= (r16 << 8); state.eien = ipl_ier_mask[r16][0]; state.slen = ipl_ier_mask[r16][1]; state.cren = ipl_ier_mask[r16][2]; state.pcen = ipl_ier_mask[r16][3]; state.sien = ipl_ier_mask[r16][4]; state.asten = ipl_ier_mask[r16][5]; state.check_int = true; } /** * Implementation of CALL_PAL MFPR_MCES opcode. **/ void CAlphaCPU::vmspal_call_mfpr_mces() { r0 = (p22 >> 16) & 0xff; } /** * Implementation of CALL_PAL MTPR_MCES opcode. **/ void CAlphaCPU::vmspal_call_mtpr_mces() { p22 = (p22 & U64(0xffffffffff00ffff)) | (p22 & U64(0x0000000000070000) & ~(r16 << 16)) | ((r16 << 16) & U64(0x0000000000180000)); } /** * Implementation of CALL_PAL MFPR_PCBB opcode. **/ void CAlphaCPU::vmspal_call_mfpr_pcbb() { hw_ldq(p21 + 0x10, r0); } /** * Implementation of CALL_PAL MFPR_PRBR opcode. **/ void CAlphaCPU::vmspal_call_mfpr_prbr() { hw_ldq(p21 + 0xa8, r0); } /** * Implementation of CALL_PAL MTPR_PRBR opcode. **/ void CAlphaCPU::vmspal_call_mtpr_prbr() { hw_stq(p21 + 0xa8, r16); } /** * Implementation of CALL_PAL MFPR_PTBR opcode. **/ void CAlphaCPU::vmspal_call_mfpr_ptbr() { hw_ldq(p21 + 8, r0); r0 >>= 0x0d; } /** * Implementation of CALL_PAL MFPR_SCBB opcode. **/ void CAlphaCPU::vmspal_call_mfpr_scbb() { hw_ldq(p21 + 0x170, r0); r0 >>= 0x0d; } /** * Implementation of CALL_PAL MTPR_SCBB opcode. **/ void CAlphaCPU::vmspal_call_mtpr_scbb() { hw_stq(p21 + 0x170, (r16 & U64(0xffffffff)) << 0xd); } /** * Implementation of CALL_PAL MTPR_SIRR opcode. **/ void CAlphaCPU::vmspal_call_mtpr_sirr() { if (r16 > 0 && r16 < 16) { state.sir |= 1 << r16; state.check_int = true; } } /** * Implementation of CALL_PAL MFPR_SISR opcode. **/ void CAlphaCPU::vmspal_call_mfpr_sisr() { r0 = state.sir; } /** * Implementation of CALL_PAL MFPR_TBCHK opcode. **/ void CAlphaCPU::vmspal_call_mfpr_tbchk() { r0 = U64(0x8000000000000000); } /** * Implementation of CALL_PAL MTPR_TBIA opcode. **/ void CAlphaCPU::vmspal_call_mtpr_tbia() { tbia(ACCESS_READ); tbia(ACCESS_EXEC); flush_icache(); } /** * Implementation of CALL_PAL MTPR_TBIAP opcode. **/ void CAlphaCPU::vmspal_call_mtpr_tbiap() { tbiap(ACCESS_READ); tbiap(ACCESS_EXEC); flush_icache_asm(); } /** * Implementation of CALL_PAL MTPR_TBIS opcode. **/ void CAlphaCPU::vmspal_call_mtpr_tbis() { tbis(r16, ACCESS_READ); tbis(r16, ACCESS_EXEC); } /** * Implementation of CALL_PAL MFPR_ESP opcode. **/ void CAlphaCPU::vmspal_call_mfpr_esp() { u64 t; hw_ldq(p21 + 0x10, t); hw_ldq(t + 8, r0); } /** * Implementation of CALL_PAL MTPR_ESP opcode. **/ void CAlphaCPU::vmspal_call_mtpr_esp() { u64 t; hw_ldq(p21 + 0x10, t); hw_stq(t + 8, r16); } /** * Implementation of CALL_PAL MFPR_SSP opcode. **/ void CAlphaCPU::vmspal_call_mfpr_ssp() { u64 t; hw_ldq(p21 + 0x10, t); hw_ldq(t + 0x10, r0); } /** * Implementation of CALL_PAL MTPR_SSP opcode. **/ void CAlphaCPU::vmspal_call_mtpr_ssp() { u64 t; hw_ldq(p21 + 0x10, t); hw_stq(t + 0x10, r16); } /** * Implementation of CALL_PAL MFPR_USP opcode. **/ void CAlphaCPU::vmspal_call_mfpr_usp() { u64 t; hw_ldq(p21 + 0x10, t); hw_ldq(t + 0x18, r0); } /** * Implementation of CALL_PAL MTPR_USP opcode. **/ void CAlphaCPU::vmspal_call_mtpr_usp() { u64 t; hw_ldq(p21 + 0x10, t); hw_stq(t + 0x18, r16); } /** * Implementation of CALL_PAL MTPR_TBISD opcode. **/ void CAlphaCPU::vmspal_call_mtpr_tbisd() { tbis(r16, ACCESS_READ); } /** * Implementation of CALL_PAL MTPR_TBISI opcode. **/ void CAlphaCPU::vmspal_call_mtpr_tbisi() { tbis(r16, ACCESS_EXEC); } /** * Implementation of CALL_PAL MFPR_ASTEN opcode. **/ void CAlphaCPU::vmspal_call_mfpr_asten() { r0 = state.aster; } /** * Implementation of CALL_PAL MFPR_ASTSR opcode. **/ void CAlphaCPU::vmspal_call_mfpr_astsr() { r0 = state.astrr; } /** * Implementation of CALL_PAL MFPR_VPTB opcode. **/ void CAlphaCPU::vmspal_call_mfpr_vptb() { hw_ldq(p21, r0); } /** * Implementation of CALL_PAL MTPR_DATFX opcode. **/ void CAlphaCPU::vmspal_call_mtpr_datfx() { u64 t; u64 u; hw_ldq(p21 + 0x10, t); hw_ldq(t, u); u |= U64(0x1) << 0x3f; u &= ~(r16 << 0x3f); hw_stq(t, u); } /** * Implementation of CALL_PAL MFPR_WHAMI opcode. **/ void CAlphaCPU::vmspal_call_mfpr_whami() { hw_ldq(p21 + 0x98, r0); } /** * Implementation of CALL_PAL IMB opcode. **/ void CAlphaCPU::vmspal_call_imb() { if (p22 & 0x18) { hw_ldq(p21 + 0x10, p20); hw_ldl(p20 + 0x3c, p5); p5 |= 1; hw_stl(p20 + 0x3c, p5); } flush_icache(); } /** * Implementation of CALL_PAL PROBER opcode. **/ void CAlphaCPU::vmspal_call_prober() { u64 pa; p23 = state.pc; p4 = r18 & 3; // alt mode p5 = p22 & 0x18; p5 >>= 3; // current mode p6 = p5 - p4; if (p5 > p4) p4 = p5; state.alt_cm = (int)p4; hw_stq(p21 + 0x140, state.pc); hw_stq(p21 + 0x148, p4); if (virt2phys(r16, &pa, ACCESS_READ | ALT | PROBE, NULL, 0) < 0) return; if (virt2phys(r16 + r17, &pa, ACCESS_READ | ALT | PROBE, NULL, 0) < 0) return; r0 = 1; return; } /** * Implementation of CALL_PAL PROBEW opcode. **/ void CAlphaCPU::vmspal_call_probew() { u64 pa; p23 = state.pc; p4 = r18 & 3; // alt mode p5 = p22 & 0x18; p5 >>= 3; // current mode p6 = p5 - p4; if (p5 > p4) p4 = p5; state.alt_cm = (int)p4; hw_stq(p21 + 0x140, state.pc); hw_stq(p21 + 0x148, p4); if (virt2phys(r16, &pa, ACCESS_WRITE | ALT | PROBE | PROBEW, NULL, 0) < 0) return; if (virt2phys(r16 + r17, &pa, ACCESS_WRITE | ALT | PROBE | PROBEW, NULL, 0) < 0) return; r0 = 1; return; } /** * Implementation of CALL_PAL RD_PS opcode. **/ void CAlphaCPU::vmspal_call_rd_ps() { r0 = p22 & U64(0xffff); } /** * Implementation of CALL_PAL REI opcode. **/ int CAlphaCPU::vmspal_call_rei() { u64 phys_address; p23 = state.pc; p7 = p22 & 0x18; // old cm hw_stq(p21 + 0x150, p23); if (r30 & 0x3f) { // stack not aligned p4 = 0x430; hw_stq(p21 + 0x158, p4); return vmspal_int_initiate_exception(); } if (p7) { // what to do if the next instruction results in a TNV exception? ldq(r30 + 0x38, p20); state.bIntrFlag = false; ldq(r30 + 0x30, p23); p4 = p20 & 0x18; // new cm if ((p4 < p7) || (p20 & ~U64(0x3f0000000000001b))) { p4 = 0x430; hw_stq(p21 + 0x158, p4); vmspal_int_initiate_exception(); return 0; } ldq(r30 + 0x10, state.r[4]); ldq(r30 + 0x18, state.r[5]); ldq(r30 + 0x20, state.r[6]); ldq(r30 + 0x28, state.r[7]); ldq(r30, r2); ldq(r30 + 8, r3); hw_ldq(p21 + 0x10, p6); p5 = (p20 >> 56) & 0xff; p7 += p6; p6 += p4; p20 &= 0xffff; p22 &= ~U64(0xffff); state.cm = (int)(p4 >> 3) & 3; p22 |= p20; p23 &= ~U64(0x3); p20 = r30 + 0x40; p20 |= p5; hw_stq(p7, p20); hw_ldq(p6, r30); set_pc(p23); return 0; } ldq(r30 + 0x38, p20); state.bIntrFlag = false; p7 = (p20 >> 8) & 0xff; ldq(r30 + 0x10, state.r[4]); ldq(r30 + 0x18, state.r[5]); ldq(r30 + 0x20, state.r[6]); ldq(r30 + 0x28, state.r[7]); ldq(r30, r2); ldq(r30 + 8, r3); p4 = p20 & 0x18; // new cm ldq(r30 + 0x30, p23); if (p4) { hw_ldq(p21 + 0x10, p7); p7 += p4; state.cm = (int)(p4 >> 3) & 3; p5 = (p20 >> 56) & 0xff; p20 &= 0xff; p22 &= ~U64(0xffff); p22 |= p20; p23 &= ~U64(0x3); p20 = r30 + 0x40; p20 |= p5; hw_ldq(p7, r30); hw_stq(p21 + 0x18, p20); state.eien = ipl_ier_mask[0][0]; state.slen = ipl_ier_mask[0][1]; state.cren = ipl_ier_mask[0][2]; state.pcen = ipl_ier_mask[0][3]; state.sien = ipl_ier_mask[0][4]; state.asten = ipl_ier_mask[0][5]; state.check_int = true; set_pc(p23); return 0; } p5 = (p20 >> 56) & 0xff; p20 &= 0xffff; p22 &= ~U64(0xffff); p7 = (p20 >> 8) & 0xff; p22 |= p20; p23 &= ~U64(0x3); p20 = r30 + 0x40; r30 = p20 | p5; state.eien = ipl_ier_mask[p7][0]; state.slen = ipl_ier_mask[p7][1]; state.cren = ipl_ier_mask[p7][2]; state.pcen = ipl_ier_mask[p7][3]; state.sien = ipl_ier_mask[p7][4]; state.asten = ipl_ier_mask[p7][5]; state.check_int = true; set_pc(p23); return 0; } /** * Implementation of CALL_PAL SWASTEN opcode. **/ void CAlphaCPU::vmspal_call_swasten() { r0 = (state.aster & (1 << ((p22 >> 3) & 3))) ? 1 : 0; if (r16 & 1) { state.aster |= (1 << ((p22 >> 3) & 3)); state.check_int = true; } else state.aster &= ~(1 << ((p22 >> 3) & 3)); } /** * Implementation of CALL_PAL WR_PS_SW opcode. **/ void CAlphaCPU::vmspal_call_wr_ps_sw() { p22 &= ~U64(0x3); p22 |= r16 & U64(0x3); } /** * Implementation of CALL_PAL RSCC opcode. **/ void CAlphaCPU::vmspal_call_rscc() { hw_ldq(p21 + 0xa0, r0); if ((state.cc & U64(0xffffffff)) < (r0 & U64(0x00000000ffffffff))) r0 += U64(0x1) << 0x20; r0 &= U64(0xffffffff00000000); r0 |= (state.cc & U64(0xffffffff)); hw_stq(p21 + 0xa0, r0); } /** * Implementation of CALL_PAL READ_UNQ opcode. **/ void CAlphaCPU::vmspal_call_read_unq() { u64 t; hw_ldq(p21 + 0x10, t); hw_ldq(t + 0x48, r0); } /** * Implementation of CALL_PAL WRITE_UNQ opcode. **/ void CAlphaCPU::vmspal_call_write_unq() { u64 t; hw_ldq(p21 + 0x10, t); hw_stq(t + 0x48, r16); } //\} /***************************************************************************/ /** * \name VMS_pal_int * Internal routines used by VMS PALcode replacement. ******************************************************************************/ //\{ /** * Pass control to the OS for handling the exception. **/ int CAlphaCPU::vmspal_int_initiate_exception() { u64 phys_address; p4 = p22 & U64(0x18); hw_ldq(p21 + U64(0x10), p20); if (p4) { // change mode to kernel state.cm = 0; // switch to kernel stack p20 += p4; hw_stq(p20, r30); hw_ldq(p21 + U64(0x18), r30); } p20 = r30 & U64(0x3f); r30 &= ~U64(0x3f); stq(r30 - U64(0x40), r2); stq(r30 - U64(0x38), r3); hw_stq(p21 + U64(0xf0), r1); r3 = p21; stq(r30 - U64(0x30), state.r[4]); stq(r30 - U64(0x28), state.r[5]); stq(r30 - U64(0x20), state.r[6]); stq(r30 - U64(0x18), state.r[7]); hw_ldq(state.r[3] + U64(0x160), state.r[4]); hw_ldq(state.r[3] + U64(0x168), state.r[5]); hw_ldq(p21 + U64(0xf0), r1); hw_ldq(p21 + U64(0x170), p4); hw_ldq(p21 + U64(0x158), p5); p7 = p22 & U64(0xffff); p20 <<= 0x38; p20 |= p7; p4 += p5; hw_ldq(p4 + U64(0x00), r2); hw_ldq(p4 + U64(0x08), r3); p22 &= ~U64(0x1b); r30 -= U64(0x40); hw_ldq(p21 + U64(0x150), p6); r2 &= ~U64(0x3); stq(r30 + U64(0x38), p20); stq(r30 + U64(0x30), p6); set_pc(r2); return -1; } /** * Pass control to the OS for handling the interrupt. **/ int CAlphaCPU::vmspal_int_initiate_interrupt() { u64 phys_address; p4 = p22 & U64(0x18); hw_ldq(p21 + U64(0x10), p20); if (p4) { // change mode to kernel state.cm = 0; // switch to kernel stack p20 += p4; hw_stq(p20, r30); hw_ldq(p21 + U64(0x18), r30); } p20 = r30 & U64(0x3f); r30 &= ~U64(0x3f); stq(r30 - U64(0x40), r2); stq(r30 - U64(0x38), r3); hw_stq(p21 + U64(0xf0), r1); r3 = p21; stq(r30 - U64(0x30), state.r[4]); stq(r30 - U64(0x28), state.r[5]); stq(r30 - U64(0x20), state.r[6]); stq(r30 - U64(0x18), state.r[7]); hw_ldq(state.r[3] + U64(0x160), state.r[4]); hw_ldq(state.r[3] + U64(0x168), state.r[5]); hw_ldq(p21 + U64(0xf0), r1); hw_ldq(p21 + U64(0x170), p4); hw_ldq(p21 + U64(0x158), p5); p7 = p22 & U64(0xffff); p20 <<= 0x38; p20 |= p7; p4 += p5; hw_ldq(p4, r2); hw_ldq(p4 + U64(0x08), r3); hw_ldq(p21 + U64(0x128), p4); p22 &= ~U64(0xffff); p22 |= p4; r30 -= U64(0x40); hw_ldq(p21 + U64(0x150), p6); r2 &= ~U64(0x3); stq(r30 + U64(0x38), p20); stq(r30 + U64(0x30), p6); set_pc(r2); return -1; } //\} /***************************************************************************/ /** * \name VMS_pal_ent * VMS PALcode replacement trap/exception entry-points. ******************************************************************************/ //\{ /** * Interrupt entry point for Software Interrupts. **/ int CAlphaCPU::vmspal_ent_sw_int(int si) { int x; state.exc_addr = state.current_pc; p23 = state.current_pc; for (x = 15; x >= 0; x--) { if (si & (1 << x)) break; } if (x <= 0) return 0; p7 = x; p4 = (u64)x << 4; p5 = p4 + 0x500; hw_stq(p21 + 0x158, p5); hw_stq(p21 + 0x150, state.current_pc); state.sir &= ~(1 << x); state.eien = ipl_ier_mask[x][0]; state.slen = ipl_ier_mask[x][1]; state.cren = ipl_ier_mask[x][2]; state.pcen = ipl_ier_mask[x][3]; state.sien = ipl_ier_mask[x][4]; state.asten = ipl_ier_mask[x][5]; state.check_int = true; p20 = (u64)x << 8; p20 |= 4; hw_stq(p21 + 0x128, p20); return vmspal_int_initiate_interrupt(); } /** * Interrupt entry point for External Interrupts. **/ int CAlphaCPU::vmspal_ent_ext_int(int ei) { bool do_11670 = false; state.exc_addr = state.current_pc; p23 = state.current_pc; p7 = ei; if (ei & 0x04) { // TIMER interrupt cSystem->clear_clock_int(state.iProcNum); p6 = cSystem->get_c_misc(); p22 += U64(0x0000010000000000); p22 &= U64(0xffff0fffffffffff); hw_ldq(p21 + 0xa0, p20); p6 = U64(0x1) << 0x20; p4 = (state.cc & U64(0xffffffff)); p5 = p20 & U64(0xffffffff); p20 &= U64(0xffffffff00000000); p7 = p4 - p5; if (((s64)p7) < 0) p20 += p6; p20 |= (state.cc & U64(0xffffffff)); hw_stq(p21 + 0xa0, p20); hw_stq(p21 + 0x1c8, 0); p20 = 0x600; p7 = 0x16; do_11670 = true; } else if (ei & 0x02) { p5 = cSystem->get_c_dir(state.iProcNum); if (test_bit_64(p5, 0x32)) FAILURE(NotImplemented, "Can't handle IRQ 50"); p4 = 0x100; p20 = 8; while (p20 < 0x30) { if (p4 & p5) break; p4 *= 2; p20++; } if (p4 & p5) { p20 <<= 4; p20 += 0x900; p7 = 0x15; do_11670 = true; } else if (test_bit_64(p5, 0x37)) { // irq 55 - PIC - isa p5 = U64(0x00000801f8000000); // pic_read_vector hw_ldl(p5, p5); p4 = p5 & 0xff; if (p4 == 0x07) FAILURE(NotImplemented, "Can't handle PIC interrupt 7"); if (p4 >= 0x10) return 0; p4 <<= 4; p20 = p4 + 0x800; hw_ldq(0x148, p5); if (!p5 || p20 != U64(0x830)) { p7 = 0x15; do_11670 = true; } else { hw_stq(0x150, p20); p4 = U64(0x00000801a0000000); p5 = U64(0x2000); hw_stq(p4 + 0x80, p5); hw_ldq(p4 + 0x80, p5); return 0; } } else { p6 = p5 & U64(0x0060000000000000); if (p6) cSystem->set_c_dim(state.iProcNum, cSystem->get_c_dim(state.iProcNum) & ~p6); return 0; } } if (do_11670) { hw_stq(p21 + 0x150, state.current_pc); hw_stq(p21 + 0x158, p20); hw_stq(p21 + 0x1d8, p20); state.eien = ipl_ier_mask[p7][0]; state.slen = ipl_ier_mask[p7][1]; state.cren = ipl_ier_mask[p7][2]; state.pcen = ipl_ier_mask[p7][3]; state.sien = ipl_ier_mask[p7][4]; state.asten = ipl_ier_mask[p7][5]; state.check_int = true; p20 = p7 << 8; p20 |= 4; hw_stq(p21 + 0x128, p20); return vmspal_int_initiate_interrupt(); } return 0; } /** * Interrupt entry point for Asynchronous System Traps. **/ int CAlphaCPU::vmspal_ent_ast_int(int ast) { int x; state.exc_addr = state.current_pc; p23 = state.current_pc; for (x = 0; x < 4; x++) { if (ast & (1 << x)) break; } state.asten = 0; state.sien &= ~7; p4 = (u64)x << 4; p5 = 0x240 + p4; hw_stq(p21 + 0x158, p5); hw_stq(p21 + 0x150, p23); state.astrr &= ~(1 << x); p20 = (p22 & 4) + 0x200; hw_stq(p21 + 0x128, p20); return vmspal_int_initiate_interrupt(); } /** * Entry point for Single Data Translation Buffer Miss. **/ int CAlphaCPU::vmspal_ent_dtbm_single(int flags) { u64 pte_phys; u64 t25; u64 t26; p23 = state.exc_addr; p4 = va_form(state.fault_va, false); p5 = state.mm_stat; p7 = state.exc_sum; p6 = state.fault_va; p7 &= ~U64(0x1); if (test_bit_64(p22, 63)) { // 1-ON-1 translations p5 = 0xff01; p4 = p6 >> 0x0d; if (!test_bit_64(p6, 43)) { p4 &= ~U64(0xffffffffbfc00000); if ((p4 >= 0x400a0000 && p4 <= 0x400bffff) || (p4 >= 0x400c8000 && p4 <= 0x400cffff) || (p4 >= 0x400e0000 && p4 <= 0x400fbfff) || (p4 >= 0x400ff800 && p4 <= 0x400fffff) || (p4 >= 0x40180000 && p4 <= 0x401bffff) || (p4 >= 0x401c8000 && p4 <= 0x401fbfff) || (p4 >= 0x401fb000 && p4 <= 0x401fffff) || (p4 >= 0x40200000 && p4 <= 0x403fffff)) p5 = U64(0x1); } p4 <<= 0x20; p4 |= p5; add_tb_d(p6, p4); return 0; } p4 &= ~U64(0x7); if (virt2phys(p4, &pte_phys, ACCESS_READ | NO_CHECK | VPTE | (flags & (PROBE | PROBEW)), NULL, 0)) return -1; p4 = cSystem->ReadMem(pte_phys, 64, this); if (!test_bit_64(p4, 0)) { if (flags & PROBE) { t25 = U64(0x30000); p4 |= t25; t26 = p5 & 1; // write or read? t26 *= 4; t25 = ((p22 >> 3) & 3) | 8; t26 += t25; t25 = p4 >> 0x10; if (!(t25 & 1)) t26 = 8; t26 = (t25 << t26) & p4; p7 = t26 ? 0x90 : 0x80; // page fault or acv hw_stq(p21 + 0x158, p7); t26 = p23 & ~U64(0x3); hw_ldq(p21 + 0x148, p7); if (test_bit_64(p4, (int)(8 + p7 + ((flags & PROBEW) ? 4 : 0)))) return 1; r0 = 0; return -1; } if (state.current_pc & 1) { t25 = U64(0x30000); p4 |= t25; t26 = p5 & 1; // write or read? t26 *= 4; t25 = ((p22 >> 3) & 3) | 8; t26 += t25; t25 = p4 >> 0x10; if (!(t25 & 1)) t26 = 8; t26 = (t25 << t26) & p4; p7 = t26 ? 0x90 : 0x80; // page fault or acv hw_stq(p21 + 0x158, p7); t26 = p23 & ~U64(0x3); hw_stq(p21 + 0x118, state.r[25]); hw_stq(p21 + 0x120, state.r[26]); state.r[25] = t25; state.r[26] = t26; set_pc(0xd981); return -1; // return vmspal_int_dfault_in_palmode(); } hw_stq(p21 + U64(0x150), p23); hw_stq(p21 + U64(0x160), p6); p20 = ((p22 >> 3) & 3) + 8; if ((test_bit_64(p5, 0) && ((p5 >> 4) & 0x3f) == 0x18) || (!test_bit_64(p5, 0) && ((p7 >> 8) & 0x1f) == 0x1f)) { // write "MISC" or read to R31 set_pc(state.current_pc + 4); return -1; } p5 &= U64(0x1); p7 = p5 << 0x3f; p6 = 4 * p5; hw_stq(p21 + U64(0x168), p7); p6 += p20; p6 = U64(0x1) << p6; p6 &= p4; p20 = (p6) ? 0x90 : 0x80; hw_stq(p21 + U64(0x158), p20); return vmspal_int_initiate_exception(); } add_tb_d(p6, p4); return 0; } /** * Entry point for Instruction Translation Buffer Miss. **/ int CAlphaCPU::vmspal_ent_itbm(int flags) { u64 pte_phys; p4 = va_form(state.exc_addr, true); p23 = state.exc_addr; p6 = p23; if (test_bit_64(p22, 63)) { // 1-ON-1 translations enabled p6 = p23 >> 0x0d; p5 = 0xf01; p6 <<= 0x0d; p6 |= p5; add_tb_i(p23, p6); return 0; } p4 &= ~U64(0x7); if (virt2phys(p4, &pte_phys, ACCESS_READ | NO_CHECK | VPTE, NULL, 0)) return -1; p4 = cSystem->ReadMem(pte_phys, 64, this); p6 = 0xfff; p5 = p4 & p6; p6 = p4 >> 0x20; p6 <<= 0x0d; if (!(p4 & 1) || (p4 & 8)) { p20 = (p4 & 1) ? 0xc0 : 0x90; p6 = p22 >> 3; hw_stq(p21 + 0x160, p23); hw_stq(p21 + 0x150, p23); p6 &= U64(0x3); p5 = 1; p6 += 8; hw_stq(p21 + 0x168, p5); p6 = p5 << p6; p6 &= p4; // Access Violation if (!p6) p20 = 0x80; hw_stq(p21 + 0x158, p20); return vmspal_int_initiate_exception(); } p6 |= p5; add_tb_i(p23, p6); return 0; } /** * Entry point for Double Data Translation Buffer Miss. **/ int CAlphaCPU::vmspal_ent_dtbm_double_3(int flags) { u64 t25; u64 t26; p7 &= 0xffff; p5 <<= 16; p7 |= p5; p5 = state.exc_addr; p7 |= 1; hw_ldq(p21 + 8, t25); // PTBR t26 = (p4 << 0x1f) >> 0x33; // L1 index t25 += t26; hw_ldq(t25, t25); t26 = (p4 << 0x29) >> 0x33; if (t25 & 1) { t25 >>= 0x20; t25 <<= 0x0d; t25 += t26; hw_ldq(t25, t25); if (t25 & 1) { add_tb_d(p4, t25); return 0; } } // PAGE FAULT... p4 = p5; // return address... p5 = p7 >> 0x10; t26 = state.pal_base; if (flags & PROBE) // in PALmode!! { p4 = t25 & ~U64(0x30000); t26 = p5 & 1; // write or read? t26 *= 4; t25 = ((p22 >> 3) & 3) | 8; t26 += t25; t25 = p4 >> 16; if (!(t25 & 1)) t26 = 8; t26 = (U64(0x1) << t26) & p4; p7 = t26 ? 0x90 : 0x80; // page fault or acv hw_stq(p21 + 0x158, p7); if (p7 != 0x80) { p5 = (flags & PROBEW) ? U64(0x8000000000000000) : 0; hw_stq(p21 + 0x160, p6); hw_stq(p21 + 0x168, p5); hw_stq(p21 + 0x150, state.current_pc); return vmspal_int_initiate_exception(); } r0 = 0; return -1; } if (p23 & 1) // in PALmode!! { p4 = t25 & ~U64(0x30000); t26 = p5 & 1; // write or read? t26 *= 4; t25 = ((p22 >> 3) & 3) | 8; t26 += t25; t25 = p4 >> 16; if (!(t25 & 1)) t26 = 8; t26 = (U64(0x1) << t26) & p4; p7 = t26 ? 0x90 : 0x80; // page fault or acv hw_stq(p21 + 0x158, p7); t26 = p23 & ~U64(0x3); hw_stq(p21 + 0x118, state.r[25]); hw_stq(p21 + 0x120, state.r[26]); state.r[25] = t25; state.r[26] = t26; set_pc(0xd981); return -1; } p20 = p4 & ~U64(0x3); p4 = t25 & 0x100; hw_stq(p21 + 0x150, p23); p20 = t26 - p20; t26 = p20 + 0x590; if (!t26) { p5 = 1; hw_stq(p21 + 0x160, p23); p20 = p4 ? 0x90 : 0x80; hw_stq(p21 + 0x168, p5); hw_stq(p21 + 0x158, p20); return vmspal_int_initiate_exception(); } if ((test_bit_64(p5, 0) && ((p5 >> 4) & 0x3f) == 0x18) || (!test_bit_64(p5, 0) && ((p7 >> 8) & 0x1f) == 0x1f)) { set_pc(p23 + 4); return -1; } p5 <<= 0x3f; p20 = p4 ? 0x90 : 0x80; hw_stq(p21 + 0x168, p5); hw_stq(p21 + 0x160, p6); hw_stq(p21 + 0x158, p20); return vmspal_int_initiate_exception(); } /** * Entry point for IStream Access Violation **/ int CAlphaCPU::vmspal_ent_iacv(int flags) { p6 = state.current_pc; p4 = 1; p5 = 0x80; hw_stq(p21 + 0x168, p4); hw_stq(p21 + 0x158, p5); if (state.current_pc & 1) { p7 = state.current_pc; p20 = 5; hw_stq(p21 + 0xc8, p20); p23 = state.current_pc; set_pc(0xde01); return -1; } hw_stq(p21 + 0x160, state.current_pc); hw_stq(p21 + 0x150, state.current_pc); return vmspal_int_initiate_exception(); } /** * Entry point for DStream Fault. **/ int CAlphaCPU::vmspal_ent_dfault(int flags) { u64 t25; u64 t26; p6 = state.current_pc; p7 = state.exc_sum; p5 = state.mm_stat; if (flags & PROBE) { hw_stq(p21 + 0xd0, p23); p23 = p6; t26 = p6 & ~U64(0x3); p6 = state.fault_va; t25 = p5 & 2; p7 = 0x80; if (!t25) { t25 = p5 & 8; p7 = t25 ? 0xb0 : 0xa0; tbis(p6, ACCESS_READ); } hw_stq(p21 + 0x158, p7); p4 = 1; hw_ldq(p21 + 0x158, p7); if (p7 == 0xa0 || p7 == 0xb0) { return 1; } r0 = 0; return -1; } if (state.current_pc & 1) { hw_stq(p21 + 0xd0, p23); p23 = p6; t26 = p6 & ~U64(0x3); p6 = state.fault_va; t25 = p5 & 2; p7 = 0x80; if (!t25) { t25 = p5 & 8; p7 = t25 ? 0xb0 : 0xa0; tbis(p6, ACCESS_READ); } hw_stq(p21 + 0x158, p7); p4 = 1; hw_stq(p21 + 0x118, state.r[25]); hw_stq(p21 + 0x120, state.r[26]); state.r[25] = t25; state.r[26] = t26; set_pc(0xd981); return -1; } p20 = state.fault_va; if (((state.mm_stat & 1) && (((state.mm_stat >> 4) & 0x3f) == 0x18)) || (!(state.mm_stat & 1) && (((state.exc_sum >> 8) & 0x1f) == 0x1f))) { set_pc(state.current_pc + 4); return -1; } p7 = 0x80; if (!(state.mm_stat & 2)) { p7 = (state.mm_stat & 4) ? 0xa0 : 0xb0; tbis(p20, ACCESS_READ); } hw_stq(p21 + 0x158, p7); hw_stq(p21 + 0x160, p20); p5 <<= 0x3f; hw_stq(p21 + 0x168, p5); hw_stq(p21 + 0x150, p6); return vmspal_int_initiate_exception(); } //\} ================================================ FILE: src/AlphaSim.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DPR.hpp" #include "Flash.hpp" #include "StdAfx.hpp" #include "System.hpp" #include "lockstep.hpp" #if defined(HAVE_SDL) #include "SDL/SDL.h" #endif /// "standard" locations for a configuration file. This will be port specific. const char *path[] = { #if defined(_WIN32) ".\\es40.cfg", "c:\\es40.cfg", "c:\\windows\\es40.cfg", #elif defined(__VMS) "[]ES40.CFG", #else "./es40.cfg", "/etc/es40.cfg", "/usr/etc/es40.cfg", "/usr/local/etc/es40.cfg", #endif 0}; #ifdef DEBUG_BACKTRACE #ifdef __GNUG__ #include #include #define HAS_BACKTRACE #define BTCOUNT 100 void *btbuffer[BTCOUNT]; void segv_handler(int signum) { int nptrs = backtrace(btbuffer, BTCOUNT); char **strings; printf("%%SYS-F-SEGFAULT: The Alpha Simulator has Segfaulted.\n"); printf("-SYS-F-SEGFAULT: Backtrace follows.\n"); printf("backtrace() returned %d addresses.\n", nptrs); strings = backtrace_symbols(btbuffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(1); } for (int i = 0; i < nptrs; i++) { printf("%3d %s\n", nptrs - i, strings[i]); } free(strings); if (signum == SIGSEGV) _exit(1); } #else #warning "Your compiler isn't configured to support backtraces." #endif // __GNUG__ #endif /** * Entry point for simulation. * * Does the following: * - Try to find the configuration file. * - Reads the configuration file, and uses the configurator to instantiate all *system components. * - Creates the trace-engine if in debug mode. * - Runs the emulator. * - Cleans up. * . **/ int main_sim(int argc, char *argv[]) { const char *filename = 0; FILE *f; #ifdef HAS_BACKTRACE signal(SIGSEGV, &segv_handler); signal(SIGUSR1, &segv_handler); #endif try { #if defined(IDB) && (defined(LS_MASTER) || defined(LS_SLAVE)) lockstep_init(); #endif #if defined(IDB) if ((argc == 2 || argc == 3) && argv[1][0] != '@') #else if (argc == 2) #endif { filename = argv[1]; } else { for (int i = 0; path[i]; i++) { filename = path[i]; f = fopen(filename, "r"); if (f != NULL) { fclose(f); filename = path[i]; break; } else { filename = NULL; } } if (filename == NULL) FAILURE(FileNotFound, "configuration file"); } char *ch1; size_t ll1; f = fopen(filename, "rb"); if (f == NULL) FAILURE(File, "configuration file"); fseek(f, 0, SEEK_END); ll1 = ftell(f); ch1 = (char *)calloc(ll1, 1); fseek(f, 0, SEEK_SET); ll1 = fread(ch1, 1, ll1, f); new CConfigurator(0, 0, 0, ch1, ll1); fclose(f); free(ch1); if (!theSystem) FAILURE(Configuration, "no system initialized"); #if defined(IDB) trc = new CTraceEngine(theSystem); #endif theSystem->LoadROM(); theDPR->init(); #if defined(PROFILE) { u64 p_i; for (p_i = PROFILE_FROM; p_i < PROFILE_TO; p_i += (4 * PROFILE_BUCKSIZE)) PROFILE_BUCKET(p_i) = 0; profiled_insts = 0; } #endif #if defined(IDB) theSystem->start_threads(); if (argc > 1 && argc < 4 && argv[argc - 1][0] == '@') trc->run_script(argv[argc - 1] + 1); else trc->run_script(NULL); #else theSystem->Run(); #endif } catch (CGracefulException &e) { printf("Exiting gracefully: %s\n", e.displayText().c_str()); theSystem->stop_threads(); // save flash and dpr rom only if not terminated with a fatal error theSROM->SaveStateF(); theDPR->SaveStateF(); #if defined(PROFILE) { FILE *p_fp; u64 p_max = 0; u64 p_i; int p_j; printf("Writing profile to profile.txt"); p_fp = fopen("profile.txt", "w"); for (p_i = PROFILE_FROM; p_i < PROFILE_TO; p_i += (4 * PROFILE_BUCKSIZE)) { if (PROFILE_BUCKET(p_i) > p_max) p_max = PROFILE_BUCKET(p_i); } fprintf(p_fp, "p_max = %10" PRId64 "; %10" PRId64 " profiled instructions.\n\n", p_max, profiled_insts); for (p_i = PROFILE_FROM; p_i < PROFILE_TO; p_i += (4 * PROFILE_BUCKSIZE)) { if (PROFILE_BUCKET(p_i)) { fprintf(p_fp, "%016" PRIx64 ": %10" PRId64 " ", p_i, PROFILE_BUCKET(p_i)); for (p_j = 0; p_j < (((float)PROFILE_BUCKET(p_i) / (float)p_max) * 100); p_j++) fprintf(p_fp, "*"); fprintf(p_fp, "\n"); } } fclose(p_fp); } #endif delete theSystem; } catch (CException &e) { printf("Emulator Failure: %s\n", e.displayText().c_str()); if (theSystem) { theSystem->stop_threads(); delete theSystem; } } return 0; } ================================================ FILE: src/Cirrus.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Cirrus.hpp" #include "AliM1543C.hpp" #include "StdAfx.hpp" #include "System.hpp" #include "gui/gui.hpp" static unsigned old_iHeight = 0, old_iWidth = 0, old_MSL = 0; static const u8 ccdat[16][4] = { {0x00, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}, {0x00, 0xff, 0x00, 0x00}, {0xff, 0xff, 0x00, 0x00}, {0x00, 0x00, 0xff, 0x00}, {0xff, 0x00, 0xff, 0x00}, {0x00, 0xff, 0xff, 0x00}, {0xff, 0xff, 0xff, 0x00}, {0x00, 0x00, 0x00, 0xff}, {0xff, 0x00, 0x00, 0xff}, {0x00, 0xff, 0x00, 0xff}, {0xff, 0xff, 0x00, 0xff}, {0x00, 0x00, 0xff, 0xff}, {0xff, 0x00, 0xff, 0xff}, {0x00, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff}, }; /** * Set a specific tile's updated variable. * * Only reference the array if the tile numbers are within the bounds * of the array. If out of bounds, do nothing. **/ #define SET_TILE_UPDATED(xtile, ytile, value) \ do { \ if (((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \ state.vga_tile_updated[(xtile)][(ytile)] = value; \ } while (0) /** * Get a specific tile's updated variable. * * Only reference the array if the tile numbers are within the bounds * of the array. If out of bounds, return 0. **/ #define GET_TILE_UPDATED(xtile, ytile) \ ((((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \ ? state.vga_tile_updated[(xtile)][(ytile)] \ : 0) /** * Thread entry point. * * The thread first initializes the GUI, and then starts looping the * following actions until interrupted (by StopThread being set to true) * - Handle any GUI events (mouse moves, keypresses) * - Update the GUI to match the screen buffer * - Flush the updated GUI content to the screen * . **/ void CCirrus::run() { try { // initialize the GUI (and let it know our tilesize) bx_gui->init(state.x_tilesize, state.y_tilesize); for (;;) { // Terminate thread if StopThread is set to true if (StopThread) return; // Handle GUI events (100 times per second) for (int i = 0; i < 10; i++) { bx_gui->lock(); bx_gui->handle_events(); bx_gui->unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Update the screen (10 times per second) bx_gui->lock(); update(); bx_gui->flush(); bx_gui->unlock(); } } catch (CException &e) { printf("Exception in Cirrus thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** Size of ROM image */ static unsigned int rom_max; /** ROM image */ static u8 option_rom[65536]; /** PCI Configuration Space data block */ static u32 cirrus_cfg_data[64] = { /*00*/ 0x00a81013, // CFID: vendor + device /*04*/ 0x011f0000, // CFCS: command + status /*08*/ 0x03000002, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0xf8000000, // BAR0: FB /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x281401ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** PCI Configuration Space mask block */ static u32 cirrus_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x0000ffff, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xfc000000, // BAR0: FB /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Constructor. * **/ CCirrus::CCirrus(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CVGA(cfg, c, pcibus, pcidev) { // initialize state memset(&state, 1, sizeof(struct SCirrus_state)); } /** * Initialize the Cirrus device. **/ void CCirrus::init() { // Register PCI device add_function(0, cirrus_cfg_data, cirrus_cfg_mask); // Initialize all state variables to 0 memset((void *)&state, 0, sizeof(state)); // Register VGA I/O ports at 3b4, 3b5, 3ba, 3c0..cf, 3d4, 3d5, 3da add_legacy_io(1, 0x3b4, 2); add_legacy_io(3, 0x3ba, 2); add_legacy_io(2, 0x3c0, 16); add_legacy_io(8, 0x3d4, 2); add_legacy_io(9, 0x3da, 1); /* The VGA BIOS we use sends text messages to port 0x500. We listen for these messages at port 500. */ add_legacy_io(7, 0x500, 1); bios_message_size = 0; bios_message[0] = '\0'; // Legacy video address space: A0000 -> bffff add_legacy_mem(4, 0xa0000, 128 * 1024); // Reset the base PCI device ResetPCI(); /* The configuration file variable "rom" should point to a VGA BIOS image. If not, try "vgabios.bin". */ FILE *rom = fopen(myCfg->get_text_value("rom", "vgabios.bin"), "rb"); if (!rom) { FAILURE_1(FileNotFound, "cirrus rom file %s not found.", myCfg->get_text_value("rom", "vgabios.bin")); } rom_max = (unsigned)fread(option_rom, 1, 65536, rom); fclose(rom); // Option ROM address space: C0000 add_legacy_mem(5, 0xc0000, rom_max); state.vga_enabled = 1; state.misc_output.color_emulation = 1; state.misc_output.enable_ram = 1; state.misc_output.horiz_sync_pol = 1; state.misc_output.vert_sync_pol = 1; state.attribute_ctrl.mode_ctrl.enable_line_graphics = 1; state.line_offset = 80; state.line_compare = 1023; state.vertical_display_end = 399; state.attribute_ctrl.video_enabled = 1; state.attribute_ctrl.color_plane_enable = 0x0f; state.pel.dac_state = 0x01; state.pel.mask = 0xff; state.graphics_ctrl.memory_mapping = 2; // monochrome text mode state.sequencer.reset1 = 1; state.sequencer.reset2 = 1; state.sequencer.extended_mem = 1; // display mem greater than 64K state.sequencer.odd_even = 1; // use sequential addressing mode state.memsize = 0x40000; state.memory = new u8[state.memsize]; memset(state.memory, 0, state.memsize); state.last_bpp = 8; state.CRTC.reg[0x09] = 16; state.graphics_ctrl.memory_mapping = 3; // color text mode state.vga_mem_updated = 1; printf("%s: $Id: Cirrus.cpp,v 1.23 2008/05/31 15:47:09 iamcamiel Exp $\n", devid_string); } /** * Create and start thread. **/ void CCirrus::start_threads() { if (!myThread) { printf(" cirrus"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); } } /** * Stop and destroy thread. **/ void CCirrus::stop_threads() { // Signal the thread to stop StopThread = true; if (myThread) { printf(" cirrus"); // Wait for the thread to end execution myThread->join(); // And delete the Thread object myThread = nullptr; } } /** * Destructor. **/ CCirrus::~CCirrus() { stop_threads(); } /** * Read from one of the Legacy (fixed-address) memory ranges. **/ u32 CCirrus::ReadMem_Legacy(int index, u32 address, int dsize) { u32 data = 0; switch (index) { // IO Port 0x3b4 case 1: data = io_read(address + 0x3b4, dsize); break; // IO Port 0x3c0..0x3cf case 2: data = io_read(address + 0x3c0, dsize); break; // IO Port 0x3ba case 3: data = io_read(address + 0x3ba, dsize); break; // VGA Memory case 4: data = legacy_read(address, dsize); break; // ROM case 5: data = rom_read(address, dsize); break; // IO Port 0x3d4 case 8: data = io_read(address + 0x3d4, dsize); break; // IO Port 0x3da case 9: data = io_read(address + 0x3da, dsize); break; } return data; } /** * Write to one of the Legacy (fixed-address) memory ranges. **/ void CCirrus::WriteMem_Legacy(int index, u32 address, int dsize, u32 data) { switch (index) { // IO Port 0x3b4 case 1: io_write(address + 0x3b4, dsize, data); return; // IO Port 0x3c0..0x3cf case 2: io_write(address + 0x3c0, dsize, data); return; // IO Port 0x3ba case 3: io_write(address + 0x3ba, dsize, data); return; // VGA Memory case 4: legacy_write(address, dsize, data); return; // BIOS Message IO Port (0x500) case 7: bios_message[bios_message_size++] = (char)data & 0xff; if (((data & 0xff) == 0x0a) || ((data & 0xff) == 0x0d)) { if (bios_message_size > 1) { bios_message[bios_message_size - 1] = '\0'; printf("cirrus: %s\n", bios_message); } bios_message_size = 0; } return; // IO Port 0x3d4 case 8: /* io port */ io_write(address + 0x3d4, dsize, data); return; // IO Port 0x3da case 9: io_write(address + 0x3da, dsize, data); return; } } /** * Read from one of the PCI BAR (configurable address) memory ranges. **/ u32 CCirrus::ReadMem_Bar(int func, int bar, u32 address, int dsize) { switch (bar) { // PCI memory range case 0: return mem_read(address, dsize); } return 0; } /** * Write to one of the PCI BAR (configurable address) memory ranges. **/ void CCirrus::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { switch (bar) { // PCI Memory range case 0: mem_write(address, dsize, data); return; } } /** * Check if threads are still running. **/ void CCirrus::check_state() { if (myThreadDead.load()) FAILURE(Thread, "Cirrus thread has died"); } static u32 cirrus_magic1 = 0xC1AA4500; static u32 cirrus_magic2 = 0x0054AA1C; /** * Save state to a Virtual Machine State file. **/ int CCirrus::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&cirrus_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&cirrus_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CCirrus::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != cirrus_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != cirrus_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * Read from Framebuffer. * * Not functional. **/ u32 CCirrus::mem_read(u32 address, int dsize) { u32 data = 0; // printf("cirrus: mem read: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, // data); return data; } /** * Write to Framebuffer. * * Not functional. **/ void CCirrus::mem_write(u32 address, int dsize, u32 data) { // printf("cirrus: mem write: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, // data); switch (dsize) { case 8: case 16: case 32: break; } } /** * Read from Legacy VGA Memory * * Calls vga_mem_read to read the data 1 byte at a time. **/ u32 CCirrus::legacy_read(u32 address, int dsize) { u32 data = 0; switch (dsize) { case 32: data |= (u64)vga_mem_read((u32)address + 0xA0003) << 24; data |= (u64)vga_mem_read((u32)address + 0xA0002) << 16; case 16: data |= (u64)vga_mem_read((u32)address + 0xA0001) << 8; case 8: data |= (u64)vga_mem_read((u32)address + 0xA0000); } // //printf("cirrus: legacy read: %" PRIx64 ", %d, %" PRIx64 " \n", address, // dsize, data); return data; } /** * Write to Legacy VGA Memory * * Calls vga_mem_write to write the data 1 byte at a time. **/ void CCirrus::legacy_write(u32 address, int dsize, u32 data) { // //printf("cirrus: legacy write: %" PRIx64 ", %d, %" PRIx64 " \n", address, // dsize, data); switch (dsize) { case 32: vga_mem_write((u32)address + 0xA0002, (u8)(data >> 16)); vga_mem_write((u32)address + 0xA0003, (u8)(data >> 24)); case 16: vga_mem_write((u32)address + 0xA0001, (u8)(data >> 8)); case 8: vga_mem_write((u32)address + 0xA0000, (u8)(data)); } } /** * Read from Option ROM */ u32 CCirrus::rom_read(u32 address, int dsize) { u32 data = 0x00; u8 *x = (u8 *)option_rom; if (address <= rom_max) { x += address; switch (dsize) { case 8: data = (u32)endian_8((*((u8 *)x)) & 0xff); break; case 16: data = (u32)endian_16((*((u16 *)x)) & 0xffff); break; case 32: data = (u32)endian_32((*((u32 *)x)) & 0xffffffff); break; } // printf("cirrus: rom read: %" PRIx64 ", %d, %" PRIx64 "\n", address, // dsize,data); } else { printf("cirrus: (BAD) rom read: %" PRIu32 "x, %d, %" PRIu32 "x\n", address, dsize, data); } return data; } /** * Read from I/O Port */ u32 CCirrus::io_read(u32 address, int dsize) { u32 data = 0; if (dsize != 8) FAILURE(InvalidArgument, "Unsupported dsize!\n"); switch (address) { case 0x3c0: data = read_b_3c0(); break; case 0x3c1: data = read_b_3c1(); break; case 0x3c2: data = read_b_3c2(); break; case 0x3c3: data = read_b_3c3(); break; case 0x3c4: data = read_b_3c4(); break; case 0x3c5: data = read_b_3c5(); break; case 0x3c9: data = read_b_3c9(); break; case 0x3ca: data = read_b_3ca(); break; case 0x3cc: data = read_b_3cc(); break; case 0x3cf: data = read_b_3cf(); break; case 0x3b4: case 0x3d4: data = read_b_3d4(); break; case 0x3b5: case 0x3d5: data = read_b_3d5(); break; case 0x3ba: case 0x3da: data = read_b_3da(); break; default: FAILURE_1(NotImplemented, "Unhandled port %x read", address); } // printf("cirrus: io read: %" PRIx64 ", %d, %" PRIx64 " \n", address+VGA_BASE, // dsize, data); return data; } /** * Write to I/O Port * * Calls io_write_b to write the data 1 byte at a time. */ void CCirrus::io_write(u32 address, int dsize, u32 data) { // printf("cirrus: io write: %" PRIx64 ", %d, %" PRIx64 " \n", address+VGA_BASE, // dsize, data); switch (dsize) { case 8: io_write_b(address, (u8)data); break; case 16: io_write_b(address, (u8)data); io_write_b(address + 1, (u8)(data >> 8)); break; default: FAILURE(InvalidArgument, "Weird IO size!"); } } /** * Write one byte to a VGA I/O port. **/ void CCirrus::io_write_b(u32 address, u8 data) { switch (address) { case 0x3c0: write_b_3c0(data); break; case 0x3c2: write_b_3c2(data); break; case 0x3c4: write_b_3c4(data); break; case 0x3c5: write_b_3c5(data); break; case 0x3c6: write_b_3c6(data); break; case 0x3c7: write_b_3c7(data); break; case 0x3c8: write_b_3c8(data); break; case 0x3c9: write_b_3c9(data); break; case 0x3ce: write_b_3ce(data); break; case 0x3cf: write_b_3cf(data); break; case 0x3b4: case 0x3d4: write_b_3d4(data); break; case 0x3b5: case 0x3d5: write_b_3d5(data); break; default: FAILURE_1(NotImplemented, "Unhandled port %x write", address); } } /** * Write to the attribute controller I/O port (0x3c0) * * The attribute controller registers are used to select the 16 color * and 64 color palettes used for EGA/CGA compatibility. * * The attribute registers are accessed in an indexed fashion. * The address register is read and written via port 3C0h. * The data register is written to port 3C0h and read from port 3C1h. * The index and the data are written to the same port, one after * another. A flip-flop inside the card keeps track of whether the * next write will be handled is an index or data. Because there is * no standard method of determining the state of this flip-flop, the * ability to reset the flip-flop such that the next write will be * handled as an index is provided. This is accomplished by reading * the Input Status #1 Register (normally port 3DAh) (the data * received is not important.) * * Attribute registers: * - Palette Index registers (index 0x00 - 0x0f) * - Attribute Mode Control register (index 0x10) * - Overscan Color register (index 0x11) * - Color Plane Enable register (index 0x12) * - Horizontal Pixel Panning register (index 0x13) * - Color Select register (index 0x14) * . * * \code * Attribute Address Register(3C0h) * +---+-+---------+ * | |5|4 3 2 1 0| * +---+-+---------+ * ^ ^ * | +-- 0..4: Attribute Address: This field specifies the index * | value of the attribute register to be read or written * +----------- 5: Palette Address Source: This bit is set to 0 to load * color values to the registers in the internal palette. * It is set to 1 for normal operation of the attribute * controller. * * Palette Index Registers (index 0x00 - 0x0f) * +---+-----------+ * | |5 4 3 2 1 0| * +---+-----------+ * ^ * +--- 0..5: Internal Palette Index: These 6-bit registers allow a * dynamic mapping between the text attribute or graphic * color input value and the display color on the CRT * screen. These internal palette values are sent off-chip * to the video DAC, where they serve as addresses into * the DAC registers. * * Attribute Mode Control Register (index 0x10) * +-+-+-+-+-+-+-+-+ * |7|6|5| |3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +- 0: ATGE - Attribute Controller Graphics Enable: * | | | | | | 0: Disables the graphics mode of operation. * | | | | | | 1: Selects the graphics mode of operation. * | | | | | +--- 1: MONO - Monochrome Emulation: This bit is present and * | | | | | programmable in all of the hardware but it apparently * | | | | | does nothing. * | | | | +----- 2: LGE - Line Graphics Enable: This field is used in 9 * | | | | bit wide character modes to provide continuity for the * | | | | horizontal line characters in the range C0h-DFh: * | | | | 0: the 9th column is replicated from the 8th column. * | | | | 1: the 9th column is set to the background. * | | | +------- 3: BLINK - Blink Enable: * | | | 0: Bit 7 of the attribute selects the background * | | | intensity (allows 16 colors for background). * | | | 1: Bit 7 of the attribute enables blinking. * | | +----------- 5: PPM -- Pixel Panning Mode: Allows the upper half of * | | the screen to pan independently of the lower screen. * | | 0: nothing special occurs during a successful line * | | compare (see the Line Compare field.) * | | 1: upon a successful line compare, the bottom portion * | | of the screen is displayed as if the Pixel Shift * | | Count and Byte Panning fields are set to 0. * | +------------- 6: 8BIT -- 8-bit Color Enable: * | 1: The video data is sampled so that eight bits are * | available to select a color in the 256-color mode. * | 0: All other modes. * +--------------- 7: P54S -- Palette Bits 5-4 Select: Selects the source for * the P5 and P4 video bits that act as inputs to the video * DAC. * 0: P5 and P4 are the outputs of the Internal Palette * registers. * 1: P5 and P4 are bits 1 and 0 of the Color Select * register. * * Overscan Color Register (index 0x11) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * +----- 0..7: Overscan Palette Index: Selects a color from one of the * DAC registers for the border. * * Color Plane Enable Register (index 0x12) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Plane Enable: Setting a bit to 1 enables the * corresponding display-memory color plane. * * Horizontal Pixel Panning Register (index 0x13) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Pixel Shift Count: These bits select the number of pels * that the video data is shifted to the left. * * Color Select Register (index 0x14) * +-------+---+---+ * | |3 2|1 0| * +-------+---+---+ * ^ ^ * | +- 0..1: Color Select 5-4: These bits can be used in place of * | the P4 and P5 bits from the Internal Palette registers * | to form the 8-bit digital color value to the video *DAC. | Selecting these bits is done in the Attribute Mode | Control *register (index 0x10). * +----- 2..3: Color Select 7-6: In modes other than mode 0x13 * (256-color VGA), these are the two most-significant *bits of the 8-bit digital color value to the video DAC. \endcode **/ void CCirrus::write_b_3c0(u8 value) { // Variables to save old state (to detect transitions) bool prev_video_enabled; bool prev_line_graphics; bool prev_int_pal_size; /* The flip-flop determines whether the write goes to the index-register (address) or the data-register. */ if (state.attribute_ctrl.flip_flop == 0) { // Write goes to the index-register. /* The index register also has a bit that controls whether video output is enabled or not. We check this bit, and compare it to it's previous state, to determine whether we need to perform an enable or disable transition. */ prev_video_enabled = state.attribute_ctrl.video_enabled; state.attribute_ctrl.video_enabled = (value >> 5) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c0: video_enabled = %u \n", (unsigned)state.attribute_ctrl.video_enabled); #endif if (state.attribute_ctrl.video_enabled == 0) { if (prev_video_enabled) { #if defined(DEBUG_VGA) printf("found disable transition \n"); #endif // Video output has been disabled. Clear the screen. bx_gui->lock(); bx_gui->clear_screen(); bx_gui->unlock(); } } else if (!prev_video_enabled) { #if defined(DEBUG_VGA) printf("found enable transition \n"); #endif // Video output has been enabled. Draw the screen. redraw_area(0, 0, old_iWidth, old_iHeight); } // Determine what register should be addressed. value &= 0x1f; /* address = bits 0..4 */ state.attribute_ctrl.address = value; /* Registers 0x00..0x0f are palette selection registers. Write a debugging message for all other registers. */ #if defined(DEBUG_VGA) if (value > 0x0f) printf("io write 3c0: address mode reg=%u \n", (unsigned)value); #endif } else { // Write should go to the data-register. // Registers 0x00..0x0f are palette selection registers. if (state.attribute_ctrl.address <= 0x0f) { // Update palette selection only of there is a change. if (value != state.attribute_ctrl.palette_reg[state.attribute_ctrl.address]) { // Update the palette selection. state.attribute_ctrl.palette_reg[state.attribute_ctrl.address] = value; // Requires redrawing the screen. redraw_area(0, 0, old_iWidth, old_iHeight); } } else { switch (state.attribute_ctrl.address) { // Mode control register case 0x10: prev_line_graphics = state.attribute_ctrl.mode_ctrl.enable_line_graphics; prev_int_pal_size = state.attribute_ctrl.mode_ctrl.internal_palette_size; state.attribute_ctrl.mode_ctrl.graphics_alpha = (value >> 0) & 0x01; state.attribute_ctrl.mode_ctrl.display_type = (value >> 1) & 0x01; state.attribute_ctrl.mode_ctrl.enable_line_graphics = (value >> 2) & 0x01; state.attribute_ctrl.mode_ctrl.blink_intensity = (value >> 3) & 0x01; state.attribute_ctrl.mode_ctrl.pixel_panning_compat = (value >> 5) & 0x01; state.attribute_ctrl.mode_ctrl.pixel_clock_select = (value >> 6) & 0x01; state.attribute_ctrl.mode_ctrl.internal_palette_size = (value >> 7) & 0x01; if (((value >> 2) & 0x01) != prev_line_graphics) { bx_gui->lock(); bx_gui->set_text_charmap( &state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } if (((value >> 7) & 0x01) != prev_int_pal_size) { redraw_area(0, 0, old_iWidth, old_iHeight); } #if defined(DEBUG_VGA) printf("io write 3c0: mode control: %02x h \n", (unsigned)value); #endif break; // Overscan Color Register case 0x11: /* We don't do anything with this. Our display doesn't show the overscan part of the normal monitor. */ state.attribute_ctrl.overscan_color = (value & 0x3f); #if defined(DEBUG_VGA) printf("io write 3c0: overscan color = %02x \n", (unsigned)value); #endif break; // Color Plane Enable Register case 0x12: state.attribute_ctrl.color_plane_enable = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: color plane enable = %02x \n", (unsigned)value); #endif break; // Horizontal Pixel Panning Register case 0x13: state.attribute_ctrl.horiz_pel_panning = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: horiz pel panning = %02x \n", (unsigned)value); #endif break; // Color Select Register case 0x14: state.attribute_ctrl.color_select = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: color select = %02x \n", (unsigned)state.attribute_ctrl.color_select); #endif break; default: FAILURE_1(NotImplemented, "io write 3c0: data-write mode %02x h", (unsigned)state.attribute_ctrl.address); } } } // Flip the flip-flop state.attribute_ctrl.flip_flop = !state.attribute_ctrl.flip_flop; } /** * Write to the VGA Miscellaneous Output Register (0x3c2) * * \code * +-+-+-+-+---+-+-+ * |7|6|5| |3 2|1|0| * +-+-+-+-+---+-+-+ * ^ ^ ^ ^ ^ ^ * | | | | | +- 0: I/OAS -- Input/Output Address Select: Selects the CRT * | | | | | controller addresses. * | | | | | 0: Compatibility with monochrome adapter * | | | | | (0x3b4,0x3b5,0x03ba) * | | | | | 1: Compatibility with color graphics adapter (CGA) * | | | | | (0x3d4,0x3d5,0x03da) * | | | | +--- 1: RAM Enable: Controls access from the system: * | | | | 0: Disables access to the display buffer * | | | | 1: Enables access to the display buffer * | | | +--- 2..3: Clock Select: Controls the selection of the dot clocks * | | | used in driving the display timing: * | | | 00: Select 25 Mhz clock (320/640 pixel wide modes) * | | | 01: Select 28 Mhz clock (360/720 pixel wide modes) * | | | 10: Undefined (possible external clock) * | | | 11: Undefined (possible external clock) * | | +----------- 5: Odd/Even Page Select: Selects the upper/lower 64K page * | | of memory when the system is in an even/odd mode. * | | 0: Selects the low page. * | | 1: Selects the high page. * | +------------- 6: Horizontal Sync Polarity * | 0: Positive sync pulse. * | 1: Negative sync pulse. * +--------------- 7: Vertical Sync Polarity * 0: Positive sync pulse. * 1: Negative sync pulse. * \endcode **/ void CCirrus::write_b_3c2(u8 value) { state.misc_output.color_emulation = (value >> 0) & 0x01; state.misc_output.enable_ram = (value >> 1) & 0x01; state.misc_output.clock_select = (value >> 2) & 0x03; state.misc_output.select_high_bank = (value >> 5) & 0x01; state.misc_output.horiz_sync_pol = (value >> 6) & 0x01; state.misc_output.vert_sync_pol = (value >> 7) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c2: \n"); printf(" color_emulation = %u \n", (unsigned)state.misc_output.color_emulation); printf(" enable_ram = %u \n", (unsigned)state.misc_output.enable_ram); printf(" clock_select = %u \n", (unsigned)state.misc_output.clock_select); printf(" select_high_bank = %u \n", (unsigned)state.misc_output.select_high_bank); printf(" horiz_sync_pol = %u \n", (unsigned)state.misc_output.horiz_sync_pol); printf(" vert_sync_pol = %u \n", (unsigned)state.misc_output.vert_sync_pol); #endif } /** * Write to the VGA sequencer index register (0x3c4) * * The Sequencer registers control how video data is sent to the DAC. * * The Sequencer registers are accessed in an indexed fashion. By writing a byte * to the Sequencer Index Register (0x3c4) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the Sequencer Data Register (0x3c5). * * Sequencer registers: * - Reset register (index 0x00) * - Clocking Mode register (index 0x01) * - Map Mask register (index 0x02) * - Character Map Select register (index 0x03) * - Memory Mode register (index 0x04) * . * * \code * Reset register (index 0x00) * +-----------+-+-+ * | |1|0| * +-----------+-+-+ * ^ ^ * | +- 0: Asynchronous Reset: * | 0: Commands the sequencer to asynchronously clear and * | halt. Resetting the sequencer with this bit can * | cause loss of video data. * | 1: Allows the sequencer to function normally. * +--- 1: Sychnronous Reset: * 0: Commands the sequencer to synchronously clear and * halt. * 1: Allows the sequencer to function normally. * Bits 1 and 0 must be 1 to allow the sequencer to operate. * To prevent the loss of data, bit 1 must be set to 0 during the active display * interval before changing the clock selection. The clock is changed through *the Clocking Mode register or the Miscellaneous Output register. * * Clocking Mode register (index 0x01) * +---+-+-+-+-+-+-+ * | |5|4|3|2| |0| * +---+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ * | | | | +- 0: 9/8 Dot Mode: Selects whether a character is 8 or 9 dots * | | | | wide. This can be used to select between 720 and 640 * | | | | pixel modes (or 360 and 320) and also is used to provide * | | | | 9 bit wide character fonts in text mode: * | | | | 0: Selects 9 dots per character. * | | | | 1: Selects 8 dots per character. * | | | +----- 2: Shift/Load Rate: * | | | 0: Video serializers are loaded every character clock. * | | | 1: Video serializers are loaded every other character * | | | clock, which is useful when 16 bits are fetched per * | | | cycle and chained together in the shift registers. * | | +------- 3: Dot Clock Rate: * | | 0: Selects the normal dot clocks derived from the * | | sequencer master clock input. * | | 1: The master clock will be divided by 2 to generate * | | the dot clock. All other timings are affected * | | because they are derived from the dot clock. The *dot | | clock divided by 2 is used for 320 and 360 horizontal * | | PEL modes. * | +--------- 4: Shift Four Enable: * | 0: Video serializers are loaded every character clock. * | 1: Video serializers are loaded every fourth character * | clock, which is useful when 32 bits are fetched per * | cycle and chained together in the shift registers. * +----------- 5: Screen Disable: * 0: Display enabled. * 1: Display blanked. Maximum memory bandwidth assigned *to the system. * * Map Mask register (index 0x02) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Memory Plane Write Enable: If a bit is set, then write * operations will modify the respective plane of display * memory. If a bit is not set then write operations will *not affect the respective plane of display memory. * * Character Map Select register (index 0x03) * +---+-+-+---+---+ * | |5|4|3 2|1 0| * +---+-+-+---+---+ * ^ ^ ^ ^ * | +--|---+- 0..1,4: Character Set B Select: This field is used to select *the | | font that is used in text mode when bit 3 of the *attribute | | byte for a character is set to 0. (*) * +----+----- 2..3,5: Character Set A Select: This field is used to select *the font that is used in text mode when bit 3 of the attribute byte for a *character is set to 1. (*) * * (*) Note that this field is not contiguous in order to provide EGA *compatibility. The font selected resides in plane 2 of display memory at the *address specified by this field, as follows: * +------+---------------+ * | val | font at | * +------+---------------+ * | 000b | 0000h - 1FFFh | * | 001b | 4000h - 5FFFh | * | 010b | 8000h - 9FFFh | * | 011b | C000h - DFFFh | * | 100b | 2000h - 3FFFh | * | 101b | 6000h - 7FFFh | * | 110b | A000h - BFFFh | * | 111b | E000h - FFFFh | * +------+---------------+ * * Memory Mode register (index 0x04) * +-------+-+-+-+-+ * | |3|2|1| | * +-------+-+-+-+-+ * ^ ^ ^ * | | +--- 1: Extended Memory: * | | 0: 64 KB of video memory enabled * | | 1: 256 KB of video memory enabled. This bit must be *set to 1 to | | enable the character map selection described for *the | | previous register. | +----- 2: Odd/Even Host Memory Write *Adressing Disable: | 0: Even system addresses access maps 0 and 2, *while odd system | addresses access maps 1 and 3. | 1: System *addresses sequentially access data within a bit map, | and the *maps are accessed according to the value in the Map | Mask *register (index 0x02). * +------- 3: Chain 4 Enable: This bit controls the map selected *during system read operations. 0: Enables system addresses to sequentially *access data within a bit map by using the Map Mask register. 1: Causes the two *low-order bits to select the map accessed as shown below: * +----+----+--------------+ * | A0 | A1 | Map Selected | * +----+----+--------------+ * | 0 | 0 | 0 | * | 0 | 1 | 1 | * | 1 | 0 | 2 | * | 1 | 1 | 3 | * +----+----+--------------+ * \endcode **/ void CCirrus::write_b_3c4(u8 value) { state.sequencer.index = value; } /** * Write to the VGA sequencer data register (0x3c5) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ void CCirrus::write_b_3c5(u8 value) { unsigned i; u8 charmap1; u8 charmap2; switch (state.sequencer.index) { // Sequencer: reset register case 0: #if defined(DEBUG_VGA) printf("write 0x3c5: sequencer reset: value=0x%02x \n", (unsigned)value); #endif if (state.sequencer.reset1 && ((value & 0x01) == 0)) { state.sequencer.char_map_select = 0; state.charmap_address = 0; bx_gui->lock(); bx_gui->set_text_charmap(&state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } state.sequencer.reset1 = (value >> 0) & 0x01; state.sequencer.reset2 = (value >> 1) & 0x01; break; // Sequencer: clocking mode register case 1: #if defined(DEBUG_VGA) printf("io write 3c5=%02x: clocking mode reg: ignoring \n", (unsigned)value); #endif state.sequencer.reg1 = value & 0x3f; state.x_dotclockdiv2 = ((value & 0x08) > 0); break; // Sequencer: map mask register case 2: state.sequencer.map_mask = (value & 0x0f); for (i = 0; i < 4; i++) state.sequencer.map_mask_bit[i] = (value >> i) & 0x01; break; // Sequencer: character map select register case 3: state.sequencer.char_map_select = value; charmap1 = value & 0x13; if (charmap1 > 3) charmap1 = (charmap1 & 3) + 4; charmap2 = (value & 0x2C) >> 2; if (charmap2 > 3) charmap2 = (charmap2 & 3) + 4; if (state.CRTC.reg[0x09] > 0) { state.charmap_address = (charmap1 << 13); bx_gui->lock(); bx_gui->set_text_charmap(&state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } if (charmap2 != charmap1) printf("char map select: #2=%d (unused) \n", charmap2); break; // Sequencer: memory mode register case 4: state.sequencer.extended_mem = (value >> 1) & 0x01; state.sequencer.odd_even = (value >> 2) & 0x01; state.sequencer.chain_four = (value >> 3) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c5: index 4: \n"); printf(" extended_mem %u \n", (unsigned)state.sequencer.extended_mem); printf(" odd_even %u \n", (unsigned)state.sequencer.odd_even); printf(" chain_four %u \n", (unsigned)state.sequencer.chain_four); #endif break; default: FAILURE_1(NotImplemented, "io write 3c5: index %u unhandled", (unsigned)state.sequencer.index); } } /** * Write to VGA DAC Pixel Mask register (0x3c6) * * The pixel inputs (R, G and B) are anded with this value. Set to FFh * for normal operation. **/ void CCirrus::write_b_3c6(u8 value) { state.pel.mask = value; #if defined(DEBUG_VGA) if (state.pel.mask != 0xff) printf("io write 3c6: PEL mask=0x%02x != 0xFF \n", value); #endif // state.pel.mask should be and'd with final value before // indexing into color register state.pel.data[] } /** * Write VGA DAC Address Read Mode register (0x3c7) * * The Color Registers in the standard VGA provide a mapping between the * palette of between 2 and 256 colors to a larger 18-bit color space. * This capability allows for efficient use of video memory while * providing greater flexibility in color choice. The standard VGA has * 256 palette entries containing six bits each of red, green, and blue * values. The palette RAM is accessed via a pair of address registers * and a data register. * * To write a palette entry, output the palette entry's index value to * the DAC Address Write Mode Register (0x3c8) then perform 3 writes to * the DAC Data Register (0x3c9), loading the red, green, then blue * values into the palette RAM. The internal write address automatically * advances allowing the next value's RGB values to be loaded without * having to reprogram the DAC Address Write Mode Register. This allows * the entire palette to be loaded in one write operation. * * To read a palette entry, output the palette entry's index to the DAC * Address Read Mode Register (0x3c7). Then perform 3 reads from the DAC * Data Register (0x3c9), loading the red, green, then blue values from * palette RAM. The internal read address automatically advances * allowing the next RGB values to be read without having to reprogram * the DAC Address Read Mode Register. * * The data values are 6-bits each. **/ void CCirrus::write_b_3c7(u8 value) { state.pel.read_data_register = value; state.pel.read_data_cycle = 0; state.pel.dac_state = 0x03; } /** * Write VGA DAC Address Write Mode register (0x3c8) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ void CCirrus::write_b_3c8(u8 value) { state.pel.write_data_register = value; state.pel.write_data_cycle = 0; state.pel.dac_state = 0x00; } /** * Write VGA DAC Data register (0x3c9) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ void CCirrus::write_b_3c9(u8 value) { switch (state.pel.write_data_cycle) { case 0: state.pel.data[state.pel.write_data_register].red = value; break; case 1: state.pel.data[state.pel.write_data_register].green = value; break; case 2: { state.pel.data[state.pel.write_data_register].blue = value; // Palette write complete. Check if value has changed bx_gui->lock(); bool changed = bx_gui->palette_change( state.pel.write_data_register, state.pel.data[state.pel.write_data_register].red << 2, state.pel.data[state.pel.write_data_register].green << 2, state.pel.data[state.pel.write_data_register].blue << 2); bx_gui->unlock(); // If palette value has changed, redraw the screen. if (changed) redraw_area(0, 0, old_iWidth, old_iHeight); } break; } // Move on to next RGB component state.pel.write_data_cycle++; // palette entry complete, move on to next one if (state.pel.write_data_cycle >= 3) { // BX_INFO(("state.pel.data[%u] {r=%u, g=%u, b=%u}", // (unsigned) state.pel.write_data_register, // (unsigned) state.pel.data[state.pel.write_data_register].red, // (unsigned) state.pel.data[state.pel.write_data_register].green, // (unsigned) state.pel.data[state.pel.write_data_register].blue); state.pel.write_data_cycle = 0; state.pel.write_data_register++; } } /** * Write to VGA Graphics Controller Index Register (0x3ce) * * The Graphics Controller registers control how the system accesses video RAM. * * The Graphics registers are accessed in an indexed fashion. By writing a byte * to the Graphics Index Register (0x3ce) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the Graphics Data Register (0x3cf). * * Graphics registers: * - Set/Reset register (index 0x00) * - Enable Set/Reset register (index 0x01) * - Color Compare register (index 0x02) * - Data Rotate register (index 0x03) * - Read Map Select register (index 0x04) * - Graphics Mode register (index 0x05) * - Miscellaneous Graphics register (index 0x06) * - Color Don't Care register (index 0x07) * - Bit Mask register (index 0x08) * . * * \code * Set/Reset register (index 0x00) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Set/Reset: Bits 3-0 of this field represent planes 3-0 *of the VGA display memory. This field is used by Write Mode 0 and Write Mode 3 *(See the Write Mode field.) In Write Mode 0, if the corresponding bit in the *Enable Set/Reset field is set, and in Write Mode 3 regardless of the Enable * Set/Reset field, the value of the bit in this field is * expanded to 8 bits and substituted for the data of the * respective plane and passed to the next stage in the * graphics pipeline, which for Write Mode 0 is the Logical * Operation unit and for Write Mode 3 is the Bit Mask *unit. * * Enable Set/Reset Register (index 0x01) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Enable Set/Reset: Bits 3-0 of this field represent *planes 3-0 of the VGA display memory. This field is used in Write Mode 0 (See *the Write Mode field) to select whether data for each plane is derived from *host data or from expansion of the respective bit in the Set/Reset field. * * Color Compare Register (index 0x02) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Compare: Bits 3-0 of this field represent planes *3-0 of the VGA display memory. This field holds a reference color that is used *by Read Mode 1 (See the Read Mode field.) Read Mode 1 returns the result of *the comparison between this value and a location of display memory, modified *by the Color Don't Care field. * * Data Rotate Register (index 0x03) * +-----+---+-----+ * | |4 3|2 1 0| * +-----+---+-----+ * ^ ^ * | +- 0..2: Rotate Count: * | This field is used in Write Mode 0 and Write Mode 3 *(See | the Write Mode field.) In these modes, the host data is | *rotated to the right by the value specified by the value of | this *field. A rotation operation consists of moving bits | 7-1 right *one position to bits 6-0, simultaneously | wrapping bit 0 around *to bit 7, and is repeated the number | of times specified by this *field. * +------ 3..4: Logical Operation: * This field is used in Write Mode 0 and Write Mode 2 *(See the Write Mode field.) The logical operation stage of the graphics *pipeline is 32 bits wide (1 byte * 4 planes) and performs the operations on *its inputs from the previous stage in the graphics pipeline and the latch *register. The latch register remains unchanged and the result is passed on to *the next stage in the pipeline. The results based on the value of this field *are: 00: Result is input from previous stage unmodified. 01: Result is input *from previous stage logical ANDed with latch register. 10: Result is input *from previous stage logical ORed with latch register. 11: Result is input from *previous stage logical XORed with latch register. * * Read Map Select register (index 0x04) * +-----------+---+ * | |1 0| * +-----------+---+ * ^ * +- 0..1: Read Map Select: The value of this field is used in *Read Mode 0 (see the Read Mode field) to specify the display memory plane to *transfer data from. Due to the arrangement of video memory, this field must be *modified four times to read one or more pixels values in the planar video *modes. * * Graphics Mode Register (index 0x05) * +-+-+-+-+-+-+---+ * | |6|5|4|3| |1 0| * +-+-+-+-+-+-+---+ * ^ ^ ^ ^ ^ * | | | | +- 0..1: Write Mode * | | | | This field selects between four write modes, simply *known | | | | as Write Modes 0-3, based upon the value of this *field: | | | | 00: Write Mode 0: In this mode, the host data is *first | | | | rotated as per the Rotate Count field, then *the | | | | Enable Set/Reset mechanism selects data from *this or | | | | the Set/Reset field. Then the selected *Logical | | | | Operation is performed on the resulting data *and the | | | | data in the latch register. Then the Bit *Mask field | | | | is used to select which bits come from *the resulting | | | | data and which come from the latch *register. Finally, | | | | only the bit planes enabled by *the Memory Plane Write | | | | Enable field are written to *memory. | | | | 01: Write Mode 1: In this mode, data is *transferred directly | | | | from the 32 bit latch register *to display memory, | | | | affected only by the Memory Plane *Write Enable field. | | | | The host data is not used in *this mode. | | | | 10: Write Mode 2: In this mode, the bits 3-0 *of the host | | | | data are replicated across all 8 bits of *their | | | | respective planes. Then the selected Logical *Operation | | | | is performed on the resulting data and the *data in the | | | | latch register. Then the Bit Mask field *is used to | | | | select which bits come from the resulting *data and which | | | | come from the latch register. *Finally, only the bit | | | | planes enabled by the Memory *Plane Write Enable field | | | | are written to memory. | | *| | 11: Write Mode 3: In this mode, the data in the Set/Reset | *| | | field is used as if the Enable Set/Reset field were *set | | | | to 1111b. Then the host data is first rotated as *per the | | | | Rotate Count field, then logical ANDed with *the value of | | | | the Bit Mask field. The resulting value *is used on the | | | | data obtained from the Set/Reset *field in the same way | | | | that the Bit Mask field would *ordinarily be used. to | | | | select which bits come from *the expansion of the | | | | Set/Reset field and which come *from the latch register. | | | | Finally, only the bit *planes enabled by the Memory Plane | | | | Write Enable *field are written to memory. | | | +--------- 3: Read Mode: | | | This field *selects between two read modes, simply known as Read | | | Mode *0, and Read Mode 1, based upon the value of this field: | | | 0: Read Mode 0: *In this mode, a byte from one of the four | | | planes is *returned on read operations. The plane from | | | which the *data is returned is determined by the value of | | | the *Read Map Select field. | | | 1: Read Mode 1: In this mode, a *comparison is made between | | | display memory and a *reference color defined by the Color | | | Compare field. *Bit planes not set in the Color Don't Care | | | field then *the corresponding color plane is not considered | | | in *the comparison. Each bit in the returned result | | | represents one *comparison between the reference color, with | | | the bit *being set if the comparison is true. | | +----------- 4: Host Odd/Even Memory *Read Addressing Enable: | | 0: Selects the standard *addressing mode. | | 1: Selects the odd/even addressing mode *used by the IBM CGA | | Adapter. | | Normally, the value *here follows the value of Memory Mode | | register bit 2 in *the sequencer." | +------------- 5: Shift Register Interleave Mode: | 1: *Directs the shift registers in the graphics controller to | format the serial *data stream with even-numbered bits from | both maps on *even-numbered maps, and odd-numbered bits from | both *maps on the odd-numbered maps. This bit is used for | modes 4 and 5. * +--------------- 6: 256-Color Shift Mode: * 0: Allows bit 5 to control the loading of the shift *registers. 1: Causes the shift registers to be loaded in a manner that * supports the 256-color mode. * * Miscellaneous Graphics register (index 0x06) * +-------+---+-+-+ * | |3 2|1|0| * +-------+---+-+-+ * ^ ^ ^ * | | +- 0: Alphanumeric Mode Disable: * | | This bit controls alphanumeric mode addressing. * | | 0: Text mode. * | | 1: Graphics modes, disables character generator *latches. | +--- 1: Chain Odd/Even Enable | 1: Directs the system *address bit, A0, to be replaced by a | higher-order bit. The odd *map is then selected when A0 is 1, | and the even map when A0 is *0. * +--- 2..3: Memory Map Select * This field specifies the range of host memory addresses *that is decoded by the VGA hardware and mapped into display memory accesses. *The values of this field and their corresponding host memory ranges are: 00: *A0000h-BFFFFh (128K region) 01: A0000h-AFFFFh (64K region) 10: B0000h-B7FFFh *(32K region) 11: B8000h-BFFFFh (32K region) * * Color Don't Care register (index 0x07) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Don't Care: Bits 3-0 of this field represent *planes 3-0 of the VGA display memory. This field selects the planes that are *used in the comparisons made by Read Mode 1 (See the Read Mode field.) Read *Mode 1 returns the result of the comparison between the value of the Color * Compare field and a location of display memory. If a bit * in this field is set, then the corresponding display * plane is considered in the comparison. If it is not set, * then that plane is ignored for the results of the * comparison. * * Bit Mask register (index 0x08) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * +----- 0..7: Bit Mask: This field is used in Write Modes 0, 2, and 3 * (See the Write Mode field.) It it is applied to one byte * of data in all four display planes. If a bit is set, * then the value of corresponding bit from the previous * stage in the graphics pipeline is selected; otherwise * the value of the corresponding bit in the latch register * is used instead. In Write Mode 3, the incoming data *byte, after being rotated is logical ANDed with this byte and the resulting *value is used in the same way this field would normally be used by itself. * \endcode **/ void CCirrus::write_b_3ce(u8 value) { #if defined(DEBUG_VGA) if (value > 0x08) /* ??? */ printf("io write: 3ce: value > 8 \n"); #endif state.graphics_ctrl.index = value; } /** * Write to VGA Graphics Controller Data Register (0x3cf) * * For a description of the Graphics registers, see CCirrus::write_b_3ce **/ void CCirrus::write_b_3cf(u8 value) { u8 prev_memory_mapping; bool prev_graphics_alpha; bool prev_chain_odd_even; /* Graphics Controller Registers 00..08 */ switch (state.graphics_ctrl.index) { case 0: /* Set/Reset */ state.graphics_ctrl.set_reset = value & 0x0f; break; case 1: /* Enable Set/Reset */ state.graphics_ctrl.enable_set_reset = value & 0x0f; break; case 2: /* Color Compare */ state.graphics_ctrl.color_compare = value & 0x0f; break; case 3: /* Data Rotate */ state.graphics_ctrl.data_rotate = value & 0x07; /* ??? is this bits 3..4 or 4..5 */ state.graphics_ctrl.raster_op = (value >> 3) & 0x03; /* ??? */ break; case 4: /* Read Map Select */ state.graphics_ctrl.read_map_select = value & 0x03; #if defined(DEBUG_VGA) printf("io write to 03cf = %02x (RMS) \n", (unsigned)value); #endif break; case 5: /* Mode */ state.graphics_ctrl.write_mode = value & 0x03; state.graphics_ctrl.read_mode = (value >> 3) & 0x01; state.graphics_ctrl.odd_even = (value >> 4) & 0x01; state.graphics_ctrl.shift_reg = (value >> 5) & 0x03; #if defined(DEBUG_VGA) if (state.graphics_ctrl.odd_even) printf("io write: 3cf: reg 05: value = %02xh \n", (unsigned)value); if (state.graphics_ctrl.shift_reg) printf("io write: 3cf: reg 05: value = %02xh \n", (unsigned)value); #endif break; case 6: /* Miscellaneous */ prev_graphics_alpha = state.graphics_ctrl.graphics_alpha; prev_chain_odd_even = state.graphics_ctrl.chain_odd_even; prev_memory_mapping = state.graphics_ctrl.memory_mapping; state.graphics_ctrl.graphics_alpha = value & 0x01; state.graphics_ctrl.chain_odd_even = (value >> 1) & 0x01; state.graphics_ctrl.memory_mapping = (value >> 2) & 0x03; #if defined(DEBUG_VGA) printf("memory_mapping set to %u \n", (unsigned)state.graphics_ctrl.memory_mapping); printf("graphics mode set to %u \n", (unsigned)state.graphics_ctrl.graphics_alpha); printf("odd_even mode set to %u \n", (unsigned)state.graphics_ctrl.odd_even); printf("io write: 3cf: reg 06: value = %02xh \n", (unsigned)value); #endif if (prev_memory_mapping != state.graphics_ctrl.memory_mapping) { redraw_area(0, 0, old_iWidth, old_iHeight); } if (prev_graphics_alpha != state.graphics_ctrl.graphics_alpha) { redraw_area(0, 0, old_iWidth, old_iHeight); old_iHeight = 0; } break; case 7: /* Color Don't Care */ state.graphics_ctrl.color_dont_care = value & 0x0f; break; case 8: /* Bit Mask */ state.graphics_ctrl.bitmask = value; break; default: /* ??? */ FAILURE_1(NotImplemented, "io write: 3cf: index %u unhandled", (unsigned)state.graphics_ctrl.index); } } /** * Write to VGA CRTC Index Register (0x3b4 or 0x3d4) * * The VGA CRTC Registers control how the video is output to the display. * * The CRTC registers are accessed in an indexed fashion. By writing a byte * to the CRTC Index Register (0x3d4) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the CRTC Data Register (0x3d5). * * CRTC registers: * - Horizontal Total Register (index 0x00) * - End Horizontal Display Register (index 0x01) * - Start Horizontal Blanking Register (index 0x02) * - End Horizontal Blanking Register (index 0x03) * - Start Horizontal Retrace Register (index 0x04) * - End Horizontal Retrace Register (index 0x05) * - Vertical Total Register (index 0x06) * - Overflow Register (index 0x07) * - Preset Row Scan Register (index 0x08) * - Maximum Scan Line Register (index 0x09) * - Cursor Start Register (index 0x0a) * - Cursor End Register (index 0x0b) * - Start Address High Register (index 0x0c) * - Start Address Low Register (index 0x0d) * - Cursor Location High Register (index 0x0e) * - Cursor Location Low Register (index 0x0f) * - Vertical Retrace Start Register (index 0x10) * - Vertical Retrace End Register (index 0x11) * - Vertical Display End Register (index 0x12) * - Offset Register (index 0x13) * - Underline Location Register (index 0x14) * - Start Vertical Blanking Register (index 0x15) * - End Vertical Blanking (index 0x16) * - CRTC Mode Control Register (index 0x17) * - Line Compare Register (index 0x18) * . * * \code * Horizontal Total register (index 0x00) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Horizontal Total: * This field is used to specify the number of character clocks per scan line. * This field, along with the dot rate selected, controls the horizontal * refresh rate of the VGA by specifying the amount of time one scan line * takes. This field is not programmed with the actual number of character * clocks, however. Due to timing factors of the VGA hardware (which, for * compatibility purposes has been emulated by VGA compatible chipsets), the * actual horizontal total is 5 character clocks more than the value stored in * this field, thus one needs to subtract 5 from the actual horizontal total * value desired before programming it into this register. * * End Horizontal Display register (index 0x01) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: End Horizontal Display: * This field is used to control the point that the sequencer stops outputting * pixel values from display memory, and sequences the pixel value specified by * the Overscan Palette Index field for the remainder of the scan line. The * overscan begins the character clock after the the value programmed into this * field. This register should be programmed with the number of character * clocks in the active display - 1. Note that the active display may be * affected by the Display Enable Skew field. * * Start Horizontal Blanking register (index 0x02) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Start Horizontal Blanking: * This field is used to specify the character clock at which the horizontal * blanking period begins. During the horizontal blanking period, the VGA * hardware forces the DAC into a blanking state, where all of the intensities * output are at minimum value, no matter what color information the attribute * controller is sending to the DAC. This field works in conjunction with the * End Horizontal Blanking field to specify the horizontal blanking period. * Note that the horizontal blanking can be programmed to appear anywhere within * the scan line, as well as being programmed to a value greater than the * Horizontal Total field preventing the horizontal blanking from occurring at * all. * * End Horizontal Blanking register (index 0x03) * +-+---+---------+ * |7|6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ ^ * | | +-- 0..4: End Horizontal Blanking: * | | Contains bits 4-0 of the End Horizontal Blanking field * | | which specifies the end of the horizontal blanking * | | period. Bit 5 is located in bit 7 of the End Horizontal * | | Retrace register (index 0x05). After the period has * | | begun as specified by the Start Horizontal Blanking * | | field, the 6-bit value of this field is compared against * | | the lower 6 bits of the character clock. When a match * | | occurs, the horizontal blanking signal is disabled. This * | | provides from 1 to 64 character clocks although some * | | implementations may match in the character clock * | | specified by the Start Horizontal Blanking field, in *which | | case the range is 0 to 63. Note that if blanking *extends | | past the end of the scan line, it will end on the *first | | match of this field on the next scan line. | *+--------- 5..6: Display Enable Skew: | This field affects *the timings of the display enable | circuitry in the VGA. *The value of this field is the number | of character clocks *that the display enable "signal" is | delayed. In all known *VGA cards, this field is always | programmed to 0. *Programming it to non-zero values results | in the overscan *being displayed over the number of | characters programmed *into this field at the beginning of | the scan line, as well *as the end of the active display | being shifted the number *of characters programmed into this | field. The characters *that extend past the normal end of the | active display can *be garbled in certain circumstances that | is dependent on *the particular VGA implementation. According | to *documentation from IBM, "This skew control is needed to | provide sufficient *time for the CRT controller to read a | character and *attribute code from the video buffer, to gain | access to *the character generator, and go through the | Horizontal PEL *Panning register in the attribute controller. | Each access *requires the 'display enable' signal to be | skewed one *character clock so that the video output is | synchronized *with the horizontal and vertical retrace | signals." as well *as "Note: Character skew is not adjustable | on the Type 2 *video and the bits are ignored; however, | programs should *set these bits for the appropriate skew to | maintain *compatibility." This may be required for some early | IBM *VGA implementations or may be simply an unused "feature" | carried over along *with its register description from the IBM | EGA *implementations that require the use of this field. * +--------------- 7: Enable Vertical Retrace Access: * This field was used in the IBM EGA to provide access to *the light pen input values as the light pen registers were mapped over CRTC *indexes 10h-11h. The VGA lacks capability for light pen input, thus this field *is normally forced to 1 (although always writing it as 1 might be a good idea *for compatibility), which in the EGA would enable access to the vertical *retrace fields instead of the light pen fields. * * Start Horizontal Retrace register (index 0x04) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Start Horizontal Retrace: * This field specifies the character clock at which the VGA begins sending the * horizontal synchronization pulse to the display which signals the monitor to *retrace back to the left side of the screen. The end of this pulse is *controlled by the End Horizontal Retrace field. This pulse may appear anywhere *in the scan line, as well as set to a position beyond the Horizontal Total *field which effectively disables the horizontal synchronization pulse. * * End Horizontal Retrace register (index 0x05) * +-+---+---------+ * |7|6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ ^ * | | +-- 0..4: End Horizontal Retrace: * | | This field specifies the end of the horizontal retrace *period, | | which begins at the character clock specified in *the Start | | Horizontal Retrace field. The horizontal *retrace signal is | | enabled until the lower 5 bits of the *character counter match | | the 5 bits of this field. This *provides for a horizontal | | retrace period from 1 to 32 *character clocks. Note that some | | implementations may *match immediately instead of 32 clocks | | away, making the *effective range 0 to 31 character clocks. | +--------- 5..6: Horizontal *Retrace Skew: | This field delays the start of the *horizontal retrace period | by the number of character *clocks equal to the value of this | field. From *observation, this field is programmed to 0, with | the *exception of the 40 column text modes where this field is | set to 1. The VGA *hardware simply acts as if this value is | added to the *Start Horizontal Retrace field. According to IBM | documentation, "For certain *modes, the 'horizontal retrace' | signal takes up the entire *blanking interval. Some internal | timings are generated by *the falling edge of the 'horizontal | retrace' signal. To *ensure that the signals are latched | properly, the *'retrace' signal is started before the end of | the 'display *enable' signal and then skewed several character | clock *times to provide the proper screen centering." This does | not appear to be *the case, leading me to believe this is yet | another *holdout from the IBM EGA implementations that do | require *the use of this field. * +--------------- 7: End Horizontal Blanking (bit 5): * This contains bit 5 of the End Horizontal Blanking field *in the End Horizontal Blanking register (index 0x03). * * Vertical Total register (index 0x06) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Vertical Total * This contains the lower 8 bits of the Vertical Total field. Bits 9-8 of this *field are located in the Overflow Register (index 0x07). This field determines *the number of scanlines in the active display and thus the length of each *vertical retrace. This field contains the value of the scanline counter at the *beginning of the last scanline in the vertical period. * * Overflow register (index 0x07) * +-+-+-+-+-+-+-+-+ * |7|6|5|4|3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ ^ * | | +-|-|-|-|-+- 0,5: Bit 8,9 of Vertical Total (index 0x06) * | +---|-|-|-+--- 1,6: Bit 8,9 of Vertical Display End (index 0x12) * +-----|-|-+----- 2,7: Bit 8,9 of Vertical Retrace Start (index 0x10) * | +--------- 3: Bit 8 of Start Vertical Blanking (index 0x15) * +----------- 4: Bit 8 of Line Compare (index 0x18) * * Preset Row Scan register (index 0x08) * +-+---+---------+ * | |6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ * | +-- 0..4: Preset Row Scan: * | This field is used when using text mode or any mode with *a non-zero | Maximum Scan Line field (index 0x09) to provide *for more precise | vertical scrolling than the Start Address *Register provides. The | value of this field specifies how many *scan lines to scroll the | display upwards. Valid values range *from 0 to the value of the | Maximum Scan Line field. Invalid *values may cause undesired effects | and seem to be dependent *upon the particular VGA implementation. * +--------- 5..6: Byte Panning: * The value of this field is added to the Start Address *Register when calculating the display memory address for the upper left hand *pixel or character of the screen. This allows for a maximum shift of 15, 31, *or 35 pixels without having to reprogram the Start Address Register. * * Maximum Scan Line register (index 0x09) * +-+-+-+---------+ * |7|6|5|4 3 2 1 0| * +-+-+-+---------+ * ^ ^ ^ ^ * | | | +-- 0..4: Maximum Scan Line: * | | | In text modes, this field is programmed with the *character height - 1 | | | (scan line numbers are zero based.) *In graphics modes, a non-zero | | | value in this field will *cause each scan line to be repeated by the | | | value of this *field + 1. | | +----------- 5: Bit 9 of Start Vertical Blanking (index 0x15) * | +------------- 6: Bit 9 of Line Compare (index 0x18) * +--------------- 7: Scan Doubling: * When this bit is set to 1, 200-scan-line video data is *converted to 400-scan-line output. To do this, the clock in the row scan *counter is divided by 2, which allows the 200-line modes to be displayed as *400 lines on the display (this is called double scanning; each line is * displayed twice). When this bit is set to 0, the clock *to the row scan counter is equal to the horizontal scan rate. * * Cursor Start Register (index 0x0a) * +---+-+---------+ * | |5|4 3 2 1 0| * +---+-+---------+ * ^ ^ * | +-- 0..4: Cursor Scan Line Start: * | This field controls the appearance of the text-mode *cursor by | specifying the scan line location within a character *cell at which | the cursor should begin, with the top-most scan *line in a character | cell being 0 and the bottom being with the *value of the Maximum Scan | Line field. * +------------5: Cursor Disable: * This field controls whether or not the text-mode cursor *is displayed: 0: Cursor Enabled. 1: Cursor Disabled. * * Cursor End Register (index 0x0b) * +-+---+---------+ * | |6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ * | +-- 0..4: Cursor Scan Line End: * | This field controls the appearance of the text-mode *cursor by | specifying the scan line location within a *character cell at which | the cursor should end, with the *top-most scan line in a character | cell being 0 and the bottom *being with the value of the Maximum Scan | Line field. If this *field is less than the Cursor Scan Line Start | field, the *cursor is not drawn. Some graphics adapters, such as the | IBM *EGA display a split-block cursor instead. * +------------ 5: Cursor Skew: * This field was necessary in the EGA to synchronize the *cursor with internal timing. In the VGA it basically is added to the cursor * location. In some cases when this value is non-zero and *the cursor is near the left or right edge of the screen, the cursor will not *appear at all, or a second cursor above and to the left of the actual one may * appear. This behavior may not be the same on all VGA *compatible adapter cards. * * Start Address High register (index 0x0c) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 8..15 of the Start Address. * Bits 0..7 are in the Start Address Low register (index 0x0d). The Start *Address field specifies the display memory address of the upper left pixel or *character of the screen. Because the standard VGA has a maximum of 256K of *memory, and memory is accessed 32 bits at a time, this 16-bit field is *sufficient to allow the screen to start at any memory address. Normally this *field is programmed to 0h, except when using virtual resolutions, paging, * and/or split-screen operation. Note that the VGA display will wrap around in *display memory if the starting address is too high. (This may or may not be *desirable, depending on your intentions.) * * Start Address Low register (index 0x0d) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of the Start Address. See Start Address High register (index *0x0c) * * Cursor Location High register (index 0x0e) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 8..15 of the Cursor Location. * Bits 0..7 are in the Cursor Location Low register (index 0x0d). When the VGA *hardware is displaying text mode and the text-mode cursor is enabled, the *hardware compares the address of the character currently being displayed with *sum of value of this field and the sum of the Cursor Skew field. If the values *equal then the scan lines in that character specified by the Cursor Scan Line *Start field and the Cursor Scan Line End field are replaced with the * foreground color. * * Cursor Location Low register (index 0x0f) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of the Cursor Location. See Cursor Location High register *(index 0x0f) * * Vertical Retrace Start register (index 0x10) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Vertical Retrace Start * Bits 8 and 9 are in the Overflow Register (index 0x07). This field controls *the start of the vertical retrace pulse which signals the display to move up *to the beginning of the active display. This field contains the value of the *vertical scanline counter at the beginning of the first scanline where the *vertical retrace signal is asserted. * * Vertical Retrace End register (index 0x11) * +-+-+---+-------+ * |7|6|5|4|3 2 1 0| * +-+-+---+-------+ * ^ ^ ^ ^ ^ * | | | | +-- 0..3: Vertical Retrace End: * | | | | This field determines the end of the vertical retrace *pulse, and thus its | | | | length. This field contains the lower *four bits of the vertical scanline | | | | counter at the *beginning of the scanline immediately after the last | | | | scanline where *the vertical retrace signal is asserted. | | | +--------- 4: End Vertical *Interrupt | | +----------- 5: Enable Vertical Interrupt | +------------- 6: *Memory Refresh Bandwidth: | Nearly all video chipsets *include a few registers that control memory, bus, | or other *timings not directly related to the output of the video card. Most | VGA/SVGA *implementations ignore the value of this field; however, in the | least, IBM *VGA adapters do utilize it and thus for compatibility with these | chipsets *this field should be programmed. This register is used in the IBM | VGA *hardware to control the number of DRAM refresh cycles per scan line. | The *three refresh cycles per scanline is appropriate for the IBM VGA | horizontal *frequency of approximately 31.5 kHz. For horizontal frequencies | greater than *this, this setting will work as the DRAM will be refreshed more | often. *However, refreshing not often enough for the DRAM can cause memory | loss. *Thus at some point slower than 31.5 kHz the five refresh cycle setting | *should be used. At which particular point this should occur, would require | *better knowledge of the IBM VGA's schematics than I have available. | *According to IBM documentation, "Selecting five refresh cycles allows use of * | the VGA chip with 15.75 kHz displays." which isn't *really enough to go by | unless the mode you are defining *has a 15.75 kHz horizontal frequency. * +--------------- 7: CRTC Registers Protect Enable: * This field is used to protect the video timing registers *from being changed by programs written for earlier graphics chipsets that *attempt to program these registers with values unsuitable for VGA timings. *When this field is set to 1, the CRTC register indexes 00h-07h ignore write *access, with the exception of bit 4 of the Overflow Register, which holds bit *8 of the Line Compare field. * * Vertical Display End register (index 0x12) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Vertical Display End * Bits 8 and 9 are in the Overflow Register (index 0x07). This field contains *the value of the vertical scanline counter at the beggining of the scanline *immediately after the last scanline of active display. * * Offset register (index 0x13) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Offset: * This field specifies the address difference between consecutive scan lines or *two lines of characters. Beginning with the second scan line, the starting *scan line is increased by twice the value of this register multiplied by the *current memory address size (byte = 1, word = 2, double-word = 4) each line. *For text modes the following equation is used: Offset = Width / ( *MemoryAddressSize * 2 ) and in graphics mode, the following equation is used: * Offset = Width / ( PixelsPerAddress * MemoryAddressSize * 2 ) * where Width is the width in pixels of the screen. This register can be *modified to provide for a virtual resolution, in which case Width is the width *is the width in pixels of the virtual screen. PixelsPerAddress is the number *of pixels stored in one display memory address, and MemoryAddressSize is the *current memory addressing size. * * Underline Location register (index 0x14) * +-+-+-+---------+ * | |6|5|4 3 2 1 0| * +-+-+-+---------+ * ^ ^ ^ * | | +-- 0..4: Underline Location * | | These bits specify the horizontal scan line of a *character row on which an | | underline occurs. The value *programmed is the scan line desired minus 1. | +----------- 5: Divide Memory *Address Clock by 4: | 1: The memory-address counter is *clocked with the character clock divided | by 4, which is *used when doubleword addresses are used. * +------------- 6: Double-Word Addressing: * 1: Memory addresses are doubleword addresses. See the *description of the word/byte mode bit (bit 6) in the CRT Mode Control Register *(index 0x17) * * Start Vertical Blanking register (index 0x15) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Start Vertical Blanking * Bit 8 is in the Overflow Register (index 0x07), and bit 9 is in the Maximum *Scan Line register (index 0x09). This field determines when the vertical *blanking period begins, and contains the value of the vertical scanline *counter at the beginning of the first vertical scanline of blanking. * * End Vertical Blanking register (index 0x16) * +-+-------------+ * | |6 5 4 3 2 1 0| * +-+-------------+ * ^ * +---- 0..6: End Vertical Blanking: * This field determines when the vertical blanking period *ends, and contains the value of the vertical scanline counter at the beginning *of the vertical scanline immediately after the last scanline of blanking. * * CRTC Mode Control Register (index 0x17) * +-+-+-+-+-+-+-+-+ * |7|6|5| |3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +- 0: Map Display Address 13: * | | | | | | This bit selects the source of bit 13 of the output *multiplexer: | | | | | | 0: Bit 0 of the row scan counter is the *source. | | | | | | 1: Bit 13 of the address counter is the source. * | | | | | | The CRT controller used on the IBM Color/Graphics *Adapter was capable of | | | | | | using 128 horizontal scan-line *addresses. For the VGA to obtain 640-by-200 | | | | | | graphics *resolution, the CRT controller is programmed for 100 horizontal | | | | | | *scan lines with two scan-line addresses per character row. Row scan address | *| | | | | bit 0 becomes the most-significant address bit to the *display buffer. | | | | | | Successive scan lines of the display image *are displaced in 8KB of memory. | | | | | | This bit allows *compatibility with the graphics modes of earlier adapters. | | | | | +--- 1: *Map Display Address 14: | | | | | This bit selects the source of bit *14 of the output multiplexer: | | | | | 0: Bit 1 of the row scan *counter is the source. | | | | | 1: Bit 14 of the address counter *is the source. | | | | +----- 2: Divide Scan Line clock by 2: | | | | This *bit selects the clock that controls the vertical timing counter: | | | | 0: *The horizontal retrace clock. | | | | 1: The horizontal retrace *clock divided by 2. | | | | Dividing the clock effectively doubles *the vertical resolution of the CRT | | | | controller. The *vertical counter has a maximum resolution of 1024 scan lines | | | | because *the vertical total value is 10-bits wide. If the vertical counter is | | | | *clocked with the horizontal retrace divided by 2, the vertical resolution is * | | | | doubled to 2048 scan lines." * | | | +------- 3: Divide Memory Address clock by 2: * | | | This bit selects the clock that controlls the address *counter: | | | 0: The character clock. | | | 1: The character *clock divided by 2. | | | This bit is used to create either a *byte or word refresh address for the | | | display buffer. | | *+----------- 5: Address Wrap Select: | | This bit selects the *memory-address bit, bit MA 13 or MA 15, that appears on | | the output pin MA *0, in the word address mode. If the VGA is not in the word | | address mode, *bit 0 from the address counter appears on the output pin, MA 0. | | 0: Selects *MA 13. Used in applications where only 64KB of video memory is | | present. | *| 1: Selects MA 15. In odd/even mode, this bit should be set *to 1 because | | 256KB of video memory is installed on *the system board. | | This function maintains compatibility *with the IBM Color/Graphics Monitor | | Adapter. | *+------------- 6: Word/Byte Mode Select: | 0: Selects the *word address mode. The word mode shifts the memory-address | counter bits to *the left by one bit; the most-significant bit of the | counter appears on the *least-significant bit of the memory address | outputs. * | 1: Selects the byte address mode. * | The doubleword bit in the Underline Location register *(index 0x14) also | controls the addressing. When the *doubleword bit is 0, the word/byte bit | selects the mode. *When the doubleword bit is set to 1, the addressing is | shifted by two bits. * +--------------- 7: Sync Enable: * 0: Disables the horizontal and vertical retrace *signals and forces them to an inactive level. 1: Enables the horizontal and *vertical retrace signals. This bit does not reset any other registers or *signal outputs. * * Line Compare register (index 0x18) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Line Compare * Bit 8 is in the Overflow Register (index 0x07), and bit 9 is in the Maximum *Scan Line register (index 0x09). The Line Compare field specifies the scan *line at which a horizontal division can occur, providing for split-screen *operation. If no horizontal division is required, this field should be set to *3FFh. When the scan line counter reaches the value in the Line Compare field, *the current scan line address is reset to 0 and the Preset Row Scan is *presumed to be 0. If the Pixel Panning Mode field is set to 1 then the Pixel *Shift Count and Byte Panning fields are reset to 0 for the remainder of the *display cycle. \endcode **/ void CCirrus::write_b_3d4(u8 value) { state.CRTC.address = value & 0x7f; #if defined(DEBUG_VGA) if (state.CRTC.address > 0x18) printf("write: invalid CRTC register 0x%02x selected", (unsigned)state.CRTC.address); #endif } /** * Write to VGA CRTC Data Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ void CCirrus::write_b_3d5(u8 value) { /* CRTC Registers */ if (state.CRTC.address > 0x18) { #if defined(DEBUG_VGA) printf("write: invalid CRTC register 0x%02x ignored", (unsigned)state.CRTC.address); #endif return; } if (state.CRTC.write_protect && (state.CRTC.address < 0x08)) { if (state.CRTC.address == 0x07) { state.CRTC.reg[state.CRTC.address] &= ~0x10; state.CRTC.reg[state.CRTC.address] |= (value & 0x10); state.line_compare &= 0x2ff; if (state.CRTC.reg[0x07] & 0x10) state.line_compare |= 0x100; redraw_area(0, 0, old_iWidth, old_iHeight); return; } else { return; } } if (value != state.CRTC.reg[state.CRTC.address]) { state.CRTC.reg[state.CRTC.address] = value; switch (state.CRTC.address) { case 0x07: state.vertical_display_end &= 0xff; if (state.CRTC.reg[0x07] & 0x02) state.vertical_display_end |= 0x100; if (state.CRTC.reg[0x07] & 0x40) state.vertical_display_end |= 0x200; state.line_compare &= 0x2ff; if (state.CRTC.reg[0x07] & 0x10) state.line_compare |= 0x100; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x08: // Vertical pel panning change redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x09: state.y_doublescan = ((value & 0x9f) > 0); state.line_compare &= 0x1ff; if (state.CRTC.reg[0x09] & 0x40) state.line_compare |= 0x200; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x0A: case 0x0B: case 0x0E: case 0x0F: // Cursor size / location change state.vga_mem_updated = 1; break; case 0x0C: case 0x0D: // Start address change if (state.graphics_ctrl.graphics_alpha) { redraw_area(0, 0, old_iWidth, old_iHeight); } else { state.vga_mem_updated = 1; } break; case 0x12: state.vertical_display_end &= 0x300; state.vertical_display_end |= state.CRTC.reg[0x12]; break; case 0x13: case 0x14: case 0x17: // Line offset change state.line_offset = state.CRTC.reg[0x13] << 1; if (state.CRTC.reg[0x14] & 0x40) state.line_offset <<= 2; else if ((state.CRTC.reg[0x17] & 0x40) == 0) state.line_offset <<= 1; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x18: state.line_compare &= 0x300; state.line_compare |= state.CRTC.reg[0x18]; redraw_area(0, 0, old_iWidth, old_iHeight); break; } } } /** * Read from the attribute controller index register (0x3c0) * * For a description of the attribute controller registers, see *CCirrus::write_b_3c0. **/ u8 CCirrus::read_b_3c0() { if (state.attribute_ctrl.flip_flop == 0) { // BX_INFO(("io read: 0x3c0: flip_flop = 0")); return (state.attribute_ctrl.video_enabled << 5) | state.attribute_ctrl.address; } else { FAILURE(NotImplemented, "io read: 0x3c0: flip_flop != 0"); } } /** * Read from the attribute controller data register (0x3c1) * * For a description of the attribute controller registers, see *CCirrus::write_b_3c0. **/ u8 CCirrus::read_b_3c1() { u8 retval; switch (state.attribute_ctrl.address) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: retval = state.attribute_ctrl.palette_reg[state.attribute_ctrl.address]; return (retval); break; case 0x10: /* mode control register */ retval = (state.attribute_ctrl.mode_ctrl.graphics_alpha << 0) | (state.attribute_ctrl.mode_ctrl.display_type << 1) | (state.attribute_ctrl.mode_ctrl.enable_line_graphics << 2) | (state.attribute_ctrl.mode_ctrl.blink_intensity << 3) | (state.attribute_ctrl.mode_ctrl.pixel_panning_compat << 5) | (state.attribute_ctrl.mode_ctrl.pixel_clock_select << 6) | (state.attribute_ctrl.mode_ctrl.internal_palette_size << 7); return (retval); break; case 0x11: /* overscan color register */ return (state.attribute_ctrl.overscan_color); break; case 0x12: /* color plane enable */ return (state.attribute_ctrl.color_plane_enable); break; case 0x13: /* horizontal PEL panning register */ return (state.attribute_ctrl.horiz_pel_panning); break; case 0x14: /* color select register */ return (state.attribute_ctrl.color_select); break; default: FAILURE_1(NotImplemented, "io read: 0x3c1: unknown register 0x%02x", (unsigned)state.attribute_ctrl.address); } } /** * Read from the VGA Input Status register (0x3c2) * * \code * +-----+-+-------+ * | |4| | * +-----+-+-------+ * ^ * +--------- 4: Switch Sense: * Returns the status of the four sense switches as *selected by the Clock Select field of the Miscellaneous Output Register (See * CCirrus::write_b_3c2) * \endcode **/ u8 CCirrus::read_b_3c2() { return 0; // input status register } /** * Read from the VGA Enable register (0x3c3) * * (Not sure where this comes from; doesn't seem to be in the VGA specs.) **/ u8 CCirrus::read_b_3c3() { return state.vga_enabled; } /** * Read from the VGA sequencer index register (0x3c4) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ u8 CCirrus::read_b_3c4() { return state.sequencer.index; } /** * Read from the VGA sequencer data register (0x3c5) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ u8 CCirrus::read_b_3c5() { switch (state.sequencer.index) { case 0: /* sequencer: reset */ #if defined(DEBUG_VGA) BX_DEBUG(("io read 0x3c5: sequencer reset")); #endif return (state.sequencer.reset1 ? 1 : 0) | (state.sequencer.reset2 ? 2 : 0); break; case 1: /* sequencer: clocking mode */ #if defined(DEBUG_VGA) BX_DEBUG(("io read 0x3c5: sequencer clocking mode")); #endif return state.sequencer.reg1; break; case 2: /* sequencer: map mask register */ return state.sequencer.map_mask; break; case 3: /* sequencer: character map select register */ return state.sequencer.char_map_select; break; case 4: /* sequencer: memory mode register */ return (state.sequencer.extended_mem << 1) | (state.sequencer.odd_even << 2) | (state.sequencer.chain_four << 3); break; default: FAILURE_1(NotImplemented, "io read 0x3c5: index %u unhandled", (unsigned)state.sequencer.index); } } /** * Read from VGA DAC Data register (0x3c9) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ u8 CCirrus::read_b_3c9() { u8 retval; if (state.pel.dac_state == 0x03) { switch (state.pel.read_data_cycle) { case 0: retval = state.pel.data[state.pel.read_data_register].red; break; case 1: retval = state.pel.data[state.pel.read_data_register].green; break; case 2: retval = state.pel.data[state.pel.read_data_register].blue; break; default: retval = 0; // keep compiler happy } state.pel.read_data_cycle++; if (state.pel.read_data_cycle >= 3) { state.pel.read_data_cycle = 0; state.pel.read_data_register++; } } else { retval = 0x3f; } return retval; } /** * Read from the VGA Feature Control register (index 0x3ca) * * \code * +-----------+-+-+ * | |1|0| * +-----------+-+-+ * ^ ^ * | +- 0: Feature Control 0 (reserved) * +--- 1: Feature Control 1 (reserved) * \endcode **/ u8 CCirrus::read_b_3ca() { return 0; } /** * Write to the VGA Miscellaneous Output Register (0x3cc) * * For a description of the Miscellaneous Output register, see *CCirrus::write_b_3c2 **/ u8 CCirrus::read_b_3cc() { /* Miscellaneous Output / Graphics 1 Position ??? */ return ((state.misc_output.color_emulation & 0x01) << 0) | ((state.misc_output.enable_ram & 0x01) << 1) | ((state.misc_output.clock_select & 0x03) << 2) | ((state.misc_output.select_high_bank & 0x01) << 5) | ((state.misc_output.horiz_sync_pol & 0x01) << 6) | ((state.misc_output.vert_sync_pol & 0x01) << 7); } /** * Read from VGA Graphics Controller Data Register (0x3cf) * * For a description of the Graphics registers, see CCirrus::write_b_3ce **/ u8 CCirrus::read_b_3cf() { u8 retval; switch (state.graphics_ctrl.index) { case 0: /* Set/Reset */ return (state.graphics_ctrl.set_reset); break; case 1: /* Enable Set/Reset */ return (state.graphics_ctrl.enable_set_reset); break; case 2: /* Color Compare */ return (state.graphics_ctrl.color_compare); break; case 3: /* Data Rotate */ retval = ((state.graphics_ctrl.raster_op & 0x03) << 3) | ((state.graphics_ctrl.data_rotate & 0x07) << 0); return (retval); break; case 4: /* Read Map Select */ return (state.graphics_ctrl.read_map_select); break; case 5: /* Mode */ retval = ((state.graphics_ctrl.shift_reg & 0x03) << 5) | ((state.graphics_ctrl.odd_even & 0x01) << 4) | ((state.graphics_ctrl.read_mode & 0x01) << 3) | ((state.graphics_ctrl.write_mode & 0x03) << 0); #if defined(DEBUG_VGA) if (state.graphics_ctrl.odd_even || state.graphics_ctrl.shift_reg) BX_DEBUG(("io read 0x3cf: reg 05 = 0x%02x", (unsigned)retval)); #endif return (retval); break; case 6: /* Miscellaneous */ return ((state.graphics_ctrl.memory_mapping & 0x03) << 2) | ((state.graphics_ctrl.odd_even & 0x01) << 1) | ((state.graphics_ctrl.graphics_alpha & 0x01) << 0); break; case 7: /* Color Don't Care */ return (state.graphics_ctrl.color_dont_care); break; case 8: /* Bit Mask */ return (state.graphics_ctrl.bitmask); break; default: FAILURE_1(NotImplemented, "io read: 0x3cf: index %u unhandled", (unsigned)state.graphics_ctrl.index); } } /** * Read from VGA CRTC Index Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ u8 CCirrus::read_b_3d4() { return state.CRTC.address; } /** * Read from VGA CRTC Data Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ u8 CCirrus::read_b_3d5() { if (state.CRTC.address > 0x18) { FAILURE_1(NotImplemented, "io read: invalid CRTC register 0x%02x \n", (unsigned)state.CRTC.address); } return state.CRTC.reg[state.CRTC.address]; } /** * Read from the VGA Input Status 1 register (0x3ba or 0x3da) * * \code * +-------+-+---+-+ * | |3| |0| * +-------+-+---+-+ * ^ ^ * | +- 0: Display Disabled: * | 1: Indicates a horizontal or vertical retrace *interval. This | bit is the real-time status of the inverted *'display | enable' signal. Programs have used this status bit *to | restrict screen updates to the inactive display intervals * | in order to reduce screen flicker. The video *subsystem is | designed to eliminate this software requirement; *screen | updates may be made at any time without screen *degradation. * +------- 1: Vertical Retrace: * 1: Indicates that the display is in a vertical retrace *interval. This bit can be programmed, through the Vertical Retrace End * register, to generate an interrupt at the start of *the vertical retrace. \endcode **/ u8 CCirrus::read_b_3da() { /* Input Status 1 (color emulation modes) */ u8 retval = 0; // bit3: Vertical Retrace // 0 = display is in the display mode // 1 = display is in the vertical retrace mode // bit0: Display Enable // 0 = display is in the display mode // 1 = display is not in the display mode; either the // horizontal or vertical retrace period is active // using 72 Hz vertical frequency /*** TO DO ??? *** usec = bx_pc_system.time_usec(); switch ( ( state.misc_output.vert_sync_pol << 1) | state.misc_output.horiz_sync_pol ) { case 0: vertres = 200; break; case 1: vertres = 400; break; case 2: vertres = 350; break; default: vertres = 480; break; } if ((usec % 13888) < 70) { vert_retrace = 1; } if ((usec % (13888 / vertres)) == 0) { horiz_retrace = 1; } if (horiz_retrace || vert_retrace) retval = 0x01; if (vert_retrace) retval |= 0x08; *** TO DO ??? ***/ /* reading this port resets the flip-flop to address mode */ state.attribute_ctrl.flip_flop = 0; return retval; } u8 CCirrus::get_actl_palette_idx(u8 index) { return state.attribute_ctrl.palette_reg[index]; } void CCirrus::redraw_area(unsigned x0, unsigned y0, unsigned width, unsigned height) { unsigned xti; unsigned yti; unsigned xt0; unsigned xt1; unsigned yt0; unsigned yt1; unsigned xmax; unsigned ymax; if ((width == 0) || (height == 0)) { return; } state.vga_mem_updated = 1; if (state.graphics_ctrl.graphics_alpha) { // graphics mode xmax = old_iWidth; ymax = old_iHeight; xt0 = x0 / X_TILESIZE; yt0 = y0 / Y_TILESIZE; if (x0 < xmax) { xt1 = (x0 + width - 1) / X_TILESIZE; } else { xt1 = (xmax - 1) / X_TILESIZE; } if (y0 < ymax) { yt1 = (y0 + height - 1) / Y_TILESIZE; } else { yt1 = (ymax - 1) / Y_TILESIZE; } for (yti = yt0; yti <= yt1; yti++) { for (xti = xt0; xti <= xt1; xti++) { SET_TILE_UPDATED(xti, yti, 1); } } } else { // text mode memset(state.text_snapshot, 0, sizeof(state.text_snapshot)); } } void CCirrus::update(void) { unsigned iHeight; unsigned iWidth; /* no screen update necessary */ if (state.vga_mem_updated == 0) return; /* skip screen update when vga/video is disabled or the sequencer is in reset * mode */ if (!state.vga_enabled || !state.attribute_ctrl.video_enabled || !state.sequencer.reset2 || !state.sequencer.reset1) return; // fields that effect the way video memory is serialized into screen output: // GRAPHICS CONTROLLER: // state.graphics_ctrl.shift_reg: // 0: output data in standard VGA format or CGA-compatible 640x200 2 color // graphics mode (mode 6) // 1: output data in CGA-compatible 320x200 4 color graphics mode // (modes 4 & 5) // 2: output data 8 bits at a time from the 4 bit planes // (mode 13 and variants like modeX) // if (state.vga_mem_updated==0 || state.attribute_ctrl.video_enabled == 0) if (state.graphics_ctrl.graphics_alpha) { u8 color; unsigned bit_no; unsigned r; unsigned c; unsigned x; unsigned y; unsigned long byte_offset; unsigned long start_addr; unsigned xc; unsigned yc; unsigned xti; unsigned yti; start_addr = (state.CRTC.reg[0x0c] << 8) | state.CRTC.reg[0x0d]; // BX_DEBUG(("update: shiftreg=%u, chain4=%u, mapping=%u", // (unsigned) state.graphics_ctrl.shift_reg, // (unsigned) state.sequencer.chain_four, // (unsigned) state.graphics_ctrl.memory_mapping); determine_screen_dimensions(&iHeight, &iWidth); if ((iWidth != old_iWidth) || (iHeight != old_iHeight) || (state.last_bpp > 8)) { bx_gui->dimension_update(iWidth, iHeight); old_iWidth = iWidth; old_iHeight = iHeight; state.last_bpp = 8; } switch (state.graphics_ctrl.shift_reg) { case 0: u8 attribute, palette_reg_val, DAC_regno; unsigned long line_compare; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; if (state.graphics_ctrl.memory_mapping == 3) { // CGA 640x200x2 for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; /* 0 or 0x2000 */ byte_offset = start_addr + ((y & 1) << 13); /* to the start of the line */ byte_offset += (320 / 4) * (y / 2); /* to the byte start */ byte_offset += (x / 8); bit_no = 7 - (x % 8); palette_reg_val = (((state.memory[byte_offset]) >> bit_no) & 1); DAC_regno = state.attribute_ctrl.palette_reg[palette_reg_val]; state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } else { // output data in serial fashion with each display plane // output on its associated serial output. Standard EGA/VGA format plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; line_compare = state.line_compare; if (state.y_doublescan) line_compare >>= 1; for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; if (state.x_dotclockdiv2) x >>= 1; bit_no = 7 - (x % 8); if (y > line_compare) { byte_offset = x / 8 + ((y - line_compare - 1) * state.line_offset); } else { byte_offset = start_addr + x / 8 + (y * state.line_offset); } attribute = (((plane0[byte_offset] >> bit_no) & 0x01) << 0) | (((plane1[byte_offset] >> bit_no) & 0x01) << 1) | (((plane2[byte_offset] >> bit_no) & 0x01) << 2) | (((plane3[byte_offset] >> bit_no) & 0x01) << 3); attribute &= state.attribute_ctrl.color_plane_enable; // undocumented feature ???: colors 0..7 high intensity, // colors 8..15 blinking using low/high intensity. Blinking is // not implemented yet. if (state.attribute_ctrl.mode_ctrl.blink_intensity) attribute ^= 0x08; palette_reg_val = state.attribute_ctrl.palette_reg[attribute]; if (state.attribute_ctrl.mode_ctrl.internal_palette_size) { // use 4 lower bits from palette register // use 4 higher bits from color select register // 16 banks of 16-color registers DAC_regno = (palette_reg_val & 0x0f) | (state.attribute_ctrl.color_select << 4); } else { // use 6 lower bits from palette register // use 2 higher bits from color select register // 4 banks of 64-color registers DAC_regno = (palette_reg_val & 0x3f) | ((state.attribute_ctrl.color_select & 0x0c) << 4); } // DAC_regno &= video DAC mask register ??? state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } break; // case 0 case 1: // output the data in a CGA-compatible 320x200 4 color graphics // mode. (modes 4 & 5) /* CGA 320x200x4 start */ for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; if (state.x_dotclockdiv2) x >>= 1; /* 0 or 0x2000 */ byte_offset = start_addr + ((y & 1) << 13); /* to the start of the line */ byte_offset += (320 / 4) * (y / 2); /* to the byte start */ byte_offset += (x / 4); attribute = 6 - 2 * (x % 4); palette_reg_val = (state.memory[byte_offset]) >> attribute; palette_reg_val &= 3; DAC_regno = state.attribute_ctrl.palette_reg[palette_reg_val]; state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } /* CGA 320x200x4 end */ break; // case 1 case 2: // output the data eight bits at a time from the 4 bit plane // (format for VGA mode 13 hex) case 3: // FIXME: is this really the same ??? if (state.sequencer.chain_four) { unsigned long pixely; unsigned long pixelx; unsigned long plane; if (state.misc_output.select_high_bank != 1) { FAILURE(NotImplemented, "update: select_high_bank != 1 \n"); } for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { pixely = yc + r; if (state.y_doublescan) pixely >>= 1; for (c = 0; c < X_TILESIZE; c++) { pixelx = (xc + c) >> 1; plane = (pixelx % 4); byte_offset = start_addr + (plane * 65536) + (pixely * state.line_offset) + (pixelx & ~0x03); color = state.memory[byte_offset]; state.tile[r * X_TILESIZE + c] = color; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } else { // chain_four == 0, modeX unsigned long pixely; // chain_four == 0, modeX unsigned long pixelx; // chain_four == 0, modeX unsigned long plane; for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { pixely = yc + r; if (state.y_doublescan) pixely >>= 1; for (c = 0; c < X_TILESIZE; c++) { pixelx = (xc + c) >> 1; plane = (pixelx % 4); byte_offset = (plane * 65536) + (pixely * state.line_offset) + (pixelx >> 2); color = state.memory[start_addr + byte_offset]; state.tile[r * X_TILESIZE + c] = color; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } break; // case 2 default: FAILURE_1(NotImplemented, "update: shift_reg == %u \n", (unsigned)state.graphics_ctrl.shift_reg); } state.vga_mem_updated = 0; return; } else { // text mode unsigned long start_address; unsigned long cursor_address; unsigned long cursor_x; unsigned long cursor_y; bx_vga_tminfo_t tm_info; unsigned VDE; unsigned MSL; unsigned cols; unsigned rows; unsigned cWidth; tm_info.start_address = 2 * ((state.CRTC.reg[12] << 8) + state.CRTC.reg[13]); tm_info.cs_start = state.CRTC.reg[0x0a] & 0x3f; tm_info.cs_end = state.CRTC.reg[0x0b] & 0x1f; tm_info.line_offset = state.CRTC.reg[0x13] << 2; tm_info.line_compare = state.line_compare; tm_info.h_panning = state.attribute_ctrl.horiz_pel_panning & 0x0f; tm_info.v_panning = state.CRTC.reg[0x08] & 0x1f; tm_info.line_graphics = state.attribute_ctrl.mode_ctrl.enable_line_graphics; tm_info.split_hpanning = state.attribute_ctrl.mode_ctrl.pixel_panning_compat; if ((state.sequencer.reg1 & 0x01) == 0) { if (tm_info.h_panning >= 8) tm_info.h_panning = 0; else tm_info.h_panning++; } else { tm_info.h_panning &= 0x07; } // Verticle Display End: find out how many lines are displayed VDE = state.vertical_display_end; // Maximum Scan Line: height of character cell MSL = state.CRTC.reg[0x09] & 0x1f; if (MSL == 0) { #if defined(DEBUG_VGA) BX_ERROR(("character height = 1, skipping text update")); #endif return; } cols = state.CRTC.reg[1] + 1; if ((MSL == 1) && (VDE == 399)) { // emulated CGA graphics mode 160x100x16 colors MSL = 3; } rows = (VDE + 1) / (MSL + 1); if (rows > BX_MAX_TEXT_LINES) { BX_PANIC(("text rows>%d: %d", BX_MAX_TEXT_LINES, rows)); return; } cWidth = ((state.sequencer.reg1 & 0x01) == 1) ? 8 : 9; iWidth = cWidth * cols; iHeight = VDE + 1; if ((iWidth != old_iWidth) || (iHeight != old_iHeight) || (MSL != old_MSL) || (state.last_bpp > 8)) { bx_gui->dimension_update(iWidth, iHeight, MSL + 1, cWidth); old_iWidth = iWidth; old_iHeight = iHeight; old_MSL = MSL; state.last_bpp = 8; } // pass old text snapshot & new VGA memory contents start_address = 2 * ((state.CRTC.reg[12] << 8) + state.CRTC.reg[13]); cursor_address = 2 * ((state.CRTC.reg[0x0e] << 8) + state.CRTC.reg[0x0f]); if (cursor_address < start_address) { cursor_x = 0xffff; cursor_y = 0xffff; } else { cursor_x = ((cursor_address - start_address) / 2) % (iWidth / cWidth); cursor_y = ((cursor_address - start_address) / 2) / (iWidth / cWidth); } bx_gui->text_update(state.text_snapshot, &state.memory[start_address], cursor_x, cursor_y, tm_info, rows); // screen updated, copy new VGA memory contents into text snapshot memcpy(state.text_snapshot, &state.memory[start_address], 2 * cols * rows); state.vga_mem_updated = 0; } } void CCirrus::determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth) { int ai[0x20]; int i; int h; int v; for (i = 0; i < 0x20; i++) ai[i] = state.CRTC.reg[i]; h = (ai[1] + 1) * 8; v = (ai[18] | ((ai[7] & 0x02) << 7) | ((ai[7] & 0x40) << 3)) + 1; if (state.graphics_ctrl.shift_reg == 0) { *piWidth = 640; *piHeight = 480; if (state.CRTC.reg[6] == 0xBF) { if (state.CRTC.reg[23] == 0xA3 && state.CRTC.reg[20] == 0x40 && state.CRTC.reg[9] == 0x41) { *piWidth = 320; *piHeight = 240; } else { if (state.x_dotclockdiv2) h <<= 1; *piWidth = h; *piHeight = v; } } else if ((h >= 640) && (v >= 480)) { *piWidth = h; *piHeight = v; } } else if (state.graphics_ctrl.shift_reg == 2) { if (state.sequencer.chain_four) { *piWidth = h; *piHeight = v; } else { *piWidth = h; *piHeight = v; } } else { if (state.x_dotclockdiv2) h <<= 1; *piWidth = h; *piHeight = v; } } u8 CCirrus::vga_mem_read(u32 addr) { u32 offset; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; u8 retval = 0; switch (state.graphics_ctrl.memory_mapping) { case 1: // 0xA0000 .. 0xAFFFF if (addr > 0xAFFFF) return 0xff; offset = addr & 0xFFFF; break; case 2: // 0xB0000 .. 0xB7FFF if ((addr < 0xB0000) || (addr > 0xB7FFF)) return 0xff; offset = addr & 0x7FFF; break; case 3: // 0xB8000 .. 0xBFFFF if (addr < 0xB8000) return 0xff; offset = addr & 0x7FFF; break; default: // 0xA0000 .. 0xBFFFF offset = addr & 0x1FFFF; } if (state.sequencer.chain_four) { // Mode 13h: 320 x 200 256 color mode: chained pixel representation return state.memory[(offset & ~0x03) + (offset % 4) * 65536]; } plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; /* addr between 0xA0000 and 0xAFFFF */ if (state.graphics_ctrl.read_mode) { u8 color_compare; u8 color_dont_care; u8 latch0; u8 latch1; u8 latch2; u8 latch3; color_compare = state.graphics_ctrl.color_compare & 0x0f; color_dont_care = state.graphics_ctrl.color_dont_care & 0x0f; latch0 = state.graphics_ctrl.latch[0] = plane0[offset]; latch1 = state.graphics_ctrl.latch[1] = plane1[offset]; latch2 = state.graphics_ctrl.latch[2] = plane2[offset]; latch3 = state.graphics_ctrl.latch[3] = plane3[offset]; latch0 ^= ccdat[color_compare][0]; latch1 ^= ccdat[color_compare][1]; latch2 ^= ccdat[color_compare][2]; latch3 ^= ccdat[color_compare][3]; latch0 &= ccdat[color_dont_care][0]; latch1 &= ccdat[color_dont_care][1]; latch2 &= ccdat[color_dont_care][2]; latch3 &= ccdat[color_dont_care][3]; retval = ~(latch0 | latch1 | latch2 | latch3); } else { state.graphics_ctrl.latch[0] = plane0[offset]; state.graphics_ctrl.latch[1] = plane1[offset]; state.graphics_ctrl.latch[2] = plane2[offset]; state.graphics_ctrl.latch[3] = plane3[offset]; retval = state.graphics_ctrl.latch[state.graphics_ctrl.read_map_select]; } return retval; } /** * Write to Legacy VGA Memory **/ void CCirrus::vga_mem_write(u32 addr, u8 value) { u32 offset; u8 new_val[4]; unsigned start_addr; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; /* The memory_mapping bits of the graphics controller determine * what window of VGA memory is available. * * 00: 0xA0000 .. 0xBFFFF (128K) * 01: 0xA0000 .. 0xAFFFF (64K) (also used for VGA text mode) * 02: 0xB0000 .. 0xB7FFF (32K) * 03: 0xB8000 .. 0xBFFFF (32K) (also used for CGA text mode) */ switch (state.graphics_ctrl.memory_mapping) { // 0xA0000 .. 0xAFFFF case 1: if (addr > 0xAFFFF) return; offset = addr - 0xA0000; break; // 0xB0000 .. 0xB7FFF case 2: if ((addr < 0xB0000) || (addr > 0xB7FFF)) return; offset = addr - 0xB0000; break; // 0xB8000 .. 0xBFFFF case 3: if (addr < 0xB8000) return; offset = addr - 0xB8000; break; // 0xA0000 .. 0xBFFFF default: offset = addr - 0xA0000; } start_addr = (state.CRTC.reg[0x0c] << 8) | state.CRTC.reg[0x0d]; if (state.graphics_ctrl.graphics_alpha) { if (state.graphics_ctrl.memory_mapping == 3) { // Text mode, and memory 0xB8000 .. 0xBFFFF selected => CGA text mode unsigned x_tileno; unsigned x_tileno2; unsigned y_tileno; /* CGA 320x200x4 / 640x200x2 start */ state.memory[offset] = value; offset -= start_addr; if (offset >= 0x2000) { y_tileno = offset - 0x2000; y_tileno /= (320 / 4); y_tileno <<= 1; // 2 * y_tileno; y_tileno++; x_tileno = (offset - 0x2000) % (320 / 4); x_tileno <<= 2; //*= 4; } else { y_tileno = offset / (320 / 4); y_tileno <<= 1; // 2 * y_tileno; x_tileno = offset % (320 / 4); x_tileno <<= 2; //*=4; } x_tileno2 = x_tileno; if (state.graphics_ctrl.shift_reg == 0) { x_tileno *= 2; x_tileno2 += 7; } else { x_tileno2 += 3; } if (state.x_dotclockdiv2) { x_tileno /= (X_TILESIZE / 2); x_tileno2 /= (X_TILESIZE / 2); } else { x_tileno /= X_TILESIZE; x_tileno2 /= X_TILESIZE; } if (state.y_doublescan) { y_tileno /= (Y_TILESIZE / 2); } else { y_tileno /= Y_TILESIZE; } state.vga_mem_updated = 1; SET_TILE_UPDATED(x_tileno, y_tileno, 1); if (x_tileno2 != x_tileno) { SET_TILE_UPDATED(x_tileno2, y_tileno, 1); } return; /* CGA 320x200x4 / 640x200x2 end */ } if (state.graphics_ctrl.memory_mapping != 1) { FAILURE_1(NotImplemented, "mem_write: graphics: mapping = %u \n", (unsigned)state.graphics_ctrl.memory_mapping); } if (state.sequencer.chain_four) { unsigned x_tileno; unsigned y_tileno; // 320 x 200 256 color mode: chained pixel representation state.memory[(offset & ~0x03) + (offset % 4) * 65536] = value; if (state.line_offset > 0) { offset -= start_addr; x_tileno = (offset % state.line_offset) / (X_TILESIZE / 2); if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } state.vga_mem_updated = 1; SET_TILE_UPDATED(x_tileno, y_tileno, 1); } return; } } /* addr between 0xA0000 and 0xAFFFF */ plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; switch (state.graphics_ctrl.write_mode) { unsigned i; // Write mode 0 case 0: { /* Write Mode 0 is the standard and most general write mode. * While the other write modes are designed to perform a specific * task, this mode can be used to perform most tasks as all five * operations are performed on the data: * - The data byte from the host is first rotated as specified * by the Rotate Count field, then is replicated across all * four planes. * - Then the Enable Set/Reset field selects which planes will * receive their values from the host data and which will * receive their data from that plane's Set/Reset field * location. * - Then the operation specified by the Logical Operation * field is performed on the resulting data and the data in * the read latches. * - The Bit Mask field is then used to select between the * resulting data and data from the latch register. * - Finally, the resulting data is written to the display * memory planes enabled in the Memory Plane Write Enable * field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask; const u8 set_reset = state.graphics_ctrl.set_reset; const u8 enable_set_reset = state.graphics_ctrl.enable_set_reset; /* perform rotate on CPU data in case its needed */ if (state.graphics_ctrl.data_rotate) { value = (value >> state.graphics_ctrl.data_rotate) | (value << (8 - state.graphics_ctrl.data_rotate)); } new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // replace new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? bitmask : 0) : (value & bitmask)); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? bitmask : 0) : (value & bitmask)); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? bitmask : 0) : (value & bitmask)); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? bitmask : 0) : (value & bitmask)); break; case 1: // AND new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? (state.graphics_ctrl.latch[0] & bitmask) : 0) : (value & state.graphics_ctrl.latch[0]) & bitmask); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? (state.graphics_ctrl.latch[1] & bitmask) : 0) : (value & state.graphics_ctrl.latch[1]) & bitmask); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? (state.graphics_ctrl.latch[2] & bitmask) : 0) : (value & state.graphics_ctrl.latch[2]) & bitmask); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? (state.graphics_ctrl.latch[3] & bitmask) : 0) : (value & state.graphics_ctrl.latch[3]) & bitmask); break; case 2: // OR new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? bitmask : (state.graphics_ctrl.latch[0] & bitmask)) : ((value | state.graphics_ctrl.latch[0]) & bitmask)); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? bitmask : (state.graphics_ctrl.latch[1] & bitmask)) : ((value | state.graphics_ctrl.latch[1]) & bitmask)); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? bitmask : (state.graphics_ctrl.latch[2] & bitmask)) : ((value | state.graphics_ctrl.latch[2]) & bitmask)); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? bitmask : (state.graphics_ctrl.latch[3] & bitmask)) : ((value | state.graphics_ctrl.latch[3]) & bitmask)); break; case 3: // XOR new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? (~state.graphics_ctrl.latch[0] & bitmask) : (state.graphics_ctrl.latch[0] & bitmask)) : (value ^ state.graphics_ctrl.latch[0]) & bitmask); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? (~state.graphics_ctrl.latch[1] & bitmask) : (state.graphics_ctrl.latch[1] & bitmask)) : (value ^ state.graphics_ctrl.latch[1]) & bitmask); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? (~state.graphics_ctrl.latch[2] & bitmask) : (state.graphics_ctrl.latch[2] & bitmask)) : (value ^ state.graphics_ctrl.latch[2]) & bitmask); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? (~state.graphics_ctrl.latch[3] & bitmask) : (state.graphics_ctrl.latch[3] & bitmask)) : (value ^ state.graphics_ctrl.latch[3]) & bitmask); break; default: FAILURE_1(NotImplemented, "vga_mem_write: write mode 0: op = %u", (unsigned)state.graphics_ctrl.raster_op); } } break; // Write mode 1 case 1: /* Write Mode 1 is used to transfer the data in the latches * register directly to the screen, affected only by the * Memory Plane Write Enable field. This can facilitate * rapid transfer of data on byte boundaries from one area * of video memory to another or filling areas of the * display with a pattern of 8 pixels. * When Write Mode 0 is used with the Bit Mask field set to * 00000000b the operation of the hardware is identical to * this mode. */ for (i = 0; i < 4; i++) { new_val[i] = state.graphics_ctrl.latch[i]; } break; // Write mode 2 case 2: { /* Write Mode 2 is used to unpack a pixel value packed into * the lower 4 bits of the host data byte into the 4 display * planes: * - In the byte from the host, the bit representing each * plane will be replicated across all 8 bits of the * corresponding planes. * - Then the operation specified by the Logical Operation * field is performed on the resulting data and the data * in the read latches. * - The Bit Mask field is then used to select between the * resulting data and data from the latch register. * - Finally, the resulting data is written to the display * memory planes enabled in the Memory Plane Write Enable * field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask; new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // write new_val[0] |= (value & 1) ? bitmask : 0; new_val[1] |= (value & 2) ? bitmask : 0; new_val[2] |= (value & 4) ? bitmask : 0; new_val[3] |= (value & 8) ? bitmask : 0; break; case 1: // AND new_val[0] |= (value & 1) ? (state.graphics_ctrl.latch[0] & bitmask) : 0; new_val[1] |= (value & 2) ? (state.graphics_ctrl.latch[1] & bitmask) : 0; new_val[2] |= (value & 4) ? (state.graphics_ctrl.latch[2] & bitmask) : 0; new_val[3] |= (value & 8) ? (state.graphics_ctrl.latch[3] & bitmask) : 0; break; case 2: // OR new_val[0] |= (value & 1) ? bitmask : (state.graphics_ctrl.latch[0] & bitmask); new_val[1] |= (value & 2) ? bitmask : (state.graphics_ctrl.latch[1] & bitmask); new_val[2] |= (value & 4) ? bitmask : (state.graphics_ctrl.latch[2] & bitmask); new_val[3] |= (value & 8) ? bitmask : (state.graphics_ctrl.latch[3] & bitmask); break; case 3: // XOR new_val[0] |= (value & 1) ? (~state.graphics_ctrl.latch[0] & bitmask) : (state.graphics_ctrl.latch[0] & bitmask); new_val[1] |= (value & 2) ? (~state.graphics_ctrl.latch[1] & bitmask) : (state.graphics_ctrl.latch[1] & bitmask); new_val[2] |= (value & 4) ? (~state.graphics_ctrl.latch[2] & bitmask) : (state.graphics_ctrl.latch[2] & bitmask); new_val[3] |= (value & 8) ? (~state.graphics_ctrl.latch[3] & bitmask) : (state.graphics_ctrl.latch[3] & bitmask); break; } } break; // Write mode 3 case 3: { /* Write Mode 3 is used when the color written is fairly * constant but the Bit Mask field needs to be changed * frequently, such as when drawing single color lines or * text: * - The value of the Set/Reset field is expanded as if * the Enable Set/Reset field were set to 1111b, * regardless of its actual value. * - The host data is first rotated as specified by the * Rotate Count field, then is ANDed with the Bit * Mask field. * - The resulting value is used where the Bit Mask * field normally would be used, selecting data from * either the expansion of the Set/Reset field or the * latch register. * - Finally, the resulting data is written to the * display memory planes enabled in the Memory Plane * Write Enable field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask & value; const u8 set_reset = state.graphics_ctrl.set_reset; /* perform rotate on CPU data */ if (state.graphics_ctrl.data_rotate) { value = (value >> state.graphics_ctrl.data_rotate) | (value << (8 - state.graphics_ctrl.data_rotate)); } new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; value &= bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // write new_val[0] |= (set_reset & 1) ? value : 0; new_val[1] |= (set_reset & 2) ? value : 0; new_val[2] |= (set_reset & 4) ? value : 0; new_val[3] |= (set_reset & 8) ? value : 0; break; case 1: // AND new_val[0] |= ((set_reset & 1) ? value : 0) & state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) & state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) & state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) & state.graphics_ctrl.latch[3]; break; case 2: // OR new_val[0] |= ((set_reset & 1) ? value : 0) | state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) | state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) | state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) | state.graphics_ctrl.latch[3]; break; case 3: // XOR new_val[0] |= ((set_reset & 1) ? value : 0) ^ state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) ^ state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) ^ state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) ^ state.graphics_ctrl.latch[3]; break; } } break; default: FAILURE_1(NotImplemented, "vga_mem_write: write mode %u ?", (unsigned)state.graphics_ctrl.write_mode); } // state.sequencer.map_mask determines which bitplanes the write should // actually go to if (state.sequencer.map_mask & 0x0f) { state.vga_mem_updated = 1; if (state.sequencer.map_mask & 0x01) plane0[offset] = new_val[0]; if (state.sequencer.map_mask & 0x02) plane1[offset] = new_val[1]; if (state.sequencer.map_mask & 0x04) { // Plane 2 contains the character map if ((offset & 0xe000) == state.charmap_address) { // printf("Updating character map %04x with %02x...\n ", (offset & // 0x1fff), new_val[2]); bx_gui->lock(); bx_gui->set_text_charbyte((u16)(offset & 0x1fff), new_val[2]); bx_gui->unlock(); } plane2[offset] = new_val[2]; } if (state.sequencer.map_mask & 0x08) plane3[offset] = new_val[3]; unsigned x_tileno; unsigned y_tileno; if (state.graphics_ctrl.shift_reg == 2) { offset -= start_addr; x_tileno = (offset % state.line_offset) * 4 / (X_TILESIZE / 2); if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } else { if (state.line_compare < state.vertical_display_end) { if (state.line_offset > 0) { if (state.x_dotclockdiv2) { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 16); } else { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 8); } if (state.y_doublescan) { y_tileno = ((offset / state.line_offset) * 2 + state.line_compare + 1) / Y_TILESIZE; } else { y_tileno = ((offset / state.line_offset) + state.line_compare + 1) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } } if (offset >= start_addr) { offset -= start_addr; if (state.line_offset > 0) { if (state.x_dotclockdiv2) { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 16); } else { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 8); } if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } } } } } ================================================ FILE: src/Cirrus.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_Cirrus_H_) #define INCLUDED_Cirrus_H_ #include "VGA.hpp" #include "gui/vga.hpp" /* video card has 4M of ram */ #define VIDEO_RAM_SIZE 22 #define CRTC_MAX 0x57 /** * \brief Cirrus Video Card * * Documentation consulted: * - VGADOC4b * (http://home.worldonline.dk/~finth/) * . **/ class CCirrus : public CVGA { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); virtual void WriteMem_Legacy(int index, u32 address, int dsize, u32 data); virtual u32 ReadMem_Legacy(int index, u32 address, int dsize); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); CCirrus(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CCirrus(); void update(void); void run(void); virtual void init(); virtual void start_threads(); virtual void stop_threads(); virtual u8 get_actl_palette_idx(u8 index); virtual void redraw_area(unsigned x0, unsigned y0, unsigned width, unsigned height); private: u32 mem_read(u32 address, int dsize); void mem_write(u32 address, int dsize, u32 data); u32 io_read(u32 address, int dsize); void io_write(u32 address, int dsize, u32 data); void io_write_b(u32 address, u8 data); void write_b_3c0(u8 data); void write_b_3c2(u8 data); void write_b_3c4(u8 data); void write_b_3c5(u8 data); void write_b_3c6(u8 data); void write_b_3c7(u8 data); void write_b_3c8(u8 data); void write_b_3c9(u8 data); void write_b_3ce(u8 data); void write_b_3cf(u8 data); void write_b_3d4(u8 data); void write_b_3d5(u8 data); u8 read_b_3c0(); u8 read_b_3c1(); u8 read_b_3c2(); u8 read_b_3c3(); u8 read_b_3c4(); u8 read_b_3c5(); u8 read_b_3c9(); u8 read_b_3ca(); u8 read_b_3cc(); u8 read_b_3cf(); u8 read_b_3d4(); u8 read_b_3d5(); u8 read_b_3da(); u32 legacy_read(u32 address, int dsize); void legacy_write(u32 address, int dsize, u32 data); u32 rom_read(u32 address, int dsize); void determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth); char bios_message[200]; int bios_message_size; void vga_mem_write(u32 addr, u8 value); u8 vga_mem_read(u32 addr); std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; bool StopThread; /// The state structure contains all elements that need to be saved to the /// statefile struct SCirrus_state { // u8 disabled; // u8 framebuffer[1<. \n",cur_value); strip_string(cur_value); add_value(cur_name, cur_value); state = STATE_NONE; } else if (text[curtext] == '{') { value_len = curtext - value_start; state = STATE_CHILD; child_start = curtext + 1; child_depth = 1; } break; case STATE_QUOTE: if ((text[curtext] == '\"') && (text[curtext + 1] == '\"')) { curtext++; } else if (text[curtext] == '\"') { state = STATE_VALUE; } break; case STATE_CHILD: if (text[curtext] == '{') child_depth++; else if (text[curtext] == '}') { child_depth--; if (!child_depth) { cur_name = (char *)malloc(name_len + 1); memcpy(cur_name, &text[name_start], name_len); cur_name[name_len] = '\0'; cur_value = (char *)malloc(value_len + 1); memcpy(cur_value, &text[value_start], value_len); cur_value[value_len] = '\0'; child_len = curtext - child_start; state = STATE_NONE; strip_string(cur_value); pChildren[iNumChildren++] = new CConfigurator( this, cur_name, cur_value, &text[child_start], child_len); } } } } int i; if (parent == 0) { myFlags = 0; for (i = 0; i < iNumChildren; i++) { pChildren[i]->initialize(); } } } /** * Destructor. * * \bug This does nothing now; it should: * - if we are a top-level component (System or GUI) delete the component *(which will delete the components children). * - delete our children. * - free memory we allocated for values. * . **/ CConfigurator::~CConfigurator(void) {} /** * Reduce a quoted string to it's real value. * Some values are enclosed in double quotes("), in this case, we take off the *quotes, and replace all double double quotes ("") with single double quotes *("). Quoting values is particularly useful if values contain forbidden *characters such as spaces, quotes, semicolons, etc. e.g. a text like * "c:\program files\putty\putty.exe" telnet://localhost:8000 * should be quoted as * """c:\program files\putty\putty.exe"" telnet://localhost:8000" **/ char *CConfigurator::strip_string(char *c) { char *pos = c; char *org = c + 1; bool end_it = false; if (c[0] == '\"') { while (!end_it) { if (*org == '\"') { org++; if (*org == '\"') *pos++ = *org++; else end_it = true; } else *pos++ = *org++; } *pos = '\0'; } return c; } /** * Add a value to our list of values. **/ void CConfigurator::add_value(char *n, char *v) { pValues[iNumValues].name = n; pValues[iNumValues].value = v; iNumValues++; } /** * Return a text value, if the name of the value can't be found in * our list of values, return def. **/ char *CConfigurator::get_text_value(const char *n, const char *def) { int i; for (i = 0; i < iNumValues; i++) { if (!strcmp(pValues[i].name, n)) return pValues[i].value; } return (char *)def; } /** * Return a boolean value, if the name of the value can't be found in * our list of values, or if the value isn't valid, return def. * * Valid values are strings that have a first character of: * - t (true) * - y (yes, evaluates to true) * - 1 (evaluates to true) * - f (false) * - n (no, evaluates to false) * - 0 (evaluates to false) * . **/ bool CConfigurator::get_bool_value(const char *n, bool def) { int i; for (i = 0; i < iNumValues; i++) { if (!strcmp(pValues[i].name, n)) { switch (pValues[i].value[0]) { case 't': case 'T': case 'y': case 'Y': case '1': return true; case 'f': case 'F': case 'n': case 'N': case '0': return false; default: FAILURE_2(Configuration, "Illegal boolean value (%s) for %s", pValues[i].value, n); } } } return def; } /** * Return a numeric value, if the name of the value can't be found in * our list of values, return def. **/ u64 CConfigurator::get_num_value(const char *n, bool decimal, u64 def) { int i; u64 multiplier = decimal ? 1000 : 1024; for (i = 0; i < iNumValues; i++) { if (!strcmp(pValues[i].name, n)) { u64 retval = 0; u64 partval = 0; char *val = pValues[i].value; int j = 0; for (;;) { while (val[j] && strchr("0123456789", val[j])) { partval *= 10; partval += val[j] - '0'; j++; } switch (val[j]) { case 'T': partval *= multiplier; case 'G': partval *= multiplier; case 'M': partval *= multiplier; case 'K': retval += partval * multiplier; partval = 0; j++; break; case '\0': retval += partval; return retval; default: FAILURE_2(Configuration, "Illegal numeric value (%s) for %s", pValues[i].value, n); } } } } return def; } // THIS IS WHERE THINGS GET COMPLICATED... #define NO_FLAGS 0 #define IS_CS 1 #define ON_CS 2 #define HAS_PCI 4 #define IS_PCI 8 #define HAS_ISA 16 #define IS_ISA 32 #define HAS_DISK 64 #define IS_DISK 128 #define IS_GUI 256 #define ON_GUI 512 #define IS_NIC 1024 #define N_P 2048 // no parent typedef struct { const char *name; classid id; int flags; } classinfo; classinfo classes[] = {{"tsunami", c_tsunami, N_P | IS_CS | HAS_PCI}, {"ev68cb", c_ev68cb, ON_CS}, {"ali", c_ali, IS_PCI | HAS_ISA}, {"ali_ide", c_ali_ide, IS_PCI | HAS_DISK}, {"ali_usb", c_ali_usb, IS_PCI}, {"serial", c_serial, ON_CS}, {"s3", c_s3, IS_PCI | ON_GUI}, {"cirrus", c_cirrus, IS_PCI | ON_GUI}, {"radeon", c_radeon, IS_PCI | ON_GUI}, {"dec21143", c_dec21143, IS_PCI | IS_NIC}, {"sym53c895", c_sym53c895, IS_PCI | HAS_DISK}, {"sym53c810", c_sym53c810, IS_PCI | HAS_DISK}, {"floppy", c_floppy, ON_CS | HAS_DISK}, {"file", c_file, IS_DISK}, {"device", c_device, IS_DISK}, {"ramdisk", c_ramdisk, IS_DISK}, {"sdl", c_sdl, N_P | IS_GUI}, {"win32", c_win32, N_P | IS_GUI}, {"X11", c_x11, N_P | IS_GUI}, {0, c_none, 0}}; /** * Determine what device this configurator represents, and instantiate it; * then call initialize for our children. **/ void CConfigurator::initialize() { myClassId = c_none; int i = 0; int pcibus = 0; int pcidev = 0; int idedev = 0; int idebus = 0; int fdcbus = 0; int number; char *pt; for (i = 0; classes[i].id != c_none; i++) { if (!strcmp(myValue, classes[i].name)) { myClassId = classes[i].id; myFlags = classes[i].flags; break; } } if (myClassId == c_none) FAILURE_2(Configuration, "Class %s for %s not known", myValue, myName); if (myFlags & N_P) { if (pParent->get_flags()) FAILURE_2(Configuration, "Class %s for %s needs a parent", myValue, myName); } if (myFlags & ON_CS) { if (!(pParent->get_flags() & IS_CS)) FAILURE_2(Configuration, "Class %s for %s needs a chipset parent", myValue, myName); } if (myFlags & ON_GUI) { if (!bx_gui) FAILURE_2(Configuration, "Class %s for %s needs a GUI", myValue, myName); } if (myFlags & IS_GUI) { if (bx_gui) FAILURE_2(Configuration, "Class %s for %s already found a gui", myValue, myName); } #if !defined(HAVE_PCAP) if (myFlags & IS_NIC) FAILURE_2(Configuration, "Class %s for %s needs compilation with libpcap support", myValue, myName); #endif if (myFlags & IS_PCI) { if (strncmp(myName, "pci", 3)) FAILURE_2(Configuration, "Name %s for class %s should be pci.", myName, myValue); if (!(pParent->get_flags() & HAS_PCI)) { FAILURE_2(Configuration, "Class %s for %s should have a pci-bus capable parent device", myValue, myName); } pt = &myName[3]; pcibus = atoi(pt); pt = strchr(pt, '.'); if (!pt) FAILURE_2(Configuration, "Name %s for class %s should be pci.", myName, myValue); pt++; pcidev = atoi(pt); // Validate PCI slot assignments. // On the Tsunami chipset, certain PCI slots are reserved for system-internal // devices (ali, ali_ide, ali_usb). User devices (SCSI controllers, VGA, NIC) // must go on free slots: pci0.1-pci0.4 or pci1.1-pci1.6. // Placing devices on wrong slots causes the SRM firmware to malfunction // (e.g. SCSI disks not detected, device conflicts). if (pcibus == 0) { bool is_system_device = (myClassId == c_ali || myClassId == c_ali_ide || myClassId == c_ali_usb); if (!is_system_device) { if (pcidev == 0) FAILURE_2(Configuration, "%s (%s): PCI slot pci0.0 is reserved and cannot be used for " "add-in devices. Use pci0.1 through pci0.4", myName, myValue); if (pcidev == 7 || pcidev == 15 || pcidev == 19) FAILURE_3(Configuration, "%s (%s): PCI slot pci0.%d is reserved for a system-internal " "device. Use pci0.1 through pci0.4 for add-in devices", myName, myValue, pcidev); } } } if (myClassId == c_floppy) { if (strncmp(myName, "fdc", 3)) FAILURE_2(Configuration, "Name %s for class %s should be fdc", myName, myValue); pt = &myName[3]; fdcbus = atoi(pt); } if (myFlags & IS_DISK) { if (strncmp(myName, "disk", 4)) FAILURE_2(Configuration, "Name %s for class %s should be disk.", myName, myValue); if (!(pParent->get_flags() & HAS_DISK)) { FAILURE_2(Configuration, "Class %s for %s should have a disk-controller parent device", myValue, myName); } pt = &myName[4]; idebus = atoi(pt); pt = strchr(pt, '.'); if (!pt) FAILURE_2(Configuration, "Name %s for class %s should be disk.", myName, myValue); pt++; idedev = atoi(pt); } switch (myClassId) { case c_tsunami: myDevice = new CSystem(this); new CDPR(this, (CSystem *)myDevice); new CFlash(this, (CSystem *)myDevice); break; case c_ev68cb: myDevice = new CAlphaCPU(this, (CSystem *)pParent->get_device()); break; case c_ali: myDevice = new CAliM1543C(this, (CSystem *)pParent->get_device(), pcibus, pcidev); new CPort80(this, (CSystem *)pParent->get_device()); new CKeyboard(this, (CSystem *)pParent->get_device()); new CDMA(this, (CSystem *)pParent->get_device()); break; case c_floppy: /* For disk controllers, myDevice points to the * CDiskController part of the class as it's used * to register disks to. */ myDevice = (CDiskController *)new CFloppyController( this, (CSystem *)pParent->get_device(), fdcbus); break; case c_ali_ide: /* For disk controllers, myDevice points to the * CDiskController part of the class as it's used * to register disks to. */ myDevice = (CDiskController *)new CAliM1543C_ide( this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_ali_usb: myDevice = new CAliM1543C_usb(this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_s3: myDevice = new CS3Trio64(this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_cirrus: myDevice = new CCirrus(this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_radeon: #if defined(HAVE_RADEON) myDevice = new CRadeon(this, (CSystem *)pParent->get_device(), pcibus, pcidev); #else FAILURE_2(Configuration, "Class %s for %s needs compilation with Radeon support", myValue, myName); #endif break; #if defined(HAVE_PCAP) case c_dec21143: myDevice = new CDEC21143(this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; #endif case c_sym53c895: /* For disk controllers, myDevice points to the * CDiskController part of the class as it's used * to register disks to. */ myDevice = (CDiskController *)new CSym53C895( this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_sym53c810: /* For disk controllers, myDevice points to the * CDiskController part of the class as it's used * to register disks to. */ myDevice = (CDiskController *)new CSym53C810( this, (CSystem *)pParent->get_device(), pcibus, pcidev); break; case c_file: myDevice = new CDiskFile(this, theSystem, (CDiskController *)pParent->get_device(), idebus, idedev); break; case c_device: myDevice = new CDiskDevice(this, theSystem, (CDiskController *)pParent->get_device(), idebus, idedev); break; case c_ramdisk: myDevice = new CDiskRam(this, theSystem, (CDiskController *)pParent->get_device(), idebus, idedev); break; case c_serial: number = 0; if (!strncmp(myName, "serial", 6)) { pt = &myName[6]; number = atoi(pt); } myDevice = new CSerial(this, (CSystem *)pParent->get_device(), number); break; case c_sdl: #if defined(HAVE_SDL) PLUG_load_plugin(this, sdl); #else FAILURE_2(Configuration, "Class %s for %s needs compilation with SDL support", myValue, myName); #endif break; case c_win32: #if defined(_WIN32) //PLUG_load_plugin(this, win32); #else FAILURE_2(Configuration, "Class %s for %s needs a Win32 platform", myValue, myName); #endif break; case c_x11: #if defined(HAVE_X11) PLUG_load_plugin(this, x11); #else FAILURE_2(Configuration, "Class %s for %s needs an X11 platform", myValue, myName); #endif break; case c_none: break; } for (i = 0; i < iNumChildren; i++) pChildren[i]->initialize(); if (myFlags & IS_CS) ((CSystem *)myDevice)->init(); } ================================================ FILE: src/Configurator.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__CONFIGURATOR_H__) #define __CONFIGURATOR_H__ #define CFG_MAX_CHILDREN 25 #define CFG_MAX_VALUES 50 #include "StdAfx.hpp" typedef enum { c_none, // chipsets c_tsunami, // system devices c_ev68cb, c_serial, c_floppy, // pci devices c_ali, c_ali_ide, c_ali_usb, c_s3, c_cirrus, c_radeon, c_dec21143, c_sym53c895, c_sym53c810, // disk devices c_file, c_device, c_ramdisk, // gui's c_sdl, c_win32, c_x11 } classid; class CConfigurator { public: CConfigurator(class CConfigurator *parent, char *name, char *value, char *text, size_t textlen); ~CConfigurator(void); char *strip_string(char *c); void add_value(char *n, char *v); char *get_text_value(const char *n) { return get_text_value(n, (char *)0); }; char *get_text_value(const char *n, const char *def); bool get_bool_value(const char *n) { return get_bool_value(n, false); }; bool get_bool_value(const char *n, bool def); u64 get_num_value(const char *n, bool decimal_suffixes) { return get_num_value(n, decimal_suffixes, 0); }; u64 get_num_value(const char *n, bool decimal_suffixes, u64 def); classid get_class_id() { return myClassId; }; void *get_device() { return myDevice; }; int get_flags() { return myFlags; }; char *get_myName() { return myName; }; char *get_myValue() { return myValue; }; CConfigurator *get_myParent() { return pParent; }; void initialize(); private: class CConfigurator *pParent; class CConfigurator *pChildren[CFG_MAX_CHILDREN]; int iNumChildren; char *myName; char *myValue; void *myDevice; classid myClassId; int myFlags; int iNumValues; struct SCfg_Value { char *name; char *value; } pValues[CFG_MAX_VALUES]; }; #endif //! defined(__CONFIGURATOR_H__) ================================================ FILE: src/DEC21143.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2021 Dietmar M. Zettl * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon GXemul, which is Copyright (C) 2004-2007 * Anders Gavare. All rights reserved. */ #include "StdAfx.hpp" #if defined(HAVE_PCAP) #include "DEC21143.hpp" #include "System.hpp" #if defined(DEBUG_NIC) #define DEBUG_NIC_FILTER #define DEBUG_NIC_SROM #endif /* Internal states during MII data stream decode: */ #define MII_STATE_RESET 0 #define MII_STATE_START_WAIT 1 #define MII_STATE_READ_OP 2 #define MII_STATE_READ_PHYADDR_REGADDR 3 #define MII_STATE_A 4 #define MII_STATE_D 5 #define MII_STATE_IDLE 6 /** * Thread entry point. **/ void CDEC21143::run() { try { for (;;) { if (StopThread) return; receive_process(); bool asserted; if ((state.reg[CSR_OPMODE / 8] & OPMODE_ST)) while (dec21143_tx()) ; /* Normal and Abnormal interrupt summary: */ state.reg[CSR_STATUS / 8] &= ~(STATUS_NIS | STATUS_AIS); if (state.reg[CSR_STATUS / 8] & state.reg[CSR_INTEN / 8] & 0x00004845) state.reg[CSR_STATUS / 8] |= STATUS_NIS; if (state.reg[CSR_STATUS / 8] & state.reg[CSR_INTEN / 8] & 0x0c0037ba) state.reg[CSR_STATUS / 8] |= STATUS_AIS; asserted = (state.reg[CSR_STATUS / 8] & state.reg[CSR_INTEN / 8] & 0x0c01ffff) != 0; if (asserted != state.irq_was_asserted) { if (do_pci_interrupt(0, asserted)) state.irq_was_asserted = asserted; } std::this_thread::sleep_for(std::chrono::milliseconds(20)); } } catch (CException &e) { printf("Exception in NIC thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } u32 dec21143_cfg_data[64] = { /*00*/ 0x00191011, // CFID: vendor + device /*04*/ 0x02800000, // CFCS: command + status /*08*/ 0x02000030, // CFRV: class + revision //dth:was 41 /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000001, // BAR0: CBIO /*14*/ 0x00000000, // BAR1: CBMA /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x500b1011, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x281401ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; u32 dec21143_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x0000ffff, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xffffff00, // BAR0 /*14*/ 0xffffff00, // BAR1: CBMA /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int CDEC21143::nic_num = 0; /** * Constructor. **/ CDEC21143::CDEC21143(CConfigurator *confg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(confg, c, pcibus, pcidev) {} /** * Initialize the network device. **/ void CDEC21143::init() { pcap_if_t *alldevs; pcap_if_t *d; u_int inum; u_int i = 0; char errbuf[PCAP_ERRBUF_SIZE]; char *cfg; add_function(0, dec21143_cfg_data, dec21143_cfg_mask); cfg = myCfg->get_text_value("adapter"); if (!cfg) { printf("\n%s: Choose a network adapter to connect to:\n", devid_string); if (pcap_findalldevs(&alldevs, errbuf) == -1) { FAILURE_1(Runtime, "Error in pcap_findalldevs_ex: %s", errbuf); } /* Print the list */ for (d = alldevs; d; d = d->next) { printf("%d. %s\n ", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); } if (i == 0) FAILURE(Runtime, "No network interfaces found"); if (i == 1) inum = 1; else { inum = 0; while (inum < 1 || inum > i) { printf("%%NIC-Q-NICNO: Enter the interface number (1-%d):", i); (void)!scanf("%d", &inum); } } /* Jump to the selected adapter */ for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++) ; cfg = d->name; } #if defined(WIN32) // Opening with pcap_open on Windows allows specification of // PCAP_OPENFLAG_NOCAPTURE_LOCAL, which stops the pcap device from seeing it's // own transmitted packets. // // This is important because: // 1. Real ethernet cards don't reflect packets except while in loopback // mode(s). // 2. Reflecting all packets increases inbound packet processing and host // load. // 3. DECNET Phase IV will think a reflected packet is from another node // that has the same DECNET Phase IV address (AA-xx-xx-xx-xx-xx), // and will panic on startup and abort. // 4. Libpcap doesn't reflect packets, and we want winpcap/libpcap // processing to be identical. // Loopback packets are handled via direct entry in the receive queue. if ((fp = pcap_open(cfg, 65536 /*snaplen: capture entire packets */, PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_LOCAL /*promiscuous */, 10 /*read timeout: 10ms. */, 0 /* auth structure */, errbuf)) == NULL) // connect to pcap... #else if ((fp = pcap_open_live(cfg, 65536 /*snaplen: capture entire packets */, 1 /*promiscuous */, 1 /*read timeout: 1ms. */, errbuf)) == nullptr) // connect to pcap... #endif FAILURE_2(Runtime, "Error opening adapter %s:\n %s", cfg, errbuf); if (pcap_setnonblock(fp, 1, errbuf) == PCAP_ERROR) FAILURE_2(Runtime, "Error setting adapter %s non-blocking:\n %s", cfg, errbuf); // set default mac = Digital ethernet prefix: 08-00-2B + hexified "ES40" + nic // number state.mac[0] = 0x08; state.mac[1] = 0x00; state.mac[2] = 0x2B; state.mac[3] = 0xE5; state.mac[4] = 0x40; state.mac[5] = nic_num++; // set assigned mac cfg = myCfg->get_text_value("mac"); if (cfg) { const char *mac_chars = "0123456789abcdefABCDEF-.:"; const char *hex_chars = "0123456789abcdefABCDEF"; const char *hex_scanf = "%hx"; bool mac_replaced = false; if ((strlen(cfg) == 17) && (strspn(cfg, mac_chars) == 17)) { char newmac[18]; strcpy(newmac, cfg); newmac[2] = newmac[5] = newmac[8] = newmac[11] = newmac[14] = 0; if ((strspn(&newmac[0], hex_chars) == 2) && (strspn(&newmac[3], hex_chars) == 2) && (strspn(&newmac[6], hex_chars) == 2) && (strspn(&newmac[9], hex_chars) == 2) && (strspn(&newmac[12], hex_chars) == 2) && (strspn(&newmac[15], hex_chars) == 2)) { short unsigned int num; sscanf(&newmac[0], hex_scanf, &num); state.mac[0] = num & 0xff; sscanf(&newmac[3], hex_scanf, &num); state.mac[1] = num & 0xff; sscanf(&newmac[6], hex_scanf, &num); state.mac[2] = num & 0xff; sscanf(&newmac[9], hex_scanf, &num); state.mac[3] = num & 0xff; sscanf(&newmac[12], hex_scanf, &num); state.mac[4] = num & 0xff; sscanf(&newmac[15], hex_scanf, &num); state.mac[5] = num & 0xff; mac_replaced = true; } } if (mac_replaced) { printf("%s: MAC set to %s\n", devid_string, cfg); } else { FAILURE_1(Configuration, "MAC address (%s) should have xx-xx-xx-xx-xx-xx format", cfg); } } else { char mac[18]; sprintf(mac, "%02X-%02X-%02X-%02X-%02X-%02X", state.mac[0], state.mac[1], state.mac[2], state.mac[3], state.mac[4], state.mac[5]); printf("%s: MAC defaulted to %s\n", devid_string, mac); } rx_queue = new CPacketQueue("rx_queue", (int)myCfg->get_num_value("queue", false, 100)); calc_crc = myCfg->get_bool_value("crc", false); state.rx.cur_buf = NULL; state.tx.cur_buf = (unsigned char *)malloc(1514); state.irq_was_asserted = false; state.tx.idling = 0; ResetPCI(); printf("%s: $Id: DEC21143.cpp,v 1.36 2008/05/31 15:47:09 iamcamiel Exp $\n", devid_string); } void CDEC21143::start_threads() { if (!myThread) { printf(" nic"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); } } void CDEC21143::stop_threads() { StopThread = true; if (myThread) { printf(" nic"); myThread->join(); myThread = nullptr; } } /** * Destructor. **/ CDEC21143::~CDEC21143() { stop_threads(); pcap_close(fp); delete rx_queue; } u32 CDEC21143::ReadMem_Bar(int func, int bar, u32 address, int dsize) { switch (bar) { case 0: // CBIO case 1: // CBMA return nic_read(address, dsize); } printf("21143: ReadMem_Bar: unsupported bar %d\n", bar); return 0; } void CDEC21143::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { switch (bar) { case 0: // CBIO case 1: // CBMA nic_write(address, dsize, (u32)endian_bits(data, dsize)); return; } printf("21143: WriteMem_Bar: unsupported bar %d\n", bar); } /** * Check if threads are still running. **/ void CDEC21143::check_state() { if (myThreadDead.load()) FAILURE(Thread, "NIC thread has died"); } void CDEC21143::receive_process() { struct pcap_pkthdr *packet_header; const u_char *packet_data = NULL; // if receive process active if (state.reg[CSR_OPMODE / 8] & OPMODE_SR) { // get packets from host nic if not in internal loopback mode if (!(state.reg[CSR_OPMODE / 8] & OPMODE_OM_INTLOOP)) { while (pcap_next_ex(fp, &packet_header, &packet_data) > 0) { rx_queue->add_tail(packet_data, packet_header->caplen, calc_crc, true); state.reg[CSR_SIASTAT / 8] |= SIASTAT_TRA; // set 10bT activity } } // process a receive descriptor until we run out of // descriptors or packets to process while (dec21143_rx()) ; } } /** * Read from the NIC registers. **/ u32 CDEC21143::nic_read(u32 address, int dsize) { u32 data = 0; int regnr = (int)(address >> 3); if ((address & 7) == 0 && regnr < 32) { data = state.reg[regnr]; } else printf("dec21143: WARNING! unaligned access (0x%x) \n", (int)address); #if defined(DEBUG_NIC) printf("21143: nic_read - CSR(%d), value: %08x\n", regnr, data); #endif return data; } /** * Write to the NIC registers. **/ void CDEC21143::nic_write(u32 address, int dsize, u32 data) { uint32_t oldreg = 0; int regnr = (int)(address >> 3); #if defined(DEBUG_NIC) printf("21143: nic_write - CSR(%d), value: %08x\n", regnr, data); #endif // printf("21143: rx_queue->name= %s\n", rx_queue->name); if ((address & 7) == 0 && regnr < 32) { oldreg = state.reg[regnr]; switch (regnr) { case CSR_STATUS / 8: /* Zero-on-write */ state.reg[regnr] &= ~((u32)data & 0x0c01ffff); break; case CSR_MISSED / 8: /* Read only */ break; default: state.reg[regnr] = (u32)data; } } else printf("[ dec21143: WARNING! unaligned access (0x%x) ]\n", (int)address); switch (address) { case CSR_BUSMODE: /* csr0 */ if (data & BUSMODE_SWR) { ResetNIC(); data &= ~BUSMODE_SWR; } // calculate descriptor skip length in bytes state.descr_skip = ((data & BUSMODE_DSL) >> 2) * 4; break; case CSR_TXPOLL: /* csr1 */ /* CaVa interpretation... */ state.reg[CSR_STATUS / 8] &= ~STATUS_TU; state.tx.suspend = false; state.tx.idling = state.tx.idling_threshold; // DoClock(); break; case CSR_RXPOLL: /* csr2 */ // DoClock(); break; case CSR_RXLIST: /* csr3 */ /* debug("[ dec21143: setting RXLIST to 0x%x ]\n", (int)data); */ if (data & 0x3) printf("[ dec21143: WARNING! RXLIST not aligned? (0x%x) ]\n", data); data &= ~0x3; state.rx.cur_addr = data; break; case CSR_TXLIST: /* csr4 */ /* debug("[ dec21143: setting TXLIST to 0x%x ]\n", (int)data); */ if (data & 0x3) printf("[ dec21143: WARNING! TXLIST not aligned? (0x%x) ]\n", data); data &= ~0x3; state.tx.cur_addr = data; break; case CSR_STATUS: /* csr5 */ case CSR_INTEN: /* csr7 */ /* Recalculate interrupt assertion. */ // DoClock(); break; case CSR_OPMODE: /* csr6: */ if (data & 0x02000000) { /* A must-be-one bit. */ data &= ~0x02000000; } if (data & OPMODE_ST) { // data &= ~OPMODE_ST; } else { /* Turned off TX? Then idle: */ // state.reg[CSR_STATUS/8] |= STATUS_TPS; } if (data & OPMODE_SR) { // data &= ~OPMODE_SR; } else { /* Turned off RX? Then go to stopped state: */ // state.reg[CSR_STATUS/8] &= ~STATUS_RS; } // Did Start/Stop Transmission change state ? if ((data ^ oldreg) & OPMODE_ST) { if (data & OPMODE_ST) { // ST went high set_tx_state(STATUS_TS_SUSPENDED); } else { // ST went low set_tx_state(STATUS_TS_STOPPED); } } // Did Start/Stop Receive change state ? if ((data ^ oldreg) & OPMODE_SR) { if (data & OPMODE_SR) { // SR went high set_rx_state(STATUS_RS_WAIT); } else { // SR went low set_tx_state(STATUS_RS_STOPPED); } } data &= ~(OPMODE_HBD | OPMODE_SCR | OPMODE_PCS | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD | OPMODE_TR | OPMODE_OM); // if (data & OPMODE_PNIC_IT) { // data &= ~OPMODE_PNIC_IT; // state.tx.idling = state.tx.idling_threshold; // } // if (data != 0) { // printf("[ dec21143: UNIMPLEMENTED OPMODE bits: // 0x%08x ]\n", (int)data); // } // DoClock(); break; case CSR_MISSED: /* csr8 */ break; case CSR_MIIROM: /* csr9 */ if (data & MIIROM_MDC) mii_access(oldreg, (u32)data); else srom_access(oldreg, (u32)data); break; case CSR_SIASTAT: /* csr12 */ if (((data & SIASTAT_ANS) == SIASTAT_ANS_START) && (state.reg[CSR_SIATXRX / 8] & SIATXRX_ANE)) { // autonegotiation restart... completes immediately in our emulated // environment. state.reg[CSR_SIASTAT / 8] &= ~SIASTAT_ANS; state.reg[CSR_SIASTAT / 8] |= (SIASTAT_ANS_FLPGOOD | SIASTAT_LPN | SIASTAT_LPC); state.reg[CSR_STATUS / 8] |= STATUS_LNPANC; state.reg[CSR_SIATXRX / 8] &= ~(SIATXRX_TH | SIATXRX_THX | SIATXRX_T4); state.reg[CSR_SIATXRX / 8] |= SIATXRX_TXF; // DoClock(); } else { state.reg[CSR_SIASTAT / 8] = oldreg; } break; case CSR_SIATXRX: /* csr14 */ break; case CSR_SIACONN: /* csr13 */ if ((data & SIACONN_SRL) && (state.reg[CSR_SIATXRX / 8] & SIATXRX_ANE)) { // SIA started with autonegotiation... completes immediately in our // emulated environment. state.reg[CSR_SIASTAT / 8] &= ~SIASTAT_ANS; state.reg[CSR_SIASTAT / 8] |= (SIASTAT_ANS_FLPGOOD | SIASTAT_LPN | SIASTAT_LPC); state.reg[CSR_STATUS / 8] |= STATUS_LNPANC; state.reg[CSR_SIATXRX / 8] &= ~(SIATXRX_TH | SIATXRX_THX | SIATXRX_T4); state.reg[CSR_SIATXRX / 8] |= SIATXRX_TXF; // DoClock(); } break; case CSR_SIAGEN: /* csr15 */ break; default: printf("[ dec21143: write to unimplemented 0x%02x: 0x%02x ]\n", (int)address, (int)data); } } void CDEC21143::set_tx_state(int tx_state) { state.reg[CSR_STATUS / 8] &= ~STATUS_TS; state.reg[CSR_STATUS / 8] |= (tx_state & STATUS_TS); } void CDEC21143::set_rx_state(int rx_state) { state.reg[CSR_STATUS / 8] &= ~STATUS_RS; state.reg[CSR_STATUS / 8] |= (rx_state & STATUS_RS); } /** * This function handles accesses to the MII. Data streams seem to be of the * following format: * * vv---- starting delimiter * ... 01 xx yyyyy zzzzz a[a] dddddddddddddddd * ^---- I am starting with mii_bit = 0 here * * where x = opcode (10 = read, 01 = write) * y = PHY address * z = register address * a = on Reads: ACK bit (returned, should be 0) * on Writes: _TWO_ dummy bits (10) * d = 16 bits of data (MSB first) **/ void CDEC21143::mii_access(uint32_t oldreg, uint32_t idata) { int obit; int ibit = 0; uint16_t tmp; /* Only care about data during clock cycles: */ if (!(idata & MIIROM_MDC)) return; if (idata & MIIROM_MDC && oldreg & MIIROM_MDC) return; /* printf("[ mii_access(): 0x%08x ]\n", (int)idata); */ if (idata & MIIROM_BR) { printf("[ mii_access(): MIIROM_BR: TODO ]\n"); return; } obit = idata & MIIROM_MDO ? 1 : 0; if (state.mii.state >= MII_STATE_START_WAIT && state.mii.state <= MII_STATE_READ_PHYADDR_REGADDR && idata & MIIROM_MIIDIR) printf("[ mii_access(): bad dir? ]\n"); switch (state.mii.state) { case MII_STATE_RESET: /* Wait for a starting delimiter (0 followed by 1). */ if (obit) return; if (idata & MIIROM_MIIDIR) return; /* printf("[ mii_access(): got a 0 delimiter ]\n"); */ state.mii.state = MII_STATE_START_WAIT; state.mii.opcode = 0; state.mii.phyaddr = 0; state.mii.regaddr = 0; break; case MII_STATE_START_WAIT: /* Wait for a starting delimiter (0 followed by 1). */ if (!obit) return; if (idata & MIIROM_MIIDIR) { state.mii.state = MII_STATE_RESET; return; } /* printf("[ mii_access(): got a 1 delimiter ]\n"); */ state.mii.state = MII_STATE_READ_OP; state.mii.bit = 0; break; case MII_STATE_READ_OP: if (state.mii.bit == 0) { state.mii.opcode = obit << 1; /* printf("[ mii_access(): got first opcode bit (%i) ]\n", obit); */ } else { state.mii.opcode |= obit; /* printf("[ mii_access(): got opcode = %i ]\n", state.mii.opcode); */ state.mii.state = MII_STATE_READ_PHYADDR_REGADDR; } state.mii.bit++; break; case MII_STATE_READ_PHYADDR_REGADDR: /* printf("[ mii_access(): got phy/reg addr bit nr %i (%i)" " ]\n", state.mii.bit - 2, obit); */ if (state.mii.bit <= 6) state.mii.phyaddr |= obit << (6 - state.mii.bit); else state.mii.regaddr |= obit << (11 - state.mii.bit); state.mii.bit++; if (state.mii.bit >= 12) { /* printf("[ mii_access(): phyaddr=0x%x regaddr=0x" "%x ]\n", state.mii.phyaddr, state.mii.regaddr); */ state.mii.state = MII_STATE_A; } break; case MII_STATE_A: switch (state.mii.opcode) { case MII_COMMAND_WRITE: if (state.mii.bit >= 13) state.mii.state = MII_STATE_D; break; case MII_COMMAND_READ: ibit = 0; state.mii.state = MII_STATE_D; break; default: printf("[ mii_access(): UNIMPLEMENTED MII opcode %i (probably just a bug " "in GXemul's MII data stream handling) ]\n", state.mii.opcode); state.mii.state = MII_STATE_RESET; } state.mii.bit++; break; case MII_STATE_D: switch (state.mii.opcode) { case MII_COMMAND_WRITE: if (idata & MIIROM_MIIDIR) printf("[ mii_access(): write: bad dir? ]\n"); obit = obit ? (0x8000 >> (state.mii.bit - 14)) : 0; tmp = state.mii.phy_reg[(state.mii.phyaddr << 5) + state.mii.regaddr] | obit; if (state.mii.bit >= 29) { state.mii.state = MII_STATE_IDLE; // debug("[ mii_access(): WRITE to // phyaddr=0x%x regaddr=0x%x: 0x%04x ]\n", // state.mii.phyaddr, state.mii.regaddr, // tmp); } break; case MII_COMMAND_READ: if (!(idata & MIIROM_MIIDIR)) break; tmp = state.mii.phy_reg[(state.mii.phyaddr << 5) + state.mii.regaddr]; // if (state.mii.bit == 13) // debug("[ mii_access(): READ phyaddr=0x%x // regaddr=0x%x: 0x%04x ]\n", // state.mii.phyaddr, state.mii.regaddr, // tmp); ibit = tmp & (0x8000 >> (state.mii.bit - 13)); if (state.mii.bit >= 28) state.mii.state = MII_STATE_IDLE; break; } state.mii.bit++; break; case MII_STATE_IDLE: state.mii.bit++; if (state.mii.bit >= 31) state.mii.state = MII_STATE_RESET; break; } state.reg[CSR_MIIROM / 8] &= ~MIIROM_MDI; if (ibit) state.reg[CSR_MIIROM / 8] |= MIIROM_MDI; } /** * This function handles reads from the Ethernet Address ROM. This is not a * 100% correct implementation, as it was reverse-engineered from OpenBSD * sources; it seems to work with OpenBSD, NetBSD, and Linux, though. * * Each transfer (if I understood this correctly) is of the following format: * * 1xx yyyyyy zzzzzzzzzzzzzzzz * * where 1xx = operation (6 means a Read), * yyyyyy = ROM address * zz...z = data * * y and z are _both_ read and written to at the same time; this enables the * operating system to sense the number of bits in y (when reading, all y bits * are 1 except the last one). **/ void CDEC21143::srom_access(uint32_t oldreg, uint32_t idata) { int obit; int ibit; /* debug("CSR9 WRITE! 0x%08x\n", (int)idata); */ /* New selection? Then reset internal state. */ if (idata & MIIROM_SR && !(oldreg & MIIROM_SR)) { state.srom.curbit = 0; state.srom.opcode = 0; state.srom.opcode_has_started = 0; state.srom.addr = 0; } /* Only care about data during clock cycles: */ if (!(idata & MIIROM_SROMSK)) return; obit = 0; ibit = idata & MIIROM_SROMDI ? 1 : 0; /* debug("CLOCK CYCLE! (bit %i): ", state.srom.curbit); */ /* * Linux sends more zeroes before starting the actual opcode, than * OpenBSD and NetBSD. Hopefully this is correct. (I'm just guessing * that all opcodes should start with a 1, perhaps that's not really * the case.) */ if (!ibit && !state.srom.opcode_has_started) return; if (state.srom.curbit < 3) { state.srom.opcode_has_started = 1; state.srom.opcode <<= 1; state.srom.opcode |= ibit; /* debug("opcode input '%i'\n", ibit); */ } else { switch (state.srom.opcode) { case TULIP_SROM_OPC_READ: if (state.srom.curbit < 6 + 3) { obit = state.srom.curbit < 6 + 2; state.srom.addr <<= 1; state.srom.addr |= ibit; } else { uint16_t romword = state.srom.data[state.srom.addr * 2] + (state.srom.data[state.srom.addr * 2 + 1] << 8); // if (state.srom.curbit == 6 + 3) // debug("[ dec21143: ROM read from // offset 0x%03x: 0x%04x ]\n", // state.srom.addr, romword); obit = romword & (0x8000 >> (state.srom.curbit - 6 - 3)) ? 1 : 0; #if defined(DEBUG_NIC_SROM) printf("%%NIC-I-SROMREAD: Read %04x from %04x\n", romword, state.srom.addr); #endif } break; default: printf("[ dec21243: unimplemented SROM/EEPROM opcode %i ]\n", state.srom.opcode); } state.reg[CSR_MIIROM / 8] &= ~MIIROM_SROMDO; if (obit) state.reg[CSR_MIIROM / 8] |= MIIROM_SROMDO; /* debug("input '%i', output '%i'\n", ibit, obit); */ } state.srom.curbit++; /* * Done opcode + addr + data? Then restart. (At least NetBSD does * sequential reads without turning selection off and then on.) */ if (state.srom.curbit >= 3 + 6 + 16) { state.srom.curbit = 0; state.srom.opcode = 0; state.srom.opcode_has_started = 0; state.srom.addr = 0; } } /* bool CDEC21143:acquire_rx_descriptor(u32 status_true, u32 status_false) { // get current receive descriptor do_pci_read(state.rx.cur_addr, descr, 4, 4); if (rdes0 & TDSTAT_OWN) { // 21143 owns descriptor state.reg[CSR_STATUS/8] &= ~STATUS_RU; // clear buffers unavailable if (state.reg[CSR_OPMODE/8] & OPMODE_SR) { // if receive start state.reg[CSR_STATUS/8] = (state.reg[CSR_STATUS/8] & ~STATUS_RS) | STATUS_RS_WAIT; } else { } return true; // indicate nothing was processed } else { state.reg[CSR_STATUS/8] = (state.reg[CSR_STATUS/8] & ~(STATUS_RS | STATUS_RU)) | STATUS_R return true; } } */ /** * Receive a packet. (If there is no current packet, then check for newly * arrived ones. If the current packet couldn't be fully transfered the * last time, then continue on that packet.) **/ int CDEC21143::dec21143_rx() { static u32 descr[4]; static u32 &rdes0 = descr[0]; static u32 &rdes1 = descr[1]; static u32 &rdes2 = descr[2]; static u32 &rdes3 = descr[3]; u32 bufaddr; int bufsize; int buf1_size; int buf2_size; int to_xfer; /* Is current packet finished? Then check for new ones. */ if (state.rx.current.used >= state.rx.current.len) { /* Nothing available? Then abort. */ if (rx_queue->count() == 0) return 0; // indicate nothing was processed // printf("pcap recv: %d bytes (%d captured) for // %02x:%02x:%02x:%02x:%02x:%02x \n",packet_header->len, // packet_header->caplen,packet_data[0],packet_data[1],packet_data[2],packet_data[3],packet_data[4],packet_data[5]); // get next packet from receive queue rx_queue->get_head(state.rx.current); /* Append a 4 byte CRC: */ // state.rx.cur_buf_len += 4; // CHECK_REALLOCATION(state.rx.cur_buf, realloc(state.rx.cur_buf, // state.rx.cur_buf_len), unsigned char); /* Get the next packet into our buffer: */ // memcpy(state.rx.cur_buf, packet_data, state.rx.cur_buf_len); /* Well... the CRC is just zeros, for now. */ // memset(state.rx.cur_buf + state.rx.cur_buf_len - 4, 0, 4); // state.rx.cur_offset = 0; } // read current descriptor do_pci_read(state.rx.cur_addr, descr, 4, 4); // rdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24); // rdes1 = descr[4] + (descr[5]<<8) + (descr[6]<<16) + (descr[7]<<24); // rdes2 = descr[8] + (descr[9]<<8) + (descr[10]<<16) + (descr[11]<<24); // rdes3 = descr[12] + (descr[13]<<8) + (descr[14]<<16) + (descr[15]<<24); /* Only use descriptors owned by the 21143: */ if (!(rdes0 & TDSTAT_OWN)) { // set recive buffers unavailable and receive state to suspended state.reg[CSR_STATUS / 8] = (state.reg[CSR_STATUS / 8] & ~STATUS_RS) | STATUS_RU | STATUS_RS_SUSPENDED; return 0; // indicate nothing was processed } buf1_size = rdes1 & TDCTL_SIZE1; buf2_size = (rdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT; bufaddr = buf1_size ? rdes2 : rdes3; bufsize = buf1_size ? buf1_size : buf2_size; // state.reg[CSR_STATUS/8] &= ~STATUS_RS; // dth: wrong, this is receive state // stopped // printf("{ dec21143_rx: base = 0x%08x }\n", (int)addr); // debug("{ RX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x // }\n", // (long long)addr, rdes0, rdes1, rdes2, rdes3, bufsize, // (int)bufaddr); // Turn off all status bits, and give up ownership rdes0 = 0x00000000; // Is this the first buffer of the frame? if (state.rx.current.used == 0) rdes0 |= TDSTAT_Rx_FS; // use buffer 1, if length is non-zero if (buf1_size > 0) { to_xfer = state.rx.current.len - state.rx.current.used; if (to_xfer > buf1_size) to_xfer = buf1_size; // DMA bytes from the packet into buffer 1 do_pci_write(rdes2, &state.rx.current.frame[state.rx.current.used], 1, to_xfer); // update used state.rx.current.used += to_xfer; } // use buffer 2, if length is non-zero and not a chain buffer if ((buf2_size > 0) && (!(rdes1 & TDCTL_CH))) { to_xfer = state.rx.current.len - state.rx.current.used; if (to_xfer > buf2_size) to_xfer = buf2_size; // DMA bytes from the packet into buffer 2 do_pci_write(rdes3, state.rx.current.frame + state.rx.current.used, 1, to_xfer); // update used state.rx.current.used += to_xfer; } // Frame completed? if (state.rx.current.used >= state.rx.current.len) { // debug("frame complete.\n"); rdes0 |= TDSTAT_Rx_LS; /* Set the frame length: */ rdes0 |= (state.rx.current.len << 16) & TDSTAT_Rx_FL; /* Frame too long? (1518 is max ethernet frame length) */ if (state.rx.current.len > 1518) rdes0 |= TDSTAT_Rx_TL; // set receive interrupt and receive state to waiting-for-packet state.reg[CSR_STATUS / 8] = (state.reg[CSR_STATUS / 8] & ~STATUS_RS) | STATUS_RI | STATUS_RS_WAIT; } // Writeback rdes0, others are read-only state.reg[CSR_STATUS / 8] = (state.reg[CSR_STATUS / 8] & ~STATUS_RS) | STATUS_RS_CLOSE; do_pci_write(state.rx.cur_addr, descr, 4, 1); // move to next descriptor if (rdes1 & TDCTL_ER) // end-of-ring, return to base state.rx.cur_addr = state.reg[CSR_RXLIST / 8]; else { if (rdes1 & TDCTL_CH) // explicit chain, use chain address state.rx.cur_addr = rdes3; else // implicit chain state.rx.cur_addr += sizeof(descr) + state.descr_skip; } return 1; // indicate processing has occurred } /** * Transmit a packet, if the guest OS has marked a descriptor as containing * data to transmit. **/ int CDEC21143::dec21143_tx() { u32 addr = state.tx.cur_addr; u32 bufaddr; unsigned char descr[16]; u32 tdes0; u32 tdes1; u32 tdes2; u32 tdes3; int bufsize; int buf1_size; int buf2_size; if (state.tx.suspend) return 0; do_pci_read(addr, descr, 1, 16); tdes0 = descr[0] + (descr[1] << 8) + (descr[2] << 16) + (descr[3] << 24); tdes1 = descr[4] + (descr[5] << 8) + (descr[6] << 16) + (descr[7] << 24); tdes2 = descr[8] + (descr[9] << 8) + (descr[10] << 16) + (descr[11] << 24); tdes3 = descr[12] + (descr[13] << 8) + (descr[14] << 16) + (descr[15] << 24); /* printf("{ dec21143_tx: base=0x%08x, tdes0=0x%08x }\n", (int)addr, * (int)tdes0); */ /* Only process packets owned by the 21143: */ if (!(tdes0 & TDSTAT_OWN)) { if (state.tx.idling > state.tx.idling_threshold) { state.reg[CSR_STATUS / 8] |= STATUS_TU; state.tx.suspend = true; state.tx.idling = 0; } else state.tx.idling++; return 0; } buf1_size = tdes1 & TDCTL_SIZE1; buf2_size = (tdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT; bufaddr = buf1_size ? tdes2 : tdes3; bufsize = buf1_size ? buf1_size : buf2_size; // state.reg[CSR_STATUS/8] &= ~STATUS_TS; if (tdes1 & TDCTL_ER) // end-of-ring, return to base state.tx.cur_addr = state.reg[CSR_TXLIST / 8]; else { if (tdes1 & TDCTL_CH) // explicit chain, use chain address state.tx.cur_addr = tdes3; else // implicit chain state.tx.cur_addr += (4 * sizeof(uint32_t)) + state.descr_skip; } /* printf("{ TX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", (long long)addr, tdes0, tdes1, tdes2, tdes3, bufsize, (int)bufaddr); */ /* Assume no error: */ tdes0 &= ~(TDSTAT_Tx_UF | TDSTAT_Tx_EC | TDSTAT_Tx_LC | TDSTAT_Tx_NC | TDSTAT_Tx_LO | TDSTAT_Tx_TO | TDSTAT_ES); if (tdes1 & TDCTL_Tx_SET) { /* * Setup Packet. * * TODO. For now, just ignore it, and pretend it worked. */ // printf("{ TX: setup packet }\n"); if (bufsize != 192) printf("[ dec21143: setup packet len = %i, should be 192! ]\n", (int)bufsize); do_pci_read(bufaddr, state.setup_filter, 1, 192); SetupFilter(); if (tdes1 & TDCTL_Tx_IC) state.reg[CSR_STATUS / 8] |= STATUS_TI; /* New descriptor values, according to the docs: */ tdes0 = 0x7fffffff; tdes1 = 0xffffffff; tdes2 = 0xffffffff; tdes3 = 0xffffffff; } else { /* * Data Packet. */ // printf("{ TX: data packet: "); if (tdes1 & TDCTL_Tx_FS) { /* First segment. Let's allocate a new buffer: */ /* printf("new frame }\n"); */ // CHECK_ALLOCATION(state.tx.cur_buf = (unsigned char *)malloc(bufsize)); state.tx.cur_buf_len = 0; } else { /* Not first segment. Increase the length of the current buffer: */ /* printf("continuing last frame }\n"); */ if (state.tx.cur_buf == NULL) printf("[ dec21143: WARNING! tx: middle segment, but no first " "segment?! ]\n"); // CHECK_REALLOCATION(state.tx.cur_buf, realloc(state.tx.cur_buf, // state.tx.cur_buf_len + bufsize), unsigned char); } /* "DMA" data from emulated physical memory into the buf: */ do_pci_read(bufaddr, state.tx.cur_buf + state.tx.cur_buf_len, 1, bufsize); state.tx.cur_buf_len += bufsize; /* only partial frames were written to the pcap filter, because the second * buffer was not considered when collecting the ethernet frames in dec21143_tx. * It can happen that both buffers to which tdes2 and tdes3 point contain data. * When this happens the data of both buffers have to be combined to get a valid * ethernet frame and hence IP packet. The patch simply checks if buf2_size is * greater 0 and if that's true append the data from the buffer pointed to by * tdes3 to the current frame. */ if ((buf2_size > 0) && (!(tdes1 & TDCTL_CH))) { do_pci_read(tdes3, state.tx.cur_buf + state.tx.cur_buf_len, 1, buf2_size); state.tx.cur_buf_len += buf2_size; } /* Last segment? Then actually transmit it: */ if (tdes1 & TDCTL_Tx_LS) { /* printf("{ TX: data frame complete. }\n"); */ // if not in internal loopback mode, transmit packet to wire if (!(state.reg[CSR_OPMODE / 8] & OPMODE_OM_INTLOOP)) { // printf("pcap send: %d bytes \n", state.tx.cur_buf_len); if (pcap_sendpacket(fp, state.tx.cur_buf, state.tx.cur_buf_len)) printf("Error sending the packet: %s\n", pcap_geterr(fp)); } // if in internal or external loopback mode, add packet to read queue if (state.reg[CSR_OPMODE / 8] & OPMODE_OM) { bool crc = !(tdes1 & TDCTL_Tx_AC); // printf("21143: %s packet, AC: %d\n", (state.reg[CSR_OPMODE / 8] & // OPMODE_OM_INTLOOP) ? "IL" : "EL", !crc); printf("21143: TX enabled: // %d, RX enabled: %d\n", state.reg[CSR_OPMODE /8] & OPMODE_ST, // state.reg[CSR_OPMODE /8] & OPMODE_SR); printf("21143: tx(), len=%d, // addr=%08x, mode=%s\n", state.tx.cur_buf_len, (int) state.tx.cur_buf, // (state.reg[CSR_OPMODE / 8] & OPMODE_OM_INTLOOP) ? "IL" : "EL"); // printf("21143: tx(), data=|"); // unsigned char* aptr = state.tx.cur_buf; // for(int i=0; iadd_tail(state.tx.cur_buf, state.tx.cur_buf_len, calc_crc, crc); } // free(state.tx.cur_buf); // state.tx.cur_buf = NULL; state.tx.cur_buf_len = 0; /* Interrupt, if Tx_IC is set: */ if (tdes1 & TDCTL_Tx_IC) state.reg[CSR_STATUS / 8] |= STATUS_TI; } /* We are done with this segment. */ tdes0 &= ~TDSTAT_OWN; } /* Error summary: */ if (tdes0 & (TDSTAT_Tx_UF | TDSTAT_Tx_EC | TDSTAT_Tx_LC | TDSTAT_Tx_NC | TDSTAT_Tx_LO | TDSTAT_Tx_TO)) tdes0 |= TDSTAT_ES; /* Descriptor writeback: */ descr[0] = (u8)tdes0; descr[1] = (u8)(tdes0 >> 8); descr[2] = (u8)(tdes0 >> 16); descr[3] = (u8)(tdes0 >> 24); descr[4] = (u8)tdes1; descr[5] = (u8)(tdes1 >> 8); descr[6] = (u8)(tdes1 >> 16); descr[7] = (u8)(tdes1 >> 24); descr[8] = (u8)tdes2; descr[9] = (u8)(tdes2 >> 8); descr[10] = (u8)(tdes2 >> 16); descr[11] = (u8)(tdes2 >> 24); descr[12] = (u8)tdes3; descr[13] = (u8)(tdes3 >> 8); descr[14] = (u8)(tdes3 >> 16); descr[15] = (u8)(tdes3 >> 24); do_pci_write(addr, descr, 1, 16); return 1; } void CDEC21143::SetupFilter() { u8 mac[16][6]; char mac_txt[16][20]; char filter[1000]; int i; int j; int numUnique; int unique[16]; bool u; #if defined(DEBUG_NIC_FILTER) printf("Building a filter...\n"); #endif for (i = 0; i < 16; i++) { mac[i][0] = state.setup_filter[i * 12]; mac[i][1] = state.setup_filter[i * 12 + 1]; mac[i][2] = state.setup_filter[i * 12 + 4]; mac[i][3] = state.setup_filter[i * 12 + 5]; mac[i][4] = state.setup_filter[i * 12 + 8]; mac[i][5] = state.setup_filter[i * 12 + 9]; sprintf(mac_txt[i], "%02x:%02x:%02x:%02x:%02x:%02x", mac[i][0], mac[i][1], mac[i][2], mac[i][3], mac[i][4], mac[i][5]); #if defined(DEBUG_NIC_FILTER) printf("MAC[%d] = %s. \n", i, mac_txt[i]); #endif } #if defined(DEBUG_NIC_FILTER) printf("Filter mode: "); if (state.reg[CSR_OPMODE / 8] & OPMODE_PR) printf("promiscuous.\n"); else { if (state.reg[CSR_OPMODE / 8] & OPMODE_IF) printf("inverse "); if (state.reg[CSR_OPMODE / 8] & OPMODE_HP) { printf("hash "); if (state.reg[CSR_OPMODE / 8] & OPMODE_HO) printf("only "); } else printf("perfect "); printf("filtering.\n"); } #endif numUnique = 0; for (i = 0; i < 16; i++) { u = true; for (j = 0; j < numUnique; j++) { if (mac[i][0] == mac[unique[j]][0] && mac[i][1] == mac[unique[j]][1] && mac[i][2] == mac[unique[j]][2] && mac[i][3] == mac[unique[j]][3] && mac[i][4] == mac[unique[j]][4] && mac[i][5] == mac[unique[j]][5]) { u = false; break; } } if (u) { unique[numUnique] = i; numUnique++; } } #if defined(DEBUG_NIC_FILTER) for (i = 0; i < numUnique; i++) printf("Unique MAC[%d] = %s. \n", i, mac_txt[unique[i]]); #endif filter[0] = '\0'; // strcat(filter,"ether broadcast"); // There must be at least one unique item; at least the mac of the card strcat(filter, "ether dst "); strcat(filter, mac_txt[unique[0]]); for (i = 1; i < numUnique; i++) { strcat(filter, " or ether dst "); strcat(filter, mac_txt[unique[i]]); } #if defined(DEBUG_NIC_FILTER) printf("FILTER = %s. \n", filter); #endif if (pcap_compile(fp, &fcode, filter, 1, 0xffffffff) < 0) FAILURE_1(Logic, "Unable to compile the packet filter (%s)", filter); if (pcap_setfilter(fp, &fcode) < 0) FAILURE(Runtime, "Error setting the filter."); } /** * Reset the network interface internals to their condition immediately * after power-up, including the PCI configuration. **/ void CDEC21143::ResetPCI() { CPCIDevice::ResetPCI(); ResetNIC(); } /** * Reset the network interface internals to their condition immediately * after power-up. This does not affect the PCI configuration. * * Code for computing the SROM checksum is taken from [T64]. **/ void CDEC21143::ResetNIC() { int leaf; if (state.rx.cur_buf != NULL) free(state.rx.cur_buf); // if (state.tx.cur_buf != NULL) // free(state.tx.cur_buf); state.rx.cur_buf = /*state.tx.cur_buf = */ NULL; memset(state.reg, 0, sizeof(uint32_t) * 32); memset(state.srom.data, 0, sizeof(state.srom.data)); memset(state.mii.phy_reg, 0, sizeof(state.mii.phy_reg)); /* Register values at reset, according to the manual: */ state.reg[CSR_BUSMODE / 8] = 0xfe000000; /* csr0 */ state.reg[CSR_MIIROM / 8] = 0xfff483ff; /* csr9 */ state.reg[CSR_SIACONN / 8] = 0xffff0000; /* csr13 */ state.reg[CSR_SIATXRX / 8] = 0xffffffff; /* csr14 */ state.reg[CSR_SIAGEN / 8] = 0x8ff00000; /* csr15 */ state.tx.idling_threshold = 10; state.rx.cur_addr = state.tx.cur_addr = 0; /* Version (= 1) and Chip count (= 1): */ state.srom.data[TULIP_ROM_SROM_FORMAT_VERION] = 1; state.srom.data[TULIP_ROM_CHIP_COUNT] = 1; /* Set the MAC address: */ memcpy(state.srom.data + TULIP_ROM_IEEE_NETWORK_ADDRESS, state.mac, 6); leaf = 30; state.srom.data[TULIP_ROM_CHIPn_DEVICE_NUMBER(0)] = 0; state.srom.data[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)] = leaf & 255; state.srom.data[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0) + 1] = leaf >> 8; state.srom.data[leaf + TULIP_ROM_IL_SELECT_CONN_TYPE] = 0; /* Not used? */ state.srom.data[leaf + TULIP_ROM_IL_MEDIA_COUNT] = 2; leaf += TULIP_ROM_IL_MEDIAn_BLOCK_BASE; state.srom.data[leaf] = 7; /* descriptor length */ state.srom.data[leaf + 1] = TULIP_ROM_MB_21142_SIA; state.srom.data[leaf + 2] = TULIP_ROM_MB_MEDIA_100TX; /* here comes 4 bytes of GPIO control/data settings */ leaf += state.srom.data[leaf]; state.srom.data[leaf] = 15; /* descriptor length */ state.srom.data[leaf + 1] = TULIP_ROM_MB_21142_MII; state.srom.data[leaf + 2] = 0; /* PHY nr */ state.srom.data[leaf + 3] = 0; /* len of select sequence */ state.srom.data[leaf + 4] = 0; /* len of reset sequence */ /* 5,6, 7,8, 9,10, 11,12, 13,14 = unused by GXemul */ leaf += state.srom.data[leaf]; /* MII PHY initial state: */ state.mii.state = MII_STATE_RESET; /* PHY #0: */ state.mii.phy_reg[MII_BMSR] = BMSR_100TXFDX | BMSR_10TFDX | BMSR_ACOMP | BMSR_ANEG | BMSR_LINK; state.tx.suspend = false; // compute the CRC for the SROM data. This code is from the // tu sample driver from HP in if_tu.c, which was apparently // derived from the 21140 Hardware Specification unsigned int POLY = 0x04c11db6; unsigned int FlippedCrc = 0; unsigned int Crc = 0xffffffff; unsigned char i; unsigned char CurrentByte; unsigned char Bit; unsigned char Msb; unsigned char chksm_1; unsigned char chksm_2; for (i = 0; i < 126; i++) { CurrentByte = state.srom.data[i]; for (Bit = 0; Bit < 8; Bit++) { Msb = (Crc >> 31) & 1; Crc <<= 1; if (Msb ^ (CurrentByte & 1)) { Crc ^= POLY; Crc |= 1; } CurrentByte >>= 1; } } for (i = 0; i < 32; i++) { FlippedCrc <<= 1; Bit = Crc & 1; Crc >>= 1; FlippedCrc += Bit; } Crc = FlippedCrc ^ 0xffffffff; chksm_1 = Crc & 0xff; chksm_2 = Crc >> 8; state.srom.data[126] = chksm_1; state.srom.data[127] = chksm_2; #if defined(DEBUG_NIC_SROM) printf("%%NIC-I-CKSUM: SROM checksum bytes are %02x, %02x\n", state.srom.data[126], state.srom.data[127]); #endif } static u32 nic_magic1 = 0xDEC21143; static u32 nic_magic2 = 0x21143DEC; /** * Save state to a Virtual Machine State file. **/ int CDEC21143::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&nic_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&nic_magic2, sizeof(u32), 1, f); printf("%s: %ld bytes saved.\n", devid_string, ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CDEC21143::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != nic_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != nic_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %ld bytes restored.\n", devid_string, ss); return 0; } #endif // defined(HAVE_PCAP) ================================================ FILE: src/DEC21143.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon GXemul, which is Copyright (C) 2004-2007 * Anders Gavare. All rights reserved. */ #if !defined(INCLUDED_DEC21143_H_) #define INCLUDED_DEC21143_H_ #include "DEC21143_mii.hpp" #include "DEC21143_tulipreg.hpp" #include "PCIDevice.hpp" #if defined(WIN32) #define HAVE_REMOTE #endif #include "Ethernet.hpp" #include /** * \brief Emulated DEC 21143 NIC device. * * Documentation consulted: * - 21143 PCI/Cardbus 10/100Mb/s Ethernet LAN Controller Hardware Reference *Manual [HRM]. (http://download.intel.com/design/network/manuals/27807401.pdf) * - Tru64 Device Driver Kit Version 2 (Ethernet sample = tu driver!) [T64]. *(http://h30097.www3.hp.com/docs/dev_doc/DOCUMENTATION/HTML/dev_docs_r2.html) * . **/ class CDEC21143 : public CPCIDevice { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); CDEC21143(CConfigurator *confg, class CSystem *c, int pcibus, int pcidev); virtual ~CDEC21143(); virtual void ResetPCI(); void ResetNIC(); void SetupFilter(); void receive_process(); void run(); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: static int nic_num; std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; bool StopThread; u32 nic_read(u32 address, int dsize); void nic_write(u32 address, int dsize, u32 data); void mii_access(uint32_t oldreg, uint32_t idata); void srom_access(uint32_t oldreg, uint32_t idata); int dec21143_rx(); int dec21143_tx(); void set_tx_state(int tx_state); void set_rx_state(int rx_state); CPacketQueue *rx_queue; pcap_t *fp; struct bpf_program fcode; bool calc_crc; /// The state structure contains all elements that need to be saved to the /// statefile. struct SNIC_state { bool irq_was_asserted; /**< remember state of IRQ */ u8 mac[6]; /**< ethernet address */ u8 setup_filter[192]; /**< filter for perfect filtering */ int descr_skip; // Descriptor Skip Length [DSL] (in bytes) /// SROM emulation struct SNIC_srom { u8 data[1 << (7)]; int curbit; int opcode; int opcode_has_started; int addr; } srom; /// MII PHY emulation struct SNIC_mii { u16 phy_reg[MII_NPHY * 32]; int state; int bit; int opcode; int phyaddr; int regaddr; } mii; u32 reg[32]; /**< 21143 registers */ /// Internal TX state struct SNIC_tx { u32 cur_addr; unsigned char *cur_buf; int cur_buf_len; int idling; int idling_threshold; bool suspend; } tx; /// Internal RX state struct SNIC_rx { u32 cur_addr; unsigned char *cur_buf; int cur_buf_len; int cur_offset; eth_packet current; } rx; } state; }; #endif // !defined(INCLUDED_DEC21143_H) ================================================ FILE: src/DEC21143_mii.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon NetBsd. * * Copyright (c) 1997 Manuel Bouyer. All rights reserved. * * Modification to match BSD/OS 3.0 MII interface by Jason R. Thorpe, * Numerical Aerospace Simulation Facility, NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Manuel Bouyer. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DEV_MII_MII_H_ #define _DEV_MII_MII_H_ /* * Registers common to all PHYs. */ #define MII_NPHY 32 /* max # of PHYs per MII */ /* * MII commands, used if a device must drive the MII lines * manually. */ #define MII_COMMAND_START 0x01 #define MII_COMMAND_READ 0x02 #define MII_COMMAND_WRITE 0x01 #define MII_COMMAND_ACK 0x02 #define MII_BMCR 0x00 /* Basic mode control register (rw) */ #define BMCR_RESET 0x8000 /* reset */ #define BMCR_LOOP 0x4000 /* loopback */ #define BMCR_SPEED0 0x2000 /* speed selection (LSB) */ #define BMCR_AUTOEN 0x1000 /* autonegotiation enable */ #define BMCR_PDOWN 0x0800 /* power down */ #define BMCR_ISO 0x0400 /* isolate */ #define BMCR_STARTNEG 0x0200 /* restart autonegotiation */ #define BMCR_FDX 0x0100 /* Set duplex mode */ #define BMCR_CTEST 0x0080 /* collision test */ #define BMCR_SPEED1 0x0040 /* speed selection (MSB) */ #define BMCR_S10 0x0000 /* 10 Mb/s */ #define BMCR_S100 BMCR_SPEED0 /* 100 Mb/s */ #define BMCR_S1000 BMCR_SPEED1 /* 1000 Mb/s */ #define BMCR_SPEED(x) ((x) & (BMCR_SPEED0 | BMCR_SPEED1)) #define MII_BMSR 0x01 /* Basic mode status register (ro) */ #define BMSR_100T4 0x8000 /* 100 base T4 capable */ #define BMSR_100TXFDX 0x4000 /* 100 base Tx full duplex capable */ #define BMSR_100TXHDX 0x2000 /* 100 base Tx half duplex capable */ #define BMSR_10TFDX 0x1000 /* 10 base T full duplex capable */ #define BMSR_10THDX 0x0800 /* 10 base T half duplex capable */ #define BMSR_100T2FDX 0x0400 /* 100 base T2 full duplex capable */ #define BMSR_100T2HDX 0x0200 /* 100 base T2 half duplex capable */ #define BMSR_EXTSTAT 0x0100 /* Extended status in register 15 */ #define BMSR_MFPS 0x0040 /* MII Frame Preamble Suppression */ #define BMSR_ACOMP 0x0020 /* Autonegotiation complete */ #define BMSR_RFAULT 0x0010 /* Link partner fault */ #define BMSR_ANEG 0x0008 /* Autonegotiation capable */ #define BMSR_LINK 0x0004 /* Link status */ #define BMSR_JABBER 0x0002 /* Jabber detected */ #define BMSR_EXTCAP 0x0001 /* Extended capability */ /* * Note that the EXTSTAT bit indicates that there is extended status * info available in register 15, but 802.3 section 22.2.4.3 also * states that that all 1000 Mb/s capable PHYs will set this bit to 1. */ #define BMSR_MEDIAMASK \ (BMSR_100T4 | BMSR_100TXFDX | BMSR_100TXHDX | BMSR_10TFDX | BMSR_10THDX | \ BMSR_100T2FDX | BMSR_100T2HDX) /* * Convert BMSR media capabilities to ANAR bits for autonegotiation. * Note the shift chopps off the BMSR_ANEG bit. */ #define BMSR_MEDIA_TO_ANAR(x) (((x)&BMSR_MEDIAMASK) >> 6) #define MII_PHYIDR1 0x02 /* ID register 1 (ro) */ #define MII_PHYIDR2 0x03 /* ID register 2 (ro) */ #define IDR2_OUILSB 0xfc00 /* OUI LSB */ #define IDR2_MODEL 0x03f0 /* vendor model */ #define IDR2_REV 0x000f /* vendor revision */ #define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */ /* section 28.2.4.1 and 37.2.6.1 */ #define ANAR_NP 0x8000 /* Next page (ro) */ #define ANAR_ACK 0x4000 /* link partner abilities acknowledged (ro) */ #define ANAR_RF 0x2000 /* remote fault (ro) */ #define ANAR_FC 0x0400 /* local device supports PAUSE */ #define ANAR_T4 0x0200 /* local device supports 100bT4 */ #define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */ #define ANAR_TX 0x0080 /* local device supports 100bTx */ #define ANAR_10_FD 0x0040 /* local device supports 10bT FD */ #define ANAR_10 0x0020 /* local device supports 10bT */ #define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */ #define ANAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ #define ANAR_X_PAUSE_NONE (0 << 10) #define ANAR_X_PAUSE_SYM (1 << 10) #define ANAR_X_PAUSE_ASYM (2 << 10) #define ANAR_X_PAUSE_TOWARDS (3 << 10) #define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */ /* section 28.2.4.1 and 37.2.6.1 */ #define ANLPAR_NP 0x8000 /* Next page (ro) */ #define ANLPAR_ACK 0x4000 /* link partner accepted ACK (ro) */ #define ANLPAR_RF 0x2000 /* remote fault (ro) */ #define ANLPAR_FC 0x0400 /* link partner supports PAUSE */ #define ANLPAR_T4 0x0200 /* link partner supports 100bT4 */ #define ANLPAR_TX_FD 0x0100 /* link partner supports 100bTx FD */ #define ANLPAR_TX 0x0080 /* link partner supports 100bTx */ #define ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */ #define ANLPAR_10 0x0020 /* link partner supports 10bT */ #define ANLPAR_CSMA 0x0001 /* protocol selector CSMA/CD */ #define ANLPAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANLPAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ #define ANLPAR_X_PAUSE_MASK (3 << 10) #define ANLPAR_X_PAUSE_NONE (0 << 10) #define ANLPAR_X_PAUSE_SYM (1 << 10) #define ANLPAR_X_PAUSE_ASYM (2 << 10) #define ANLPAR_X_PAUSE_TOWARDS (3 << 10) #define MII_ANER 0x06 /* Autonegotiation expansion (ro) */ /* section 28.2.4.1 and 37.2.6.1 */ #define ANER_MLF 0x0010 /* multiple link detection fault */ #define ANER_LPNP 0x0008 /* link parter next page-able */ #define ANER_NP 0x0004 /* next page-able */ #define ANER_PAGE_RX 0x0002 /* Page received */ #define ANER_LPAN 0x0001 /* link parter autoneg-able */ #define MII_ANNP 0x07 /* Autonegotiation next page */ /* section 28.2.4.1 and 37.2.6.1 */ #define MII_ANLPRNP 0x08 /* Autonegotiation link partner rx next page */ /* section 32.5.1 and 37.2.6.1 */ /* This is also the 1000baseT control register */ #define MII_100T2CR 0x09 /* 100base-T2 control register */ #define GTCR_TEST_MASK 0xe000 /* see 802.3ab ss. 40.6.1.1.2 */ #define GTCR_MAN_MS 0x1000 /* enable manual master/slave control */ #define GTCR_ADV_MS 0x0800 /* 1 = adv. master, 0 = adv. slave */ #define GTCR_PORT_TYPE 0x0400 /* 1 = DCE, 0 = DTE (NIC) */ #define GTCR_ADV_1000TFDX 0x0200 /* adv. 1000baseT FDX */ #define GTCR_ADV_1000THDX 0x0100 /* adv. 1000baseT HDX */ /* This is also the 1000baseT status register */ #define MII_100T2SR 0x0a /* 100base-T2 status register */ #define GTSR_MAN_MS_FLT 0x8000 /* master/slave config fault */ #define GTSR_MS_RES 0x4000 /* result: 1 = master, 0 = slave */ #define GTSR_LRS 0x2000 /* local rx status, 1 = ok */ #define GTSR_RRS 0x1000 /* remove rx status, 1 = ok */ #define GTSR_LP_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */ #define GTSR_LP_1000THDX 0x0400 /* link partner 1000baseT HDX capable */ #define GTSR_LP_ASM_DIR 0x0200 /* link partner asym. pause dir. capable */ #define GTSR_IDLE_ERR 0x00ff /* IDLE error count */ #define MII_EXTSR 0x0f /* Extended status register */ #define EXTSR_1000XFDX 0x8000 /* 1000X full-duplex capable */ #define EXTSR_1000XHDX 0x4000 /* 1000X half-duplex capable */ #define EXTSR_1000TFDX 0x2000 /* 1000T full-duplex capable */ #define EXTSR_1000THDX 0x1000 /* 1000T half-duplex capable */ #define EXTSR_MEDIAMASK \ (EXTSR_1000XFDX | EXTSR_1000XHDX | EXTSR_1000TFDX | EXTSR_1000THDX) #endif /* _DEV_MII_MII_H_ */ ================================================ FILE: src/DEC21143_tulipreg.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon NetBsd. * * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __volatile #define __volatile #endif #ifndef _DEV_IC_TULIPREG_H_ #define _DEV_IC_TULIPREG_H_ /* * Register description for the Digital Semiconductor ``Tulip'' (21x4x) * Ethernet controller family. */ /* * Descriptor Status bits common to transmit and receive. */ #define TDSTAT_OWN 0x80000000 /* Tulip owns descriptor */ #define TDSTAT_ES 0x00008000 /* Error Summary */ /* * Descriptor Status bits for Receive Descriptor. */ #define TDSTAT_Rx_FF 0x40000000 /* Filtering Fail */ #define TDSTAT_Rx_FL 0x3fff0000 /* Frame Length including CRC */ #define TDSTAT_Rx_DE 0x00004000 /* Descriptor Error */ #define TDSTAT_Rx_DT 0x00003000 /* Data Type */ #define TDSTAT_Rx_RF 0x00000800 /* Runt Frame */ #define TDSTAT_Rx_MF 0x00000400 /* Multicast Frame */ #define TDSTAT_Rx_FS 0x00000200 /* First Descriptor */ #define TDSTAT_Rx_LS 0x00000100 /* Last Descriptor */ #define TDSTAT_Rx_TL 0x00000080 /* Frame Too Long */ #define TDSTAT_Rx_CS 0x00000040 /* Collision Seen */ #define TDSTAT_Rx_RT 0x00000020 /* Frame Type */ #define TDSTAT_Rx_RW 0x00000010 /* Receive Watchdog */ #define TDSTAT_Rx_RE 0x00000008 /* Report on MII Error */ #define TDSTAT_Rx_DB 0x00000004 /* Dribbling Bit */ #define TDSTAT_Rx_CE 0x00000002 /* CRC Error */ #define TDSTAT_Rx_ZER 0x00000001 /* Zero (always 0) */ #define TDSTAT_Rx_LENGTH(x) (((x)&TDSTAT_Rx_FL) >> 16) #define TDSTAT_Rx_DT_SR 0x00000000 /* Serial Received Frame */ #define TDSTAT_Rx_DT_IL 0x00001000 /* Internal Loopback Frame */ #define TDSTAT_Rx_DT_EL 0x00002000 /* External Loopback Frame */ #define TDSTAT_Rx_DT_r 0x00003000 /* Reserved */ /* * Descriptor Status bits for Transmit Descriptor. */ #define TDSTAT_Tx_TO 0x00004000 /* Transmit Jabber Timeout */ #define TDSTAT_Tx_LO 0x00000800 /* Loss of Carrier */ #define TDSTAT_Tx_NC 0x00000400 /* No Carrier */ #define TDSTAT_Tx_LC 0x00000200 /* Late Collision */ #define TDSTAT_Tx_EC 0x00000100 /* Excessive Collisions */ #define TDSTAT_Tx_HF 0x00000080 /* Heartbeat Fail */ #define TDSTAT_Tx_CC 0x00000078 /* Collision Count */ #define TDSTAT_Tx_LF 0x00000004 /* Link Fail */ #define TDSTAT_Tx_UF 0x00000002 /* Underflow Error */ #define TDSTAT_Tx_DE 0x00000001 /* Deferred */ #define TDSTAT_Tx_COLLISIONS(x) (((x)&TDSTAT_Tx_CC) >> 3) /* * Descriptor Control bits common to transmit and receive. */ #define TDCTL_SIZE1 0x000007ff /* Size of buffer 1 */ #define TDCTL_SIZE1_SHIFT 0 #define TDCTL_SIZE2 0x003ff800 /* Size of buffer 2 */ #define TDCTL_SIZE2_SHIFT 11 #define TDCTL_ER 0x02000000 /* End of Ring */ #define TDCTL_CH 0x01000000 /* Second Address Chained */ /* * Descriptor Control bits for Transmit Descriptor. */ #define TDCTL_Tx_IC 0x80000000 /* Interrupt on Completion */ #define TDCTL_Tx_LS 0x40000000 /* Last Segment */ #define TDCTL_Tx_FS 0x20000000 /* First Segment */ #define TDCTL_Tx_FT1 0x10000000 /* Filtering Type 1 */ #define TDCTL_Tx_SET 0x08000000 /* Setup Packet */ #define TDCTL_Tx_AC 0x04000000 /* Add CRC Disable */ #define TDCTL_Tx_DPD 0x00800000 /* Disabled Padding */ #define TDCTL_Tx_FT0 0x00400000 /* Filtering Type 0 */ /* * The Tulip filter is programmed by "transmitting" a Setup Packet * (indicated by TDCTL_Tx_SET). The filtering type is indicated * as follows: * * FT1 FT0 Description * --- --- ----------- * 0 0 Perfect Filtering: The Tulip interprets the * descriptor buffer as a table of 16 MAC addresses * that the Tulip should receive. * * 0 1 Hash Filtering: The Tulip interprets the * descriptor buffer as a 512-bit hash table * plus one perfect address. If the incoming * address is Multicast, the hash table filters * the address, else the address is filtered by * the perfect address. * * 1 0 Inverse Filtering: Like Perfect Filtering, except * the table is addresses that the Tulip does NOT * receive. * * 1 1 Hash-only Filtering: Like Hash Filtering, but * physical addresses are matched by the hash table * as well, and not by matching a single perfect * address. * * A Setup Packet must always be 192 bytes long. The Tulip can store * 16 MAC addresses. If not all 16 are specified in Perfect Filtering * or Inverse Filtering mode, then unused entries should duplicate * one of the valid entries. */ #define TDCTL_Tx_FT_PERFECT 0 #define TDCTL_Tx_FT_HASH TDCTL_Tx_FT0 #define TDCTL_Tx_FT_INVERSE TDCTL_Tx_FT1 #define TDCTL_Tx_FT_HASHONLY (TDCTL_Tx_FT1 | TDCTL_Tx_FT0) #define TULIP_SETUP_PACKET_LEN 192 #define TULIP_MAXADDRS 16 #define TULIP_MCHASHSIZE 512 /* * Maximum size of a Tulip Ethernet Address ROM or SROM. */ #define TULIP_ROM_SIZE(bits) (2 << (bits)) #define TULIP_MAX_ROM_SIZE 512 /* * Format of the standard Tulip SROM information: * * Byte offset Size Usage * 0 18 reserved * 18 1 SROM Format Version * 19 1 Chip Count * 20 6 IEEE Network Address * 26 1 Chip 0 Device Number * 27 2 Chip 0 Info Leaf Offset * 29 1 Chip 1 Device Number * 30 2 Chip 1 Info Leaf Offset * 32 1 Chip 2 Device Number * 33 2 Chip 2 Info Leaf Offset * ... 1 Chip n Device Number * ... 2 Chip n Info Leaf Offset * ... ... ... * Chip Info Leaf Information * ... * ... * ... * 126 2 CRC32 checksum */ #define TULIP_ROM_SROM_FORMAT_VERION 18 /* B */ #define TULIP_ROM_CHIP_COUNT 19 /* B */ #define TULIP_ROM_IEEE_NETWORK_ADDRESS 20 #define TULIP_ROM_CHIPn_DEVICE_NUMBER(n) (26 + ((n)*3)) /* B */ #define TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(n) (27 + ((n)*3)) /* W */ #define TULIP_ROM_CRC32_CHECKSUM 126 /* W */ #define TULIP_ROM_CRC32_CHECKSUM1 94 /* W */ #define TULIP_ROM_IL_SELECT_CONN_TYPE 0 /* W */ #define TULIP_ROM_IL_MEDIA_COUNT 2 /* B */ #define TULIP_ROM_IL_MEDIAn_BLOCK_BASE 3 #define SELECT_CONN_TYPE_TP 0x0000 #define SELECT_CONN_TYPE_BNC 0x0001 #define SELECT_CONN_TYPE_AUI 0x0002 #define SELECT_CONN_TYPE_100TX 0x0003 #define SELECT_CONN_TYPE_100T4 0x0006 #define SELECT_CONN_TYPE_100FX 0x0007 #define SELECT_CONN_TYPE MII_10T 0x0009 #define SELECT_CONN_TYPE_MII_100TX 0x000d #define SELECT_CONN_TYPE_MII_100T4 0x000f #define SELECT_CONN_TYPE_MII_100FX 0x0010 #define SELECT_CONN_TYPE_TP_AUTONEG 0x0100 #define SELECT_CONN_TYPE_TP_FDX 0x0204 #define SELECT_CONN_TYPE_MII_10T_FDX 0x020a #define SELECT_CONN_TYPE_100TX_FDX 0x020e #define SELECT_CONN_TYPE_MII_100TX_FDX 0x0211 #define SELECT_CONN_TYPE_TP_NOLINKPASS 0x0400 #define SELECT_CONN_TYPE_ASENSE 0x0800 #define SELECT_CONN_TYPE_ASENSE_POWERUP 0x8800 #define SELECT_CONN_TYPE_ASENSE_AUTONEG 0x0900 #define TULIP_ROM_MB_MEDIA_CODE 0x3f #define TULIP_ROM_MB_MEDIA_TP 0x00 #define TULIP_ROM_MB_MEDIA_BNC 0x01 #define TULIP_ROM_MB_MEDIA_AUI 0x02 #define TULIP_ROM_MB_MEDIA_100TX 0x03 #define TULIP_ROM_MB_MEDIA_TP_FDX 0x04 #define TULIP_ROM_MB_MEDIA_100TX_FDX 0x05 #define TULIP_ROM_MB_MEDIA_100T4 0x06 #define TULIP_ROM_MB_MEDIA_100FX 0x07 #define TULIP_ROM_MB_MEDIA_100FX_FDX 0x08 #define TULIP_ROM_MB_EXT 0x40 #define TULIP_ROM_MB_CSR13 1 /* W */ #define TULIP_ROM_MB_CSR14 3 /* W */ #define TULIP_ROM_MB_CSR15 5 /* W */ #define TULIP_ROM_MB_SIZE(mc) (((mc)&TULIP_ROM_MB_EXT) ? 7 : 1) #define TULIP_ROM_MB_NOINDICATOR 0x8000 #define TULIP_ROM_MB_DEFAULT 0x4000 #define TULIP_ROM_MB_POLARITY 0x0080 #define TULIP_ROM_MB_OPMODE(x) (((x)&0x71) << 18) #define TULIP_ROM_MB_BITPOS(x) (1 << (((x)&0x0e) >> 1)) #define TULIP_ROM_MB_21140_GPR 0 /* 21140[A] GPR block */ #define TULIP_ROM_MB_21140_MII 1 /* 21140[A] MII block */ #define TULIP_ROM_MB_21142_SIA 2 /* 2114[23] SIA block */ #define TULIP_ROM_MB_21142_MII 3 /* 2114[23] MII block */ #define TULIP_ROM_MB_21143_SYM 4 /* 21143 SYM block */ #define TULIP_ROM_MB_21143_RESET 5 /* 21143 reset block */ #define TULIP_ROM_GETW(data, off) \ ((uint32_t)(data)[(off)] | (uint32_t)((data)[(off) + 1]) << 8) /* * Tulip control registers. */ #define TULIP_CSR0 0x00 #define TULIP_CSR1 0x08 #define TULIP_CSR2 0x10 #define TULIP_CSR3 0x18 #define TULIP_CSR4 0x20 #define TULIP_CSR5 0x28 #define TULIP_CSR6 0x30 #define TULIP_CSR7 0x38 #define TULIP_CSR8 0x40 #define TULIP_CSR9 0x48 #define TULIP_CSR10 0x50 #define TULIP_CSR11 0x58 #define TULIP_CSR12 0x60 #define TULIP_CSR13 0x68 #define TULIP_CSR14 0x70 #define TULIP_CSR15 0x78 #define TULIP_CSR16 0x80 #define TULIP_CSR17 0x88 #define TULIP_CSR18 0x90 #define TULIP_CSR19 0x98 #define TULIP_CSR20 0xa0 #define TULIP_CSR21 0xa8 #define TULIP_CSR22 0xb0 #define TULIP_CSR23 0xb8 #define TULIP_CSR24 0xc0 #define TULIP_CSR25 0xc8 #define TULIP_CSR26 0xd0 #define TULIP_CSR27 0xd8 #define TULIP_CSR28 0xe0 #define TULIP_CSR29 0xe8 #define TULIP_CSR30 0xf0 #define TULIP_CSR31 0xf8 #define TULIP_CSR_INDEX(csr) ((csr) >> 3) /* CSR0 - Bus Mode */ #define CSR_BUSMODE TULIP_CSR0 #define BUSMODE_SWR 0x00000001 /* software reset */ #define BUSMODE_BAR 0x00000002 /* bus arbitration */ #define BUSMODE_DSL 0x0000007c /* descriptor skip length */ #define BUSMODE_BLE 0x00000080 /* big endian */ /* programmable burst length */ #define BUSMODE_PBL_DEFAULT 0x00000000 /* default value */ #define BUSMODE_PBL_1LW 0x00000100 /* 1 longword */ #define BUSMODE_PBL_2LW 0x00000200 /* 2 longwords */ #define BUSMODE_PBL_4LW 0x00000400 /* 4 longwords */ #define BUSMODE_PBL_8LW 0x00000800 /* 8 longwords */ #define BUSMODE_PBL_16LW 0x00001000 /* 16 longwords */ #define BUSMODE_PBL_32LW 0x00002000 /* 32 longwords */ /* cache alignment */ #define BUSMODE_CAL_NONE 0x00000000 /* no alignment */ #define BUSMODE_CAL_8LW 0x00004000 /* 8 longwords */ #define BUSMODE_CAL_16LW 0x00008000 /* 16 longwords */ #define BUSMODE_CAL_32LW 0x0000c000 /* 32 longwords */ #define BUSMODE_DAS 0x00010000 /* diagnostic address space */ /* must be zero on most */ /* transmit auto-poll */ #define BUSMODE_TAP_NONE 0x00000000 /* no auto-polling */ #define BUSMODE_TAP_200us 0x00020000 /* 200 uS */ #define BUSMODE_TAP_800us 0x00040000 /* 400 uS */ #define BUSMODE_TAP_1_6ms 0x00060000 /* 1.6 mS */ #define BUSMODE_TAP_12_8us 0x00080000 /* 12.8 uS (21041+) */ #define BUSMODE_TAP_25_6us 0x000a0000 /* 25.6 uS (21041+) */ #define BUSMODE_TAP_51_2us 0x000c0000 /* 51.2 uS (21041+) */ #define BUSMODE_TAP_102_4us 0x000e0000 /* 102.4 uS (21041+) */ #define BUSMODE_DBO 0x00100000 /* desc-only b/e (21041+) */ #define BUSMODE_RME 0x00200000 /* rd/mult enab (21140+) */ #define BUSMODE_RLE 0x00800000 /* rd/line enab (21140+) */ #define BUSMODE_WLE 0x01000000 /* wt/line enab (21140+) */ /* CSR1 - Transmit Poll Demand */ #define CSR_TXPOLL TULIP_CSR1 #define TXPOLL_TPD 0x00000001 /* transmit poll demand */ /* CSR2 - Receive Poll Demand */ #define CSR_RXPOLL TULIP_CSR2 #define RXPOLL_RPD 0x00000001 /* receive poll demand */ /* CSR3 - Receive List Base Address */ #define CSR_RXLIST TULIP_CSR3 /* CSR4 - Transmit List Base Address */ #define CSR_TXLIST TULIP_CSR4 /* CSR5 - Status */ #define CSR_STATUS TULIP_CSR5 #define STATUS_TI 0x00000001 /* transmit interrupt */ #define STATUS_TPS 0x00000002 /* transmit process stopped */ #define STATUS_TU 0x00000004 /* transmit buffer unavail */ #define STATUS_TJT 0x00000008 /* transmit jabber timeout */ #define STATUS_LNPANC 0x00000010 /* link pass (21041) */ #define STATUS_UNF 0x00000020 /* transmit underflow */ #define STATUS_RI 0x00000040 /* receive interrupt */ #define STATUS_RU 0x00000080 /* receive buffer unavail */ #define STATUS_RPS 0x00000100 /* receive process stopped */ #define STATUS_RWT 0x00000200 /* receive watchdog timeout */ #define STATUS_AT \ 0x00000400 /* SIA AUI/TP pin changed \ (21040) */ #define STATUS_ETI \ 0x00000400 /* early transmit interrupt \ (21142) */ #define STATUS_FD \ 0x00000800 /* full duplex short frame \ received (21040) */ #define STATUS_TM 0x00000800 /* timer expired (21041) */ #define STATUS_LNF 0x00001000 /* link fail (21040) */ #define STATUS_SE 0x00002000 /* system error */ #define STATUS_ER 0x00004000 /* early receive (21041) */ #define STATUS_AIS 0x00008000 /* abnormal interrupt summary */ #define STATUS_NIS 0x00010000 /* normal interrupt summary */ #define STATUS_RS 0x000e0000 /* receive process state */ #define STATUS_RS_STOPPED 0x00000000 /* Stopped */ #define STATUS_RS_FETCH \ 0x00020000 /* Running - fetch receive \ descriptor */ #define STATUS_RS_CHECK \ 0x00040000 /* Running - check for end \ of receive */ #define STATUS_RS_WAIT 0x00060000 /* Running - wait for packet */ #define STATUS_RS_SUSPENDED 0x00080000 /* Suspended */ #define STATUS_RS_CLOSE \ 0x000a0000 /* Running - close receive \ descriptor */ #define STATUS_RS_FLUSH \ 0x000c0000 /* Running - flush current \ frame from FIFO */ #define STATUS_RS_QUEUE \ 0x000e0000 /* Running - queue current \ frame from FIFO into \ buffer */ #define STATUS_TS 0x00700000 /* transmit process state */ #define STATUS_TS_STOPPED 0x00000000 /* Stopped */ #define STATUS_TS_FETCH \ 0x00100000 /* Running - fetch transmit \ descriptor */ #define STATUS_TS_WAIT \ 0x00200000 /* Running - wait for end \ of transmission */ #define STATUS_TS_READING \ 0x00300000 /* Running - read buffer from \ memory and queue into \ FIFO */ #define STATUS_TS_RESERVED 0x00400000 /* RESERVED */ #define STATUS_TS_SETUP 0x00500000 /* Running - Setup packet */ #define STATUS_TS_SUSPENDED 0x00600000 /* Suspended */ #define STATUS_TS_CLOSE 0x00700000 /* Running - close transmit descriptor */ #define STATUS_EB 0x03800000 /* error bits */ #define STATUS_EB_PARITY 0x00000000 /* parity errror */ #define STATUS_EB_MABT 0x00800000 /* master abort */ #define STATUS_EB_TABT 0x01000000 /* target abort */ #define STATUS_GPPI 0x04000000 /* GPIO interrupt (21142) */ #define STATUS_LC 0x08000000 /* 100baseTX link change (21142) */ #define STATUS_X3201_PMEIS \ 0x10000000 /* power management event interrupt summary */ #define STATUS_X3201_SFIS \ 0x80000000 /* second function (Modem) interrupt status */ /* CSR6 - Operation Mode */ #define CSR_OPMODE TULIP_CSR6 #define OPMODE_HP 0x00000001 /* hash/perfect mode (ro) */ #define OPMODE_SR 0x00000002 /* start receive */ #define OPMODE_HO 0x00000004 /* hash only mode (ro) */ #define OPMODE_PB 0x00000008 /* pass bad frames */ #define OPMODE_IF 0x00000010 /* inverse filter mode (ro) */ #define OPMODE_SB 0x00000020 /* start backoff counter */ #define OPMODE_PR 0x00000040 /* promiscuous mode */ #define OPMODE_PM 0x00000080 /* pass all multicast */ #define OPMODE_FKD 0x00000100 /* flaky oscillator disable */ #define OPMODE_FD 0x00000200 /* full-duplex mode */ #define OPMODE_OM 0x00000c00 /* operating mode */ #define OPMODE_OM_NORMAL 0x00000000 /* normal mode */ #define OPMODE_OM_INTLOOP 0x00000400 /* internal loopback */ #define OPMODE_OM_EXTLOOP 0x00000800 /* external loopback */ #define OPMODE_FC 0x00001000 /* force collision */ #define OPMODE_ST 0x00002000 /* start transmitter */ #define OPMODE_TR 0x0000c000 /* threshold control */ #define OPMODE_TR_72 0x00000000 /* 72 bytes */ #define OPMODE_TR_96 0x00004000 /* 96 bytes */ #define OPMODE_TR_128 0x00008000 /* 128 bytes */ #define OPMODE_TR_160 0x0000c000 /* 160 bytes */ #define OPMODE_BP 0x00010000 /* backpressure enable */ #define OPMODE_CA 0x00020000 /* capture effect enable */ #define OPMODE_PS 0x00040000 /* port select: 1 = MII/SYM, 0 = SRL (21140) */ #define OPMODE_HBD \ 0x00080000 /* heartbeat disable: set in MII/SYM 100mbps, set according to \ PHY in MII 10mbps mode (21140) */ #define OPMODE_SF 0x00200000 /* store and forward mode (21140) */ #define OPMODE_TTM \ 0x00400000 /* Transmit Threshold Mode: 1 = 10mbps, 0 = 100mbps (21140) */ #define OPMODE_PCS 0x00800000 /* PCS function (21140) */ #define OPMODE_SCR 0x01000000 /* scrambler mode (21140) */ #define OPMODE_MBO 0x02000000 /* must be one (21140) */ #define OPMODE_IDAMSB 0x04000000 /* ignore dest addr MSB (21142) */ #define OPMODE_RA 0x40000000 /* receive all (21140) */ #define OPMODE_SC 0x80000000 /* special capture effect enable (21041+) */ /* Shorthand for media-related OPMODE bits */ #define OPMODE_MEDIA_BITS \ (OPMODE_FD | OPMODE_PS | OPMODE_TTM | OPMODE_PCS | OPMODE_SCR) /* CSR7 - Interrupt Enable */ #define CSR_INTEN TULIP_CSR7 /* See bits for CSR5 -- Status */ /* CSR8 - Missed Frames */ #define CSR_MISSED TULIP_CSR8 #define MISSED_MFC 0x0000ffff /* missed packet count */ #define MISSED_MFO \ 0x00010000 /* missed packet count \ overflowed */ #define MISSED_FOC \ 0x0ffe0000 /* fifo overflow counter \ (21140) */ #define MISSED_OCO \ 0x10000000 /* overflow counter overflowed \ (21140) */ #define MISSED_GETMFC(x) ((x)&MISSED_MFC) #define MISSED_GETFOC(x) (((x)&MISSED_FOC) >> 17) /* CSR9 - MII, SROM, Boot ROM, Ethernet Address ROM register. */ #define CSR_MIIROM TULIP_CSR9 #define MIIROM_DATA 0x000000ff /* byte of data to/from Boot ROM (21041+) */ #define MIIROM_SROMCS 0x00000001 /* SROM chip select */ #define MIIROM_SROMSK 0x00000002 /* SROM clock */ #define MIIROM_SROMDI 0x00000004 /* SROM data in (to) */ #define MIIROM_SROMDO 0x00000008 /* SROM data out (from) */ #define MIIROM_REG 0x00000400 /* external register select */ #define MIIROM_SR 0x00000800 /* SROM select */ #define MIIROM_BR 0x00001000 /* boot ROM select */ #define MIIROM_WR 0x00002000 /* write to boot ROM */ #define MIIROM_RD 0x00004000 /* read from boot ROM */ #define MIIROM_MOD 0x00008000 /* mode select (ro) (21041) */ #define MIIROM_MDC 0x00010000 /* MII clock */ #define MIIROM_MDO 0x00020000 /* MII data out */ #define MIIROM_MIIDIR \ 0x00040000 /* MII direction mode \ 1 = PHY in read, \ 0 = PHY in write */ #define MIIROM_MDI 0x00080000 /* MII data in */ #define MIIROM_DN 0x80000000 /* data not valid (21040) */ /* SROM opcodes */ #define TULIP_SROM_OPC_ERASE 0x04 #define TULIP_SROM_OPC_WRITE 0x05 #define TULIP_SROM_OPC_READ 0x06 /* CSR10 - Boot ROM address register (21041+). */ #define CSR_ROMADDR TULIP_CSR10 #define ROMADDR_MASK 0x000003ff /* boot rom address */ /* CSR11 - General Purpose Timer (21041+). */ #define CSR_GPT TULIP_CSR11 #define GPT_VALUE 0x0000ffff /* timer value */ #define GPT_CON 0x00010000 /* continuous mode */ /* 21143-PD and 21143-TD Interrupt Mitigation bits */ #define GPT_NRX 0x000e0000 /* number of Rx packets */ #define GPT_RXT 0x00f00000 /* Rx timer */ #define GPT_NTX 0x07000000 /* number of Tx packets */ #define GPT_TXT 0x78000000 /* Tx timer */ #define GPT_CYCLE 0x80000000 /* cycle size */ /* CSR12 - SIA Status Register. */ #define CSR_SIASTAT TULIP_CSR12 #define SIASTAT_PAUI 0x00000001 /* pin AUI/TP indication (21040) */ #define SIASTAT_MRA 0x00000001 /* MII receive activity (21142) */ #define SIASTAT_NCR 0x00000002 /* network connection error */ #define SIASTAT_LS100 0x00000002 /* 100baseT link status 0 == pass (21142) */ #define SIASTAT_LKF 0x00000004 /* link fail status */ #define SIASTAT_LS10 0x00000004 /* 10baseT link status 0 == pass (21142) */ #define SIASTAT_APS 0x00000008 /* auto polarity status */ #define SIASTAT_DSD 0x00000010 /* PLL self test done */ #define SIASTAT_DSP 0x00000020 /* PLL self test pass */ #define SIASTAT_DAZ 0x00000040 /* PLL all zero */ #define SIASTAT_DAO 0x00000080 /* PLL all one */ #define SIASTAT_SRA 0x00000100 /* selected port receive activity (21041) */ #define SIASTAT_ARA 0x00000100 /* AUI receive activity (21142) */ #define SIASTAT_NRA \ 0x00000200 /* non-selected port receive activity (21041) \ */ #define SIASTAT_TRA 0x00000200 /* 10base-T receive activity (21142) */ #define SIASTAT_NSN 0x00000400 /* non-stable NLPs detected (21041) */ #define SIASTAT_TRF 0x00000800 /* transmit remote fault (21041) */ #define SIASTAT_ANS 0x00007000 /* autonegotiation state (21041) */ #define SIASTAT_ANS_DIS 0x00000000 /* disabled */ #define SIASTAT_ANS_TXDIS 0x00001000 /* transmit disabled */ #define SIASTAT_ANS_START 0x00001000 /* (MX98715AEC) */ #define SIASTAT_ANS_ABD 0x00002000 /* ability detect */ #define SIASTAT_ANS_ACKD 0x00003000 /* acknowledge detect */ #define SIASTAT_ANS_ACKC 0x00004000 /* complete acknowledge */ #define SIASTAT_ANS_FLPGOOD 0x00005000 /* FLP link good */ #define SIASTAT_ANS_LINKCHECK 0x00006000 /* link check */ #define SIASTAT_LPN 0x00008000 /* link partner negotiable (21041) */ #define SIASTAT_LPC 0xffff0000 /* link partner code word */ #define SIASTAT_GETLPC(x) (((x)&SIASTAT_LPC) >> 16) /* CSR13 - SIA Connectivity Register. */ #define CSR_SIACONN TULIP_CSR13 #define SIACONN_SRL 0x00000001 /* SIA reset (0 == reset) */ #define SIACONN_PS 0x00000002 /* pin AUI/TP selection (21040) */ #define SIACONN_CAC 0x00000004 /* CSR autoconfiguration */ #define SIACONN_AUI 0x00000008 /* select AUI (0 = TP) */ #define SIACONN_EDP 0x00000010 /* SIA PLL external input enable (21040) */ #define SIACONN_ENI 0x00000020 /* encoder input multiplexer (21040) */ #define SIACONN_SIM \ 0x00000040 /* serial interface input multiplexer (21040) \ */ #define SIACONN_ASE 0x00000080 /* APLL start enable (21040) */ #define SIACONN_SEL \ 0x00000f00 /* external port output multiplexer select (21040) */ #define SIACONN_IE 0x00001000 /* input enable (21040) */ #define SIACONN_OE1_3 0x00002000 /* output enable 1, 3 (21040) */ #define SIACONN_OE2_4 0x00004000 /* output enable 2, 4 (21040) */ #define SIACONN_OE5_6_7 0x00008000 /* output enable 5, 6, 7 (21040) */ #define SIACONN_SDM \ 0x0000ef00 /* SIA diagnostic mode; always set to this value for normal \ operation (21041) */ /* CSR14 - SIA Transmit Receive Register. */ #define CSR_SIATXRX TULIP_CSR14 #define SIATXRX_ECEN 0x00000001 /* encoder enable */ #define SIATXRX_LBK 0x00000002 /* loopback enable */ #define SIATXRX_DREN 0x00000004 /* driver enable */ #define SIATXRX_LSE 0x00000008 /* link pulse send enable */ #define SIATXRX_CPEN 0x00000030 /* compensation enable */ #define SIATXRX_CPEN_DIS0 0x00000000 /* disabled */ #define SIATXRX_CPEN_DIS1 0x00000010 /* disabled */ #define SIATXRX_CPEN_HIGHPWR 0x00000020 /* high power */ #define SIATXRX_CPEN_NORMAL 0x00000030 /* normal */ #define SIATXRX_MBO 0x00000040 /* must be one (21041 pass 2) */ #define SIATXRX_TH 0x00000040 /* 10baseT HDX enable (21142) */ #define SIATXRX_ANE 0x00000080 /* autonegotiation enable (21041/21142) */ #define SIATXRX_RSQ 0x00000100 /* receive squelch enable */ #define SIATXRX_CSQ 0x00000200 /* collision squelch enable */ #define SIATXRX_CLD 0x00000400 /* collision detect enable */ #define SIATXRX_SQE 0x00000800 /* signal quality generation enable */ #define SIATXRX_LTE 0x00001000 /* link test enable */ #define SIATXRX_APE 0x00002000 /* auto-polarity enable */ #define SIATXRX_SPP 0x00004000 /* set polarity plus */ #define SIATXRX_TAS \ 0x00008000 /* 10base-T/AUI autosensing enable (21041/21142) */ #define SIATXRX_THX 0x00010000 /* 100baseTX-HDX (21142) */ #define SIATXRX_TXF 0x00020000 /* 100baseTX-FDX (21142) */ #define SIATXRX_T4 0x00040000 /* 100baseT4 (21142) */ /* CSR15 - SIA General Register. */ #define CSR_SIAGEN TULIP_CSR15 #define SIAGEN_JBD 0x00000001 /* jabber disable */ #define SIAGEN_HUJ 0x00000002 /* host unjab */ #define SIAGEN_JCK 0x00000004 /* jabber clock */ #define SIAGEN_ABM 0x00000008 /* BNC select (21041) */ #define SIAGEN_RWD 0x00000010 /* receive watchdog disable */ #define SIAGEN_RWR 0x00000020 /* receive watchdog release */ #define SIAGEN_LE1 0x00000040 /* LED 1 enable (21041) */ #define SIAGEN_LV1 0x00000080 /* LED 1 value (21041) */ #define SIAGEN_TSCK 0x00000100 /* test clock */ #define SIAGEN_FUSQ 0x00000200 /* force unsquelch */ #define SIAGEN_FLF 0x00000400 /* force link fail */ #define SIAGEN_LSD 0x00000800 /* LED stretch disable (21041) */ #define SIAGEN_LEE 0x00000800 /* Link extend enable (21142) */ #define SIAGEN_DPST 0x00001000 /* PLL self-test start */ #define SIAGEN_FRL 0x00002000 /* force receiver low */ #define SIAGEN_LE2 0x00004000 /* LED 2 enable (21041) */ #define SIAGEN_RMP 0x00004000 /* received magic packet (21143) */ #define SIAGEN_LV2 0x00008000 /* LED 2 value (21041) */ #define SIAGEN_HCKR 0x00008000 /* hacker (21143) */ #define SIAGEN_MD 0x000f0000 /* general purpose mode/data */ #define SIAGEN_LGS0 0x00100000 /* LED/GEP 0 select */ #define SIAGEN_LGS1 0x00200000 /* LED/GEP 1 select */ #define SIAGEN_LGS2 0x00400000 /* LED/GEP 2 select */ #define SIAGEN_LGS3 0x00800000 /* LED/GEP 3 select */ #define SIAGEN_GEI0 0x01000000 /* GEP pin 0 intr enable */ #define SIAGEN_GEI1 0x02000000 /* GEP pin 1 intr enable */ #define SIAGEN_RME 0x04000000 /* receive match enable */ #define SIAGEN_CWE 0x08000000 /* control write enable */ #define SIAGEN_GI0 0x10000000 /* GEP pin 0 interrupt */ #define SIAGEN_GI1 0x20000000 /* GEP pin 1 interrupt */ #define SIAGEN_RMI 0x40000000 /* receive match interrupt */ /* CSR12 - General Purpose Port (21140+). */ #define CSR_GPP TULIP_CSR12 #define GPP_MD 0x000000ff /* general purpose mode/data */ #define GPP_GPC 0x00000100 /* general purpose control */ /* * Digital Semiconductor 21142/21143 registers. */ /* SIA configuration for 10baseT (from the 21143 manual) */ #define SIACONN_21142_10BASET 0x00000001 #define SIATXRX_21142_10BASET 0x00007f3f #define SIAGEN_21142_10BASET 0x00000008 /* SIA configuration for 10baseT full-duplex (from the 21143 manual) */ #define SIACONN_21142_10BASET_FDX 0x00000001 #define SIATXRX_21142_10BASET_FDX 0x00007f3d #define SIAGEN_21142_10BASET_FDX 0x00000008 /* SIA configuration for 10base5 (from the 21143 manual) */ #define SIACONN_21142_AUI 0x00000009 #define SIATXRX_21142_AUI 0x00004705 #define SIAGEN_21142_AUI 0x0000000e /* SIA configuration for 10base2 (from the 21143 manual) */ #define SIACONN_21142_BNC 0x00000009 #define SIATXRX_21142_BNC 0x00004705 #define SIAGEN_21142_BNC 0x00000006 #endif /* _DEV_IC_TULIPREG_H_ */ ================================================ FILE: src/DMA.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DMA.hpp" #include "AliM1543C.hpp" #include "PCIDevice.hpp" #include "StdAfx.hpp" #include "System.hpp" #define DEBUG_DMA CDMA *theDMA = 0; /** * Constructor. **/ CDMA::CDMA(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { int i; // DMA Setup #define LEGACY_IO(id, port, size) \ c->RegisterMemory(this, id, U64(0x00000801fc000000) + port, size) LEGACY_IO(DMA0_IO_CHANNEL, 0x00, 8); LEGACY_IO(DMA0_IO_MAIN, 0x08, 8); LEGACY_IO(DMA_IO_LPAGE, 0x81, 11); LEGACY_IO(DMA1_IO_CHANNEL, 0xc0, 16); LEGACY_IO(DMA1_IO_MAIN, 0xd0, 16); LEGACY_IO(DMA_IO_HPAGE, 0x481, 11); LEGACY_IO(DMA0_IO_EXT, 0x040b, 1); LEGACY_IO(DMA1_IO_EXT, 0x04D6, 1); for (i = 0; i < 8; i++) { state.channel[i].c_lobyte = true; state.channel[i].a_lobyte = true; } state.controller[0].mask = 0xff; state.controller[1].mask = 0xff; theDMA = this; printf("dma: $Id: DMA.cpp,v 1.9 2008/04/29 09:24:52 iamcamiel Exp $\n"); } /** * Destructor. **/ CDMA::~CDMA() {} int CDMA::DoClock() { return 0; } const char *dma_index_names[] = { "DMA0_IO_MAIN", "DMA1_IO_MAIN", "DMA_IO_LPAGE", "DMA_IO_HPAGE", "DMA0_IO_CHANNEL", "DMA1_IO_CHANNEL", "DMA0_IO_EXT", "DMA1_IO_EXT"}; #define DMA_INDEX(n) dma_index_names[n - DMA_IO_BASE] u64 CDMA::ReadMem(int index, u64 address, int dsize) { u64 ret; u8 data = 0; int num; // printf("dma: Readmem %s, %" PRIx64 ", %x\n",DMA_INDEX(index),address, dsize); switch (dsize) { case 32: ret = ReadMem(index, address, 8); ret |= ReadMem(index, address + 1, 8) << 8; ret |= ReadMem(index, address + 2, 8) << 16; ret |= ReadMem(index, address + 3, 8) << 32; return ret; case 16: ret = ReadMem(index, address, 8); ret |= ReadMem(index, address + 1, 8) << 8; return ret; case 8: if (index == DMA1_IO_CHANNEL || index == DMA1_IO_MAIN) address >>= 1; switch (index) { case DMA0_IO_CHANNEL: case DMA1_IO_CHANNEL: num = ((address & 0x0e) >> 1) + (index * 4); if (address & 1) { // word count registers data = (state.channel[num].count >> (state.channel[num].c_lobyte ? 8 : 0)) & 0xff; state.channel[num].c_lobyte = !state.channel[num].c_lobyte; } else { // base address data = (state.channel[num].current >> (state.channel[num].a_lobyte ? 8 : 0)) & 0xff; state.channel[num].a_lobyte = !state.channel[num].a_lobyte; } break; case DMA0_IO_MAIN: case DMA1_IO_MAIN: num = ((address & 0x0e) >> 1) + ((index - DMA_IO_BASE) * 4); printf("num: %d\n", num); for (int i = 0; i < 4; i++) data |= ((state.channel[(num * 4) + i].count == state.channel[(num * 4) + 1].current) ? 1 : 0) << i; data |= (state.controller[num].request & 0x0f) << 4; break; default: FAILURE(InvalidArgument, "dma: ReadMem index out of range"); } #if defined(DEBUG_DMA) printf("dma: read %s,%" PRIx64 ": %" PRIx8 ". \n", DMA_INDEX(index), address, data); #endif } return data; } void CDMA::WriteMem(int index, u64 address, int dsize, u64 data) { int num = 0; int channelmap[] = {2, 3, 1, 0xff, 0xff, 0xff, 0, 0xff, 6, 7, 5, 0xff, 0xff, 0xff, 4}; switch (dsize) { case 32: WriteMem(index, address + 0, 8, (data >> 0) & 0xff); WriteMem(index, address + 1, 8, (data >> 8) & 0xff); WriteMem(index, address + 2, 8, (data >> 16) & 0xff); WriteMem(index, address + 3, 8, (data >> 24) & 0xff); return; case 16: WriteMem(index, address + 0, 8, (data >> 0) & 0xff); WriteMem(index, address + 1, 8, (data >> 8) & 0xff); return; case 8: data &= 0xff; if (index == DMA1_IO_CHANNEL || index == DMA1_IO_MAIN) address >>= 1; #if defined(DEBUG_DMA) // printf("dma: write %s, %02x: %02x. \n", DMA_INDEX(index), // (u32)address, data); #endif switch (index) { case DMA1_IO_CHANNEL: num = 1; case DMA0_IO_CHANNEL: num = ((address & 0x0e) >> 1) + (num * 4); if (address & 1) { if (state.channel[num].c_lobyte) state.channel[num].count = (state.channel[num].count & 0xff00) | data; else state.channel[num].count = (state.channel[num].count & 0xff) | (data << 8); state.channel[num].c_lobyte = !state.channel[num].c_lobyte; #if defined(DEBUG_DMA) printf("dma channel %d count: %04x\n", num, state.channel[num].count); #endif } else { if (state.channel[num].a_lobyte) state.channel[num].base = (state.channel[num].base & 0xff00) | data; else state.channel[num].base = (state.channel[num].base & 0xff) | (data << 8); state.channel[num].a_lobyte = !state.channel[num].a_lobyte; #if defined(DEBUG_DMA) printf("dma channel %d base: %04x\n", num, state.channel[num].count); #endif } break; case DMA1_IO_MAIN: num = 1; case DMA0_IO_MAIN: switch (address) { case 0: // command printf("dma: command register %d written with %" PRIx64 "\n", num, data); state.controller[num].command = data; break; case 1: // request set_request(num, data & 0x03, (data & 0x04) >> 2); break; case 2: // single mask printf("dma: mask single on %d : %" PRIx64 " %s\n", num, data & 0x03, data & 0x4 ? "Masked" : "Unmasked"); state.controller[num].mask = (state.controller[num].mask & ~(1 << (data & 0x03))) | ((data & 0x04) >> 2); printf(" Mask status: %x\n", state.controller[num].mask); do_dma(); break; case 3: // mode register printf("dma: mode register %d for channel %" PRIx64 " written with %" PRIx64 "\n", num, (num * 4) + (data & 0x03), data); printf(" Mode: %s, Address %s, Autoinit %s, Command: %s\n", (data & 0x80 ? (data & 0x40 ? "Cascade" : "Block") : (data & 0x40 ? "Single" : "Demand")), (data & 0x20 ? "Increment" : "Decrement"), (data & 0x10 ? "Enable" : "Disable"), (data & 0x08 ? (data & 0x04 ? "Illegal" : "Read") : (data & 0x04 ? "Write" : "Verify"))); state.channel[(num * 4) + (data & 0x03)].mode = data; break; case 4: // clear flipflop(s) printf("dma: flipflops cleared for dma %d\n", num); for (int i = (num * 4); i < ((num + 1) * 4); i++) state.channel[i].a_lobyte = state.channel[i].c_lobyte = true; break; case 5: // master reset #if defined(DEBUG_DMA) printf("DMA-I-RESET: DMA %d reset.", index - DMA_IO_BASE); #endif for (int i = (num * 4); i < ((num + 1) * 4); i++) state.channel[i].a_lobyte = state.channel[i].c_lobyte = true; state.controller[num].mask = 0xff; break; case 6: // master enable state.controller[num].mask = 0x00; do_dma(); break; case 7: // master mask state.controller[num].mask = data; do_dma(); break; } break; case DMA_IO_LPAGE: case DMA_IO_HPAGE: if (channelmap[address] == 0xff) { printf("dma: unknown page register %" PRIx64 "\n", address); return; } num = channelmap[address]; if (index == DMA_IO_LPAGE) state.channel[num].pagebase = (state.channel[num].pagebase & 0xff00) | data; else state.channel[num].pagebase = (state.channel[num].pagebase & 0xff) | (data << 8); #if defined(DEBUG_DMA) printf("dma channel %d pagebase: %04x\n", num, state.channel[num].pagebase); #endif break; case DMA0_IO_EXT: case DMA1_IO_EXT: printf("dma: extended mode register %d written: %" PRIx64 "\n", index - DMA0_IO_EXT, data); break; default: FAILURE(InvalidArgument, "dma: WriteMem index out of range"); } return; } } static u32 dma_magic1 = 0x65324387; static u32 dma_magic2 = 0x24092875; /** * Save state to a Virtual Machine State file. **/ int CDMA::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&dma_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&dma_magic2, sizeof(u32), 1, f); printf("dma: %ld bytes saved.\n", ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CDMA::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("dma: unexpected end of file!\n"); return -1; } if (m1 != dma_magic1) { printf("dma: MAGIC 1 does not match!\n"); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("dma: unexpected end of file!\n"); return -1; } if (ss != sizeof(state)) { printf("dma: STRUCT SIZE does not match!\n"); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("dma: unexpected end of file!\n"); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("dma: unexpected end of file!\n"); return -1; } if (m2 != dma_magic2) { printf("dma: MAGIC 1 does not match!\n"); return -1; } printf("dma: %ld bytes restored.\n", ss); return 0; } /** * Set the software request bit for a channel, and initiate DMA **/ void CDMA::set_request(int num, int channel, int data) { state.controller[num].request = (state.controller[num].request & ~(1 << (data & 0x03))) | ((data & 0x04) >> 2); state.channel[(num * 4) + (data & 0x03)].current = 0; do_dma(); } /** * Perform a DMA if one is ready. * * \todo I'm not sure what would actually trigger this, so its mostly just a * placeholder. **/ void CDMA::do_dma() { for (int ctrlr = 0; ctrlr < 2; ctrlr++) { if ((state.controller[ctrlr].command & 0x04) == 0) // controller not disabled. { for (int chnl = 0; chnl < 4; chnl++) { if ((state.controller[ctrlr].mask & (1 << chnl)) == 0) // channel not masked { if (state.controller[ctrlr].request & (1 << chnl)) // channel has request { // Do it! } } } } } } /** * This can be called by a device to perform a DMA in one fell swoop. **/ void CDMA::send_data(int channel, void *data) { if ((state.controller[channel < 4 ? 0 : 1].command & 0x04) == 0) { if ((state.controller[channel < 4 ? 0 : 1].mask & (1 << channel)) == 0) { u64 addr = (state.channel[channel].pagebase << 16) + state.channel[channel].base; int count = get_count(channel); printf("DMA send_data: %x @ %16" PRIx64 "x\n ", count, addr); for (int i = 0; i < count; i++) { printf("%02x ", *((char *)data + i) & 0xff); if (i % 16 == 15) printf("\n "); } printf("\n"); // increment theAli->do_pci_write(addr, data, 1, count); // set the terminal count bit if (channel < 4) state.controller[0].status |= 1 << channel; else state.controller[1].status |= 1 << channel; } else { printf("dma: dma requested by device on channel %d, but it is masked.\n", channel); } } else { printf("dma: dma requested by device, but controller %d is disabled.\n", channel < 4 ? 0 : 1); } } void CDMA::recv_data(int channel, void *data) {} ================================================ FILE: src/DMA.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_DMA_H) #define INCLUDED_DMA_H #include "SystemComponent.hpp" /** * \brief Emulated DMA controller. **/ class CDMA : public CSystemComponent { public: CDMA(CConfigurator *cfg, CSystem *c); virtual ~CDMA(); virtual int DoClock(); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u64 ReadMem(int index, u64 address, int dsize); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void set_request(int index, int channel, int data); void send_data(int channel, void *data); void recv_data(int channel, void *data); int get_count(int channel) { return state.channel[channel].count; }; private: void do_dma(); /// The state structure contains all elements that need to be saved to the /// statefile. struct SDMA_state { /// DMA channel state struct SDMA_chan { bool a_lobyte; // address lobyte expected bool c_lobyte; // count lobyte expected u16 current; u16 base; u16 pagebase; u16 count; u8 mode; } channel[8]; /// DMA controller state struct SDMA_ctrl { u8 status; u8 command; u8 request; u8 mask; } controller[2]; } state; }; #define DMA_IO_BASE 0x1000 #define DMA0_IO_MAIN DMA_IO_BASE + 0 #define DMA1_IO_MAIN DMA_IO_BASE + 1 #define DMA_IO_LPAGE DMA_IO_BASE + 2 #define DMA_IO_HPAGE DMA_IO_BASE + 3 #define DMA0_IO_CHANNEL DMA_IO_BASE + 4 #define DMA1_IO_CHANNEL DMA_IO_BASE + 5 #define DMA0_IO_EXT DMA_IO_BASE + 6 #define DMA1_IO_EXT DMA_IO_BASE + 7 extern CDMA *theDMA; #endif // !defined(INCLUDED_DMA_H) ================================================ FILE: src/DPR.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DPR.hpp" #include "AlphaCPU.hpp" #include "Serial.hpp" #include "StdAfx.hpp" #include "System.hpp" #include #define ToBCD(x) (((x) / 10 << 4) | ((x) % 10)) extern CSerial *srl[2]; /** * Constructor. **/ CDPR::CDPR(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { if (theDPR) FAILURE(Configuration, "More than one DPR"); theDPR = this; c->RegisterMemory(this, 0, U64(0x0000080110000000), 0x100000); // 16KB } /** * Initialize the DPR device. **/ void CDPR::init() { int i; memset(&state, 0, sizeof(state)); RestoreStateF(); for (i = 0; i < cSystem->get_cpu_num(); i++) { state.ram[i * 0x20 + 0x00] = 1; // EV6 BIST state.ram[i * 0x20 + 0x01] = (i == 0) ? 0x80 : i; // SROM status state.ram[i * 0x20 + 0x02] = 1; // STR status state.ram[i * 0x20 + 0x03] = 1; // CSC status state.ram[i * 0x20 + 0x04] = 1; // Pchip0 status state.ram[i * 0x20 + 0x05] = 1; // Pchip1 status state.ram[i * 0x20 + 0x06] = 1; // DIMx status state.ram[i * 0x20 + 0x07] = 1; // TIG bus status state.ram[i * 0x20 + 0x08] = 0xdd; // DPR test started state.ram[i * 0x20 + 0x09] = 1; // DPR status state.ram[i * 0x20 + 0x0a] = 0xff; // CPU speed status state.ram[i * 0x20 + 0x0b] = (cSystem->get_cpu(i)->get_speed() / 1000000) % 256; // speed state.ram[i * 0x20 + 0x0c] = (cSystem->get_cpu(i)->get_speed() / 1000000) / 256; // speed // powerup time BCD: time_t now = time(NULL); // Check for absolute time override at system level char *faketime = myCfg->get_text_value("time"); if (faketime) { struct tm ft; memset(&ft, 0, sizeof(ft)); ft.tm_isdst = -1; if (sscanf(faketime, "%d-%d-%d %d:%d:%d", &ft.tm_year, &ft.tm_mon, &ft.tm_mday, &ft.tm_hour, &ft.tm_min, &ft.tm_sec) >= 3) { ft.tm_year -= 1900; ft.tm_mon -= 1; now = mktime(&ft); } } struct tm *t = localtime(&now); state.ram[i * 0x20 + 0x10] = ToBCD(t->tm_hour); state.ram[i * 0x20 + 0x11] = ToBCD(t->tm_min); state.ram[i * 0x20 + 0x12] = ToBCD(t->tm_sec); state.ram[i * 0x20 + 0x13] = ToBCD(t->tm_mday); state.ram[i * 0x20 + 0x14] = ToBCD(t->tm_mon + 1); state.ram[i * 0x20 + 0x15] = ToBCD(t->tm_year - 100); // tm_year is based on 1900 #if defined(DEBUG_DPR) printf("%%DPR-I-BOOTDATE: %02x-%02x-%02x, %02x:%02x:%02x\n", state.ram[i * 0x20 + 21], state.ram[i * 0x20 + 20], state.ram[i * 0x20 + 19], state.ram[i * 0x20 + 16], state.ram[i * 0x20 + 17], state.ram[i * 0x20 + 18]); #endif state.ram[i * 0x20 + 0x16] = 0; // no error state.ram[i * 0x20 + 0x1e] = 0x80; // CPU SROM sync moet 0x80 zijn; anders --> cpu0 startup failure state.ram[i * 0x20 + 0x1f] = 8; // cach size in MB } state.ram[0xda] = 0xaa; // TIG load // DIMM config state.ram[0x80] = 0xf0; // twice-split 8 dimms array 0 state.ram[0x81] = 0x01; // 64 MB // state.ram[0x82] = 0xf1; // twice-split 8 dimms array 1 // state.ram[0x83] = 0x01; // 64 MB // state.ram[0x84] = 0xf2; // twice-split 8 dimms array 2 // state.ram[0x85] = 0x01; // 64 MB // state.ram[0x86] = 0xf3; // twice-split 8 dimms array 3 // state.ram[0x87] = 0x01; // 64 MB // powerup failure bits state.ram[0x88] = 0; // each bit is one DIMM on MMB0 state.ram[0x89] = 0x00; // MMB1 state.ram[0x8a] = 0x00; // MMB2 state.ram[0x8b] = 0x00; // MMB3 // misconfigured DIMM bits state.ram[0x8c] = 0; // each bit is one DIMM on MMB0 state.ram[0x8d] = 0; // MMB1 state.ram[0x8e] = 0; // MMB2 state.ram[0x8f] = 0; // MMB3 state.ram[0x90] = 0xff; // psu / vterm present state.ram[0x91] = 0x00; // psu ok bits state.ram[0x92] = 0x07; // ac inputs valid state.ram[0x93] = 0x25; // cpu 0 temp in C state.ram[0x94] = 0x25; // cpu 1 temp in C state.ram[0x95] = 0x25; // cpu 2 temp in C state.ram[0x96] = 0x25; // cpu 3 temp in C state.ram[0x97] = 0x25; // pci 0 temp in C state.ram[0x98] = 0x25; // pci 1 temp in C state.ram[0x99] = 0x25; // pci 2 temp in C state.ram[0x9a] = 0x8b; // fan 0 speed state.ram[0x9b] = 0x8b; // fan 1 speed state.ram[0x9c] = 0x8b; // fan 2 speed state.ram[0x9d] = 0x8b; // fan 3 speed state.ram[0x9e] = 0x8b; // fan 4 speed state.ram[0x9f] = 0x8b; // fan 5 speed // vector 680 info (various faults) for (i = 0xa0; i < 0xaa; i++) state.ram[i] = 0; state.ram[0xaa] = 0x00; // fans good // RMC read failure DIMM bits state.ram[0xab] = 0; // each bit is one DIMM on MMB0 state.ram[0xac] = 0xff; // MMB1 state.ram[0xad] = 0xff; // MMB2 state.ram[0xae] = 0xff; // MMB3 switch (cSystem->get_cpu_num()) { case 1: state.ram[0xaf] = 0x0e; // all MMB I2C's read + CPU 0 break; case 2: state.ram[0xaf] = 0x0c; // all MMB I2C's read + CPU 0 break; case 3: state.ram[0xaf] = 0x08; // all MMB I2C's read + CPU 0 break; case 4: state.ram[0xaf] = 0x00; // all MMB I2C's read + CPU 0 break; } state.ram[0xb0] = 0x00; // PCI i2c read state.ram[0xb1] = 0x00; // mainboard i2c read state.ram[0xb2] = 0x00; // psu's and scsi backplanes i2c read state.ram[0xba] = 0xba; // i2c finished state.ram[0xbb] = 0x00; // rmc error state.ram[0xbc] = 0x00; // rmc flash update error status // 680 fatal registers state.ram[0xbd] = 0x07; // ac inputs valid state.ram[0xbe] = 0; // faults state.ram[0xbf] = 0; // faults state.ram[0xda] = 0xaa; // tig load success // Power-supplies state.ram[0xdb] = 0xf4; // PS0 id state.ram[0xdc] = 0x45; // 3.3v current state.ram[0xdd] = 0x51; // 5.0v current state.ram[0xde] = 0x37; // 12v current state.ram[0xdf] = 0x8b; // fan speed state.ram[0xe0] = 0xd6; // ac voltage (230v) state.ram[0xe1] = 0x49; // internal temp. (56 C) state.ram[0xe2] = 0x4b; // inlet temp. (20 C) state.ram[0xe4] = 0xf5; // PS1 id state.ram[0xe5] = 0x45; // 3.3v current state.ram[0xe6] = 0x51; // 5.0v current state.ram[0xe7] = 0x37; // 12v current state.ram[0xe8] = 0x8b; // fan speed state.ram[0xe9] = 0xd6; // ac voltage (230v) state.ram[0xea] = 0x49; // internal temp. (56 C) state.ram[0xeb] = 0x4b; // inlet temp. (20 C) state.ram[0xed] = 0xf6; // PS2 id state.ram[0xee] = 0x45; // 3.3v current state.ram[0xef] = 0x51; // 5.0v current state.ram[0xf0] = 0x37; // 12v current state.ram[0xf1] = 0x8b; // fan speed state.ram[0xf2] = 0xd6; // ac voltage (230v) state.ram[0xf3] = 0x49; // internal temp. (56 C) state.ram[0xf4] = 0x4b; // inlet temp. (20 C) // EEROMs /* 100: MMB0 DIMM 2 200: MMB0 DIMM 3 300: MMB0 DIMM 4 400: MMB0 DIMM 5 500: MMB0 DIMM 6 600: MMB0 DIMM 7 700: MMB0 DIMM 8 800: MMB0 DIMM 1 900: MMB1 DIMM 2 a00: MMB1 DIMM 3 b00: MMB1 DIMM 4 c00: MMB1 DIMM 5 d00: MMB1 DIMM 6 e00: MMB1 DIMM 7 f00: MMB1 DIMM 8 1000: MMB1 DIMM 1 1100: MMB2 DIMM 2 1200: MMB2 DIMM 3 1300: MMB2 DIMM 4 1400: MMB2 DIMM 5 1500: MMB2 DIMM 6 1600: MMB2 DIMM 7 1700: MMB2 DIMM 8 1800: MMB2 DIMM 1 1900: MMB3 DIMM 2 1a00: MMB3 DIMM 3 1b00: MMB3 DIMM 4 1c00: MMB3 DIMM 5 1d00: MMB3 DIMM 6 1e00: MMB3 DIMM 7 1f00: MMB3 DIMM 8 2000: MMB3 DIMM 1 2100: CPU0 2200: CPU1 2300: CPU2 2400: CPU3 2500: MMB0 2600: MMB1 2700: MMB2 2800: MMB3 2900: CPB (PCI backplane) 2a00: CSB (motherboard) 3100: PSU0 cont @ 3d00 3200: PSU1 cont @ 3e00 3300: PSU2 cont @ 3f00 3b00: SCSI0 (backplane) 3c00: SCSI1 2B00:2BFF RMC Last EV6 Correctable Error ASCII character string that indicates correctable error occurred, type, FRU, and so on. 2C00:2CFF RMC Last Redundant Failure ASCII character string that indicates redundant failure occurred, type, FRU, and so on. 2D00:2DFF RMC Last System Failure ASCII character string that indicates system failure occurred, type, FRU, and so on. 2E00:2FFF RMC Uncorrectable machine logout frame (512 bytes) */ // 3000:3008 SROM Version (ASCII string) state.ram[0x3000] = 'V'; state.ram[0x3001] = '2'; state.ram[0x3002] = '.'; state.ram[0x3003] = '2'; state.ram[0x3004] = '2'; state.ram[0x3005] = 'G'; state.ram[0x3006] = 0; state.ram[0x3007] = 0; state.ram[0x3008] = 0; // 3009:300B RMC Rev Level of RMC first byte is letter Rev [x/t/v] // second 2 bytes are major/minor. // This is the rev level of the RMC on-chip code. state.ram[0x3009] = 'V'; state.ram[0x300a] = 0x31; state.ram[0x300b] = 0x30; // 300C:300E RMC Rev Level of RMC first byte is letter Rev [x/t/v] // second 2 bytes are major/minor. // This is the rev level of the RMC flash code. state.ram[0x300c] = 'V'; state.ram[0x300d] = 0x31; state.ram[0x300e] = 0x30; // 300F:3010 300F RMC Revision Field of the DPR Structure // 3400 SROM Size of Bcache in MB state.ram[0x3400] = 8; // 3401 SROM Flash SROM is valid flag; 8 = valid,0 = invalid state.ram[0x3401] = 8; // 3402 SROM System's errors determined by SROM state.ram[0x3402] = 0; for (i = 0; i < cSystem->get_cpu_num(); i++) { state.ram[0x3418 + 0x10 * i] = 0xff; // 3410:3417 SROM/SRM Jump to address for CPU0 // 3418 SROM/SRM Waiting to jump to flag for CPU0 // 3419 SROM Shadow of value written to EV6 DC_CTL register. // 341A:341E SROM Shadow of most recent writes to EV6 CBOX "Write-many" // chain. } // 34A0:34A7 SROM Array 0 to DIMM ID translation // Bits<4:0> // Bits<7:5> // 0 = Exists, No Error Bits <2:0> = // 1 = Expected Missing DIMM + 1 (1-8) // 2 = Error - Missing DIMM(s) Bits <4:3> = // 4 = Error - Illegal MMB (0-3) DIMM(s) // 6 = Error - Incompatible DIMM(s) // 34A8:34AF SROM Repeat for Array 1 of Array 0 34A0:34A7 // 34B0:34B7 SROM Repeat for Array 2 of Array 0 34A0:34A7 // 34B8:34CF SROM Repeat for Array 3 of Array 0 34A0:34A7 for (i = 0; i < 0x20; i++) state.ram[0x34a0 + i] = i; // 34C0:34FF Used as scratch area for SROM // 3500:35FF Used as the dedicated buffer in which SRM writes OCP or // FRU EEROM data. // Firmware will write this data, RMC will only // read this data. // 3600:36FF 3600 SRM Reserved // 3700:37FF SRM Reserved // 3800:3AFF RMC RMC scratch space printf("%s: $Id: DPR.cpp,v 1.23 2008/06/12 07:29:44 iamcamiel Exp $\n", devid_string); } /** * Destructor. **/ CDPR::~CDPR() {} u64 CDPR::ReadMem(int index, u64 address, int dsize) { u64 data = 0; int a = (int)(address >> 6); data = state.ram[a]; #if defined(DEBUG_DPR) printf("%%DPR-I-READ: Dual-Port RAM read @ 0x%08x: 0x%02x\n", a, (u32)(data & 0xff)); #endif return data; } void CDPR::WriteMem(int index, u64 address, int dsize, u64 data) { int i; int a = (int)(address >> 6); #if defined(DEBUG_DPR) printf("%%DPR-I-WRITE: Dual-Port RAM write 0x%08x 0x%02x:\n", a, (u32)(data & 0xff)); #endif // FOR COMMANDS: // // 0xf9: buffer size // 0xfb:fa qualifier / address // 0xfc: completion code (0 = ok, 80 = error, 81 = invalid code, 82 = // invalid qualifier) 0xfd: rmc command id for response 0xfe: command // code 0xff: rmc command id for command COMMANDS: 01: update // EEPROM 02: update baud rate 03: write to OCP F0: update RMC // flash state.ram[a] = (char)data; switch (a) { case 0xff: // command state.ram[0xfd] = state.ram[0xff]; switch (state.ram[0xfe]) { case 1: /* 100: MMB0 DIMM 2 200: MMB0 DIMM 3 300: MMB0 DIMM 4 400: MMB0 DIMM 5 500: MMB0 DIMM 6 600: MMB0 DIMM 7 700: MMB0 DIMM 8 800: MMB0 DIMM 1 900: MMB1 DIMM 2 a00: MMB1 DIMM 3 b00: MMB1 DIMM 4 c00: MMB1 DIMM 5 d00: MMB1 DIMM 6 e00: MMB1 DIMM 7 f00: MMB1 DIMM 8 1000: MMB1 DIMM 1 1100: MMB2 DIMM 2 1200: MMB2 DIMM 3 1300: MMB2 DIMM 4 1400: MMB2 DIMM 5 1500: MMB2 DIMM 6 1600: MMB2 DIMM 7 1700: MMB2 DIMM 8 1800: MMB2 DIMM 1 1900: MMB3 DIMM 2 1a00: MMB3 DIMM 3 1b00: MMB3 DIMM 4 1c00: MMB3 DIMM 5 1d00: MMB3 DIMM 6 1e00: MMB3 DIMM 7 1f00: MMB3 DIMM 8 2000: MMB3 DIMM 1 2100: CPU0 2200: CPU1 2300: CPU2 2400: CPU3 2500: MMB0 2600: MMB1 2700: MMB2 2800: MMB3 2900: CPB (PCI backplane) 2a00: CSB (motherboard) 3100: PSU0 cont @ 3d00 3200: PSU1 cont @ 3e00 3300: PSU2 cont @ 3f00 3b00: SCSI0 (backplane) 3c00: SCSI1 */ // FRU-Write switch (state.ram[0xfb]) { case 0x21: case 0x22: case 0x23: case 0x24: if ((state.ram[0xfb] - 0x20) > cSystem->get_cpu_num()) { state.ram[0xfc] = 0x80; break; } case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x31: case 0x32: case 0x33: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: for (i = 0; i < state.ram[0xf9] + 1; i++) { state.ram[state.ram[0xfb] * 0x100 + state.ram[0xfa] + i] = state.ram[0x3500 + state.ram[0xfa] + i]; #if defined(DEBUG_DPR) printf("%%DPR-I-FRU: FRU data %02x @ FRU %02x set to %02x\n", state.ram[0xfa] + i, state.ram[0xfb], state.ram[0x3500 + state.ram[0xfa] + i]); #endif } state.ram[0xfc] = 0; break; default: #if defined(DEBUG_DPR) printf("%%DPR-I-RMC: RMC Command given: %02x\r\n", state.ram[0xfe]); printf("%%DPR-I-RMC: f9:%02x fb-fa:%02x%02x\r\n", state.ram[0xf9], state.ram[0xfb], state.ram[0xfa]); #endif state.ram[0xfc] = 0x80; } break; case 2: state.ram[0xfc] = 0; break; case 3: // OCP-Write #if defined(DEBUG_DPR) sprintf(trcbuffer, "%%%%DPR-I-OCP: OCP Text set to \"0123456789abcdef\"\r\n"); memcpy(trcbuffer + 29, &(state.ram[0x3500]), 16); // srl[0]->write(trcbuffer); printf(trcbuffer); #endif state.ram[0xfc] = 0; break; case 0xf0: state.ram[0xfc] = 0; default: #if defined(DEBUG_DPR) printf("%%DPR-I-RMC: RMC Command given: %02x\r\n", state.ram[0xfe]); printf("%%DPR-I-RMC: f9:%02x fb-fa:%02x%02x\r\n", state.ram[0xf9], state.ram[0xfb], state.ram[0xfa]); #endif state.ram[0xfc] = 0x81; } break; case 0xfd: // end of command state.ram[0xff] = state.ram[0xfd]; break; case 0x3428: // start cpu 1 if (cSystem->get_cpu_num() > 1) { printf("*** DPR *** Starting CPU 1 ***\n"); cSystem->get_cpu(1)->set_pc(0x8001); // should come from dpr... cSystem->get_cpu(1)->stop_waiting(); } break; case 0x3438: // start cpu 2 if (cSystem->get_cpu_num() > 2) { printf("*** DPR *** Starting CPU 2 ***\n"); cSystem->get_cpu(2)->set_pc(0x8001); // should come from dpr... cSystem->get_cpu(2)->stop_waiting(); } break; case 0x3448: // start cpu 3 if (cSystem->get_cpu_num() > 3) { printf("*** DPR *** Starting CPU 3 ***\n"); cSystem->get_cpu(3)->set_pc(0x8001); // should come from dpr... cSystem->get_cpu(3)->stop_waiting(); } break; } return; } /** * Save state to a DPR rom file. **/ void CDPR::SaveStateF(char *fn) { FILE *ff; ff = fopen(fn, "wb"); if (ff) { SaveState(ff); fclose(ff); printf("%%DPR-I-SAVEST: DPR state saved to %s\n", fn); } else { printf("%%DPR-F-NOSAVE: DPR could not be saved to %s\n", fn); } } /** * Save state to the default DPR rom file. **/ void CDPR::SaveStateF() { SaveStateF(myCfg->get_text_value("rom.dpr", "dpr.rom")); } /** * Restore state from a DPR rom file. **/ void CDPR::RestoreStateF(char *fn) { FILE *ff; ff = fopen(fn, "rb"); if (ff) { RestoreState(ff); fclose(ff); printf("%%DPR-I-RESTST: DPR state restored from %s\n", fn); } else { printf("%%DPR-F-NOREST: DPR could not be restored from %s\n", fn); } } static u32 dpr_magic1 = 0x18A7B92D; static u32 dpr_magic2 = 0xD29B7A81; /** * Save state to a Virtual Machine State file. **/ int CDPR::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&dpr_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&dpr_magic2, sizeof(u32), 1, f); printf("%s: %ld bytes saved.\n", "dpr", ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CDPR::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", "dpr"); return -1; } if (m1 != dpr_magic1) { printf("%s: MAGIC 1 does not match!\n", "dpr"); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", "dpr"); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", "dpr"); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", "dpr"); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", "dpr"); return -1; } if (m2 != dpr_magic2) { printf("%s: MAGIC 1 does not match!\n", "dpr"); return -1; } printf("%s: %ld bytes restored.\n", "dpr", ss); return 0; } /** * Restore state from the default DPR rom file. **/ void CDPR::RestoreStateF() { RestoreStateF(myCfg->get_text_value("rom.dpr", "dpr.rom")); } CDPR *theDPR = 0; ================================================ FILE: src/DPR.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_DPR_H) #define INCLUDED_DPR_H #include "SystemComponent.hpp" /** * \brief Emulated dual-port RAM and management controller. **/ class CDPR : public CSystemComponent { public: CDPR(CConfigurator *cfg, class CSystem *c); virtual ~CDPR(); virtual void init(); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u64 ReadMem(int index, u64 address, int dsize); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void SaveStateF(); void RestoreStateF(); void SaveStateF(char *fn); void RestoreStateF(char *fn); protected: /// The state structure contains all elements that need to be saved to the /// statefile. struct SDPR_state { u8 ram[16 * 1024]; } state; }; extern CDPR *theDPR; #endif // !defined(INCLUDED_DPR_H_) ================================================ FILE: src/Disk.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Disk.hpp" #include "StdAfx.hpp" /** * \brief Constructor. **/ CDisk::CDisk(CConfigurator *cfg, CSystem *sys, CDiskController *ctrl, int idebus, int idedev) : CSystemComponent(cfg, sys) { char *a; char *b; char *c; char *d; myCfg = cfg; myCtrl = ctrl; myBus = idebus; myDev = idedev; atapi_mode = false; a = myCfg->get_myName(); b = myCfg->get_myValue(); c = myCfg->get_myParent()->get_myName(); d = myCfg->get_myParent()->get_myValue(); free(devid_string); // we override the default to include the controller. CHECK_ALLOCATION(devid_string = (char *)malloc(strlen(a) + strlen(b) + strlen(c) + strlen(d) + 6)); sprintf(devid_string, "%s(%s).%s(%s)", c, d, a, b); serial_number = myCfg->get_text_value("serial_num", "ES40EM00000"); revision_number = myCfg->get_text_value("rev_num", "0.0"); read_only = myCfg->get_bool_value("read_only"); is_cdrom = myCfg->get_bool_value("cdrom"); state.block_size = is_cdrom ? 2048 : 512; state.scsi.sense.available = false; myCtrl->register_disk(this, myBus, myDev); } /** * \brief Destructor. **/ CDisk::~CDisk(void) { free(devid_string); devid_string = nullptr; } /** * \Calculate the number of cylinders to report. **/ void CDisk::calc_cylinders() { cylinders = byte_size / state.block_size / sectors / heads; off_t_large chs_size = sectors * cylinders * heads * state.block_size; if (chs_size < byte_size) cylinders++; } /** * \brief Called when this device is selected. * * Set status fields up to begin a new SCSI command sequence * and set the SCSI bus phase to Message Out. **/ void CDisk::scsi_select_me(int bus) { state.scsi.msgo.written = 0; state.scsi.msgi.available = 0; state.scsi.msgi.read = 0; state.scsi.cmd.written = 0; state.scsi.dati.available = 0; state.scsi.dati.read = 0; state.scsi.dato.expected = 0; state.scsi.dato.written = 0; state.scsi.stat.available = 0; state.scsi.stat.read = 0; state.scsi.lun_selected = false; // state.scsi.disconnect_priv = false; // state.scsi.will_disconnect = false; // state.scsi.disconnected = false; if (atapi_mode) scsi_set_phase(bus, SCSI_PHASE_COMMAND); else scsi_set_phase(bus, SCSI_PHASE_MSG_OUT); } static u32 disk_magic1 = 0xD15D15D1; static u32 disk_magic2 = 0x15D15D5; /** * Save state to a Virtual Machine State file. **/ int CDisk::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&disk_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&disk_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CDisk::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != disk_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != disk_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } // calc_cylinders(); // state.block_size may have changed. determine_layout(); printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * \brief Return the number of bytes expected or available. * * Return the number of bytes we still expect to receive * from the initiator, or still have available for the * initiator, in the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ size_t CDisk::scsi_expected_xfer_me(int bus) { switch (scsi_get_phase(0)) { case SCSI_PHASE_DATA_OUT: return state.scsi.dato.expected - state.scsi.dato.written; case SCSI_PHASE_DATA_IN: return state.scsi.dati.available - state.scsi.dati.read; case SCSI_PHASE_COMMAND: return 256 - state.scsi.cmd.written; case SCSI_PHASE_STATUS: return state.scsi.stat.available - state.scsi.stat.read; case SCSI_PHASE_MSG_OUT: return 256 - state.scsi.msgo.written; case SCSI_PHASE_MSG_IN: return state.scsi.msgi.available - state.scsi.msgi.read; default: FAILURE_2(IllegalState, "%s: transfer requested in phase %d\n", devid_string, scsi_get_phase(0)); } } /** * \brief Return a pointer where the initiator can read or write data. * * Return a pointer to where the initiator can read or write * (the remainder of) our data in the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ void *CDisk::scsi_xfer_ptr_me(int bus, size_t bytes) { void *res = 0; switch (scsi_get_phase(0)) { case SCSI_PHASE_DATA_OUT: res = &(state.scsi.dato.data[state.scsi.dato.written]); state.scsi.dato.written += bytes; break; case SCSI_PHASE_DATA_IN: res = &(state.scsi.dati.data[state.scsi.dati.read]); state.scsi.dati.read += bytes; break; case SCSI_PHASE_COMMAND: res = &(state.scsi.cmd.data[state.scsi.cmd.written]); state.scsi.cmd.written += bytes; break; case SCSI_PHASE_STATUS: res = &(state.scsi.stat.data[state.scsi.stat.read]); state.scsi.stat.read += bytes; break; case SCSI_PHASE_MSG_OUT: res = &(state.scsi.msgo.data[state.scsi.msgo.written]); state.scsi.msgo.written += bytes; break; case SCSI_PHASE_MSG_IN: // if (PT.reselected) //{ // retval = 0x80; // identify // break; //} // if (PT.disconnected) //{ // if (!PT.dati_ptr) // retval = 0x04; // disconnect // else // { // if (state.scsi.msgi.read==0) // { // retval = 0x02; // save data pointer // state.scsi.msgi.read=1; // } // else if (state.scsi.msgi.read==1) // { // retval = 0x04; // disconnect // state.scsi.msgi.read=0; // } // } // break; //} res = &(state.scsi.msgi.data[state.scsi.msgi.read]); state.scsi.msgi.read += bytes; break; default: FAILURE_2(IllegalState, "%s: transfer requested in phase %d\n", devid_string, scsi_get_phase(0)); } return res; } /** * \brief Process data written or read. * * Determine what action (if any) should be taken after a * transfer, and what the next SCSI bus phase should be. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. * * \todo Handle disconnect/reconnect properly. **/ void CDisk::scsi_xfer_done_me(int bus) { int res; int newphase = scsi_get_phase(0); switch (scsi_get_phase(0)) { case SCSI_PHASE_DATA_OUT: if (state.scsi.dato.written < state.scsi.dato.expected) break; res = do_scsi_command(); if (res == 2) FAILURE(IllegalState, "do_command returned 2 after DATA OUT phase"); if (state.scsi.dati.available) newphase = SCSI_PHASE_DATA_IN; else newphase = SCSI_PHASE_STATUS; break; case SCSI_PHASE_DATA_IN: if (state.scsi.dati.read < state.scsi.dati.available) break; newphase = SCSI_PHASE_STATUS; break; case SCSI_PHASE_COMMAND: res = do_scsi_command(); if (res == 2) newphase = SCSI_PHASE_DATA_OUT; else if (state.scsi.dati.available) newphase = SCSI_PHASE_DATA_IN; else newphase = SCSI_PHASE_STATUS; break; case SCSI_PHASE_STATUS: if (state.scsi.stat.read < state.scsi.stat.available) break; if (atapi_mode) { scsi_free(0); return; } newphase = SCSI_PHASE_MSG_IN; break; case SCSI_PHASE_MSG_OUT: newphase = do_scsi_message(); // command break; case SCSI_PHASE_MSG_IN: // if (state.scsi.reselected) //{ // state.scsi.reselected = false; // newphase = state.scsi.disconnect_phase; //} // else if (state.scsi.disconnected) //{ // if (!state.scsi.msgi.read) // newphase = -1; //} if (state.scsi.msgi.read < state.scsi.msgi.available) break; if (state.scsi.cmd.written) { scsi_free(0); return; } else newphase = SCSI_PHASE_COMMAND; break; default: FAILURE_2(IllegalState, "%s: transfer requested in phase %d\n", devid_string, scsi_get_phase(0)); } // if data in and can disconnect... // if (state.phase!=7 && newphase==1 && PT.will_disconnect && // !PT.disconnected) //{ // printf("%s: Disconnecting now...\n",devid_string); // PT.disconnected = true; // PT.disconnect_phase = newphase; // newphase = 7; // msg in //} if (newphase != scsi_get_phase(0)) { // if (newphase==-1) // { // printf("%s: Disconnect. Timer started!\n",devid_string); // // disconnect. generate interrupt? // state.disconnected = 20; // } scsi_set_phase(0, newphase); } // getchar(); } // SCSI commands: #define SCSICMD_TEST_UNIT_READY 0x00 #define SCSICMD_REQUEST_SENSE 0x03 #define SCSICMD_INQUIRY 0x12 #define SCSICMD_READ 0x08 #define SCSICMD_READ_10 0x28 #define SCSICMD_READ_12 0xA8 #define SCSICMD_READ_16 0x88 #define SCSICMD_READ_32 0x7F #define SCSICMD_READ_LONG 0x3E #define SCSICMD_READ_CD 0xBE #define SCSICMD_WRITE 0x0A #define SCSICMD_WRITE_10 0x2A #define SCSICMD_WRITE_12 0xAA #define SCSICMD_WRITE_LONG 0x3F #define SCSICMD_MODE_SELECT 0x15 #define SCSICMD_MODE_SENSE 0x1a #define SCSICMD_START_STOP_UNIT 0x1b #define SCSICMD_PREVENT_ALLOW_REMOVE 0x1e #define SCSICMD_MODE_SENSE_10 0x5a #define SCSICMD_SYNCHRONIZE_CACHE 0x35 // SCSI block device commands: #define SCSIBLOCKCMD_READ_CAPACITY 0x25 // SCSI CD-ROM commands: #define SCSICDROM_READ_SUBCHANNEL 0x42 #define SCSICDROM_READ_TOC 0x43 // SCSI CD-R/RW commands: #define SCSICDRRW_FORMAT 0x04 #define SCSICDRRW_READ_DISC_INFO 0x51 #define SCSICDRRW_READ_TRACK_INFO 0x52 #define SCSICDRRW_RESERVE_TRACK 0x53 #define SCSICDRRW_SEND_OPC_INFO 0x54 #define SCSICDRRW_REPAIR_TRACK 0x58 #define SCSICDRRW_READ_MASTER_CUE 0x59 #define SCSICDRRW_CLOSE_TRACK 0x5b #define SCSICDRRW_READ_BUFFER_CAP 0x5c #define SCSICDRRW_SEND_CUE_SHEET 0x5d #define SCSICDRRW_BLANK 0xa1 // SCSI tape commands: #define SCSICMD_REWIND 0x01 #define SCSICMD_READ_BLOCK_LIMITS 0x05 #define SCSICMD_SPACE 0x11 // SCSI mode pages: #define SCSIMP_VENDOR 0x00 #define SCSIMP_READ_WRITE_ERRREC 0x01 #define SCSIMP_DISCONNECT_RECONNECT 0x02 #define SCSIMP_FORMAT_PARAMS 0x03 #define SCSIMP_RIGID_GEOMETRY 0x04 #define SCSIMP_FLEX_PARAMS 0x05 #define SCSIMP_CACHING 0x08 #define SCSIMP_CDROM_CAP 0x2A #define SCSI_OK 0 #define SCSI_ILL_CMD -1 /* illegal command */ #define SCSI_LBA_RANGE -2 /* LBA out of range */ #define SCSI_TOO_BIG -3 /* Too big for buffer */ void CDisk::do_scsi_error(int errcode) { state.scsi.stat.available = 1; state.scsi.stat.data[0] = 0; state.scsi.stat.read = 0; state.scsi.msgi.available = 1; state.scsi.msgi.data[0] = 0; state.scsi.msgi.read = 0; if (errcode == SCSI_OK) { #if defined(DEBUG_SCSI) printf("%s: Command returns OK status.\n", devid_string); #endif return; } state.scsi.stat.data[0] = 0x02; // check sense state.scsi.sense.data[0] = 0xf0; // error code state.scsi.sense.data[1] = 0x00; // segment number state.scsi.sense.data[3] = 0x00; // info state.scsi.sense.data[4] = 0x00; state.scsi.sense.data[5] = 0x00; state.scsi.sense.data[6] = 0x00; state.scsi.sense.data[7] = 10; // additional sense length state.scsi.sense.data[8] = 0x00; // command specific state.scsi.sense.data[9] = 0x00; state.scsi.sense.data[10] = 0x00; state.scsi.sense.data[11] = 0x00; state.scsi.sense.data[14] = 0x00; // FRU code state.scsi.sense.data[15] = 0x00; // sense key specific state.scsi.sense.data[16] = 0x00; state.scsi.sense.data[17] = 0x00; state.scsi.sense.available = 18; switch (errcode) { case SCSI_ILL_CMD: state.scsi.sense.data[2] = 0x05; // illegal request state.scsi.sense.data[12] = 0x20; // invalid command state.scsi.sense.data[13] = 0x00; #if defined(DEBUG_SCSI) printf("%s: Command returns check sense status (sense: ILLEGAL COMMAND).\n", devid_string); #endif break; case SCSI_LBA_RANGE: state.scsi.sense.data[2] = 0x05; // illegal request state.scsi.sense.data[12] = 0x21; // LBA out of range state.scsi.sense.data[13] = 0x00; #if defined(DEBUG_SCSI) printf( "%s: Command returns check sense status (sense: LBA OUT OF RANGE).\n", devid_string); #endif break; case SCSI_TOO_BIG: state.scsi.sense.data[2] = 0x05; // illegal request state.scsi.sense.data[12] = 0x55; // system resource failure state.scsi.sense.data[13] = 0x00; #if defined(DEBUG_SCSI) printf("%s: Command returns check sense status (sense: SYSTEM RESOURCE " "FAILURE).\n", devid_string); #endif } } /** * Basic algorithm taken from libcdio for converting lba to msf **/ static u32 lba2msf(off_t_large lba) { #define PREGAP_SECTORS 150 #define CD_FRAMES_PER_SEC 75 #define CD_MAX_LSN 450150 #define bin2bcd(x) ((x / 10) << 4) | (x % 10) int m; int s; int f; lba -= PREGAP_SECTORS; if (lba >= -PREGAP_SECTORS) { m = (lba + PREGAP_SECTORS) / (CD_FRAMES_PER_SEC * 60); lba -= m * (CD_FRAMES_PER_SEC * 60); s = (lba + PREGAP_SECTORS) / CD_FRAMES_PER_SEC; lba -= s * CD_FRAMES_PER_SEC; f = lba + PREGAP_SECTORS; } else { m = (lba + CD_MAX_LSN) / (CD_FRAMES_PER_SEC * 60); lba -= m * (CD_FRAMES_PER_SEC * 60); s = (lba + CD_MAX_LSN) / CD_FRAMES_PER_SEC; lba -= s * CD_FRAMES_PER_SEC; f = lba + CD_MAX_LSN; } if (m > 99) m = 99; // printf("m=%d, s=%d, f=%d == m=%x, s=%x, f=%x\n", // m,s,f,bin2bcd(m),bin2bcd(s),bin2bcd(f)); return bin2bcd(m) << 16 | bin2bcd(s) << 8 | bin2bcd(f); } /** * \brief Handle a SCSI command. * * Called when a SCSI command has been received. We parse * the command, and set up the state for the data in or * data out phases. * * If a data out phase is required, we return the value 2 * to indicate this. In that case, do_scsi_command will be * called again once the data out has been received from * the initiator. **/ int CDisk::do_scsi_command() { unsigned int retlen = 0; int q; int pagecode; u32 ofs = 0; #if defined(DEBUG_SCSI) printf("%s: %d-byte command ", devid_string, state.scsi.cmd.written); for (unsigned int x = 0; x < state.scsi.cmd.written; x++) printf("%02x ", state.scsi.cmd.data[x]); printf("\n"); #endif if (state.scsi.cmd.written < 1) return 0; if (state.scsi.cmd.data[1] & 0xe0) { #if defined(DEBUG_SCSI) printf("%s: LUN selected...\n", devid_string); #endif state.scsi.lun_selected = true; } if (state.scsi.lun_selected && state.scsi.cmd.data[0] != SCSICMD_INQUIRY && state.scsi.cmd.data[0] != SCSICMD_REQUEST_SENSE) { FAILURE_1(NotImplemented, "%s: LUN not supported!\n", devid_string); } switch (state.scsi.cmd.data[0]) { case SCSICMD_TEST_UNIT_READY: #if defined(DEBUG_SCSI) printf("%s: TEST UNIT READY.\n", devid_string); #endif // unit is always ready... do_scsi_error(SCSI_OK); break; case SCSICMD_REQUEST_SENSE: #if defined(DEBUG_SCSI) printf("%s: REQUEST SENSE.\n", devid_string); #endif retlen = state.scsi.cmd.data[4]; // FAILURE("Sense requested"); if (!state.scsi.sense.available) { #if defined(DEBUG_SCSI) printf("%s: NO SENSE.\n", devid_string); #endif state.scsi.sense.data[0] = 0xf0; // error code state.scsi.sense.data[1] = 0x00; // segment number state.scsi.sense.data[2] = 0x00; // sense key: no sense state.scsi.sense.data[3] = 0x00; // info state.scsi.sense.data[4] = 0x00; state.scsi.sense.data[5] = 0x00; state.scsi.sense.data[6] = 0x00; state.scsi.sense.data[7] = 10; // additional sense length state.scsi.sense.data[8] = 0x00; // command specific state.scsi.sense.data[9] = 0x00; state.scsi.sense.data[10] = 0x00; state.scsi.sense.data[11] = 0x00; state.scsi.sense.data[12] = 0x00; // additional sense code: no additional sense state.scsi.sense.data[13] = 0x00; // additional qualifier state.scsi.sense.data[14] = 0x00; // FRU code state.scsi.sense.data[15] = 0x00; // sense key specific state.scsi.sense.data[16] = 0x00; state.scsi.sense.data[17] = 0x00; state.scsi.sense.available = 18; } #if defined(DEBUG_SCSI) printf("%s: Returning data: ", devid_string); for (unsigned int x1 = 0; x1 < state.scsi.sense.available; x1++) printf("%02x ", state.scsi.sense.data[x1]); printf("\n"); #endif state.scsi.dati.read = 0; state.scsi.dati.available = retlen; memcpy(state.scsi.dati.data, state.scsi.sense.data, state.scsi.sense.available); for (unsigned int x2 = state.scsi.sense.available; x2 < retlen; x2++) state.scsi.dati.data[x2] = 0; do_scsi_error(SCSI_OK); break; case SCSICMD_INQUIRY: { #if defined(DEBUG_SCSI) printf("%s: INQUIRY.\n", devid_string); #endif if ((state.scsi.cmd.data[1] & 0x1e) != 0x00) { FAILURE_2(NotImplemented, "%s: Don't know how to handle INQUIRY with cmd[1]=0x%02x.\n", devid_string, state.scsi.cmd.data[1]); break; } u8 qual_dev = state.scsi.lun_selected ? 0x7F : (cdrom() ? 0x05 : 0x00); retlen = state.scsi.cmd.data[4]; state.scsi.dati.data[0] = qual_dev; // device type if (state.scsi.cmd.data[1] & 0x01) { // Vital Product Data switch (state.scsi.cmd.data[2]) { case 0x00: // Page 0 is basically a list of page codes supported, so if // any others are added, make sure to insert them in the proper // place and increase the page length. state.scsi.dati.data[1] = 0x00; // page code 0 state.scsi.dati.data[2] = 0x00; // reserved state.scsi.dati.data[3] = 0x02; // page length state.scsi.dati.data[4] = 0x00; // page 0 is supported. state.scsi.dati.data[5] = 0x80; // page 0x80 is supported. break; case 0x80: char serial_number[20]; sprintf(serial_number, "SRL%04x", scsi_initiator_id[0] * 0x0101); // unit serial number page state.scsi.dati.data[1] = 0x80; // page code: 0x80 state.scsi.dati.data[2] = 0x00; // reserved state.scsi.dati.data[3] = (u8)strlen(serial_number); memcpy(&state.scsi.dati.data[4], serial_number, strlen(serial_number)); break; default: #if 1 FAILURE_1(NotImplemented, "Don't know format for vital product data page %02x!!\n", state.scsi.cmd.data[2]); #else state.scsi.dati.data[1] = state.scsi.cmd.data[2]; // page code state.scsi.dati.data[2] = 0x00; // reserved #endif } } else { // Return values: if (retlen < 36) { printf("%s: SCSI inquiry len=%i, <36!\n", devid_string, retlen); retlen = 36; } state.scsi.dati.data[1] = 0; // not removable; state.scsi.dati.data[2] = 0x02; // ANSI scsi 2 state.scsi.dati.data[3] = 0x02; // response format state.scsi.dati.data[4] = 32; // additional length state.scsi.dati.data[5] = 0; // reserved state.scsi.dati.data[6] = 0x04; // reserved state.scsi.dati.data[7] = 0x60; // capabilities // vendor model rev. memcpy(&(state.scsi.dati.data[8]), "DEC RZ58 (C) DEC2000", 28); // Some data is different for CD-ROM drives: if (cdrom()) { state.scsi.dati.data[1] = 0x80; // 0x80 = removable // vendor model rev. memcpy(&(state.scsi.dati.data[8]), "DEC RRD42 (C) DEC 4.5d", 28); } } state.scsi.dati.read = 0; state.scsi.dati.available = retlen; #if defined(DEBUG_SCSI) printf("%s: Returning data: ", devid_string); for (unsigned int x1 = 0; x1 < 36; x1++) printf("%02x ", state.scsi.dati.data[x1]); printf("\n"); #endif do_scsi_error(SCSI_OK); } break; case SCSICMD_START_STOP_UNIT: // TODO: Implement properly // https://github.com/lenticularis39/axpbox/issues/36 do_scsi_error(SCSI_OK); break; case SCSICMD_MODE_SENSE: case SCSICMD_MODE_SENSE_10: #if defined(DEBUG_SCSI) printf("%s: MODE SENSE.\n", devid_string); #endif { int num_blk_desc = 1; if (state.scsi.cmd.data[0] == SCSICMD_MODE_SENSE) { q = 4; retlen = state.scsi.cmd.data[4]; state.scsi.dati.data[0] = retlen; // mode data length state.scsi.dati.data[1] = cdrom() ? 0x01 : 0x00; // medium type (120 mm data for CD-ROM) state.scsi.dati.data[2] = 0x00; // device specific parameter state.scsi.dati.data[3] = 8 * num_blk_desc; // block descriptor length: 1 page (?) } else { q = 8; retlen = state.scsi.cmd.data[7] * 256 + state.scsi.cmd.data[8]; state.scsi.dati.data[0] = (u8)(retlen >> 8); // mode data length state.scsi.dati.data[1] = (u8)retlen; state.scsi.dati.data[2] = cdrom() ? 0x01 : 0x00; // medium type (120 mm data for CD-ROM) state.scsi.dati.data[3] = 0x00; // device specific parameter state.scsi.dati.data[4] = 0x00; // reserved state.scsi.dati.data[5] = 0x00; // reserved state.scsi.dati.data[6] = (u8)( (8 * num_blk_desc) >> 8); // block descriptor length: 1 page (?) state.scsi.dati.data[7] = (u8)(8 * num_blk_desc); } if ((state.scsi.cmd.data[2] & 0xc0) > 0x40) { FAILURE_2(NotImplemented, "%s: mode sense, cmd[2] = 0x%02x.\n", devid_string, state.scsi.cmd.data[2]); } bool changeable = ((state.scsi.cmd.data[2] & 0xc0) == 0x40); // Return data: if (retlen > DATI_BUFSZ) { printf("%s: read too big (%d)\n", devid_string, retlen); do_scsi_error(SCSI_TOO_BIG); break; } state.scsi.dati.read = 0; state.scsi.dati.available = retlen; // Restore size. pagecode = state.scsi.cmd.data[2] & 0x3f; // printf("[ MODE SENSE pagecode=%i ]\n", pagecode); state.scsi.dati.data[q++] = 0x00; // density code state.scsi.dati.data[q++] = 0; // nr of blocks, high (0 = all remaining blocks) state.scsi.dati.data[q++] = 0; // nr of blocks, mid state.scsi.dati.data[q++] = 0; // nr of blocks, low state.scsi.dati.data[q++] = 0x00; // reserved state.scsi.dati.data[q++] = (u8)(get_block_size() >> 16) & 255; state.scsi.dati.data[q++] = (u8)(get_block_size() >> 8) & 255; state.scsi.dati.data[q++] = (u8)(get_block_size() >> 0) & 255; for (unsigned int x1 = q; x1 < retlen; x1++) state.scsi.dati.data[x1] = 0; do_scsi_error(SCSI_OK); // descriptors, 8 bytes (each) // page, n bytes (each) switch (pagecode) { case SCSIMP_VENDOR: // vendor specific // TODO: Nothing here? break; case SCSIMP_READ_WRITE_ERRREC: // read-write error recovery page state.scsi.dati.data[q + 0] = pagecode; state.scsi.dati.data[q + 1] = 10; break; case SCSIMP_FORMAT_PARAMS: // format device page state.scsi.dati.data[q + 0] = pagecode; state.scsi.dati.data[q + 1] = 22; if (!changeable) { // 10,11 = sectors per track state.scsi.dati.data[q + 10] = 0; state.scsi.dati.data[q + 11] = (u8)get_sectors(); // 12,13 = physical sector size state.scsi.dati.data[q + 12] = (u8)(get_block_size() >> 8) & 255; state.scsi.dati.data[q + 13] = (u8)(get_block_size() >> 0) & 255; } break; case SCSIMP_RIGID_GEOMETRY: // rigid disk geometry page state.scsi.dati.data[q + 0] = pagecode; state.scsi.dati.data[q + 1] = 22; if (!changeable) { state.scsi.dati.data[q + 2] = (u8)(get_cylinders() >> 16) & 255; state.scsi.dati.data[q + 3] = (u8)(get_cylinders() >> 8) & 255; state.scsi.dati.data[q + 4] = (u8)get_cylinders() & 255; state.scsi.dati.data[q + 5] = (u8)get_heads(); // rpms state.scsi.dati.data[q + 20] = (7200 >> 8) & 255; state.scsi.dati.data[q + 21] = 7200 & 255; } break; case SCSIMP_FLEX_PARAMS: // flexible disk page if (cdrom()) { FAILURE_1(NotImplemented, "%s: CD-ROM write parameter page not implemented.\n", devid_string); } state.scsi.dati.data[q + 0] = pagecode; state.scsi.dati.data[q + 1] = 0x1e; // length if (!changeable) { // 2,3 = transfer rate state.scsi.dati.data[q + 2] = ((5000) >> 8) & 255; state.scsi.dati.data[q + 3] = (5000) & 255; state.scsi.dati.data[q + 4] = (u8)get_heads(); state.scsi.dati.data[q + 5] = (u8)get_sectors(); // 6,7 = data bytes per sector state.scsi.dati.data[q + 6] = (u8)(get_block_size() >> 8) & 255; state.scsi.dati.data[q + 7] = (u8)(get_block_size() >> 0) & 255; state.scsi.dati.data[q + 8] = (u8)(get_cylinders() >> 8) & 255; state.scsi.dati.data[q + 9] = (u8)get_cylinders() & 255; // rpms state.scsi.dati.data[q + 28] = (7200 >> 8) & 255; state.scsi.dati.data[q + 29] = 7200 & 255; } break; case SCSIMP_CACHING: // Caching page state.scsi.dati.data[q + 0] = pagecode; // page code state.scsi.dati.data[q + 1] = 0x12; // page length if (!changeable) { // 2 = IC,ABPF,CAP,DISC,SIZE,WCE,MF,RCD // | | | | | | | +- read cache disable (0=no) // | | | | | | +---- multiplication factor (0=block) // | | | | | +-------- write cache enable (0=no cache) // | | | | +------------- use cache segment size (0=no) // | | | +------------------ prefetch across cyls (1=yes) // | | +---------------------- cache analysis (0=drive) // | +--------------------------- abort prefetch (1=abrt on cmd) // +------------------------------ initiator control (0=drive) state.scsi.dati.data[q + 2] = 0x0a; state.scsi.dati.data[q + 3] = 0; // read/write cache retention state.scsi.dati.data[q + 4] = 0x00; // disable prefetch state.scsi.dati.data[q + 5] = 0x00; // for req's greater than this state.scsi.dati.data[q + 6] = 0; // minimum prefetch state.scsi.dati.data[q + 7] = 0; state.scsi.dati.data[q + 8] = 0; // maximum prefetch state.scsi.dati.data[q + 9] = 0; state.scsi.dati.data[q + 10] = 0; // maximum prefetch ceiling state.scsi.dati.data[q + 11] = 0; state.scsi.dati.data[q + 12] = 0; state.scsi.dati.data[q + 13] = 0; // # cache segments state.scsi.dati.data[q + 14] = 0; // cache segement size state.scsi.dati.data[q + 15] = 0; state.scsi.dati.data[q + 16] = 0; // reserved state.scsi.dati.data[q + 17] = 0; // non-cache segement size state.scsi.dati.data[q + 18] = 0; state.scsi.dati.data[q + 19] = 0; } break; case SCSIMP_CDROM_CAP: // CD-ROM capabilities state.scsi.dati.data[q + 0] = pagecode; state.scsi.dati.data[q + 1] = 0x14; // length if (!changeable) { state.scsi.dati.data[q + 2] = 0x03; // read CD-R/CD-RW state.scsi.dati.data[q + 3] = 0x00; // no write state.scsi.dati.data[q + 4] = 0x00; // dvd/audio capabilities state.scsi.dati.data[q + 5] = 0x00; // cd-da capabilities state.scsi.dati.data[q + 6] = state.scsi.locked ? 0x23 : 0x21; // tray-loader state.scsi.dati.data[q + 7] = 0x00; state.scsi.dati.data[q + 8] = (u8)(2800 >> 8); // max read speed in kBps (2.8Mbps = 16x) state.scsi.dati.data[q + 9] = (u8)(2800 >> 0); state.scsi.dati.data[q + 10] = (u8)(0 >> 8); // number of volume levels state.scsi.dati.data[q + 11] = (u8)(0 >> 0); state.scsi.dati.data[q + 12] = (u8)(64 >> 8); // buffer size in KBytes state.scsi.dati.data[q + 13] = (u8)(64 >> 0); state.scsi.dati.data[q + 14] = (u8)(2800 >> 8); // current read speed state.scsi.dati.data[q + 15] = (u8)(2800 >> 0); state.scsi.dati.data[q + 16] = 0; // reserved state.scsi.dati.data[q + 17] = 0; // digital output format state.scsi.dati.data[q + 18] = (u8)(0 >> 8); // max write speed state.scsi.dati.data[q + 19] = (u8)(0 >> 0); state.scsi.dati.data[q + 20] = (u8)(0 >> 8); // current write speed state.scsi.dati.data[q + 21] = (u8)(0 >> 0); } break; default: FAILURE_2(NotImplemented, "%s: MODE_SENSE for page %i is not yet implemented!\n", devid_string, pagecode); } #if defined(DEBUG_SCSI) printf("%s: Returning data: ", devid_string); for (unsigned int x1 = 0; x1 < q + 30; x1++) printf("%02x ", state.scsi.dati.data[x1]); printf("\n"); #endif } break; case SCSICMD_PREVENT_ALLOW_REMOVE: if (state.scsi.cmd.data[4] & 1) { state.scsi.locked = true; #if defined(DEBUG_SCSI) printf("%s: PREVENT MEDIA REMOVAL.\n", devid_string); #endif } else { state.scsi.locked = false; #if defined(DEBUG_SCSI) printf("%s: ALLOW MEDIA REMOVAL.\n", devid_string); #endif } do_scsi_error(SCSI_OK); break; case SCSICMD_MODE_SELECT: // get data out first... state.scsi.dato.expected = 12; if (state.scsi.dato.written < state.scsi.dato.expected) return 2; #if defined(DEBUG_SCSI) printf("%s: MODE SELECT.\n", devid_string); printf("Data: "); for (unsigned int x = 0; x < state.scsi.dato.written; x++) printf("%02x ", state.scsi.dato.data[x]); printf("\n"); #endif if (state.scsi.cmd.written == 6 && state.scsi.dato.written == 12 && state.scsi.dato.data[0] == 0x00 // data length //&& state.scsi.dato.data[1] == 0x05 // medium type - ignore && state.scsi.dato.data[2] == 0x00 // dev. specific && state.scsi.dato.data[3] == 0x08 // block descriptor length && state.scsi.dato.data[4] == 0x00 // density code && state.scsi.dato.data[5] == 0x00 // all blocks && state.scsi.dato.data[6] == 0x00 // all blocks && state.scsi.dato.data[7] == 0x00 // all blocks && state.scsi.dato.data[8] == 0x00) // reserved { set_block_size((state.scsi.dato.data[9] << 16) | (state.scsi.dato.data[10] << 8) | state.scsi.dato.data[11]); #if defined(DEBUG_SCSI) printf("%s: Block size set to %d.\n", devid_string, get_block_size()); #endif } else { unsigned int x; printf("%s: MODE SELECT ignored.\nCommand: ", devid_string); for (x = 0; x < state.scsi.cmd.written; x++) printf("%02x ", state.scsi.cmd.data[x]); printf("\nData: "); for (x = 0; x < state.scsi.dato.written; x++) printf("%02x ", state.scsi.dato.data[x]); printf("\nThis might be an attempt to change our blocksize or something " "like that...\nPlease check the above data, then press enter.\n>"); getchar(); } // ignore it... do_scsi_error(SCSI_OK); break; case SCSIBLOCKCMD_READ_CAPACITY: #if defined(DEBUG_SCSI) printf("%s: READ CAPACITY.\n", devid_string); #endif if (state.scsi.cmd.data[8] & 1) { FAILURE_1( NotImplemented, "%s: Don't know how to handle READ CAPACITY with PMI bit set.\n", devid_string); break; } // READ CAPACITY returns the number of the last LBA (n-1); // not the number of LBA's (n) state.scsi.dati.data[0] = (u8)((get_lba_size() - 1) >> 24) & 255; state.scsi.dati.data[1] = (u8)((get_lba_size() - 1) >> 16) & 255; state.scsi.dati.data[2] = (u8)((get_lba_size() - 1) >> 8) & 255; state.scsi.dati.data[3] = (u8)((get_lba_size() - 1) >> 0) & 255; state.scsi.dati.data[4] = (u8)(get_block_size() >> 24) & 255; state.scsi.dati.data[5] = (u8)(get_block_size() >> 16) & 255; state.scsi.dati.data[6] = (u8)(get_block_size() >> 8) & 255; state.scsi.dati.data[7] = (u8)(get_block_size() >> 0) & 255; state.scsi.dati.read = 0; state.scsi.dati.available = 8; #if defined(DEBUG_SCSI) printf("%s: Returning data: ", devid_string); for (unsigned int x1 = 0; x1 < 8; x1++) printf("%02x ", state.scsi.dati.data[x1]); printf("\n"); #endif do_scsi_error(SCSI_OK); break; case SCSICMD_READ: case SCSICMD_READ_10: case SCSICMD_READ_12: case SCSICMD_READ_CD: #if defined(DEBUG_SCSI) printf("%s: READ.\n", devid_string); #endif // if (state.scsi.disconnect_priv) //{ // //printf("%s: Will disconnect before returning read data.\n", // devid_string); state.scsi.will_disconnect = true; //} if (state.scsi.cmd.data[0] == SCSICMD_READ) { // bits 4..0 of cmd[1], and cmd[2] and cmd[3] // hold the logical block address. // // cmd[4] holds the number of logical blocks // to transfer. (Special case if the value is // 0, actually means 256.) ofs = ((state.scsi.cmd.data[1] & 0x1f) << 16) + (state.scsi.cmd.data[2] << 8) + state.scsi.cmd.data[3]; retlen = state.scsi.cmd.data[4]; if (retlen == 0) retlen = 256; } else if (state.scsi.cmd.data[0] == SCSICMD_READ_10) { // cmd[2..5] hold the logical block address. // cmd[7..8] holds the number of logical ofs = (state.scsi.cmd.data[2] << 24) + (state.scsi.cmd.data[3] << 16) + (state.scsi.cmd.data[4] << 8) + state.scsi.cmd.data[5]; retlen = (state.scsi.cmd.data[7] << 8) + state.scsi.cmd.data[8]; } else if (state.scsi.cmd.data[0] == SCSICMD_READ_12) { // cmd[2..5] hold the logical block address. // cmd[6..9] holds the number of logical ofs = (state.scsi.cmd.data[2] << 24) + (state.scsi.cmd.data[3] << 16) + (state.scsi.cmd.data[4] << 8) + state.scsi.cmd.data[5]; retlen = (state.scsi.cmd.data[6] << 24) + (state.scsi.cmd.data[7] << 16) + (state.scsi.cmd.data[8] << 8) + state.scsi.cmd.data[9]; } else if (state.scsi.cmd.data[0] == SCSICMD_READ_CD) { if (state.scsi.cmd.data[9] != 0x10) { FAILURE_2(NotImplemented, "%s: READ CD issued with data type %02x.\n", devid_string, state.scsi.cmd.data[9]); } // cmd[2..5] hold the logical block address. // cmd[6..8] holds the number of logical blocks to transfer. ofs = (state.scsi.cmd.data[2] << 24) + (state.scsi.cmd.data[3] << 16) + (state.scsi.cmd.data[4] << 8) + state.scsi.cmd.data[5]; retlen = (state.scsi.cmd.data[6] << 16) + (state.scsi.cmd.data[7] << 8) + state.scsi.cmd.data[8]; } // Within bounds? if ((ofs + retlen) > get_lba_size()) { do_scsi_error(SCSI_LBA_RANGE); break; } // Would exceed buffer? if (retlen > DATI_BUFSZ) { printf("%s: read too big (%d)\n", devid_string, retlen); do_scsi_error(SCSI_TOO_BIG); break; } // Return data: seek_block(ofs); read_blocks(state.scsi.dati.data, retlen); state.scsi.dati.read = 0; state.scsi.dati.available = retlen * get_block_size(); #if defined(DEBUG_SCSI) printf("%s: READ ofs=%d size=%d\n", devid_string, ofs, retlen); #endif do_scsi_error(SCSI_OK); break; case SCSICMD_READ_LONG: #if defined(DEBUG_SCSI) printf("%s: READ_LONG.\n", devid_string); #endif // The read long command is used to read one block of disk data, including // ECC data. OpenVMS uses read long / write long to do host-based shadowing. // During driver initialization, OpenVMS will check each disk to see if it // supports host-based shadowing, by trying to find the right size for read // long commands. The emulated scsi disk sets the size for read long / write // long commands to 514 bytes (the first value OpenVMS tries). // cmd[2..5] hold the logical block address. // cmd[7..8] holds the number of bytes to transfer ofs = (state.scsi.cmd.data[2] << 24) + (state.scsi.cmd.data[3] << 16) + (state.scsi.cmd.data[4] << 8) + state.scsi.cmd.data[5]; retlen = (state.scsi.cmd.data[7] << 8) + state.scsi.cmd.data[8]; state.scsi.stat.available = 1; state.scsi.stat.data[0] = 0; state.scsi.stat.read = 0; state.scsi.msgi.available = 1; state.scsi.msgi.data[0] = 0; state.scsi.msgi.read = 0; // If the requested size is not 514 bytes, don't accept it. if (retlen != 514) { do_scsi_error(SCSI_ILL_CMD); break; } // Within bounds? if ((ofs + 1) > get_lba_size()) { do_scsi_error(SCSI_LBA_RANGE); break; } // Would exceed buffer? if (retlen > DATI_BUFSZ) { printf("%s: read too big (%d)\n", devid_string, retlen); do_scsi_error(SCSI_TOO_BIG); break; } // Return data: seek_block(ofs); read_blocks(state.scsi.dati.data, 1); for (unsigned int x1 = get_block_size(); x1 < retlen; x1++) state.scsi.dati.data[x1] = 0; // set ECC bytes to 0. state.scsi.dati.read = 0; state.scsi.dati.available = retlen; do_scsi_error(SCSI_OK); break; case SCSICMD_WRITE: case SCSICMD_WRITE_10: #if defined(DEBUG_SCSI) printf("%s: WRITE.\n", devid_string); #endif if (state.scsi.cmd.data[0] == SCSICMD_WRITE) { // bits 4..0 of cmd[1], and cmd[2] and cmd[3] // hold the logical block address. // // cmd[4] holds the number of logical blocks // to transfer. (Special case if the value is // 0, actually means 256.) ofs = ((state.scsi.cmd.data[1] & 0x1f) << 16) + (state.scsi.cmd.data[2] << 8) + state.scsi.cmd.data[3]; retlen = state.scsi.cmd.data[4]; if (retlen == 0) retlen = 256; } else { // cmd[2..5] hold the logical block address. // cmd[7..8] holds the number of logical blocks // to transfer. ofs = (state.scsi.cmd.data[2] << 24) + (state.scsi.cmd.data[3] << 16) + (state.scsi.cmd.data[4] << 8) + state.scsi.cmd.data[5]; retlen = (state.scsi.cmd.data[7] << 8) + state.scsi.cmd.data[8]; } // Within bounds? if (((ofs + retlen)) > get_lba_size()) { do_scsi_error(SCSI_LBA_RANGE); break; } // Would exceed buffer? if (retlen * get_block_size() > DATO_BUFSZ) { printf("%s: write too big (%d)\n", devid_string, (int)(retlen * get_block_size())); do_scsi_error(SCSI_TOO_BIG); break; } state.scsi.dato.expected = retlen * get_block_size(); if (state.scsi.dato.written < state.scsi.dato.expected) return 2; // Write data seek_block(ofs); write_blocks(state.scsi.dato.data, retlen); #if defined(DEBUG_SCSI) printf("%s: WRITE ofs=%d size=%d\n", devid_string, ofs, retlen); #endif do_scsi_error(SCSI_OK); break; case SCSICMD_SYNCHRONIZE_CACHE: #if defined(DEBUG_SCSI) printf("%s: SYNCHRONIZE CACHE.\n", devid_string); #endif do_scsi_error(SCSI_OK); break; case SCSICDROM_READ_TOC: { #if defined(DEBUG_SCSI) printf("%s: CDROM READ TOC.\n", devid_string); #endif if (state.scsi.cmd.data[2] & 0x0f) { FAILURE_2(NotImplemented, "%s: I don't understand READ TOC/PMA/ATIP with format %01x.\n", devid_string, state.scsi.cmd.data[2] & 0x0f); } if (state.scsi.cmd.data[6] > 1 && state.scsi.cmd.data[6] != 0xAA) { FAILURE_2(InvalidArgument, "%s: I don't know CD-ROM track 0x%02x.\n", devid_string, state.scsi.cmd.data[6]); } retlen = state.scsi.cmd.data[7] * 256 + state.scsi.cmd.data[8]; state.scsi.dati.available = retlen; state.scsi.dati.read = 0; int q = 2; /*Here's an actual response from a single-track pressed CD to the command 0x43, 00, 00, 00, 00, 00, 00, 00, 0x0c, 0x40, 00, 00 0000 00 0a 01 01 00 14 01 00 00 00 00 00 00 00 00 00 ................ 0010 00 43 d6 02 00 81 ff ff 19 00 00 00 00 00 00 00 .C.............. 0020 01 00 00 00 00 00 00 00 01 00 00 00 01 00 01 00 ................ 0030 00 00 00 00 00 10 00 00 00 10 00 00 01 00 00 00 ................ */ state.scsi.dati.data[q++] = 1; // first track state.scsi.dati.data[q++] = 1; // last track if (state.scsi.cmd.data[6] <= 1) { state.scsi.dati.data[q++] = 0; // reserved state.scsi.dati.data[q++] = 0x14; // adr/control (Q-channel: current // position, data track, no copy) state.scsi.dati.data[q++] = 1; // track number state.scsi.dati.data[q++] = 0; // reserved if (state.scsi.cmd.data[1] & 0x02) { u32 x = lba2msf(0); state.scsi.dati.data[q++] = 0; state.scsi.dati.data[q++] = (x & 0xff0000) >> 16; state.scsi.dati.data[q++] = (x & 0xff00) >> 8; state.scsi.dati.data[q++] = x & 0xff; } else { state.scsi.dati.data[q++] = 0 >> 24; // lba state.scsi.dati.data[q++] = 0 >> 16; state.scsi.dati.data[q++] = 0 >> 8; state.scsi.dati.data[q++] = 0; } } state.scsi.dati.data[q++] = 0; // reserved state.scsi.dati.data[q++] = 0x16; // adr/control (Q-channel: current position, data track, copy) state.scsi.dati.data[q++] = 0xAA; // track number state.scsi.dati.data[q++] = 0; // reserved if (state.scsi.cmd.data[1] & 0x02) { u32 x = lba2msf(get_lba_size()); state.scsi.dati.data[q++] = 0; state.scsi.dati.data[q++] = (x & 0xff0000) >> 16; state.scsi.dati.data[q++] = (x & 0xff00) >> 8; state.scsi.dati.data[q++] = x & 0xff; } else { state.scsi.dati.data[q++] = (u8)(get_lba_size() >> 24); // lba state.scsi.dati.data[q++] = (u8)(get_lba_size() >> 16); state.scsi.dati.data[q++] = (u8)(get_lba_size() >> 8); state.scsi.dati.data[q++] = (u8)get_lba_size(); } state.scsi.dati.data[0] = (u8)(q >> 8); state.scsi.dati.data[1] = (u8)q; #if defined(DEBUG_SCSI) printf("%s: Returning data: ", devid_string); for (unsigned int x1 = 0; x1 < q; x1++) printf("%02x ", state.scsi.dati.data[x1]); printf("\n"); #endif do_scsi_error(SCSI_OK); } break; case SCSICDRRW_FORMAT: case SCSICDRRW_READ_DISC_INFO: case SCSICDRRW_READ_TRACK_INFO: case SCSICDRRW_RESERVE_TRACK: case SCSICDRRW_SEND_OPC_INFO: case SCSICDRRW_REPAIR_TRACK: case SCSICDRRW_READ_MASTER_CUE: case SCSICDRRW_CLOSE_TRACK: case SCSICDRRW_READ_BUFFER_CAP: case SCSICDRRW_SEND_CUE_SHEET: case SCSICDRRW_BLANK: // These are CD-R/RW specific commands; we pretend to be a simple // CD-ROM player, so no support for these commands. #if defined(DEBUG_SCSI) printf("%s: CD-R/RW specific.\n", devid_string); #endif do_scsi_error(SCSI_ILL_CMD); break; default: FAILURE_2(NotImplemented, "%s: Unknown SCSI command 0x%02x.\n", devid_string, state.scsi.cmd.data[0]); } return 0; } /** * \brief Handle a (series of) SCSI message(s). * * Called when one or more SCSI messages have been received. * We parse the message(s) and return what the next SCSI bus * phase should be. **/ int CDisk::do_scsi_message() { unsigned int msg; unsigned int msglen; msg = 0; while (msg < state.scsi.msgo.written) { if (state.scsi.msgo.data[msg] & 0x80) { // identify #if defined(DEBUG_SCSI) printf("%s: MSG: identify", devid_string); #endif if (state.scsi.msgo.data[msg] & 0x40) { #if defined(DEBUG_SCSI) printf(" w/disconnect priv"); #endif // state.scsi.disconnect_priv = true; } if (state.scsi.msgo.data[msg] & 0x07) { // LUN... #if defined(DEBUG_SCSI) printf(" for lun %d%", state.scsi.msgo.data[msg] & 0x07); #endif state.scsi.lun_selected = true; } #if defined(DEBUG_SCSI) printf("\n"); #endif msg++; } else { switch (state.scsi.msgo.data[msg]) { case 0x01: #if defined(DEBUG_SCSI) printf("%s: MSG: extended: ", devid_string); #endif msglen = state.scsi.msgo.data[msg + 1]; msg += 2; switch (state.scsi.msgo.data[msg]) { case 0x01: { #if defined(DEBUG_SCSI) printf("SDTR.\n"); #endif state.scsi.msgi.available = msglen + 2; state.scsi.msgi.data[0] = 0x01; state.scsi.msgi.data[1] = msglen; for (unsigned int x = 0; x < msglen; x++) state.scsi.msgi.data[2 + x] = state.scsi.msgo.data[msg + x]; } break; case 0x03: { #if defined(DEBUG_SCSI) printf("WDTR.\n"); #endif state.scsi.msgi.available = msglen + 2; state.scsi.msgi.data[0] = 0x01; state.scsi.msgi.data[1] = msglen; for (unsigned int x = 0; x < msglen; x++) state.scsi.msgi.data[2 + x] = state.scsi.msgo.data[msg + x]; } break; default: FAILURE_2(NotImplemented, "%s: MSG: don't understand extended message %02x.\n", devid_string, state.scsi.msgo.data[msg]); } msg += msglen; break; default: FAILURE_2(NotImplemented, "%s: MSG: don't understand message %02x.\n", devid_string, state.scsi.msgo.data[msg]); } } } // return next phase if (state.scsi.msgi.available) return SCSI_PHASE_MSG_IN; else return SCSI_PHASE_COMMAND; } static int primes_54[54] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251}; // 2 3 5 7 11 13 static int pri16[16][6] = {{4, 0, 0, 0, 0, 0}, // 16 {0, 1, 1, 0, 0, 0}, // 15 {1, 0, 0, 1, 0, 0}, // 14 {0, 0, 0, 0, 0, 1}, // 13 {2, 1, 0, 0, 0, 0}, // 12 {0, 0, 0, 0, 1, 0}, // 11 {1, 0, 1, 0, 0, 0}, // 10 {0, 2, 0, 0, 0, 0}, // 9 {3, 0, 0, 0, 0, 0}, // 8 {0, 0, 0, 1, 0, 0}, // 7 {1, 1, 0, 0, 0, 0}, // 6 {0, 0, 1, 0, 0, 0}, // 5 {2, 0, 0, 0, 0, 0}, // 4 {0, 1, 0, 0, 0, 0}, // 3 {1, 0, 0, 0, 0, 0}, // 2 {0, 0, 0, 0, 0, 0}}; // 1 static off_t_large get_primes(off_t_large value, int pri[54]) { int i; for (i = 0; i < 54; i++) { pri[i] = 0; while (!(value % primes_54[i])) { pri[i]++; value /= primes_54[i]; } } return value; } /** * Calculate optimal disk layout... **/ #define MAX_HD 16 #define MAX_SEC 50 void CDisk::determine_layout() { int disk_primes[54]; int compare_primes[54]; long heads_sectors = 0; long c_heads = 0; bool b; int prime; get_primes(get_lba_size(), disk_primes); for (heads_sectors = MAX_SEC * MAX_HD; heads_sectors > 0; heads_sectors--) { if (get_primes(heads_sectors, compare_primes) > 1) continue; for (c_heads = MAX_HD; c_heads > 0; c_heads--) { b = true; for (prime = 0; prime < 6; prime++) { if (pri16[16 - c_heads][prime] > compare_primes[prime]) { b = false; break; } } if (b) break; } if (heads_sectors / c_heads > MAX_SEC) continue; b = true; for (prime = 0; prime < 54; prime++) { if (compare_primes[prime] > disk_primes[prime]) { b = false; break; } } if (b) break; } heads = c_heads; sectors = heads_sectors / c_heads; // sectors = 32; // heads = 8; cylinders = get_lba_size() / heads / sectors; } ================================================ FILE: src/Disk.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__DISK_H__) #define __DISK_H__ #include "DiskController.hpp" #include "SCSIBus.hpp" #include "SCSIDevice.hpp" #define DATO_BUFSZ 256 * 1024 #define DATI_BUFSZ 256 * 1024 /** * \brief Abstract base class for disks (connects to a CDiskController) **/ class CDisk : public CSystemComponent, public CSCSIDevice { public: CDisk(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev); virtual ~CDisk(void); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void scsi_select_me(int bus); virtual size_t scsi_expected_xfer_me(int bus); virtual void *scsi_xfer_ptr_me(int bus, size_t bytes); virtual void scsi_xfer_done_me(int bus); void set_atapi_mode() { atapi_mode = true; }; int do_scsi_command(); int do_scsi_message(); void do_scsi_error(int errcode); virtual bool seek_byte(off_t_large byte) = 0; virtual size_t read_bytes(void *dest, size_t bytes) = 0; virtual size_t write_bytes(void *src, size_t bytes) = 0; bool seek_block(off_t_large lba) { return seek_byte(lba * state.block_size); }; size_t read_blocks(void *dest, size_t blocks) { return read_bytes(dest, blocks * state.block_size) / state.block_size; }; size_t write_blocks(void *src, size_t blocks) { return write_bytes(src, blocks * state.block_size) / state.block_size; }; size_t get_block_size() { return state.block_size; }; void set_block_size(size_t bs) { state.block_size = bs; determine_layout(); /*calc_cylinders();*/ }; void determine_layout(); off_t_large get_lba_size() { return byte_size / state.block_size; }; off_t_large get_byte_size() { return byte_size; }; off_t_large get_chs_size() { return cylinders * heads * sectors; }; off_t_large get_cylinders() { return cylinders; }; long get_heads() { return heads; }; long get_sectors() { return sectors; }; char *get_serial() { return serial_number; }; char *get_model() { return model_number; }; char *get_rev() { return revision_number; }; bool ro() { return read_only; }; bool rw() { return !read_only; }; bool cdrom() { return is_cdrom; }; void calc_cylinders(); protected: CConfigurator *myCfg; CDiskController *myCtrl; int myBus; int myDev; char *serial_number; char *model_number; char *revision_number; bool read_only; bool is_cdrom; off_t_large byte_size; off_t_large cylinders; long heads; long sectors; bool atapi_mode; /// The state structure contains all elements that need to be saved to the /// statefile struct SDisk_state { size_t block_size; /**< How many bytes there are in a physical disk block. **/ off_t_large byte_pos; /**< Current byte position in the disk. **/ /// SCSI state for SCSI-connected disks struct SDisk_scsi { // State for Message In Phase (disk -> controller) struct SDisk_msgi { u8 data[256]; /**< Data buffer. **/ unsigned int available; /**< Number of bytes available to read. **/ unsigned int read; /**< Number of bytes read so far. **/ } msgi; /// State for Message Out Phase (controller -> disk) struct SDisk_msgo { u8 data[256]; /**< Data buffer. **/ unsigned int written; /**< Number of bytes in buffer. **/ } msgo; bool lun_selected; /**< A LUN has been selected. CDisk doesn't support LUNs. **/ /// state for Command phase (controller -> disk) struct SDisk_cmd { u8 data[256]; /**< Data buffer. **/ unsigned int written; /**< Number of bytes in buffer. **/ } cmd; /// State for Data In phase (disk -> controller) struct SDisk_dati { u8 data[DATI_BUFSZ]; /**< Data buffer. **/ unsigned int available; /**< Number of bytes available to read. **/ unsigned int read; /**< Number of bytes read so far. **/ } dati; /// State for Data Out phase (controller -> disk) struct SDisk_dato { u8 data[DATO_BUFSZ]; /**< Data buffer. **/ unsigned int expected; /**< Number of bytes the initiator is expected to write. **/ unsigned int written; /**< Number of bytes written sofar. **/ } dato; /// State for Status phase (disk -> controller) struct SDisk_stat { u8 data[256]; /**< Data buffer. **/ unsigned int available; /**< Number of bytes available to read. **/ unsigned int read; /**< Number of bytes read so far. **/ } stat; /// State for request sense struct SDisk_sense { u8 data[256]; unsigned int available; } sense; bool locked; /**< Media is locked (for CD-ROM type devices). **/ // bool disconnect_priv; /**< Initiator has allowed us to // disconnect/reconnect. **/ bool will_disconnect; /**< We intend to // disconnect. **/ bool disconnected; /**< We have disconnected. // **/ bool reselected; /**< We have reselected the initiator. // **/ int disconnect_phase; /**< After we reconnect, we should go // to this SCSI phase. **/ } scsi; } state; }; #endif //! defined(__DISK_H__) ================================================ FILE: src/DiskController.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DiskController.hpp" #include "Disk.hpp" #include "StdAfx.hpp" CDiskController::CDiskController(int num_busses, int num_devices) { num_bus = num_busses; num_dev = num_devices; disks = (CDisk **)calloc(num_bus * num_dev, sizeof(CDisk *)); } CDiskController::~CDiskController(void) { free(disks); } void CDiskController::register_disk(class CDisk *dsk, int bus, int dev) { if (bus >= num_bus) FAILURE(Configuration, "Can't register disk: bus number out of range"); if (dev >= num_dev) FAILURE(Configuration, "Can't register disk: device number out of range"); disks[bus * num_bus + dev] = dsk; } class CDisk *CDiskController::get_disk(int bus, int dev) { if (bus >= num_bus) return 0; if (dev >= num_dev) return 0; return disks[bus * num_bus + dev]; } ================================================ FILE: src/DiskController.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__DISKCONTROLLER_H__) #define __DISKCONTROLLER_H__ /** * \brief Abstract base class for disk controllers (uses CDisk's) **/ class CDiskController { public: CDiskController(int num_busses, int num_devs); ~CDiskController(void); virtual void register_disk(class CDisk *dsk, int bus, int dev); class CDisk *get_disk(int bus, int dev); private: int num_bus; int num_dev; class CDisk **disks; }; #endif //! defined(__DISKCONTROLLER_H__) ================================================ FILE: src/DiskDevice.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DiskDevice.hpp" #include "StdAfx.hpp" #if defined(_WIN32) #include #endif CDiskDevice::CDiskDevice(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev) : CDisk(cfg, sys, c, idebus, idedev) { filename = myCfg->get_text_value("device"); if (!filename) { FAILURE_1(Configuration, "%s: Disk has no device attached!\n", devid_string); } if (read_only) { #if defined(_WIN32) buffer = (char *)malloc(2048); buffer_size = 2048; handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); #else handle = fopen(filename, "rb"); #endif } else { #if defined(_WIN32) handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); #else handle = fopen(filename, "rb+"); #endif } #if defined(_WIN32) if (handle == INVALID_HANDLE_VALUE) { FAILURE_3(Runtime, "%s: Could not open device %s. Error %ld.", devid_string, filename, GetLastError()); } #else if (!handle) { FAILURE_2(Runtime, "%s: Could not open device %s.", devid_string, filename); } #endif // determine size... #if defined(_WIN32) DISK_GEOMETRY x; DWORD bytesret; if (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &x, sizeof(x), &bytesret, NULL)) { FAILURE_3(Runtime, "%s: Could not get drive geometry for %s. Error %ld.", devid_string, filename, GetLastError()); } sectors = x.SectorsPerTrack; heads = x.TracksPerCylinder; byte_size = x.Cylinders.QuadPart * x.TracksPerCylinder * x.SectorsPerTrack * x.BytesPerSector; dev_block_size = x.BytesPerSector; LARGE_INTEGER a; a.QuadPart = 0; SetFilePointerEx(handle, a, (PLARGE_INTEGER)&state.byte_pos, FILE_BEGIN); #else fseek_large(handle, 0, SEEK_END); byte_size = ftell_large(handle); fseek_large(handle, 0, SEEK_SET); state.byte_pos = ftell_large(handle); sectors = 32; heads = 8; #endif // calc_cylinders(); determine_layout(); model_number = myCfg->get_text_value("model_number", filename); printf("%s: Mounted device %s, %" PRId64 " %zd-byte blocks, %" PRId64 "/%ld/%ld.\n", devid_string, filename, byte_size / state.block_size, state.block_size, cylinders, heads, sectors); } CDiskDevice::~CDiskDevice(void) { printf("%s: Closing file.\n", devid_string); #if defined(_WIN32) if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); #else if (handle) fclose(handle); #endif } bool CDiskDevice::seek_byte(off_t_large byte) { if (byte >= byte_size) { FAILURE_1(InvalidArgument, "%s: Seek beyond end of file!\n", devid_string); } #if defined(_WIN32) state.byte_pos = byte; #else fseek_large(handle, byte, SEEK_SET); state.byte_pos = ftell_large(handle); #endif return true; } size_t CDiskDevice::read_bytes(void *dest, size_t bytes) { // printf("%s: read %d bytes @ %" LL // "d.\n",devid_string,bytes,state.byte_pos); #if defined(_WIN32) off_t_large byte_from = (state.byte_pos / dev_block_size) * dev_block_size; off_t_large byte_to = (((state.byte_pos + bytes - 1) / dev_block_size) + 1) * dev_block_size; DWORD byte_len = (DWORD)(byte_to - byte_from); DWORD byte_off = (DWORD)(state.byte_pos - byte_from); LARGE_INTEGER a; DWORD r; if (byte_len > buffer_size) { buffer_size = byte_len; CHECK_REALLOCATION(buffer, realloc(buffer, buffer_size), char); // printf("%s: buffer enlarged to %d bytes.\n",devid_string,buffer_size); } a.QuadPart = byte_from; SetFilePointerEx(handle, a, NULL, FILE_BEGIN); ReadFile(handle, buffer, byte_len, &r, NULL); if (r != (byte_len)) { printf("%s: Tried to read %d bytes from pos %ld, but could only read %d bytes!\n", devid_string, byte_len, byte_from, r); printf("%s: Error %ld.\n", devid_string, GetLastError()); } memcpy(dest, buffer + byte_off, bytes); state.byte_pos += bytes; return bytes; #else size_t r; r = fread(dest, 1, bytes, handle); state.byte_pos = ftell_large(handle); return r; #endif } size_t CDiskDevice::write_bytes(void *src, size_t bytes) { if (read_only) return 0; #if defined(_WIN32) off_t_large byte_from = (state.byte_pos / dev_block_size) * dev_block_size; off_t_large byte_to = (((state.byte_pos + bytes - 1) / dev_block_size) + 1) * dev_block_size; DWORD byte_len = (DWORD)(byte_to - byte_from); DWORD byte_off = (DWORD)(state.byte_pos - byte_from); LARGE_INTEGER a; DWORD r; if (byte_len > buffer_size) { buffer_size = byte_len; CHECK_REALLOCATION(buffer, realloc(buffer, buffer_size), char); } if (byte_from != state.byte_pos) { // we don't write the entire first block, so we read it // from disk first so we don't corrupt the disk a.QuadPart = byte_from; SetFilePointerEx(handle, a, NULL, FILE_BEGIN); ReadFile(handle, buffer, (DWORD)dev_block_size, &r, NULL); if (r != (dev_block_size)) { printf("%s: Tried to read %d bytes from pos %ld, but could only read %zd bytes!\n", devid_string, dev_block_size, byte_from, r); FAILURE(InvalidArgument, "Error during device write operation. " "Terminating to avoid disk corruption."); } } if ((byte_to != state.byte_pos + bytes) && (byte_to - byte_from > dev_block_size)) { // we don't write the entire last block, so we read it // from disk first so we don't corrupt the disk a.QuadPart = byte_to - dev_block_size; SetFilePointerEx(handle, a, NULL, FILE_BEGIN); ReadFile(handle, buffer + byte_len - dev_block_size, (DWORD)dev_block_size, &r, NULL); if (r != (dev_block_size)) { printf("%s: Tried to read %d bytes from pos %ld, but could only read %zd bytes!\n", devid_string, dev_block_size, byte_to - dev_block_size, r); FAILURE(InvalidArgument, "Error during device write operation. " "Terminating to avoid disk corruption."); } } // add the data we're writing to the buffer memcpy(buffer + byte_off, src, bytes); a.QuadPart = byte_from; SetFilePointerEx(handle, a, NULL, FILE_BEGIN); // and write the buffer to disk WriteFile(handle, buffer, byte_len, &r, NULL); if (r != byte_len) { printf("%s: Tried to write %d bytes to pos %ld, but could only write %d bytes!\n", devid_string, byte_len, byte_from, r); FAILURE(InvalidArgument, "Error during device write operation. Terminating " "to avoid disk corruption."); } state.byte_pos += bytes; return bytes; #else size_t r; r = fwrite(src, 1, bytes, handle); state.byte_pos = ftell_large(handle); return r; #endif } ================================================ FILE: src/DiskDevice.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__DISKDEV_H__) #define __DISKDEV_H__ #include "Disk.hpp" /** * \brief Emulated disk that uses a raw device. **/ class CDiskDevice : public CDisk { public: CDiskDevice(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev); virtual ~CDiskDevice(void); virtual bool seek_byte(off_t_large byte); virtual size_t read_bytes(void *dest, size_t bytes); virtual size_t write_bytes(void *src, size_t bytes); protected: #if defined(_WIN32) HANDLE handle; char *buffer; size_t buffer_size; size_t dev_block_size; #else FILE *handle; #endif char *filename; }; #endif //! defined(__DISKFILE_H__) ================================================ FILE: src/DiskFile.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2020 Remy van Elst * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DiskFile.hpp" #include "StdAfx.hpp" #include #include CDiskFile::CDiskFile(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev) : CDisk(cfg, sys, c, idebus, idedev) { /* If the filename exists in the config, use that, * otherwise, use a default filename */ filename = myCfg->get_text_value("file"); if (!filename) { defaultFilename = std::string(devid_string) + ".default.img"; std::cerr << devid_string << ": Disk has no filename attached! Assuming default: " << defaultFilename << std::endl; filename = const_cast(defaultFilename.c_str()); } /* if the file does not exist, create it. ifs actually checks for * accessibility, not for existence, but for compatibility with * platforms without c++17/boost and std::filesystem::exists, this * is good enough. ifstream.good() returns false if the file does * not exist.*/ std::ifstream ifs(filename, std::ios::binary); if (!ifs.good()) { std::cerr << devid_string << ": file does not exist: " << filename << std::endl; /* If the disk file size was not set and the disk file does not exist, do * not create it, but exit */ u64 diskFileSize = myCfg->get_num_value("autocreate_size", false, 0); if (!diskFileSize) { FAILURE_1(Runtime, "%s: file does not exist and no autocreate_size set.", devid_string); } /* Don't create disk file if it's supposed to be a cdrom */ if (is_cdrom) { FAILURE_1(Runtime, "%s: file does not exist and is configured as cdrom.", devid_string); } createDiskFile(filename, diskFileSize); } if (!read_only) { /* If the file is not configured as readonly, test if we can * write to it by opening it in append mode. Just using 'out' * would overwrite the contents of the file, which is * unwanted. Issue #29. */ checkFileWritable(filename); } handle = fopen(filename, read_only ? "rb" : "rb+"); // determine size... fseek_large(handle, 0, SEEK_END); byte_size = ftell_large(handle); fseek_large(handle, 0, SEEK_SET); state.byte_pos = ftell_large(handle); sectors = 32; heads = 8; // calc_cylinders(); determine_layout(); model_number = myCfg->get_text_value("model_number", filename); // skip to the filename portion of the path. char *p = model_number; #if defined(_WIN32) char x = '\\'; #elif defined(__VMS) char x = ']'; #else char x = '/'; #endif while (*p) { if (*p == x) model_number = p + 1; p++; } printf("%s: Mounted file %s, %" PRId64 " %zd-byte blocks, %" PRId64 "/%ld/%ld.\n", devid_string, filename, byte_size / state.block_size, state.block_size, cylinders, heads, sectors); } void CDiskFile::checkFileWritable(const std::string& fileName) const { std::ofstream ofs(fileName, std::ios::app); // is_open for an ofstream checks if the file can be written to bool isWritable = (ofs.is_open() && ofs.good()); if (!isWritable) { FAILURE_2(Runtime, "%s: file %s is not writable", devid_string, fileName.c_str()) } ofs.close(); } void CDiskFile::createDiskFile(const std::string &fileName, u64 diskFileSize) { std::ofstream ofs(fileName, std::ios::binary); if (ofs.is_open() && ofs.good()) { ofs.seekp((diskFileSize)-1); ofs.write("", 1); } else { FAILURE_1(Runtime, "%s: File does not exist and could not be created", devid_string); } std::cout << devid_string << " " << (diskFileSize / 1024 / 1024) << "MB file " << filename << " created" << std::endl; } CDiskFile::~CDiskFile(void) { printf("%s: Closing file.\n", devid_string); fclose(handle); } bool CDiskFile::seek_byte(off_t_large byte) { if (byte >= byte_size) { FAILURE_1(InvalidArgument, "%s: Seek beyond end of file!\n", devid_string); } fseek_large(handle, byte, SEEK_SET); state.byte_pos = ftell_large(handle); return true; } size_t CDiskFile::read_bytes(void *dest, size_t bytes) { size_t r; r = fread(dest, 1, bytes, handle); state.byte_pos = ftell_large(handle); return r; } size_t CDiskFile::write_bytes(void *src, size_t bytes) { if (read_only) return 0; size_t r; r = fwrite(src, 1, bytes, handle); state.byte_pos = ftell_large(handle); return r; } ================================================ FILE: src/DiskFile.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2020 Remy van Elst * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__DISKFILE_H__) #define __DISKFILE_H__ #include "Disk.hpp" /** * \brief Emulated disk that uses an image file. **/ class CDiskFile : public CDisk { public: CDiskFile(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev); virtual ~CDiskFile(void); virtual bool seek_byte(off_t_large byte); virtual size_t read_bytes(void *dest, size_t bytes); virtual size_t write_bytes(void *src, size_t bytes); FILE *get_handle() { return handle; }; protected: FILE *handle; char *filename; void createDiskFile(const std::string &filename, u64 diskFileSize); std::string defaultFilename; void checkFileWritable(const std::string& filename) const; }; #endif //! defined(__DISKFILE_H__) ================================================ FILE: src/DiskRam.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "DiskRam.hpp" #include "StdAfx.hpp" CDiskRam::CDiskRam(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev) : CDisk(cfg, sys, c, idebus, idedev) { byte_size = myCfg->get_num_value("size", false, 512 * 1024 * 1024); CHECK_ALLOCATION(ramdisk = malloc((size_t)byte_size)); state.byte_pos = 0; sectors = 32; heads = 8; // calc_cylinders(); determine_layout(); model_number = myCfg->get_text_value("model_number", "ES40RAMDISK"); printf("%s: Mounted RAMDISK, %" PRId64 " %zd-byte blocks, %" PRId64 "/%ld/%ld.\n", devid_string, byte_size / state.block_size, state.block_size, cylinders, heads, sectors); } CDiskRam::~CDiskRam(void) { if (ramdisk) { printf("%s: RAMDISK freed.\n", devid_string); free(ramdisk); ramdisk = 0; } } bool CDiskRam::seek_byte(off_t_large byte) { if (byte >= byte_size) { FAILURE_1(InvalidArgument, "%s: Seek beyond end of file!\n", devid_string); } state.byte_pos = byte; return true; } size_t CDiskRam::read_bytes(void *dest, size_t bytes) { if (state.byte_pos >= byte_size) return 0; while (state.byte_pos + bytes >= byte_size) bytes--; memcpy(dest, &(((char *)ramdisk)[state.byte_pos]), bytes); state.byte_pos += (unsigned long)bytes; return bytes; } size_t CDiskRam::write_bytes(void *src, size_t bytes) { if (state.byte_pos >= byte_size) return 0; while (state.byte_pos + bytes >= byte_size) bytes--; memcpy(&(((char *)ramdisk)[state.byte_pos]), src, bytes); state.byte_pos += (unsigned long)bytes; return bytes; } ================================================ FILE: src/DiskRam.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__DISKRAM_H__) #define __DISKRAM_H__ #include "Disk.hpp" /** * \brief Emulated disk that uses RAM. **/ class CDiskRam : public CDisk { public: CDiskRam(CConfigurator *cfg, CSystem *sys, CDiskController *c, int idebus, int idedev); virtual ~CDiskRam(void); virtual bool seek_byte(off_t_large byte); virtual size_t read_bytes(void *dest, size_t bytes); virtual size_t write_bytes(void *src, size_t bytes); protected: void *ramdisk; }; #endif //! defined(__DISKFILE_H__) ================================================ FILE: src/Ethernet.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon GXemul, which is Copyright (C) 2004-2007 * Anders Gavare. All rights reserved. */ #include "Ethernet.hpp" #include "telnet.hpp" /** * \brief Packet Queue for Ethernet packets. **/ CPacketQueue::CPacketQueue(const char *name, int max) { this->name = name; this->max = max; head = 0; tail = -1; cnt = 0; highwater = 0; dropped = 0; packets = new eth_packet[max]; } CPacketQueue::~CPacketQueue() { delete[] packets; printf("CPacketQueue(%s): highwater=%d, lost=%d\n", name, highwater, dropped); } void CPacketQueue::flush() { cnt = 0; head = 0; tail = -1; } static const u32 crcTable[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; static u32 eth_crc32(u32 crc, const void *vbuf, int len) { const u32 mask = 0xFFFFFFFF; const unsigned char *buf = (const unsigned char *)vbuf; crc ^= mask; while (0 != len--) crc = (crc >> 8) ^ crcTable[(crc ^ (*buf++)) & 0xFF]; return (crc ^ mask); } bool CPacketQueue::add_tail(const u8 *packet_data, int packet_len, bool calc_crc, bool need_crc) { if ((cnt >= max) || (packet_len < 1) || (packet_len > 1514)) { dropped += 1; printf("CPacketQueue(%s):add() packet lost! Size = %d", name, packet_len); printf(".. dst: %02x-%02x-%02x-%02x-%02x-%02x ", packet_data[0], packet_data[1], packet_data[2], packet_data[3], packet_data[4], packet_data[5]); printf(".. src: %02x-%02x-%02x-%02x-%02x-%02x \n", packet_data[6], packet_data[7], packet_data[8], packet_data[9], packet_data[10], packet_data[11]); return false; } tail += 1; if (tail >= max) { tail = 0; } eth_packet *next = &packets[tail]; next->len = packet_len; next->used = 0; memcpy(next->frame, packet_data, packet_len); // copy packet data if (need_crc) { // If packet needs CRC u32 crc = calc_crc ? eth_crc32(0, packet_data, packet_len) : 0; // recalculate crc if needed u32 ncrc = htonl(crc); // put crc in network order memcpy(&next->frame[packet_len], &ncrc, 4); // append CRC to packet next->len += 4; // increase packet length } cnt += 1; if (cnt > highwater) { highwater = cnt; } return true; } bool CPacketQueue::get_head(eth_packet &packet) { if (cnt <= 0) { return false; } eth_packet *headp = &packets[head]; packet.len = headp->len; packet.used = headp->used; memcpy(packet.frame, headp->frame, sizeof(packet.frame)); head += 1; if (head >= max) { head = 0; } cnt -= 1; return true; } ================================================ FILE: src/Ethernet.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon GXemul, which is Copyright (C) 2004-2007 * Anders Gavare. All rights reserved. */ #if !defined(INCLUDED_ETHERNET_H) #define INCLUDED_ETHERNET_H #include "StdAfx.hpp" #define ETH_MAX_PACKET_RAW 1514 #define ETH_MAX_PACKET_CRC 1518 struct eth_frame { // ethernet (wire) frame u8 src[6]; // source address u8 dst[6]; // destination address u8 protocol[2]; // protocol u8 data[1500]; // data: variable 46-1500 bytes u8 crc_fill[4]; // space for max packet crc }; struct eth_packet { // ethernet packet int len; // size of packet int used; // bytes used (consumed) u8 frame[ETH_MAX_PACKET_CRC]; // ethernet frame }; /** * \brief Packet Queue for Ethernet packets. **/ class CPacketQueue { // Ethernet Packet Queue // private: public: const char *name; // queue name int max; // maximum items allowed in queue int head; // first item in queue int tail; // last item in queue int cnt; // current item count int highwater; // highwater mark (statistics) int dropped; // packets dropped because queue was full eth_packet *packets; // packet array; dynamically allocated public: inline int count() { return cnt; } // get current count inline int lost() { return dropped; } // get number of lost packets void flush(); // empties packet queue bool add_tail(const u8 *packet_data, int packet_len, bool calc_crc, bool need_crc); // adds pcap packet to queue bool get_head(eth_packet &packet); // get packet at head CPacketQueue(const char *name, int max); // constructor ~CPacketQueue(); // destructor }; #endif // !defined(INCLUDED_ETHERNET_H) ================================================ FILE: src/Flash.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Flash.hpp" #include "AlphaCPU.hpp" #include "StdAfx.hpp" #include "System.hpp" // These are the modes for our flash-state-machine. #define MODE_READ 0 #define MODE_STEP1 1 #define MODE_STEP2 2 #define MODE_AUTOSEL 3 #define MODE_PROGRAM 4 #define MODE_ERASE_STEP3 5 #define MODE_ERASE_STEP4 6 #define MODE_ERASE_STEP5 7 #define MODE_CONFIRM_0 8 #define MODE_CONFIRM_1 9 extern CAlphaCPU *cpu[4]; /** * Constructor. **/ CFlash::CFlash(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { if (theSROM) FAILURE(Configuration, "More than one Flash"); theSROM = this; c->RegisterMemory(this, 0, U64(0x0000080100000000), 0x8000000); // 2MB memset(state.Flash, 0xff, 2 * 1024 * 1024); RestoreStateF(); state.mode = MODE_READ; printf("%s: $Id: Flash.cpp,v 1.19 2008/03/24 22:11:50 iamcamiel Exp $\n", devid_string); } /** * Destructor. **/ CFlash::~CFlash() {} /** * Read a byte from flashmemory. * Normally, this returns one byte from flash, however, after some commands * sent to the flash-rom, this returns identification or status information. **/ u64 CFlash::ReadMem(int index, u64 address, int dsize) { u64 data = 0; int a = (int)(address >> 6); switch (state.mode) { case MODE_AUTOSEL: switch (a) { case 0: data = 1; // manufacturer break; case 1: data = 0xad; // device break; default: data = 0; } break; case MODE_CONFIRM_0: data = 0x80; state.mode = MODE_READ; break; case MODE_CONFIRM_1: data = 0x80; state.mode = MODE_CONFIRM_0; break; default: data = state.Flash[a]; } return data; } /** * Write command or programming data to flash-rom. * * The state machine for this looks like this: * \code * | * v * MODE_READ <---------------------------+ * | write 0x5555:0xaa | * v | * MODE_STEP1 ---------------------------+ * | write 0x2aaa:0x55 | * v | * ==MODE_STEP2= ------------------------+ * 0x80| 0xa0| 0x90| write 0x5555 | * | | v | * | | MODE_AUTOSEL (read device id)-+ * | v | * | MODE_PROGRAM | * | | write data byte | * | +-------------------------------+ * v | * MODE_ERASE_STEP3 ------------------------+ * | write 0x5555:0xaa | * v | * MODE_ERASE_STEP4 ------------------------+ * | write 0x2aaa:0x55 | * v | * MODE_ERASE_STEP4 ------------------------+ * | write 0x30 | write 0x5555:0x10 | * | anywhere v | * v ERASE ENTIRE FLASH | * ERASE BLOCK | | * | | | * v v | * MODE_CONFIRM1 | * | read 0x80 | * v | * MODE_CONFIRM2 | * | read 0x80 | * +--------------------------------+ * \endcode **/ void CFlash::WriteMem(int index, u64 address, int dsize, u64 data) { int a = (int)(address >> 6); switch (state.mode) { case MODE_READ: case MODE_AUTOSEL: if ((a == 0x5555) && (data == 0xaa)) { state.mode = MODE_STEP1; return; } state.mode = MODE_READ; return; case MODE_STEP1: if ((a == 0x2aaa) && (data == 0x55)) { state.mode = MODE_STEP2; return; } state.mode = MODE_READ; return; case MODE_STEP2: if (a != 0x5555) { state.mode = MODE_READ; return; } switch (data) { case 0x90: state.mode = MODE_AUTOSEL; return; case 0xa0: state.mode = MODE_PROGRAM; return; case 0x80: state.mode = MODE_ERASE_STEP3; return; } state.mode = MODE_READ; return; case MODE_ERASE_STEP3: if ((a == 0x5555) && (data == 0xaa)) { state.mode = MODE_ERASE_STEP4; return; } state.mode = MODE_READ; return; case MODE_ERASE_STEP4: if ((a == 0x2aaa) && (data == 0x55)) { state.mode = MODE_ERASE_STEP5; return; } state.mode = MODE_READ; return; case MODE_ERASE_STEP5: if ((a == 0x5555) && (data == 0x10)) { memset(state.Flash, 0xff, 1 << 21); state.mode = MODE_CONFIRM_1; return; } if (data == 0x30) { memset(&state.Flash[(a >> 16) << 16], 0xff, 1 << 16); state.mode = MODE_CONFIRM_1; return; } state.mode = MODE_READ; return; } // we must now be in mode program... state.Flash[a] = (u8)data; state.mode = MODE_READ; } /** * Save state to a flash rom file. **/ void CFlash::SaveStateF(char *fn) { FILE *ff; ff = fopen(fn, "wb"); if (ff) { SaveState(ff); fclose(ff); printf("%%FLS-I-SAVEST: Flash state saved to %s\n", fn); } else { printf("%%FLS-F-NOSAVE: Flash could not be saved to %s\n", fn); } } /** * Save state to the default flash rom file. **/ void CFlash::SaveStateF() { SaveStateF(myCfg->get_text_value("rom.flash", "flash.rom")); } /** * Restore state from a flash rom file. **/ void CFlash::RestoreStateF(char *fn) { FILE *ff; ff = fopen(fn, "rb"); if (ff) { RestoreState(ff); fclose(ff); printf("%%FLS-I-RESTST: Flash state restored from %s\n", fn); } else { printf("%%FLS-F-NOREST: Flash could not be restored from %s\n", fn); } } /** * Restore state from the default flash rom file. **/ void CFlash::RestoreStateF() { RestoreStateF(myCfg->get_text_value("rom.flash", "flash.rom")); } static u32 flash_magic1 = 0xFF3E3FF3; static u32 flash_magic2 = 0x3FF3E3FF; /** * Save state to a Virtual Machine State file. **/ int CFlash::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&flash_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&flash_magic2, sizeof(u32), 1, f); printf("flash: %ld bytes saved.\n", ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CFlash::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("flash: unexpected end of file!\n"); return -1; } if (m1 != flash_magic1) { printf("flash: MAGIC 1 does not match!\n"); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("flash: unexpected end of file!\n"); return -1; } if (ss != sizeof(state)) { printf("flash: STRUCT SIZE does not match!\n"); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("flash: unexpected end of file!\n"); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("flash: unexpected end of file!\n"); return -1; } if (m2 != flash_magic2) { printf("flash: MAGIC 1 does not match!\n"); return -1; } printf("flash: %ld bytes restored.\n", ss); return 0; } CFlash *theSROM = 0; ================================================ FILE: src/Flash.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_FLASH_H) #define INCLUDED_FLASH_H #include "SystemComponent.hpp" /** * \brief Emulated flash memory. * * Flash memory is only used for storing configuration data (such as SRM console *variables), it is not used for firmware. **/ class CFlash : public CSystemComponent { public: virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u64 ReadMem(int index, u64 address, int dsize); CFlash(CConfigurator *cfg, class CSystem *c); virtual ~CFlash(); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void SaveStateF(); void RestoreStateF(); void SaveStateF(char *fn); void RestoreStateF(char *fn); protected: /// The state structure contains all elements that need to be saved to the /// statefile. struct SFlash_state { u8 Flash[2 * 1024 * 1024]; int mode; } state; }; extern CFlash *theSROM; #endif // !defined(INCLUDED_FLASH_H) ================================================ FILE: src/FloppyController.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "FloppyController.hpp" #include "DMA.hpp" #include "Disk.hpp" #include "StdAfx.hpp" #include "System.hpp" /** * Constructor. **/ CFloppyController::CFloppyController(CConfigurator *cfg, CSystem *c, int id) : CSystemComponent(cfg, c), CDiskController(1, 2) { c->RegisterMemory(this, 1536, U64(0x00000801fc0003f0) - (0x80 * id), 6); c->RegisterMemory(this, 1537, U64(0x00000801fc0003f7) - (0x80 * id), 1); state.cmd_parms_ptr = 0; state.cmd_res_ptr = 0; state.status.rqm = 1; state.status.dio = 0; printf("%s: $Id: FloppyController.cpp,v 1.16 2008/04/29 09:53:30 iamcamiel " "Exp $\n", devid_string); } /** * Destructor. **/ CFloppyController::~CFloppyController() {} const char *datarate_name[] = {"500 Kb/S MFM", "300 Kb/S MFM", "250 Kb/S MFM", "1 Mb/S MFM"}; struct cmdinfo_t { u8 command; u8 parms; u8 returns; const char *name; } cmdinfo[] = { {0, 0, 0, NULL}, {0, 0, 0, NULL}, {2, 9, 7, "Read Track"}, {3, 3, 0, "Specify"}, {4, 2, 1, "Sense Drive Status"}, {5, 9, 7, "Write Data"}, {6, 9, 7, "Read Data"}, {7, 2, 0, "Recalibrate"}, {8, 1, 2, "Sense Interrupt Status"}, {9, 9, 7, "Write Deleted Data"}, {10, 2, 7, "Read ID"}, {11, 0, 0, NULL}, {12, 9, 7, "Read Deleted"}, {13, 6, 7, "Format Track"}, {14, 1, 10, "DumpReg"}, {15, 3, 0, "Seek"}, {16, 1, 1, "Version"}, {17, 9, 7, "Scan Equal"}, {18, 2, 0, "Perpendicular Mode"}, {19, 4, 0, "Configure"}, {20, 1, 1, "Lock"}, {21, 0, 0, NULL}, {22, 9, 7, "Verify"}, {23, 0, 0, NULL}, {24, 0, 0, NULL}, {25, 9, 7, "Scan Low or Equal"}, {26, 0, 0, NULL}, {27, 0, 0, NULL}, {28, 0, 0, NULL}, {29, 9, 7, "Scan High or Equal"}, {30, 0, 0, NULL}, {31, 0, 0, NULL}, }; void CFloppyController::WriteMem(int index, u64 address, int dsize, u64 data) { if (index == 1537) address += 7; // printf("FDC: Write port %d, value: %x\n", address, data); switch (address) { case FDC_REG_STATUS_A: case FDC_REG_STATUS_B: printf("FDC: Read only register %" PRId64 " written.\n", address); break; case FDC_REG_DOR: // bit 4 = drive 0 motor, bit 5 = drive 1 motor // bit 3 = dma enable (ps/2 reserved?) // bit 2 = 1: fdc enable (reset), 0: hold at reset // bits 1-0: drive select 0: a, 1: b, I assume 2 & 3 are reserved. state.drive[0].motor = (data & 0x10) >> 4; state.drive[1].motor = (data & 0x20) >> 5; state.dma = (data & 0x08) >> 3; state.drive_select = data & 0x03; printf("FDC: motor a: %s, motor b: %s, dma: %s, drive: %s\n", state.drive[0].motor ? "on" : "off", state.drive[1].motor ? "on" : "off", state.dma ? "on" : "off", state.drive_select == 0 ? "A" : "B"); break; case FDC_REG_TAPE: printf("FDC: Tape register written with %" PRIx64 "\n", data); break; case FDC_REG_STATUS: // write = data rate selector // bit 7 = software reset (self clearing) // bit 6 = power down // bit 5 = reserved (0) // bit 4-2 = write precomp (000 = default) // bit 1-0 = data rate select state.datarate = data & 0x03; state.write_precomp = (data & 0x1c) >> 2; printf("FDC: data rate %s, precomp: %d\n", datarate_name[state.datarate], state.write_precomp); break; case FDC_REG_COMMAND: // the excitement happens here. if (state.status.dio) { printf("Unrequested data byte to command port. Throwing away.\n"); break; } else { state.cmd_parms[state.cmd_parms_ptr++] = data; int cmd = state.cmd_parms[0] & 0x1F; state.cmd_res_max = cmdinfo[cmd].returns; // printf("FDC: parm_ptr: %d, parms: %d\n", state.cmd_parms_ptr, // cmdinfo[cmd].parms); if (state.cmd_parms_ptr == cmdinfo[cmd].parms) { printf("FDC: command %s(", cmdinfo[cmd].name); for (int i = 1; i < state.cmd_parms_ptr; i++) { printf("%x ", state.cmd_parms[i]); } printf(")\n"); state.cmd_res_max = cmdinfo[cmd].returns; state.cmd_res_ptr = 0; state.status.rqm = 0; switch (cmd) { case 3: // specify // set up some hardware parameters. We really don't care about // the times (step rate time, head unload time, head load time}, but // we may care about the ND bit (parm byte 3, bit 1)... state.dma = ~(state.cmd_parms[2] & 0x01); break; case 6: // read data // args: // 0: bit 7 = MT (multitrack), 6 = MFM, 5 = SK (skip flag) // 1: bit 2 = HDS (head), 1 = DS1, 0 = DS0 // 2: C = cyl // 3: H = head address // 4: R = sector // 5: N = sector size, 2 = 512b // 6: EOT = end of track 0x24 = 36 sectors (18 * 2) // 7: GPL = gap length // 8: DTL = sector size (if N = 0) { int count = theDMA->get_count(2); void *buffer = malloc(count + 1); int pos = (state.cmd_parms[2] * state.cmd_parms[6]) // cyls + (state.cmd_parms[3] * (state.cmd_parms[6] / 2)) // head + state.cmd_parms[4] - 1; // sector (sectors start at 1) SEL_FDISK->seek_byte(pos * 512); SEL_FDISK->read_bytes(buffer, count); printf("FDC: read data: %x @ %x\n ", count, pos * 512); for (int i = 0; i < count; i++) { printf("%02x ", *((char *)buffer + i) & 0xff); if (i % 16 == 15) printf("\n "); } printf("\n"); theDMA->send_data(2, buffer); state.cmd_parms[4]++; if (state.cmd_parms[4] > (state.cmd_parms[6] / 2)) { state.cmd_parms[4] = 1; state.cmd_parms[3]++; if (state.cmd_parms[3] > 1) { state.cmd_parms[3] = 0; state.cmd_parms[2]++; } } state.cmd_res[0] = (state.cmd_parms[1] & 0x03) | ST0_SE | ST0_INTR; state.cmd_res[1] = 0; state.cmd_res[2] = 0; state.cmd_res[3] = state.cmd_parms[2]; state.cmd_res[4] = state.cmd_parms[3]; state.cmd_res[5] = state.cmd_parms[4]; state.cmd_res[6] = state.cmd_parms[5]; SEL_DRIVE.seeking = 1; do_interrupt(); } break; case 7: // recalibrate SEL_DRIVE.seeking = 3; // wait for 3 status reads to finish seek. SEL_DRIVE.cylinder = 0; do_interrupt(); break; case 8: // sense interrupt status if (!state.interrupt) state.cmd_res[0] = 0x80; else state.cmd_res[0] = 0x00; // ? state.cmd_res[1] = SEL_DRIVE.cylinder; // present cylinder number break; case 15: // seek // args: // 0: opcode // 1: bit 2 = HDS (head), 1 = DS1, 0 = DS0 // 2: NCN = new cylinder number SEL_DRIVE.seeking = 3; // wait 3 status reads to finish seek. SEL_DRIVE.cylinder = state.cmd_parms[2]; do_interrupt(); break; case 18: // perpendicular mode // We really don't care, somehow break; case 19: // configure // we're software, we don't care (I think) break; default: printf("Unhandled floppy command: %d = %s\n", cmd, cmdinfo[cmd].name); exit(1); } state.status.rqm = 1; if (cmdinfo[cmd].returns > 0) { state.status.dio = 1; } state.cmd_parms_ptr = 0; } else { // printf("FDC: command parameter byte %d = %x, expecting %d bytes for // %s\n", state.cmd_parms_ptr-1, data, cmdinfo[state.cmd_parms[0] & // 0x1f].parms, cmdinfo[state.cmd_parms[0] &0x1f].name); } } break; case FDC_REG_DIR: // PC/AT, PS/2 // bits 7-2 = reserved // bit 0-1 = MFM data rate state.datarate = data & 0x03; printf("FDC: data rate %s\n", datarate_name[state.datarate]); break; } } u64 CFloppyController::ReadMem(int index, u64 address, int dsize) { u64 data = 0; if (index == 1537) address += 7; switch (address) { case FDC_REG_STATUS_A: // bit 7 = interrupt pending // bit 6 = -DRV2 (second drive installed) // bit 5 = step // bit 4 = -track0 // bit 3 = head1 select // bit 2 = -index // bit 1 = -write protect // bit 0 = +direction break; case FDC_REG_STATUS_B: // bit 7-6 reserved (1) // bit 5 = drive select // bit 4 = write data // bit 3 = read data // bit 2 = write enable // bit 1 = motor 1 enable // bit 0 = motor 0 enable break; case FDC_REG_DOR: case FDC_REG_TAPE: printf("FDC: Write only register %" PRId64 " read.", address); break; case FDC_REG_STATUS: data = get_status(); break; case FDC_REG_COMMAND: // The data comes back from here. data = state.cmd_res[state.cmd_res_ptr++]; if (state.cmd_res_ptr >= state.cmd_res_max) { state.status.rqm = 1; state.status.dio = 0; } break; case FDC_REG_DIR: // PS/2 mode: // bit 7 = diskette change // bits 6-3 = 1 // bit 2 = datarate select 1 // bit 1 = datarate select 0 // bit 0 = high density select break; } printf("FDC: Read register %" PRId64 ", value: %" PRIx64 "\n", address, data); return data; } static u32 fdc_magic1 = 0x0fdc0fdc; static u32 fdc_magic2 = 0xfdc0fdc0; int CFloppyController::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&fdc_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&fdc_magic2, sizeof(u32), 1, f); printf("fdc: %ld bytes saved.\n", ss); return 0; } int CFloppyController::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("fdc: unexpected end of file!\n"); return -1; } if (m1 != fdc_magic1) { printf("fdc: MAGIC 1 does not match!\n"); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("fdc: unexpected end of file!\n"); return -1; } if (ss != sizeof(state)) { printf("fdc: STRUCT SIZE does not match!\n"); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("fdc: unexpected end of file!\n"); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("fdc: unexpected end of file!\n"); return -1; } if (m2 != fdc_magic2) { printf("fdc: MAGIC 1 does not match!\n"); return -1; } printf("fdc: %ld bytes restored.\n", ss); return 0; } void CFloppyController::do_interrupt() { // *shrug* I'll figure this out later. state.interrupt = true; } u8 CFloppyController::get_status() { // bit 7 = RQM data register is ready (0: no access is permitted) // bit 6 = 1: transfer from controller to system, 0: sys to controller // bit 5 = non dma mode // bit 4 = diskette controller is busy // bit 3-2 reserved // bit 1 = drive 1 is busy (seeking) // bit 0 = drive 0 is busy (seeking) for (int i = 0; i < 2; i++) { if (state.drive[i].seeking > 0) state.drive[i].seeking--; if (state.drive[i].seeking == 0) state.status.seeking[i] = false; else state.status.seeking[i] = true; } // we mark the controller busy if a disk is seeking or // if there is data waiting to be sent by the controller. if (state.status.seeking[0] || state.status.seeking[1] || (state.status.dio && state.status.rqm)) state.status.busy = true; else state.status.busy = false; printf("FDC Status: %s, %s, %s, %s, %s, %s\n", state.status.rqm ? "Data Register Ready" : "No Access", state.status.dio ? "C->S" : "S->C", state.status.nondma ? "No DMA" : "DMA", state.status.busy ? "BUSY" : "not busy", state.status.seeking[0] ? "Disk 1 Seeking" : "Disk 1 Idle", state.status.seeking[1] ? "Disk 0 Seeking" : "Disk 0 Idle"); u8 data = (state.status.rqm ? 0x80 : 0x00) | (state.status.dio ? 0x40 : 0x00) | (state.status.nondma ? 0x20 : 0x00) | (state.status.busy ? 0x10 : 0x00) | (state.status.seeking[1] ? 0x02 : 0x00) | (state.status.seeking[0] ? 0x01 : 0x00); return data; } ================================================ FILE: src/FloppyController.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_FLOPPYCONTROLLER_H) #define INCLUDED_FLOPPYCONTROLLER_H #include "DMA.hpp" #include "DiskController.hpp" #include "SystemComponent.hpp" /** * \brief Emulated floppy-drive controller. **/ class CFloppyController : public CSystemComponent, public CDiskController { public: virtual u64 ReadMem(int index, u64 address, int dsize); virtual void WriteMem(int index, u64 address, int dsize, u64 data); CFloppyController(class CConfigurator *cfg, class CSystem *c, int id); virtual ~CFloppyController(); virtual int RestoreState(FILE *f); virtual int SaveState(FILE *f); private: void do_interrupt(); u8 get_status(); struct { struct { int seeking; int cylinder; bool motor; } drive[2]; u8 write_precomp; u8 drive_select; bool dma; u8 datarate; struct { bool rqm; bool dio; bool nondma; bool busy; bool seeking[2]; } status; int busy; u8 cmd_parms[16]; u8 cmd_parms_ptr; u8 cmd_res[16]; u8 cmd_res_ptr; u8 cmd_res_max; bool interrupt; } state; }; #define FDC_REG_STATUS_A 0 #define FDC_REG_STATUS_B 1 #define FDC_REG_DOR 2 #define FDC_REG_TAPE 3 #define FDC_REG_STATUS 4 #define FDC_REG_COMMAND 5 #define FDC_REG_DIR 7 #define SEL_DRIVE state.drive[state.drive_select] #define SEL_FDISK get_disk(0, state.drive_select) #define DRIVE(i) state.drive[i] #define FDISK(i) get_disk(0, i) // // These defines were stolen from the Linux 1.0 fdreg.h file :) // /* Bits of FD_ST0 */ #define ST0_DS 0x03 /* drive select mask */ #define ST0_HA 0x04 /* Head (Address) */ #define ST0_NR 0x08 /* Not Ready */ #define ST0_ECE 0x10 /* Equipment chech error */ #define ST0_SE 0x20 /* Seek end */ #define ST0_INTR 0xC0 /* Interrupt code mask */ /* Bits of FD_ST1 */ #define ST1_MAM 0x01 /* Missing Address Mark */ #define ST1_WP 0x02 /* Write Protect */ #define ST1_ND 0x04 /* No Data - unreadable */ #define ST1_OR 0x10 /* OverRun */ #define ST1_CRC 0x20 /* CRC error in data or addr */ #define ST1_EOC 0x80 /* End Of Cylinder */ /* Bits of FD_ST2 */ #define ST2_MAM 0x01 /* Missing Addess Mark (again) */ #define ST2_BC 0x02 /* Bad Cylinder */ #define ST2_SNS 0x04 /* Scan Not Satisfied */ #define ST2_SEH 0x08 /* Scan Equal Hit */ #define ST2_WC 0x10 /* Wrong Cylinder */ #define ST2_CRC 0x20 /* CRC error in data field */ #define ST2_CM 0x40 /* Control Mark = deleted */ /* Bits of FD_ST3 */ #define ST3_HA 0x04 /* Head (Address) */ #define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */ #define ST3_WP 0x40 /* Write Protect */ #endif // !defined(INCLUDED_FLOPPYCONTROLLER_H) ================================================ FILE: src/FreeTextQuestion.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Question.hpp" /** * Question class that allows free-format text input. **/ class FreeTextQuestion : public Question { public: /** * Define a list of options to show following the question, * to show the user what values are acceptable. **/ void setOptions(string options) { mOptions = options; } /** * Ask the question, and return the answer. **/ virtual string ask() { for (;;) { cout << mQuestion; /* If there is an options list, display it after the * question enclosed in (). */ if (mOptions != "") cout << " (" << mOptions << ")"; /* If there is a default value, display it after the * question enclosed in []. */ if (mDefault != "") cout << " [" << mDefault << "]"; cout << ": "; /* Get the answer. */ getline(cin, mAnswer); /* If the question is answered with '?', display the * explanation, then ask again. */ if (mAnswer == "?") { explain(); continue; } /* If the question is answered with , set the * answer to the default answer. */ if (mAnswer == "") mAnswer = mDefault; /* Return the answer. */ return mAnswer; } } protected: /** List of options to show after the question. */ string mOptions; }; ================================================ FILE: src/Keyboard.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Keyboard.hpp" #include "AliM1543C.hpp" #include "StdAfx.hpp" #include "System.hpp" #include #include "gui/keymap.hpp" #include "gui/scancodes.hpp" /** * Constructor. **/ CKeyboard::CKeyboard(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { if (theKeyboard != 0) FAILURE(Configuration, "More than one Keyboard controller"); theKeyboard = this; } /** * Initialize the Keyboard device. **/ void CKeyboard::init() { int i; cSystem->RegisterMemory(this, 0, U64(0x00000801fc000060), 1); cSystem->RegisterMemory(this, 1, U64(0x00000801fc000064), 1); resetinternals(1); state.kbd_internal_buffer.led_status = 0; state.kbd_internal_buffer.scanning_enabled = 1; state.mouse_internal_buffer.num_elements = 0; for (i = 0; i < BX_MOUSE_BUFF_SIZE; i++) state.mouse_internal_buffer.buffer[i] = 0; state.mouse_internal_buffer.head = 0; state.status.pare = 0; state.status.tim = 0; state.status.auxb = 0; state.status.keyl = 1; state.status.c_d = 1; state.status.sysf = 0; state.status.inpb = 0; state.status.outb = 0; state.kbd_clock_enabled = 1; state.aux_clock_enabled = 0; state.allow_irq1 = 1; state.allow_irq12 = 1; state.kbd_output_buffer = 0; state.aux_output_buffer = 0; state.last_comm = 0; state.expecting_port60h = 0; state.irq1_requested = 0; state.irq12_requested = 0; state.expecting_mouse_parameter = 0; state.bat_in_progress = 0; state.scancodes_translate = 1; state.timer_pending = 0; // Mouse initialization stuff state.mouse.captured = myCfg->get_bool_value("mouse.enabled", true); state.mouse.sample_rate = 100; // reports per second state.mouse.resolution_cpmm = 4; // 4 counts per millimeter state.mouse.scaling = 1; /* 1:1 (default) */ state.mouse.mode = MOUSE_MODE_RESET; state.mouse.enable = 0; state.mouse.delayed_dx = 0; state.mouse.delayed_dy = 0; state.mouse.delayed_dz = 0; state.mouse.im_request = 0; // wheel mouse mode request state.mouse.im_mode = 0; // wheel mouse mode for (i = 0; i < BX_KBD_CONTROLLER_QSIZE; i++) state.kbd_controller_Q[i] = 0; state.kbd_controller_Qsize = 0; state.kbd_controller_Qsource = 0; printf("kbc: $Id: Keyboard.cpp,v 1.10 2008/05/31 15:47:09 iamcamiel Exp $\n"); } void CKeyboard::start_threads() { if (!myThread) { printf(" kbd"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); } } void CKeyboard::stop_threads() { StopThread = true; if (myThread) { printf(" kbd"); myThread->join(); myThread = nullptr; } } /** * Destructor. **/ CKeyboard::~CKeyboard() { stop_threads(); } u64 CKeyboard::ReadMem(int index, u64 address, int dsize) { switch (index) { case 0: return read_60(); break; case 1: return read_64(); break; default: FAILURE(InvalidArgument, "kbc: ReadMem index out of range"); } } void CKeyboard::WriteMem(int index, u64 address, int dsize, u64 data) { switch (index) { case 0: write_60((u8)data); break; case 1: write_64((u8)data); break; default: FAILURE(InvalidArgument, "kbc: ReadMem index out of range"); } } /** * Enqueue scancode for a keypress or key-release. Used by the GUI *implementation to send keypresses to the keyboard controller. **/ void CKeyboard::gen_scancode(u32 key) { unsigned char *scancode; u8 i; #if defined(DEBUG_KBD) printf("gen_scancode(): %s %s \n", bx_keymap->getBXKeyName(key), (key >> 31) ? "released" : "pressed"); if (!state.scancodes_translate) BX_DEBUG(("keyboard: gen_scancode with scancode_translate cleared")); #endif // Ignore scancode if keyboard clock is driven low if (state.kbd_clock_enabled == 0) return; // Ignore scancode if scanning is disabled if (state.kbd_internal_buffer.scanning_enabled == 0) return; // Source: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html // // Three scancode sets // // The usual PC keyboards are capable of producing three sets // of scancodes. Writing 0xf0 followed by 1, 2 or 3 to port // 0x60 will put the keyboard in scancode mode 1, 2 or 3. // Writing 0xf0 followed by 0 queries the mode, resulting in // a scancode byte 43, 41 or 3f from the keyboard. // // Set 1 contains the values that the XT keyboard (with only // one set of scancodes) produced, with extensions for new // keys. Someone decided that another numbering was more // logical and invented scancode Set 2. However, it was // realized that new scancodes would break old programs, so // the keyboard output was fed to a 8042 microprocessor on // the motherboard that could translate Set 2 back into Set // 1. Indeed a smart construction. This is the default today. // Finally there is the PS/2 version, Set 3, more regular, // but used by almost nobody. // // Sets 2 and 3 are designed to be translated by the 8042. // Set 1 should not be translated. // // Make and Break Codes // // The key press / key release is coded as follows: // // For Set 1, if the make code of a key is c, the break // code will be c+0x80. If the make code is e0 c, the // break code will be e0 c+0x80. The Pause key has make // code e1 1d 45 e1 9d c5 and does not generate a break code. // // For Set 2, if the make code of a key is c, the break code // will be f0 c. If the make code is e0 c, the break code // will be e0 f0 c. The Pause key has the 8-byte make code // e1 14 77 e1 f0 14 f0 77. // // For Set 3, by default most keys do not generate a break // code - only CapsLock, LShift, RShift, LCtrl and LAlt do. // However, by default all non-traditional keys do generate // a break code - thus, LWin, RWin, Menu do, and for example // on the Microsoft Internet keyboard, so do Back, Forward, // Stop, Mail, Search, Favorites, Web/Home, MyComputer, // Calculator, Sleep. On my BTC keyboard, also the Macro key // does. // // In Scancode Mode 3 it is possible to enable or disable // key repeat and the production of break codes either on a // key-by-key basis or for all keys at once. And just like // for Set 2, key release is indicated by a f0 prefix in // those cases where it is indicated. There is nothing // special with the Pause key in scancode mode 3. if (key & BX_KEY_RELEASED) scancode = (unsigned char *)scancodes[(key & 0xFF)][state.current_scancodes_set] .brek; else scancode = (unsigned char *)scancodes[(key & 0xFF)][state.current_scancodes_set] .make; // Translation // // The 8042 microprocessor translates the incoming byte stream // produced by the keyboard, and turns an f0 prefix into an OR // with 80 for the next byte. // // Unless told not to translate, the keyboard controller translates // keyboard scancodes into the scancodes it returns to the CPU using // the following table (in hex): // // +----+-------------------------------------------------+ // | | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | // +----+-------------------------------------------------+ // | 00 | ff 43 41 3f 3d 3b 3c 58 64 44 42 40 3e 0f 29 59 | // | 10 | 65 38 2a 70 1d 10 02 5a 66 71 2c 1f 1e 11 03 5b | // | 20 | 67 2e 2d 20 12 05 04 5c 68 39 2f 21 14 13 06 5d | // | 30 | 69 31 30 23 22 15 07 5e 6a 72 32 24 16 08 09 5f | // | 40 | 6b 33 25 17 18 0b 0a 60 6c 34 35 26 27 19 0c 61 | // | 50 | 6d 73 28 74 1a 0d 62 6e 3a 36 1c 1b 75 2b 63 76 | // | 60 | 55 56 77 78 79 7a 0e 7b 7c 4f 7d 4b 47 7e 7f 6f | // | 70 | 52 53 50 4c 4d 48 01 45 57 4e 51 4a 37 49 46 54 | // | 80 | 80 81 82 41 54 85 86 87 88 89 8a 8b 8c 8d 8e 8f | // | 90 | 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f | // | a0 | a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af | // | b0 | b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf | // | c0 | c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf | // | d0 | d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df | // | e0 | e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef | // | f0 | - f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff | // +----+-------------------------------------------------+ if (state.scancodes_translate) { // Translate before send u8 escaped = 0x00; for (i = 0; i < strlen((const char *)scancode); i++) { if (scancode[i] == 0xF0) { escaped = 0x80; } else { #if defined(DEBUG_KBD) printf("gen_scancode(): writing translated %02x \n", translation8042[scancode[i]] | escaped); #endif enQ(translation8042[scancode[i]] | escaped); escaped = 0x00; } } } else { // Send raw data for (i = 0; i < strlen((const char *)scancode); i++) { #if defined(DEBUG_KBD) printf("gen_scancode(): writing raw %02x \n", scancode[i]); #endif enQ(scancode[i]); } } } /** * Reset keyboard internals. **/ void CKeyboard::resetinternals(bool powerup) { state.kbd_internal_buffer.num_elements = 0; for (int i = 0; i < BX_KBD_ELEMENTS; i++) state.kbd_internal_buffer.buffer[i] = 0; state.kbd_internal_buffer.head = 0; state.kbd_internal_buffer.expecting_typematic = 0; state.kbd_internal_buffer.expecting_make_break = 0; // Default scancode set is mf2 (translation is controlled by the 8042) state.expecting_scancodes_set = 0; // state.current_scancodes_set = 1; state.current_scancodes_set = 2; // state.scancodes_translate = 1; if (powerup) { state.kbd_internal_buffer.expecting_led_write = 0; state.kbd_internal_buffer.delay = 1; // 500 mS state.kbd_internal_buffer.repeat_rate = 0x0b; // 10.9 chars/sec } } /** * Enqueue a byte (scancode) into the keyboard buffer. **/ void CKeyboard::enQ(u8 scancode) { int tail; #if defined(DEBUG_KBD) printf("enQ(0x%02x)", (unsigned)scancode); #endif if (state.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { printf("internal keyboard buffer full, ignoring scancode.(%02x) \n", (unsigned)scancode); return; } /* enqueue scancode in multibyte internal keyboard buffer */ #if defined(DEBUG_KBD) BX_DEBUG( ("enQ: putting scancode 0x%02x in internal buffer", (unsigned)scancode)); #endif tail = (state.kbd_internal_buffer.head + state.kbd_internal_buffer.num_elements) % BX_KBD_ELEMENTS; state.kbd_internal_buffer.buffer[tail] = scancode; state.kbd_internal_buffer.num_elements++; if (!state.status.outb && state.kbd_clock_enabled) { state.timer_pending = 1; return; } } /** * Read a byte from keyboard port 60. **/ u8 CKeyboard::read_60() { u8 val; /* output buffer */ if (state.status.auxb) { /* mouse byte available */ val = state.aux_output_buffer; state.aux_output_buffer = 0; state.status.outb = 0; state.status.auxb = 0; state.irq12_requested = 0; if (state.kbd_controller_Qsize) { unsigned i; state.aux_output_buffer = state.kbd_controller_Q[0]; state.status.outb = 1; state.status.auxb = 1; if (state.allow_irq12) state.irq12_requested = 1; for (i = 0; i < state.kbd_controller_Qsize - 1; i++) { // move Q elements towards head of queue by one state.kbd_controller_Q[i] = state.kbd_controller_Q[i + 1]; } state.kbd_controller_Qsize--; } // DEV_pic_lower_irq(12); state.timer_pending = 1; execute(); #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] read from 0x60 returns 0x%02x", val)); #endif return val; } else if (state.status.outb) { /* kbd byte available */ val = state.kbd_output_buffer; state.status.outb = 0; state.status.auxb = 0; state.irq1_requested = 0; state.bat_in_progress = 0; if (state.kbd_controller_Qsize) { unsigned i; state.aux_output_buffer = state.kbd_controller_Q[0]; state.status.outb = 1; state.status.auxb = 1; if (state.allow_irq1) state.irq1_requested = 1; for (i = 0; i < state.kbd_controller_Qsize - 1; i++) { // move Q elements towards head of queue by one state.kbd_controller_Q[i] = state.kbd_controller_Q[i + 1]; } #if defined(DEBUG_KBD) BX_DEBUG(("s.controller_Qsize: %02X", state.kbd_controller_Qsize)); #endif state.kbd_controller_Qsize--; } // DEV_pic_lower_irq(1); state.timer_pending = 1; execute(); #if defined(DEBUG_KBD) BX_DEBUG(("READ(60) = %02x", (unsigned)val)); #endif return val; } else { #if defined(DEBUG_KBD) BX_DEBUG(("num_elements = %d", state.kbd_internal_buffer.num_elements)); BX_DEBUG(("read from port 60h with outb empty")); BX_DEBUG(("READ(60) = %02x", state.kbd_output_buffer)); #endif return state.kbd_output_buffer; } } /** * Read a byte from keyboard port 64 * * The keyboard controller status register * * The keyboard controller has an 8-bit status register. It can be inspected by *the CPU by reading port 0x64. (Typically, it has the value 0x14: keyboard not *locked, self-test completed.) * * \code * +------+-----+------+------+-----+------+------+------+ * | PARE | TIM | AUXB | KEYL | C/D | SYSF | INPB | OUTB | * +------+-----+------+------+-----+------+------+------+ * \endcode * * Bit 7: Parity error * 0: OK. * 1: Parity error with last byte. * * Bit 6: Timeout * 0: OK. * 1: General timeout. * * Bit 5: Auxiliary output buffer full * Bit 0 tells whether a read from port 0x60 will be valid. If it is valid, *this bit 5 tells what data will be read from port 0x60. 0: Keyboard data. 1: *Mouse data. * * Bit 4: Keyboard lock * 0: Locked. * 1: Not locked. * * Bit 3: Command/Data * 0: Last write to input buffer was data (written via port 0x60). * 1: Last write to input buffer was a command (written via port 0x64). (This *bit is also referred to as Address Line A2.) * * Bit 2: System flag * Set to 0 after power on reset. Set to 1 after successful completion of the *keyboard controller self-test (Basic Assurance Test, BAT). Can also be set by *command (see below). * * Bit 1: Input buffer status * 0: Input buffer empty, can be written. * 1: Input buffer full, don't write yet. * * Bit 0: Output buffer status * 0: Output buffer empty, don't read yet. * 1: Output buffer full, can be read. (Bit 5 tells whether the available *data is from keyboard or mouse.) This bit is cleared when port 0x60 is read. **/ u8 CKeyboard::read_64() { u8 val; /* status register */ val = (state.status.pare << 7) | (state.status.tim << 6) | (state.status.auxb << 5) | (state.status.keyl << 4) | (state.status.c_d << 3) | (state.status.sysf << 2) | (state.status.inpb << 1) | (state.status.outb << 0); state.status.tim = 0; #if defined(DEBUG_KBD) BX_DEBUG(("read from 0x64 returns 0x%02x", val)); #endif return val; } /** * Write a byte to keyboard port 60. **/ void CKeyboard::write_60(u8 value) { #if defined(DEBUG_KBD) printf("kbd: port 60 write: %02x. \n", value); #endif // data byte written last to 0x60 state.status.c_d = 0; // if expecting data byte from command last sent to port 64h if (state.expecting_port60h) { state.expecting_port60h = 0; #if defined(DEBUG_KBD) if (state.status.inpb) printf("write to port 60h, not ready for write \n"); #endif switch (state.last_comm) { case 0x60: // write command byte { // The keyboard controller is provided with some RAM, for example // 32 bytes, that can be accessed by the CPU. The most important // part of this RAM is byte 0, the Controller Command Byte (CCB). // It can be read/written by writing 0x20/0x60 to port 0x64 and // then reading/writing a data byte from/to port 0x60. // // This byte has the following layout. // // +---+-------+----+----+---+------+-----+-----+ // | 0 | XLATE | ME | KE | 0 | SYSF | MIE | KIE | // +---+-------+----+----+---+------+-----+-----+ // // Bit 6: Translate // 0: No translation. // 1: Translate keyboard scancodes, using the translation table // given above. MCA type 2 controllers cannot set this bit // to 1. In this case scan code conversion is set using // keyboard command 0xf0 to port 0x60. // // Bit 5: Mouse enable // 0: Enable mouse. // 1: Disable mouse by driving the clock line low. // // Bit 4: Keyboard enable // 0: Enable keyboard. // 1: Disable keyboard by driving the clock line low. // // Bit 2: System flag // This bit is shown in bit 2 of the status register. A // "cold reboot" is one with this bit set to zero. A // "warm reboot" is one with this bit set to one (BAT // already completed). This will influence the tests and // initializations done by the POST. // // Bit 1: Mouse interrupt enable // 0: Do not use mouse interrupts. // 1: Send interrupt request IRQ12 when the mouse output // buffer is full. // // Bit 0: Keyboard interrupt enable // 0: Do not use keyboard interrupts. // 1: Send interrupt request IRQ1 when the keyboard output // buffer is full. // // When no interrupts are used, the CPU has to poll bits 0 // (and 5) of the status register. bool scan_convert; // The keyboard controller is provided with some RAM, for example bool disable_keyboard; // The keyboard controller is provided with some RAM, for example bool disable_aux; scan_convert = (value >> 6) & 0x01; disable_aux = (value >> 5) & 0x01; disable_keyboard = (value >> 4) & 0x01; state.status.sysf = (value >> 2) & 0x01; state.allow_irq1 = (value >> 0) & 0x01; state.allow_irq12 = (value >> 1) & 0x01; set_kbd_clock_enable(!disable_keyboard); set_aux_clock_enable(!disable_aux); if (state.allow_irq12 && state.status.auxb) state.irq12_requested = 1; else if (state.allow_irq1 && state.status.outb) state.irq1_requested = 1; #if defined(DEBUG_KBD) BX_DEBUG((" allow_irq12 set to %u", (unsigned)state.allow_irq12)); if (!scan_convert) BX_INFO(("keyboard: scan convert turned off")); #endif // (mch) NT needs this state.scancodes_translate = scan_convert; } break; case 0xd1: // write output port #if defined(DEBUG_KBD) BX_DEBUG(("write output port with value %02xh", (unsigned)value)); #endif break; case 0xd4: // Write to mouse // I don't think this enables the AUX clock // set_aux_clock_enable(1); // enable aux clock line ctrl_to_mouse(value); // ??? should I reset to previous value of aux enable? break; case 0xd3: // write mouse output buffer // Queue in mouse output buffer controller_enQ(value, 1); break; case 0xd2: // Queue in keyboard output buffer controller_enQ(value, 0); break; default: printf("=== unsupported write to port 60h(lastcomm=%02x): %02x \n", (unsigned)state.last_comm, (unsigned)value); } } else { // data byte written last to 0x60 state.status.c_d = 0; state.expecting_port60h = 0; /* pass byte to keyboard */ /* ??? should conditionally pass to mouse device here ??? */ if (state.kbd_clock_enabled == 0) set_kbd_clock_enable(1); ctrl_to_kbd(value); } execute(); } /** * Write a byte to keyboard port 64. **/ void CKeyboard::write_64(u8 value) { #if defined(DEBUG_KBD) printf("kbd: port 64 write: %02x. \n", value); #endif static int kbd_initialized = 0; u8 command_byte; // command byte written last to 0x64 state.status.c_d = 1; state.last_comm = value; // most commands NOT expecting port60 write next state.expecting_port60h = 0; switch (value) { case 0x20: // get keyboard command byte #if defined(DEBUG_KBD) BX_DEBUG(("get keyboard command byte")); #endif // controller output buffer must be empty if (state.status.outb) { #if defined(DEBUG_KBD) BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value)); #endif break; } command_byte = (state.scancodes_translate << 6) | ((!state.aux_clock_enabled) << 5) | ((!state.kbd_clock_enabled) << 4) | (0 << 3) | (state.status.sysf << 2) | (state.allow_irq12 << 1) | (state.allow_irq1 << 0); controller_enQ(command_byte, 0); break; case 0x60: // write command byte #if defined(DEBUG_KBD) printf("kbd_ctrl: command 60: write command byte. \n"); #endif // following byte written to port 60h is command byte state.expecting_port60h = 1; break; case 0xa0: #if defined(DEBUG_KBD) printf("kbd_ctrl: command a0: BIOS name (not supported). \n"); #endif break; case 0xa1: #if defined(DEBUG_KBD) printf("kbd_ctrl: command a0: BIOS version (not supported). \n"); #endif break; case 0xa7: // disable the aux device set_aux_clock_enable(0); #if defined(DEBUG_KBD) printf("kbd_ctrl: command a7: aux i/f disable. \n"); #endif break; case 0xa8: // enable the aux device set_aux_clock_enable(1); #if defined(DEBUG_KBD) printf("kbd_ctrl: command a7: aux i/f enable. \n"); #endif break; case 0xa9: // Test Mouse Port // controller output buffer must be empty #if defined(DEBUG_KBD) printf("kbd_ctrl: command a9: aux i/f test. \n"); #endif if (state.status.outb) { printf("kbd: OUTB set and command 0x%02x encountered", value); break; } controller_enQ(0x00, 0); // no errors detected break; case 0xaa: // motherboard controller self test #if defined(DEBUG_KBD) printf("kbd_ctrl: command aa: self test. \n"); #endif if (kbd_initialized == 0) { state.kbd_controller_Qsize = 0; state.status.outb = 0; kbd_initialized = 1; } // controller output buffer must be empty if (state.status.outb) { printf("kbd: OUTB set and command 0x%02x encountered", value); // break; // drain the queue? state.kbd_internal_buffer.head = 0; state.kbd_internal_buffer.num_elements = 0; state.status.outb = 0; } state.status.sysf = 1; // self test complete controller_enQ(0x55, 0); // controller OK break; case 0xab: // Interface Test #if defined(DEBUG_KBD) printf("kbd_ctrl: command ab: kbd i/f test. \n"); #endif // controller output buffer must be empty if (state.status.outb) { printf("kbd: OUTB set and command 0x%02x encountered", value); break; } controller_enQ(0x00, 0); break; case 0xad: // disable keyboard set_kbd_clock_enable(0); #if defined(DEBUG_KBD) printf("kbd_ctrl: command ad: kbd i/f disable. \n"); #endif break; case 0xae: // enable keyboard set_kbd_clock_enable(1); #if defined(DEBUG_KBD) printf("kbd_ctrl: command ae: kbd i/f enable. \n"); #endif break; case 0xaf: // get controller version #if defined(DEBUG_KBD) printf("kbd_ctrl: command af: controller version (not supported). \n"); #endif break; case 0xc0: // read input port #if defined(DEBUG_KBD) printf("kbd_ctrl: command c0: read input port. \n"); #endif // controller output buffer must be empty if (state.status.outb) { BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); break; } // keyboard not inhibited controller_enQ(0x80, 0); break; case 0xd0: // read output port: next byte read from port 60h #if defined(DEBUG_KBD) printf("kbd_ctrl: command d0: read output port. (partial) \n"); #endif // controller output buffer must be empty if (state.status.outb) { BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); break; } controller_enQ((state.irq12_requested << 5) | (state.irq1_requested << 4) | // (BX_GET_ENABLE_A20() << 1) | 0x01, 0); break; case 0xd1: // write output port: next byte written to port 60h #if defined(DEBUG_KBD) printf("kbd_ctrl: command d1: write output port. \n"); #endif // following byte to port 60h written to output port state.expecting_port60h = 1; break; case 0xd3: // write mouse output buffer #if defined(DEBUG_KBD) printf("kbd_ctrl: command d3: write aux output buffer. \n"); #endif // following byte to port 60h written to output port as mouse write. state.expecting_port60h = 1; break; case 0xd4: // write to mouse #if defined(DEBUG_KBD) printf("kbd_ctrl: command d4: write to aux. \n"); #endif // following byte written to port 60h state.expecting_port60h = 1; break; case 0xd2: // write keyboard output buffer #if defined(DEBUG_KBD) printf("kbd_ctrl: command d2: write kbd output buffer. \n"); #endif state.expecting_port60h = 1; break; case 0xc1: // Continuous Input Port Poll, Low case 0xc2: // Continuous Input Port Poll, High case 0xe0: // Read Test Inputs BX_PANIC(("io write 0x64: command = %02xh", (unsigned)value)); break; default: if (value == 0xff || (value >= 0xf0 && value <= 0xfd)) { /* useless pulse output bit commands ??? */ #if defined(DEBUG_KBD) BX_DEBUG(("io write to port 64h, useless command %02x", (unsigned)value)); #endif return; } BX_ERROR(("unsupported io write to keyboard port 64, value = %x", (unsigned)value)); break; } execute(); } /** * Enqueue a byte into one of the keyboard controller's output buffers. **/ void CKeyboard::controller_enQ(u8 data, unsigned source) { // source is 0 for keyboard, 1 for mouse #if defined(DEBUG_KBD) BX_DEBUG(("controller_enQ(%02x) source=%02x", (unsigned)data, source)); #endif // see if we need to Q this byte from the controller // remember this includes mouse bytes. if (state.status.outb) { if (state.kbd_controller_Qsize >= BX_KBD_CONTROLLER_QSIZE) FAILURE(Runtime, "controller_enq(): controller_Q full!"); state.kbd_controller_Q[state.kbd_controller_Qsize++] = data; state.kbd_controller_Qsource = source; return; } // the Q is empty if (source == 0) { // keyboard state.kbd_output_buffer = data; state.status.outb = 1; state.status.auxb = 0; state.status.inpb = 0; if (state.allow_irq1) state.irq1_requested = 1; } else { // mouse state.aux_output_buffer = data; state.status.outb = 1; state.status.auxb = 1; state.status.inpb = 0; if (state.allow_irq12) state.irq12_requested = 1; } } /** * Enable or disable the keyboard clock. **/ void CKeyboard::set_kbd_clock_enable(u8 value) { bool prev_kbd_clock_enabled; if (value == 0) { state.kbd_clock_enabled = 0; } else { /* is another byte waiting to be sent from the keyboard ? */ prev_kbd_clock_enabled = state.kbd_clock_enabled; state.kbd_clock_enabled = 1; if (prev_kbd_clock_enabled == 0 && state.status.outb == 0) state.timer_pending = 1; } } /** * Enable or disable the mouse clock. **/ void CKeyboard::set_aux_clock_enable(u8 value) { bool prev_aux_clock_enabled; #if defined(DEBUG_KBD) BX_DEBUG(("set_aux_clock_enable(%u)", (unsigned)value)); #endif if (value == 0) { state.aux_clock_enabled = 0; } else { /* is another byte waiting to be sent from the keyboard ? */ prev_aux_clock_enabled = state.aux_clock_enabled; state.aux_clock_enabled = 1; if (prev_aux_clock_enabled == 0 && state.status.outb == 0) state.timer_pending = 1; } } /** * Send a byte from controller to keyboard **/ void CKeyboard::ctrl_to_kbd(u8 value) { #if defined(DEBUG_KBD) BX_DEBUG(("controller passed byte %02xh to keyboard", value)); #endif if (state.kbd_internal_buffer.expecting_make_break) { state.kbd_internal_buffer.expecting_make_break = 0; #if defined(DEBUG_KBD) printf("setting key %x to make/break mode (unused) \n", value); #endif enQ(0xFA); // send ACK return; } if (state.kbd_internal_buffer.expecting_typematic) { state.kbd_internal_buffer.expecting_typematic = 0; state.kbd_internal_buffer.delay = (value >> 5) & 0x03; #if defined(DEBUG_KBD) switch (state.kbd_internal_buffer.delay) { case 0: BX_INFO(("setting delay to 250 mS (unused)")); break; case 1: BX_INFO(("setting delay to 500 mS (unused)")); break; case 2: BX_INFO(("setting delay to 750 mS (unused)")); break; case 3: BX_INFO(("setting delay to 1000 mS (unused)")); break; } #endif state.kbd_internal_buffer.repeat_rate = value & 0x1f; #if defined(DEBUG_KBD) double cps = 1 / ((double)(8 + (value & 0x07)) * (double)exp(log((double)2) * (double)((value >> 3) & 0x03)) * 0.00417); BX_INFO(("setting repeat rate to %.1f cps (unused)", cps)); #endif enQ(0xFA); // send ACK return; } if (state.kbd_internal_buffer.expecting_led_write) { state.kbd_internal_buffer.expecting_led_write = 0; state.kbd_internal_buffer.led_status = value; #if defined(DEBUG_KBD) BX_DEBUG(("LED status set to %02x", (unsigned)state.kbd_internal_buffer.led_status)); #endif enQ(0xFA); // send ACK %%% return; } if (state.expecting_scancodes_set) { state.expecting_scancodes_set = 0; if (value != 0) { if (value < 4) { state.current_scancodes_set = (value - 1); #if defined(DEBUG_KBD) BX_INFO(("Switched to scancode set %d", (unsigned)state.current_scancodes_set + 1)); #endif enQ(0xFA); } else { BX_ERROR(("Received scancodes set out of range: %d", value)); enQ(0xFF); // send ERROR } } else { // Send ACK (SF patch #1159626) enQ(0xFA); // Send current scancodes set to port 0x60 if (state.scancodes_translate) enQ(translation8042[1 + state.current_scancodes_set]); else enQ(1 + state.current_scancodes_set); } return; } switch (value) { // case 0x00: // ??? ignore and let OS timeout with no response //#if defined(DEBUG_KBD) // printf("kbd: command 00: ignored. \n"); //#endif // enQ(0xFA); // send ACK %%% // break; // // case 0x05: // ??? //#if defined(DEBUG_KBD) // printf("kbd: command 05: unknown. \n"); //#endif // // (mch) trying to get this to work... // state.status.sysf = 1; // enQ_imm(0xfe); // break; case 0xed: // LED Write state.kbd_internal_buffer.expecting_led_write = 1; #if defined(DEBUG_KBD) printf("kbd: Expecting led write info. \n"); #endif enQ_imm(0xFA); // send ACK %%% break; case 0xee: // echo #if defined(DEBUG_KBD) printf("kbd: command ee: echo. \n"); #endif enQ(0xEE); // return same byte (EEh) as echo diagnostic break; case 0xf0: // Select alternate scan code set state.expecting_scancodes_set = 1; #if defined(DEBUG_KBD) printf("kbd: Expecting scancode set info. \n"); #endif enQ(0xFA); // send ACK break; case 0xf2: // identify keyboard #if defined(DEBUG_KBD) printf("kbd: command f2: identify keyboard. \n"); #endif // Keyboard IDs // // Keyboards do report an ID as a reply to the command f2. An MF2 AT // keyboard reports ID ab 83. Translation turns this into ab 41. enQ(0xFA); enQ(0xAB); if (state.scancodes_translate) enQ(0x41); else enQ(0x83); break; case 0xf3: // typematic info state.kbd_internal_buffer.expecting_typematic = 1; #if defined(DEBUG_KBD) printf("kbd: Expecting typematic info. \n"); #endif enQ(0xFA); // send ACK break; case 0xf4: // enable keyboard state.kbd_internal_buffer.scanning_enabled = 1; #if defined(DEBUG_KBD) printf("kbd: command f4: enable keyboard. \n"); #endif enQ(0xFA); // send ACK break; case 0xf5: // reset keyboard to power-up settings and disable scanning resetinternals(1); enQ(0xFA); // send ACK state.kbd_internal_buffer.scanning_enabled = 0; #if defined(DEBUG_KBD) printf("kbd: command f5: reset and disable keyboard. \n"); #endif break; case 0xf6: // reset keyboard to power-up settings and enable scanning resetinternals(1); enQ(0xFA); // send ACK state.kbd_internal_buffer.scanning_enabled = 1; #if defined(DEBUG_KBD) printf("kbd: command f6: reset and enable keyboard. \n"); #endif break; case 0xfc: // PS/2 Set Key Type to Make/Break state.kbd_internal_buffer.expecting_make_break = 1; #if defined(DEBUG_KBD) printf("kbd: Expecting make/break info. \n"); #endif enQ(0xFA); /* send ACK */ break; case 0xfe: // resend. aiiee. printf("kbd: resend command received. \n"); break; case 0xff: // reset: internal keyboard reset and afterwards the BAT #if defined(DEBUG_KBD) printf("kbd: command ff: reset keyboard w/BAT. \n"); #endif resetinternals(1); enQ(0xFA); // send ACK state.bat_in_progress = 1; enQ(0xAA); // BAT test passed break; // case 0xd3: // enQ(0xfa); // break; case 0xf7: // PS/2 Set All Keys To Typematic case 0xf8: // PS/2 Set All Keys to Make/Break case 0xf9: // PS/2 PS/2 Set All Keys to Make case 0xfa: // PS/2 Set All Keys to Typematic Make/Break case 0xfb: // PS/2 Set Key Type to Typematic case 0xfd: // PS/2 Set Key Type to Make printf("kbd: unhandled command: %02x, ACKing \n", value); enQ(0xFA); break; default: printf("kbd: command %02x: not recognized! \n", value); enQ(0xFE); /* send NACK */ break; } } /** * enqueue scancode in multibyte internal keyboard buffer **/ void CKeyboard::enQ_imm(u8 val) { int tail; if (state.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { BX_PANIC(("internal keyboard buffer full (imm)")); return; } tail = (state.kbd_internal_buffer.head + state.kbd_internal_buffer.num_elements) % BX_KBD_ELEMENTS; state.kbd_output_buffer = val; state.status.outb = 1; if (state.allow_irq1) state.irq1_requested = 1; } /** * Send a byte from controller to mouse **/ void CKeyboard::ctrl_to_mouse(u8 value) { #if defined(DEBUG_KBD) BX_DEBUG(("MOUSE: ctrl_to_mouse(%02xh)", (unsigned)value)); BX_DEBUG((" enable = %u", (unsigned)state.mouse.enable)); BX_DEBUG((" allow_irq12 = %u", (unsigned)state.allow_irq12)); BX_DEBUG((" aux_clock_enabled = %u", (unsigned)state.aux_clock_enabled)); #endif // an ACK (0xFA) is always the first response to any valid input // received from the system other than Set-Wrap-Mode & Resend-Command if (state.expecting_mouse_parameter) { state.expecting_mouse_parameter = 0; switch (state.last_mouse_command) { case 0xf3: // Set Mouse Sample Rate state.mouse.sample_rate = value; #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Sampling rate set: %d Hz", value)); #endif if ((value == 200) && (!state.mouse.im_request)) { state.mouse.im_request = 1; } else if ((value == 100) && (state.mouse.im_request == 1)) { state.mouse.im_request = 2; } else if ((value == 80) && (state.mouse.im_request == 2)) { #if defined(DEBUG_KBD) BX_INFO(("wheel mouse mode enabled")); #endif state.mouse.im_mode = 1; state.mouse.im_request = 0; } else { state.mouse.im_request = 0; } controller_enQ(0xFA, 1); // ack break; case 0xe8: // Set Mouse Resolution switch (value) { case 0: state.mouse.resolution_cpmm = 1; break; case 1: state.mouse.resolution_cpmm = 2; break; case 2: state.mouse.resolution_cpmm = 4; break; case 3: state.mouse.resolution_cpmm = 8; break; default: BX_PANIC(("[mouse] Unknown resolution %d", value)); break; } #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Resolution set to %d counts per mm", state.mouse.resolution_cpmm)); #endif controller_enQ(0xFA, 1); // ack break; default: BX_PANIC(("MOUSE: unknown last command (%02xh)", (unsigned)state.last_mouse_command)); } } else { state.expecting_mouse_parameter = 0; state.last_mouse_command = value; // test for wrap mode first if (state.mouse.mode == MOUSE_MODE_WRAP) { // if not a reset command or reset wrap mode // then just echo the byte. if ((value != 0xff) && (value != 0xec)) { // if (bx_dbg.mouse) #if defined(DEBUG_KBD) BX_INFO(("[mouse] wrap mode: Ignoring command %0X02.", value)); #endif controller_enQ(value, 1); // bail out return; } } switch (value) { case 0xe6: // Set Mouse Scaling to 1:1 controller_enQ(0xFA, 1); // ACK state.mouse.scaling = 2; #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Scaling set to 1:1")); #endif break; case 0xe7: // Set Mouse Scaling to 2:1 controller_enQ(0xFA, 1); // ACK state.mouse.scaling = 2; #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Scaling set to 2:1")); #endif break; case 0xe8: // Set Mouse Resolution controller_enQ(0xFA, 1); // ACK state.expecting_mouse_parameter = 1; break; case 0xea: // Set Stream Mode // if (bx_dbg.mouse) #if defined(DEBUG_KBD) BX_INFO(("[mouse] Mouse stream mode on.")); #endif state.mouse.mode = MOUSE_MODE_STREAM; controller_enQ(0xFA, 1); // ACK break; case 0xec: // Reset Wrap Mode // unless we are in wrap mode ignore the command if (state.mouse.mode == MOUSE_MODE_WRAP) { // if (bx_dbg.mouse) #if defined(DEBUG_KBD) BX_INFO(("[mouse] Mouse wrap mode off.")); #endif // restore previous mode except disable stream mode reporting. // ### TODO disabling reporting in stream mode state.mouse.mode = state.mouse.saved_mode; controller_enQ(0xFA, 1); // ACK } break; case 0xee: // Set Wrap Mode // ### TODO flush output queue. // ### TODO disable interrupts if in stream mode. // if (bx_dbg.mouse) #if defined(DEBUG_KBD) BX_INFO(("[mouse] Mouse wrap mode on.")); #endif state.mouse.saved_mode = state.mouse.mode; state.mouse.mode = MOUSE_MODE_WRAP; controller_enQ(0xFA, 1); // ACK break; case 0xf0: // Set Remote Mode (polling mode, i.e. not stream mode.) // if (bx_dbg.mouse) #if defined(DEBUG_KBD) BX_INFO(("[mouse] Mouse remote mode on.")); #endif // ### TODO should we flush/discard/ignore any already queued packets? state.mouse.mode = MOUSE_MODE_REMOTE; controller_enQ(0xFA, 1); // ACK break; case 0xf2: // Read Device Type controller_enQ(0xFA, 1); // ACK if (state.mouse.im_mode) controller_enQ(0x03, 1); // Device ID (wheel z-mouse) else controller_enQ(0x00, 1); // Device ID (standard) #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Read mouse ID")); #endif break; case 0xf3: // Set Mouse Sample Rate (sample rate written to port 60h) controller_enQ(0xFA, 1); // ACK state.expecting_mouse_parameter = 1; break; case 0xf4: // Enable (in stream mode) state.mouse.enable = 1; controller_enQ(0xFA, 1); // ACK #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Mouse enabled (stream mode)")); #endif break; case 0xf5: // Disable (in stream mode) state.mouse.enable = 0; controller_enQ(0xFA, 1); // ACK #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Mouse disabled (stream mode)")); #endif break; case 0xf6: // Set Defaults state.mouse.sample_rate = 100; /* reports per second (default) */ state.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ state.mouse.scaling = 1; /* 1:1 (default) */ state.mouse.enable = 0; state.mouse.mode = MOUSE_MODE_STREAM; controller_enQ(0xFA, 1); // ACK #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Set Defaults")); #endif break; case 0xff: // Reset state.mouse.sample_rate = 100; /* reports per second (default) */ state.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ state.mouse.scaling = 1; /* 1:1 (default) */ state.mouse.mode = MOUSE_MODE_RESET; state.mouse.enable = 0; #if defined(DEBUG_KBD) if (state.mouse.im_mode) BX_INFO(("wheel mouse mode disabled")); #endif state.mouse.im_mode = 0; controller_enQ(0xFA, 1); // ACK controller_enQ(0xAA, 1); // completion code controller_enQ(0x00, 1); // ID code (standard after reset) #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Mouse reset")); #endif break; case 0xe9: // Get mouse information // should we ack here? (mch): Yes controller_enQ(0xFA, 1); // ACK controller_enQ(state.mouse.get_status_byte(), 1); // status controller_enQ(state.mouse.get_resolution_byte(), 1); // resolution controller_enQ(state.mouse.sample_rate, 1); // sample rate #if defined(DEBUG_KBD) BX_DEBUG(("[mouse] Get mouse information")); #endif break; case 0xeb: // Read Data (send a packet when in Remote Mode) controller_enQ(0xFA, 1); // ACK // perhaps we should be adding some movement here. mouse_enQ_packet(((state.mouse.button_status & 0x0f) | 0x08), 0x00, 0x00, 0x00); // bit3 of first byte always set // assumed we really aren't in polling mode, a rather odd assumption. #if defined(DEBUG_KBD) BX_ERROR(("[mouse] Warning: Read Data command partially supported.")); #endif break; case 0xbb: // OS/2 Warp 3 uses this command #if defined(DEBUG_KBD) BX_ERROR(("[mouse] ignoring 0xbb command")); #endif break; default: BX_ERROR(("[mouse] ctrl_to_mouse(): got value of 0x%02x", value)); controller_enQ(0xFE, 1); /* send NACK */ } } } /** * Put a (3/4 byte) mouse packet into the mouse buffer. **/ bool CKeyboard::mouse_enQ_packet(u8 b1, u8 b2, u8 b3, u8 b4) { int bytes = 3; if (state.mouse.im_mode) bytes = 4; if ((state.mouse_internal_buffer.num_elements + bytes) >= BX_MOUSE_BUFF_SIZE) { return (0); /* buffer doesn't have the space */ } mouse_enQ(b1); mouse_enQ(b2); mouse_enQ(b3); if (state.mouse.im_mode) mouse_enQ(b4); return (1); } /** * Put a byte into the mouse buffer. **/ void CKeyboard::mouse_enQ(u8 mouse_data) { int tail; #if defined(DEBUG_KBD) BX_DEBUG(("mouse_enQ(%02x)", (unsigned)mouse_data)); #endif if (state.mouse_internal_buffer.num_elements >= BX_MOUSE_BUFF_SIZE) { BX_ERROR(("[mouse] internal mouse buffer full, ignoring mouse data.(%02x)", (unsigned)mouse_data)); return; } /* enqueue mouse data in multibyte internal mouse buffer */ tail = (state.mouse_internal_buffer.head + state.mouse_internal_buffer.num_elements) % BX_MOUSE_BUFF_SIZE; state.mouse_internal_buffer.buffer[tail] = mouse_data; state.mouse_internal_buffer.num_elements++; if (!state.status.outb && state.aux_clock_enabled) { state.timer_pending = 1; return; } } /** * Determine what IRQ's need to be asserted. **/ unsigned CKeyboard::periodic() { u8 retval; retval = (state.irq1_requested << 0) | (state.irq12_requested << 1); state.irq1_requested = 0; state.irq12_requested = 0; if (state.timer_pending == 0) { return (retval); } if (1 >= state.timer_pending) { state.timer_pending = 0; } else { state.timer_pending--; return (retval); } if (state.status.outb) { return (retval); } /* nothing in outb, look for possible data xfer from keyboard or mouse */ if (state.kbd_internal_buffer.num_elements && (state.kbd_clock_enabled || state.bat_in_progress)) { #if defined(DEBUG_KBD) BX_DEBUG(("service_keyboard: key in internal buffer waiting")); #endif state.kbd_output_buffer = state.kbd_internal_buffer.buffer[state.kbd_internal_buffer.head]; state.status.outb = 1; // commented out since this would override the current state of the // mouse buffer flag - no bug seen - just seems wrong (das) // state.auxb = 0; state.kbd_internal_buffer.head = (state.kbd_internal_buffer.head + 1) % BX_KBD_ELEMENTS; state.kbd_internal_buffer.num_elements--; if (state.allow_irq1) state.irq1_requested = 1; } else { create_mouse_packet(0); if (state.aux_clock_enabled && state.mouse_internal_buffer.num_elements) { #if defined(DEBUG_KBD) BX_DEBUG( ("service_keyboard: key(from mouse) in internal buffer waiting")); #endif state.aux_output_buffer = state.mouse_internal_buffer.buffer[state.mouse_internal_buffer.head]; state.status.outb = 1; state.status.auxb = 1; state.mouse_internal_buffer.head = (state.mouse_internal_buffer.head + 1) % BX_MOUSE_BUFF_SIZE; state.mouse_internal_buffer.num_elements--; if (state.allow_irq12) state.irq12_requested = 1; } #if defined(DEBUG_KBD) else { BX_DEBUG(("service_keyboard(): no keys waiting")); } #endif } return (retval); } /** * Create a mouse packet and send it to the controller if needed. **/ void CKeyboard::create_mouse_packet(bool force_enq) { u8 b1; u8 b2; u8 b3; u8 b4; if (state.mouse_internal_buffer.num_elements && !force_enq) return; s16 delta_x = state.mouse.delayed_dx; s16 delta_y = state.mouse.delayed_dy; u8 button_state = state.mouse.button_status | 0x08; if (!force_enq && !delta_x && !delta_y) { return; } if (delta_x > 254) delta_x = 254; if (delta_x < -254) delta_x = -254; if (delta_y > 254) delta_y = 254; if (delta_y < -254) delta_y = -254; b1 = (button_state & 0x0f) | 0x08; // bit3 always set if ((delta_x >= 0) && (delta_x <= 255)) { b2 = (u8)delta_x; state.mouse.delayed_dx -= delta_x; } else if (delta_x > 255) { b2 = (u8)0xff; state.mouse.delayed_dx -= 255; } else if (delta_x >= -256) { b2 = (u8)delta_x; b1 |= 0x10; state.mouse.delayed_dx -= delta_x; } else { b2 = (u8)0x00; b1 |= 0x10; state.mouse.delayed_dx += 256; } if ((delta_y >= 0) && (delta_y <= 255)) { b3 = (u8)delta_y; state.mouse.delayed_dy -= delta_y; } else if (delta_y > 255) { b3 = (u8)0xff; state.mouse.delayed_dy -= 255; } else if (delta_y >= -256) { b3 = (u8)delta_y; b1 |= 0x20; state.mouse.delayed_dy -= delta_y; } else { b3 = (u8)0x00; b1 |= 0x20; state.mouse.delayed_dy += 256; } b4 = (u8)-state.mouse.delayed_dz; mouse_enQ_packet(b1, b2, b3, b4); } /** * Keyboard clock. Handle events on a clocked basis. * * Do the following: * - Let the GUI (if available) handle any pending events. * - Check if interrupts need to be asserted. * - Assert interrupts as needed. * . **/ void CKeyboard::execute() { unsigned retval; /* -- moved to VGA card -- if(bx_gui) { bx_gui->lock(); bx_gui->handle_events(); bx_gui->unlock(); } */ retval = periodic(); if (retval & 0x01) theAli->pic_interrupt(0, 1); if (retval & 0x02) theAli->pic_interrupt(1, 4); } /** * Check if threads are still running. **/ void CKeyboard::check_state() { if (myThreadDead.load()) FAILURE(Thread, "KBD thread has died"); } /** * Thread entry point. **/ void CKeyboard::run() { try { for (;;) { if (StopThread) return; execute(); std::this_thread::sleep_for(std::chrono::milliseconds(20)); } } catch (CException &e) { printf("Exception in kbd thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } static u32 kb_magic1 = 0x65481687; static u32 kb_magic2 = 0x24895375; /** * Save state to a Virtual Machine State file. **/ int CKeyboard::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&kb_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&kb_magic2, sizeof(u32), 1, f); printf("kbc: %d bytes saved.\n", (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CKeyboard::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("kbc: unexpected end of file!\n"); return -1; } if (m1 != kb_magic1) { printf("kbc: MAGIC 1 does not match!\n"); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("kbc: unexpected end of file!\n"); return -1; } if (ss != sizeof(state)) { printf("kbc: STRUCT SIZE does not match!\n"); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("kbc: unexpected end of file!\n"); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("kbc: unexpected end of file!\n"); return -1; } if (m2 != kb_magic2) { printf("kbc: MAGIC 1 does not match!\n"); return -1; } printf("kbc: %d bytes restored.\n", (int)ss); return 0; } CKeyboard *theKeyboard = 0; ================================================ FILE: src/Keyboard.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_KEYBOARD_H) #define INCLUDED_KEYBOARD_H #include "SystemComponent.hpp" #include "gui/gui.hpp" #define BX_KBD_ELEMENTS 16 #define BX_MOUSE_BUFF_SIZE 48 #define MOUSE_MODE_RESET 10 #define MOUSE_MODE_STREAM 11 #define MOUSE_MODE_REMOTE 12 #define MOUSE_MODE_WRAP 13 /** * \brief Emulated keyboard controller, keyboard and mouse. **/ class CKeyboard : public CSystemComponent { public: CKeyboard(CConfigurator *cfg, CSystem *c); virtual ~CKeyboard(); virtual void check_state(); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u64 ReadMem(int index, u64 address, int dsize); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void run(); void execute(); void gen_scancode(u32 key); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; bool StopThread; u8 read_60(); void write_60(u8 data); u8 read_64(); void write_64(u8 data); void resetinternals(bool powerup); void enQ(u8 scancode); void controller_enQ(u8 data, unsigned source); void set_kbd_clock_enable(u8 value); void set_aux_clock_enable(u8 value); void ctrl_to_kbd(u8 value); void enQ_imm(u8 val); void ctrl_to_mouse(u8 value); bool mouse_enQ_packet(u8 b1, u8 b2, u8 b3, u8 b4); void mouse_enQ(u8 mouse_data); unsigned periodic(); // void kbd_clock(); void create_mouse_packet(bool force_enq); /// The state structure contains all elements that need to be saved to the /// statefile. struct SKb_state { /// status bits matching the status port struct SAli_kbdc_status { bool pare; /**< Bit7, 1= parity error from keyboard/mouse - ignored. */ bool tim; /**< Bit6, 1= timeout from keyboard - ignored. */ bool auxb; /**< Bit5, 1= mouse data waiting for CPU to read. */ bool keyl; /**< Bit4, 1= keyswitch in lock position - ignored. */ bool c_d; /**< Bit3, 1=command to port 64h, 0=data to port 60h. */ bool sysf; /**< Bit2 */ bool inpb; /**< Bit1 */ bool outb; /**< Bit0, 1= keyboard data or mouse data ready for CPU. Check aux to see which. */ } status; /* internal to our version of the keyboard controller */ bool kbd_clock_enabled; bool aux_clock_enabled; bool allow_irq1; bool allow_irq12; u8 kbd_output_buffer; u8 aux_output_buffer; u8 last_comm; u8 expecting_port60h; u8 expecting_mouse_parameter; u8 last_mouse_command; u32 timer_pending; bool irq1_requested; bool irq12_requested; bool scancodes_translate; bool expecting_scancodes_set; u8 current_scancodes_set; bool bat_in_progress; /// mouse status struct SAli_mouse { bool captured; // host mouse capture enabled // u8 type; u8 sample_rate; u8 resolution_cpmm; // resolution in counts per mm u8 scaling; u8 mode; u8 saved_mode; // the mode prior to entering wrap mode bool enable; u8 get_status_byte() { // top bit is 0 , bit 6 is 1 if remote mode. u8 ret = (u8)((mode == MOUSE_MODE_REMOTE) ? 0x40 : 0); ret |= (enable << 5); ret |= (scaling == 1) ? 0 : (1 << 4); ret |= ((button_status & 0x1) << 2); ret |= ((button_status & 0x2) << 0); return ret; } u8 get_resolution_byte() { u8 ret = 0; switch (resolution_cpmm) { case 1: ret = 0; break; case 2: ret = 1; break; case 4: ret = 2; break; case 8: ret = 3; break; default: FAILURE(NotImplemented, "mouse: invalid resolution_cpmm"); }; return ret; } u8 button_status; s16 delayed_dx; s16 delayed_dy; s16 delayed_dz; u8 im_request; bool im_mode; } mouse; /// internal keyboard buffer struct SAli_kbdib { int num_elements; u8 buffer[BX_KBD_ELEMENTS]; int head; bool expecting_typematic; bool expecting_led_write; bool expecting_make_break; u8 delay; u8 repeat_rate; u8 led_status; bool scanning_enabled; } kbd_internal_buffer; /// internal mouse buffer struct SAli_mib { int num_elements; u8 buffer[BX_MOUSE_BUFF_SIZE]; int head; } mouse_internal_buffer; #define BX_KBD_CONTROLLER_QSIZE 5 u8 kbd_controller_Q[BX_KBD_CONTROLLER_QSIZE]; unsigned kbd_controller_Qsize; unsigned kbd_controller_Qsource; /**< 0=keyboard, 1=mouse */ } state; }; extern CKeyboard *theKeyboard; #endif // !defined(INCLUDED_KEYBOARD_H) ================================================ FILE: src/Main.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2020 Remy van Elst * Website: https://github.com/lenticularis39/axpbox * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "config.hpp" #include #include int main_sim(int argc, char *argv[]); int main_cfg(int argc, char *argv[]); int main(int argc, char **argv) { if (argc <= 1 || (strcmp(argv[1], "run") && strcmp(argv[1], "configure"))) { std::cerr << "AXPBox Alpha Emulator"; #ifdef PACKAGE_GITSHA std::cerr << " (commit " << std::string(PACKAGE_GITSHA) << ")"; #endif std::cerr << std::endl; std::cerr << "Usage: " << argv[0] << " run|configure " << std::endl; return 0; } if (strcmp(argv[1], "run") == 0) { return main_sim(argc - 1, ++argv); } if (strcmp(argv[1], "configure") == 0) { return main_cfg(argc - 1, ++argv); } } ================================================ FILE: src/MultipleChoiceQuestion.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /** * Class defining a possible answer to a multiple * choice question. **/ class Answer { public: /** * Constructor. * * Defines the answer, value and explanation of * this answer. **/ Answer(string answer, string value, string explanation) { mAnswer = answer; mValue = value; mExplanation = explanation; } /** * Equality operator. Matches on Answer. **/ bool operator==(string x) { return (mAnswer == x); } /** * Get the answer. **/ string getAnswer() { return mAnswer; } /** * Get the value. **/ string getValue() { return mValue; } /** * Get the explanation. **/ string getExplanation() { return mExplanation; } protected: /** Answer as it should be entered by the user. */ string mAnswer; /** Value as it is output to the config file. */ string mValue; /** Explanation shown when '?' is given as an answer. */ string mExplanation; }; /** * Vector containing all possible answers to a multiple * choice question. **/ typedef vector AnswerSet; /** * Question class implementing a multiple choice question. **/ class MultipleChoiceQuestion : public FreeTextQuestion { public: /** * Add an answer to the set of allowed answers. **/ void addAnswer(string answer, string value, string explanation) { mAnswerSet.push_back(Answer(answer, value, explanation)); } /** * Explain the question. **/ virtual void explain() { AnswerSet::iterator itAnswers; /* Explain the quistion as usual. */ FreeTextQuestion::explain(); /* Explain the allowed answers. */ cout << "POSSIBLE VALUES:\n"; /* Iterate through the answer set. */ for (itAnswers = mAnswerSet.begin(); itAnswers != mAnswerSet.end(); itAnswers++) { /* Print the answer and its explanation. */ cout << itAnswers->getAnswer() << "\t\t" << itAnswers->getExplanation() << "\n"; } cout << "\n"; } /** * Get the number of answers in the answer set. **/ size_t countAnswers() { return mAnswerSet.size(); } /** * Ask the question. **/ virtual string ask() { AnswerSet::iterator itAnswers; string options; /* Iterate through the answer set to create the * options list. */ for (itAnswers = mAnswerSet.begin(); itAnswers != mAnswerSet.end(); itAnswers++) { if (options != "") options += ","; options += itAnswers->getAnswer(); } /* Set the options list. */ setOptions(options); /* Keep repeating the question until a valid * answer is received. */ for (;;) { /* Ask the question as a FreeTextQuestion. This * takes care of the explanation and default * value handling. */ FreeTextQuestion::ask(); /* Try to find the answer received in the answer * set. */ itAnswers = find(mAnswerSet.begin(), mAnswerSet.end(), mAnswer); /* Has a matching answer be found? */ if (itAnswers != mAnswerSet.end()) { /* Get the value for the received answer. */ string value = itAnswers->getValue(); haveChosen(mAnswer); mAnswer = value; /* Return the value. */ return mAnswer; } /* No valid, matching answer received. */ cout << "\nPlease enter a valid value, or '?' for help.\n\n"; } }; /** * Remove one answer from the answer set. **/ void dropChoice(string choice) { AnswerSet::iterator itAnswers; /* Try to find the answer in the answer set. */ itAnswers = find(mAnswerSet.begin(), mAnswerSet.end(), mAnswer); /* Has a matching answer be found? */ if (itAnswers != mAnswerSet.end()) { /* Delete the answer from the list. */ mAnswerSet.erase(itAnswers); } } /** * Override this to do something special with * the answer received. **/ virtual void haveChosen(string choice) {} /** * Return the first possible answer. **/ string getFirstChoice() { return mAnswerSet.begin()->getAnswer(); } protected: /** Set containing all allowed answers. */ AnswerSet mAnswerSet; }; ================================================ FILE: src/NumberQuestion.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /** * Convert an integer to a string. **/ inline string i2s(int x) { ostringstream o; o << x; return o.str(); } /** * Convert a string to an integer. * * Throws a CLogicException when the input is not numeric. **/ inline int s2i(const string x) { istringstream i(x); int x1; char c; if (!(i >> x1) || i.get(c)) FAILURE(Logic, "invalid conversion"); ; return x1; } /** * Question class that accepts a numeric answer within * a defined range. **/ class NumberQuestion : public FreeTextQuestion { public: /** * Define the allowable range for the answer. **/ void setRange(int low, int high) { mLow = low; mHigh = high; } /** * Convert the answer to a number. **/ int getNum() { return s2i(mAnswer); } /** * Ask the question and return the answer. **/ virtual string ask() { /* Set the options list to (low-high). */ setOptions(i2s(mLow) + "-" + i2s(mHigh)); /* Keep repeating the question until a valid * answer is received. */ for (;;) { int value; /* Ask the question as a FreeTextQuestion. This * takes care of the explanation and default * value handling. */ FreeTextQuestion::ask(); try { /* Convert the answer to an integer. */ value = s2i(mAnswer); /* If the answer is within the allowed range, * return it. */ if (value >= mLow && value <= mHigh) return mAnswer; /* The answer is out of range. */ cout << "\nPlease enter a value that is within the indicated range, or " "'?' for help.\n\n"; } catch (CLogicException) { /* The answer is not a number. */ cout << "\nPlease enter an integer value.\n\n"; } } } protected: /** Low limit of the allowed range. */ int mLow; /** High limit of the allowed range. */ int mHigh; }; ================================================ FILE: src/PCIDevice.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "PCIDevice.hpp" #include "StdAfx.hpp" #include "System.hpp" static size_t pci_dma_chunk_limit(u64 phys_addr, size_t remaining) { const size_t dma_page = 8192; size_t page_remaining = dma_page - (size_t)(phys_addr & (dma_page - 1)); return (remaining < page_remaining) ? remaining : page_remaining; } CPCIDevice::CPCIDevice(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CSystemComponent(cfg, c) { int i; int j; for (i = 0; i < 8; i++) { device_at[i] = false; for (j = 0; j < 8; j++) pci_range_is_io[i][j] = false; } for (i = 0; i < MAX_DEV_RANGES; i++) dev_range_is_io[i] = false; myPCIBus = pcibus; myPCIDev = pcidev; } CPCIDevice::~CPCIDevice(void) {} void CPCIDevice::add_function(int func, u32 data[64], u32 mask[64]) { memcpy(std_config_data[func], data, 64 * sizeof(u32)); memcpy(std_config_mask[func], mask, 64 * sizeof(u32)); #if defined(ES40_BIG_ENDIAN) int i; for (i = 0; i < 64; i++) { std_config_data[func][i] = endian_32(std_config_data[func][i]); std_config_mask[func][i] = endian_32(std_config_mask[func][i]); } #endif device_at[func] = true; } void CPCIDevice::add_legacy_io(int id, u32 base, u32 length) { dev_range_is_io[id] = true; cSystem->RegisterMemory(this, id, U64(0x00000801fc000000) + (U64(0x0000000200000000) * myPCIBus) + base, length); } void CPCIDevice::add_legacy_mem(int id, u32 base, u32 length) { dev_range_is_io[id] = false; cSystem->RegisterMemory(this, id, U64(0x0000080000000000) + (U64(0x0000000200000000) * myPCIBus) + base, length); } u32 CPCIDevice::config_read(int func, u32 address, int dsize) { u8 *x; u32 data = 0; x = (u8 *)pci_state.config_data[func]; x += address; switch (dsize) { case 8: data = endian_8(*x); break; case 16: data = endian_16(*((u16 *)x)); break; case 32: data = endian_32(*((u32 *)x)); break; } data = config_read_custom(func, address, dsize, data); // printf("%s(%s).%d config read %d bytes @ %x = %x\n",myCfg->get_myName(), // myCfg->get_myValue(), func,dsize/8,address, data); return data; } void CPCIDevice::config_write(int func, u32 address, int dsize, u32 data) { // printf("%s(%s).%d config write %d bytes @ %x = %x\n",myCfg->get_myName(), // myCfg->get_myValue(), func,dsize/8,address, data); u8 *x; u8 *y; u32 mask = 0; u32 old_data = 0; u32 new_data = 0; x = (u8 *)pci_state.config_data[func]; x += address; y = (u8 *)pci_state.config_mask[func]; y += address; #if defined(DEBUG_PCI) if (address == 0x3c && (data & 0xff) != 0xff) printf("%s.%d PCI Interrupt set to %02x.\n", devid_string, func, data & 0xff); #endif switch (dsize) { case 8: data = endian_8(data); old_data = (*x) & 0xff; mask = (*y) & 0xff; new_data = (old_data & ~mask) | (data & mask); *x = (u8)new_data; break; case 16: data = endian_16(data); old_data = (*((u16 *)x)) & 0xffff; mask = (*((u16 *)y)) & 0xffff; new_data = (old_data & ~mask) | (data & mask); *((u16 *)x) = (u16)new_data; break; case 32: data = endian_32(data); old_data = (*((u32 *)x)); mask = (*((u32 *)y)); new_data = (old_data & ~mask) | (data & mask); *((u32 *)x) = new_data; break; } if (dsize == 32 && ((data & mask) != mask) && ((data & mask) != 0)) { switch (address) { case 0x10: case 0x14: case 0x18: case 0x1c: case 0x20: case 0x24: register_bar(func, (address - 0x10) / 4, endian_32(new_data), endian_32(mask)); break; case 0x30: register_bar(func, 6, endian_32(new_data), endian_32(mask)); break; } } config_write_custom(func, address, dsize, old_data, new_data, data); } void CPCIDevice::register_bar(int func, int bar, u32 data, u32 mask) { u32 length = ((~mask) | 1) + 1; if ((data & 1) && bar != 6) { // io space pci_range_is_io[func][bar] = true; cSystem->RegisterMemory(this, PCI_RANGE_BASE + (func * 8) + bar, U64(0x00000801fc000000) + (U64(0x0000000200000000) * myPCIBus) + (data & ~0x3), length); #if defined(DEBUG_PCI) printf("%s(%s).%d PCI BAR %d set to IO % " PRIx64 ", len %x.\n", myCfg->get_myName(), myCfg->get_myValue(), func, bar, t, length); #endif } else if ((data & 1) || bar != 6) { // io space pci_range_is_io[func][bar] = true; cSystem->RegisterMemory(this, PCI_RANGE_BASE + (func * 8) + bar, U64(0x0000080000000000) + (U64(0x0000000200000000) * myPCIBus) + (data & ~0xf), length); #if defined(DEBUG_PCI) printf("%s(%s).%d PCI BAR %d set to MEM % " PRIx64 ", len %x.\n", myCfg->get_myName(), myCfg->get_myValue(), func, bar, t, length); #endif } else { // disabled... #if defined(DEBUG_PCI) printf("%s(%s).%d PCI BAR %d should be disabled...\n", myCfg->get_myName(), myCfg->get_myValue(), func, bar); #endif } } void CPCIDevice::ResetPCI() { int i; for (i = 0; i < 8; i++) { if (device_at[i]) { cSystem->RegisterMemory(this, PCI_RANGE_BASE + (i * 8) + 7, U64(0x00000801fe000000) + (U64(0x0000000200000000) * myPCIBus) + (U64(0x0000000000000800) * myPCIDev) + (U64(0x0000000000000100) * i), 0x100); memcpy(pci_state.config_data[i], std_config_data[i], 64 * sizeof(u32)); memcpy(pci_state.config_mask[i], std_config_mask[i], 64 * sizeof(u32)); config_write(i, 0x10, 32, endian_32(pci_state.config_data[i][4])); config_write(i, 0x14, 32, endian_32(pci_state.config_data[i][5])); config_write(i, 0x18, 32, endian_32(pci_state.config_data[i][6])); config_write(i, 0x1c, 32, endian_32(pci_state.config_data[i][7])); config_write(i, 0x20, 32, endian_32(pci_state.config_data[i][8])); config_write(i, 0x24, 32, endian_32(pci_state.config_data[i][9])); config_write(i, 0x30, 32, endian_32(pci_state.config_data[i][12])); } } } u64 CPCIDevice::ReadMem(int index, u64 address, int dsize) { int func; int bar; if (dsize == 64) return ReadMem(index, address, 32) | (((u64)ReadMem(index, address + 4, 32)) << 32); if (dsize != 8 && dsize != 16 && dsize != 32) { FAILURE_5(InvalidArgument, "ReadMem: %s(%s) Unsupported dsize %d. (%d, %" PRIx64 ")\n", myCfg->get_myName(), myCfg->get_myValue(), dsize, index, address); } if (index < PCI_RANGE_BASE) { if (dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(1))) { printf("%s(%s) Legacy IO access with IO disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue()); return 0; } if (!dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(2))) { printf( "%s(%s) Legacy memory access with memory disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue()); return 0; } // printf("%s(%s) Calling ReadMem_Legacy(%d).\n",myCfg->get_myName(), // myCfg->get_myValue(), index); return ReadMem_Legacy(index, (u32)address, dsize); } index -= PCI_RANGE_BASE; bar = index & 7; func = (index / 8) & 7; if (bar == 7) return config_read(func, (u32)address, dsize); if (pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(1))) { printf("%s(%s).%d PCI IO access with IO disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue(), func); return 0; } if (!pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(2))) { printf( "%s(%s).%d PCI memory access with memory disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue(), func); return 0; } // printf("%s(%s).%d Calling ReadMem_Bar(%d,%d).\n",myCfg->get_myName(), // myCfg->get_myValue(), func,func,bar); return ReadMem_Bar(func, bar, (u32)address, dsize); } void CPCIDevice::WriteMem(int index, u64 address, int dsize, u64 data) { int func; int bar; if (dsize == 64) { WriteMem(index, address, 32, data & U64(0xffffffff)); WriteMem(index, address + 4, 32, (data >> 32) & U64(0xffffffff)); return; } if (dsize != 8 && dsize != 16 && dsize != 32) { FAILURE_6( InvalidArgument, "WriteMem: %s(%s) Unsupported dsize %d. (%d,%" PRIx64 ",%" PRIx64 ")\n", myCfg->get_myName(), myCfg->get_myValue(), dsize, index, address, data); } if (index < PCI_RANGE_BASE) { if (dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(1))) { printf("%s(%s) Legacy IO access with IO disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue()); return; } if (!dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(2))) { printf( "%s(%s) Legacy memory access with memory disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue()); return; } WriteMem_Legacy(index, (u32)address, dsize, (u32)data); return; } index -= PCI_RANGE_BASE; bar = index & 7; func = (index / 8) & 7; if (bar == 7) { config_write(func, (u32)address, dsize, (u32)data); return; } if (pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(1))) { printf("%s(%s).%d PCI IO access with IO disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue(), func); return; } if (!pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(2))) { printf( "%s(%s).%d PCI memory access with memory disabled from PCI config.\n", myCfg->get_myName(), myCfg->get_myValue(), func); return; } WriteMem_Bar(func, bar, (u32)address, dsize, (u32)data); } bool CPCIDevice::do_pci_interrupt(int func, bool asserted) { if ((endian_32(pci_state.config_data[func][0x0f]) & 0xff) != 0xff) { cSystem->interrupt(endian_32(pci_state.config_data[func][0x0f]) & 0xff, asserted); return true; } else return false; } static u32 pci_magic1 = 0xC1095A78; static u32 pci_magic2 = 0x87A5901C; /** * Save state to a Virtual Machine State file. **/ int CPCIDevice::SaveState(FILE *f) { long ss = sizeof(pci_state); fwrite(&pci_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&pci_state, sizeof(pci_state), 1, f); fwrite(&pci_magic2, sizeof(u32), 1, f); printf("%s: %d PCI bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CPCIDevice::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != pci_magic1) { printf("%s: PCI MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(pci_state)) { printf("%s: PCI STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&pci_state, sizeof(pci_state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != pci_magic2) { printf("%s: PCI MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d PCI bytes restored.\n", devid_string, (int)ss); return 0; } u32 CPCIDevice::ReadMem_Legacy(int index, u32 address, int dsize) { FAILURE_2(NotImplemented, "%s(%s) No Legacy read handler installed", myCfg->get_myName(), myCfg->get_myValue()); } void CPCIDevice::WriteMem_Legacy(int index, u32 address, int dsize, u32 data) { FAILURE_2(NotImplemented, "%s(%s) No Legacy write handler installed", myCfg->get_myName(), myCfg->get_myValue()); } u32 CPCIDevice::ReadMem_Bar(int func, int bar, u32 address, int dsize) { FAILURE_3(NotImplemented, "%s(%s).%d No BAR read handler installed", myCfg->get_myName(), myCfg->get_myValue(), func); } void CPCIDevice::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { FAILURE_3(NotImplemented, "%s(%s).%d No BAR write handler installed", myCfg->get_myName(), myCfg->get_myValue(), func); } /** * \brief Read data from the PCI bus. * * Called by the PCI-device to read data off the PCI bus. address is the * 32-bit address put on the PCI bus. element_count elements of element_size * bytes each will be read in an endian-aware manner. **/ void CPCIDevice::do_pci_read(u32 address, void *dest, size_t element_size, size_t element_count) { size_t el; char *dst = (char *)dest; if (element_count == 0) return; // get the 64-bit system wide address u64 phys_addr = cSystem->PCI_Phys(myPCIBus, address); // if there is only one element to read, this is a simple ReadMem operation. if (element_count == 1) { switch (element_size) { case 1: *(u8 *)dest = (u8)cSystem->ReadMem(phys_addr, 8, this); break; case 2: *(u16 *)dest = (u16)cSystem->ReadMem(phys_addr, 16, this); break; case 4: *(u32 *)dest = (u32)cSystem->ReadMem(phys_addr, 32, this); break; default: FAILURE(InvalidArgument, "Strange element size"); } return; } #if defined(ES40_BIG_ENDIAN) // if this is a big-endian host machine, the memcpy method is only valid // if we're transferring bytes. Otherwise, endian-conversions need to be done. if (element_size == 1) { #endif size_t remaining = element_size * element_count; u32 cur_address = address; while (remaining != 0) { u64 cur_phys = cSystem->PCI_Phys(myPCIBus, cur_address); size_t chunk = pci_dma_chunk_limit(cur_phys, remaining); // get a pointer to system memory if the address is inside main memory char *memptr = cSystem->PtrToMem(cur_phys); // Copy only within a single translated DMA page. Scatter-gather DMA does // not guarantee that adjacent PCI bus addresses map to contiguous host // physical memory beyond the current page. if (memptr) { memcpy(dst, memptr, chunk); } else { for (el = 0; el < chunk; el++) dst[el] = (u8)cSystem->ReadMem(cur_phys + el, 8, this); } dst += chunk; cur_address += (u32)chunk; remaining -= chunk; } return; #if defined(ES40_BIG_ENDIAN) } #endif // outside main memory, or inside main memory with endian-conversion // required we need to do the transfer element-by-element. switch (element_size) { case 1: for (el = 0; el < element_count; el++) { *(u8 *)dst = (u8)cSystem->ReadMem(phys_addr, 8, this); dst++; phys_addr++; } break; case 2: { *(u16 *)dst = endian_16((u16)cSystem->ReadMem(phys_addr, 16, this)); dst += 2; phys_addr += 2; } break; case 4: { *(u32 *)dst = endian_32((u32)cSystem->ReadMem(phys_addr, 32, this)); dst += 4; phys_addr += 4; } break; default: FAILURE(InvalidArgument, "Strange element size"); } } /** * \brief Write data to the PCI bus. * * Called by the PCI-device to write data to the PCI bus. address is the * 32-bit address put on the PCI bus. element_count elements of element_size * bytes each will be written in an endian-aware manner. **/ void CPCIDevice::do_pci_write(u32 address, void *source, size_t element_size, size_t element_count) { size_t el; char *src = (char *)source; if (element_count == 0) return; // get the 64-bit system wide address u64 phys_addr = cSystem->PCI_Phys(myPCIBus, address); // if there is only one element to read, this is a simple ReadMem operation. if (element_count == 1) { switch (element_size) { case 1: cSystem->WriteMem(phys_addr, 8, *(u8 *)source, this); break; case 2: cSystem->WriteMem(phys_addr, 16, *(u16 *)source, this); break; case 4: cSystem->WriteMem(phys_addr, 32, *(u32 *)source, this); break; default: FAILURE(InvalidArgument, "Strange element size"); } return; } #if defined(ES40_BIG_ENDIAN) // if this is a big-endian host machine, the memcpy method is only valid // if we're transferring bytes. Otherwise, endian-conversions need to be done. if (element_size == 1) { #endif size_t remaining = element_size * element_count; u32 cur_address = address; while (remaining != 0) { u64 cur_phys = cSystem->PCI_Phys(myPCIBus, cur_address); size_t chunk = pci_dma_chunk_limit(cur_phys, remaining); // get a pointer to system memory if the address is inside main memory char *memptr = cSystem->PtrToMem(cur_phys); if (memptr) { memcpy(memptr, src, chunk); } else { for (el = 0; el < chunk; el++) cSystem->WriteMem(cur_phys + el, 8, (u8)src[el], this); } src += chunk; cur_address += (u32)chunk; remaining -= chunk; } return; #if defined(ES40_BIG_ENDIAN) } #endif // outside main memory, or inside main memory with endian-conversion // required we need to do the transfer element-by-element. switch (element_size) { case 1: for (el = 0; el < element_count; el++) { cSystem->WriteMem(phys_addr, 8, *(u8 *)src, this); src++; phys_addr++; } break; case 2: { cSystem->WriteMem(phys_addr, 16, endian_16(*(u16 *)src), this); src += 2; phys_addr += 2; } break; case 4: { cSystem->WriteMem(phys_addr, 32, endian_32(*(u32 *)src), this); src += 4; phys_addr += 4; } break; default: FAILURE(InvalidArgument, "Strange element size"); } } ================================================ FILE: src/PCIDevice.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__PCIDEVICE_H__) #define __PCIDEVICE_H__ #define MAX_DEV_RANGES 50 #define PCI_RANGE_BASE 0x0800 #include "SystemComponent.hpp" /** * \brief Abstract base class for devices on the PCI-bus. **/ class CPCIDevice : public CSystemComponent { public: CPCIDevice(class CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); ~CPCIDevice(void); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void ResetPCI(); virtual u64 ReadMem(int index, u64 address, int dsize); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u32 ReadMem_Legacy(int index, u32 address, int dsize); virtual void WriteMem_Legacy(int index, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); u32 config_read(int func, u32 address, int dsize); void config_write(int func, u32 address, int dsize, u32 data); virtual u32 config_read_custom(int func, u32 address, int dsize, u32 data) { return data; }; virtual void config_write_custom(int func, u32 address, int dsize, u32 old_data, u32 new_data, u32 data){}; void register_bar(int func, int bar, u32 data, u32 mask); void do_pci_read(u32 address, void *dest, size_t element_size, size_t element_count); void do_pci_write(u32 address, void *source, size_t element_size, size_t element_count); protected: bool do_pci_interrupt(int func, bool asserted); void add_function(int func, u32 data[64], u32 mask[64]); void add_legacy_io(int id, u32 base, u32 length); void add_legacy_mem(int id, u32 base, u32 length); int myPCIBus; int myPCIDev; u32 std_config_data[8][64]; u32 std_config_mask[8][64]; bool device_at[8]; bool dev_range_is_io[MAX_DEV_RANGES]; bool pci_range_is_io[8][8]; /// The PCI state structure contains all elements that need to be saved to the /// statefile. struct SPCI_state { u32 config_data[8][64]; u32 config_mask[8][64]; } pci_state; }; #endif //! defined(__PCIDEVICE_H__) ================================================ FILE: src/Port80.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Port80.hpp" #include "StdAfx.hpp" #include "System.hpp" /** * Constructor. **/ CPort80::CPort80(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { c->RegisterMemory(this, 0, U64(0x00000801fc000080), 1); state.p80 = 0; } /** * Destructor. **/ CPort80::~CPort80() {} /** * Read from port 80. * Returns the value last written to port 80. **/ u64 CPort80::ReadMem(int index, u64 address, int dsize) { return state.p80; } /** * Write to port 80. **/ void CPort80::WriteMem(int index, u64 address, int dsize, u64 data) { state.p80 = (u8)data; } static u32 p80_magic1 = 0x80FFAA80; static u32 p80_magic2 = 0xAA8080FF; /** * Save state to a Virtual Machine State file. **/ int CPort80::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&p80_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&p80_magic2, sizeof(u32), 1, f); printf("%s: %ld bytes saved.\n", devid_string, ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CPort80::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != p80_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != p80_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %ld bytes restored.\n", devid_string, ss); return 0; } ================================================ FILE: src/Port80.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_PORT80_H) #define INCLUDED_PORT80_H #include "SystemComponent.hpp" /** * \brief Emulated port 80. * * Port 80 is a port without a real function, that is used to slow things down. * Since our emulator is slow enough already ;-) this port has no function at * all, but it needs to be there to avoid error messages about non-existing * hardware. **/ class CPort80 : public CSystemComponent { public: CPort80(CConfigurator *cfg, class CSystem *c); virtual ~CPort80(); virtual u64 ReadMem(int index, u64 address, int dsize); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); protected: /// The state structure contains all elements that need to be saved to the /// statefile. struct SPort80_state { u8 p80; /**< Last value written.*/ } state; }; #endif // !defined(INCLUDED_PORT80_H) ================================================ FILE: src/Question.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_QUESTION_H) #define INCLUDED_QUESTION_H /** * Abstract Question base class. **/ class Question { public: /** * Return the answer previously given. **/ string getAnswer() { return mAnswer; } /** * Set the answer. **/ void setAnswer(string answer) { mAnswer = answer; } /** * Define an explanation for the question, that will be shown when * the question is answered with '?'. **/ void setExplanation(string explanation) { mExplanation = explanation; } /** * Define the question to ask. **/ void setQuestion(string question) { mQuestion = question; } /** * Define a default value to use when the question is answered * with a . **/ void setDefault(string defval) { mDefault = defval; } /** * Ask the question, and return the value given. **/ virtual string ask() = 0; /** * Display the explanation. **/ virtual void explain() { cout << "\nEXPLANATION:\n" << mExplanation << "\n\n"; } protected: /** The stored answer. */ string mAnswer; /** An explanation for the question. */ string mExplanation; /** The question to ask. */ string mQuestion; /** Default value. */ string mDefault; }; #endif // !defined(INCLUDED_QUESTION_H) ================================================ FILE: src/S3Trio64.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "S3Trio64.hpp" #include "AliM1543C.hpp" #include "StdAfx.hpp" #include "System.hpp" #include "gui/gui.hpp" static unsigned old_iHeight = 0, old_iWidth = 0, old_MSL = 0; static const u8 ccdat[16][4] = { {0x00, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}, {0x00, 0xff, 0x00, 0x00}, {0xff, 0xff, 0x00, 0x00}, {0x00, 0x00, 0xff, 0x00}, {0xff, 0x00, 0xff, 0x00}, {0x00, 0xff, 0xff, 0x00}, {0xff, 0xff, 0xff, 0x00}, {0x00, 0x00, 0x00, 0xff}, {0xff, 0x00, 0x00, 0xff}, {0x00, 0xff, 0x00, 0xff}, {0xff, 0xff, 0x00, 0xff}, {0x00, 0x00, 0xff, 0xff}, {0xff, 0x00, 0xff, 0xff}, {0x00, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff}, }; /** * Set a specific tile's updated variable. * * Only reference the array if the tile numbers are within the bounds * of the array. If out of bounds, do nothing. **/ #define SET_TILE_UPDATED(xtile, ytile, value) \ do { \ if (((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \ state.vga_tile_updated[(xtile)][(ytile)] = value; \ } while (0) /** * Get a specific tile's updated variable. * * Only reference the array if the tile numbers are within the bounds * of the array. If out of bounds, return 0. **/ #define GET_TILE_UPDATED(xtile, ytile) \ ((((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \ ? state.vga_tile_updated[(xtile)][(ytile)] \ : 0) /** * Thread entry point. * * The thread first initializes the GUI, and then starts looping the * following actions until interrupted (by StopThread being set to true) * - Handle any GUI events (mouse moves, keypresses) * - Update the GUI to match the screen buffer * - Flush the updated GUI content to the screen * . **/ void CS3Trio64::run() { try { // initialize the GUI (and let it know our tilesize) bx_gui->init(state.x_tilesize, state.y_tilesize); for (;;) { // Terminate thread if StopThread is set to true if (StopThread) return; // Handle GUI events (100 times per second) for (int i = 0; i < 10; i++) { bx_gui->lock(); bx_gui->handle_events(); bx_gui->unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Update the screen (10 times per second) bx_gui->lock(); update(); bx_gui->flush(); bx_gui->unlock(); } } catch (CException &e) { printf("Exception in S3 thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** Size of ROM image */ static unsigned int rom_max; /** ROM image */ static u8 option_rom[65536]; /** PCI Configuration Space data block */ static u32 s3_cfg_data[64] = { /*00*/ 0x88115333, // CFID: vendor + device /*04*/ 0x011f0000, // CFCS: command + status /*08*/ 0x03000002, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0xf8000000, // BAR0: FB /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x281401ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** PCI Configuration Space mask block */ static u32 s3_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x0000ffff, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xfc000000, // BAR0: FB /*14*/ 0x00000000, // BAR1: /*18*/ 0x00000000, // BAR2: /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Constructor. * * Don't do anything, the real initialization is done by init() **/ CS3Trio64::CS3Trio64(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CVGA(cfg, c, pcibus, pcidev) {} /** * Initialize the S3 device. **/ void CS3Trio64::init() { // Register PCI device add_function(0, s3_cfg_data, s3_cfg_mask); // Initialize all state variables to 0 memset((void *)&state, 0, sizeof(state)); // Register VGA I/O ports at 3b4, 3b5, 3ba, 3c0..cf, 3d4, 3d5, 3da add_legacy_io(1, 0x3b4, 2); add_legacy_io(3, 0x3ba, 2); add_legacy_io(2, 0x3c0, 16); add_legacy_io(8, 0x3d4, 2); add_legacy_io(9, 0x3da, 1); /* The VGA BIOS we use sends text messages to port 0x500. We listen for these messages at port 500. */ add_legacy_io(7, 0x500, 1); bios_message_size = 0; bios_message[0] = '\0'; // Legacy video address space: A0000 -> bffff add_legacy_mem(4, 0xa0000, 128 * 1024); // Reset the base PCI device ResetPCI(); /* The configuration file variable "rom" should point to a VGA BIOS image. If not, try "vgabios.bin". */ FILE *rom = fopen(myCfg->get_text_value("rom", "vgabios.bin"), "rb"); if (!rom) { FAILURE_1(FileNotFound, "s3 rom file %s not found", myCfg->get_text_value("rom", "vgabios.bin")); } rom_max = (unsigned)fread(option_rom, 1, 65536, rom); fclose(rom); // Option ROM address space: C0000 add_legacy_mem(5, 0xc0000, rom_max); state.vga_enabled = 1; state.misc_output.color_emulation = 1; state.misc_output.enable_ram = 1; state.misc_output.horiz_sync_pol = 1; state.misc_output.vert_sync_pol = 1; state.attribute_ctrl.mode_ctrl.enable_line_graphics = 1; state.line_offset = 80; state.line_compare = 1023; state.vertical_display_end = 399; state.attribute_ctrl.video_enabled = 1; state.attribute_ctrl.color_plane_enable = 0x0f; state.pel.dac_state = 0x01; state.pel.mask = 0xff; state.graphics_ctrl.memory_mapping = 2; // monochrome text mode state.sequencer.reset1 = 1; state.sequencer.reset2 = 1; state.sequencer.extended_mem = 1; // display mem greater than 64K state.sequencer.odd_even = 1; // use sequential addressing mode state.memsize = 0x40000; state.memory = new u8[state.memsize]; memset(state.memory, 0, state.memsize); state.last_bpp = 8; state.CRTC.reg[0x09] = 16; state.graphics_ctrl.memory_mapping = 3; // color text mode state.vga_mem_updated = 1; printf("%s: $Id: S3Trio64.cpp,v 1.20 2008/05/31 15:47:10 iamcamiel Exp $\n", devid_string); } /** * Create and start thread. **/ void CS3Trio64::start_threads() { if (!myThread) { printf(" s3"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); } } /** * Stop and destroy thread. **/ void CS3Trio64::stop_threads() { // Signal the thread to stop StopThread = true; if (myThread) { printf(" s3"); // Wait for the thread to end execution myThread->join(); // And delete the Thread object myThread = nullptr; } } /** * Destructor. **/ CS3Trio64::~CS3Trio64() { stop_threads(); } /** * Read from one of the Legacy (fixed-address) memory ranges. **/ u32 CS3Trio64::ReadMem_Legacy(int index, u32 address, int dsize) { u32 data = 0; switch (index) { // IO Port 0x3b4 case 1: data = io_read(address + 0x3b4, dsize); break; // IO Port 0x3c0..0x3cf case 2: data = io_read(address + 0x3c0, dsize); break; // IO Port 0x3ba case 3: data = io_read(address + 0x3ba, dsize); break; // VGA Memory case 4: data = legacy_read(address, dsize); break; // ROM case 5: data = rom_read(address, dsize); break; // IO Port 0x3d4 case 8: data = io_read(address + 0x3d4, dsize); break; // IO Port 0x3da case 9: data = io_read(address + 0x3da, dsize); break; } return data; } /** * Write to one of the Legacy (fixed-address) memory ranges. **/ void CS3Trio64::WriteMem_Legacy(int index, u32 address, int dsize, u32 data) { switch (index) { // IO Port 0x3b4 case 1: io_write(address + 0x3b4, dsize, data); return; // IO Port 0x3c0..0x3cf case 2: io_write(address + 0x3c0, dsize, data); return; // IO Port 0x3ba case 3: io_write(address + 0x3ba, dsize, data); return; // VGA Memory case 4: legacy_write(address, dsize, data); return; // BIOS Message IO Port (0x500) case 7: bios_message[bios_message_size++] = (char)data & 0xff; if (((data & 0xff) == 0x0a) || ((data & 0xff) == 0x0d)) { if (bios_message_size > 1) { bios_message[bios_message_size - 1] = '\0'; printf("s3: %s\n", bios_message); } bios_message_size = 0; } return; // IO Port 0x3d4 case 8: io_write(address + 0x3d4, dsize, data); return; // IO Port 0x3da case 9: io_write(address + 0x3da, dsize, data); return; } } /** * Read from one of the PCI BAR (configurable address) memory ranges. **/ u32 CS3Trio64::ReadMem_Bar(int func, int bar, u32 address, int dsize) { switch (bar) { // PCI memory range case 0: return mem_read(address, dsize); } return 0; } /** * Write to one of the PCI BAR (configurable address) memory ranges. **/ void CS3Trio64::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { switch (bar) { // PCI Memory range case 0: mem_write(address, dsize, data); return; } } /** * Check if threads are still running. **/ void CS3Trio64::check_state() { if (myThreadDead.load()) FAILURE(Thread, "S3 thread has died"); } static u32 s3_magic1 = 0x53338811; static u32 s3_magic2 = 0x88115333; /** * Save state to a Virtual Machine State file. **/ int CS3Trio64::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&s3_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&s3_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CS3Trio64::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != s3_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != s3_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * Read from Framebuffer. * * Not functional. **/ u32 CS3Trio64::mem_read(u32 address, int dsize) { u32 data = 0; // printf("S3 mem read: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, data); return data; } /** * Write to Framebuffer. * * Not functional. **/ void CS3Trio64::mem_write(u32 address, int dsize, u32 data) { // printf("S3 mem write: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, data); switch (dsize) { case 8: case 16: case 32: break; } } /** * Read from Legacy VGA Memory * * Calls vga_mem_read to read the data 1 byte at a time. **/ u32 CS3Trio64::legacy_read(u32 address, int dsize) { u32 data = 0; switch (dsize) { case 32: data |= (u64)vga_mem_read((u32)address + 0xA0003) << 24; data |= (u64)vga_mem_read((u32)address + 0xA0002) << 16; case 16: data |= (u64)vga_mem_read((u32)address + 0xA0001) << 8; case 8: data |= (u64)vga_mem_read((u32)address + 0xA0000); } // //printf("S3 legacy read: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, // data); return data; } /** * Write to Legacy VGA Memory * * Calls vga_mem_write to write the data 1 byte at a time. **/ void CS3Trio64::legacy_write(u32 address, int dsize, u32 data) { // //printf("S3 legacy write: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, // data); switch (dsize) { case 32: vga_mem_write((u32)address + 0xA0002, (u8)(data >> 16)); vga_mem_write((u32)address + 0xA0003, (u8)(data >> 24)); case 16: vga_mem_write((u32)address + 0xA0001, (u8)(data >> 8)); case 8: vga_mem_write((u32)address + 0xA0000, (u8)(data)); } } /** * Read from Option ROM */ u32 CS3Trio64::rom_read(u32 address, int dsize) { u32 data = 0x00; u8 *x = (u8 *)option_rom; if (address <= rom_max) { x += address; switch (dsize) { case 8: data = (u32)endian_8((*((u8 *)x)) & 0xff); break; case 16: data = (u32)endian_16((*((u16 *)x)) & 0xffff); break; case 32: data = (u32)endian_32((*((u32 *)x)) & 0xffffffff); break; } // printf("S3 rom read: %" PRIx64 ", %d, %" PRIx64 "\n", address, dsize,data); } else { // printf("S3 (BAD) rom read: %" PRIx64 ", %d, %" PRIx64 "\n", address, // dsize,data); } return data; } /** * Read from I/O Port */ u32 CS3Trio64::io_read(u32 address, int dsize) { u32 data = 0; if (dsize != 8) FAILURE(InvalidArgument, "Unsupported dsize"); switch (address) { case 0x3c0: data = read_b_3c0(); break; case 0x3c1: data = read_b_3c1(); break; case 0x3c2: data = read_b_3c2(); break; case 0x3c3: data = read_b_3c3(); break; case 0x3c4: data = read_b_3c4(); break; case 0x3c5: data = read_b_3c5(); break; case 0x3c9: data = read_b_3c9(); break; case 0x3ca: data = read_b_3ca(); break; case 0x3cc: data = read_b_3cc(); break; case 0x3cf: data = read_b_3cf(); break; case 0x3b4: case 0x3d4: data = read_b_3d4(); break; case 0x3b5: case 0x3d5: data = read_b_3d5(); break; case 0x3ba: case 0x3da: data = read_b_3da(); break; default: FAILURE_1(NotImplemented, "Unhandled port %x read", address); } // printf("S3 io read: %" PRIx64 ", %d, %" PRIx64 " \n", address, dsize, data); return data; } /** * Write to I/O Port * * Calls io_write_b to write the data 1 byte at a time. */ void CS3Trio64::io_write(u32 address, int dsize, u32 data) { // printf("S3 io write: %" PRIx64 ", %d, %" PRIx64 " \n", address+VGA_BASE, // dsize, data); switch (dsize) { case 8: io_write_b(address, (u8)data); break; case 16: io_write_b(address, (u8)data); io_write_b(address + 1, (u8)(data >> 8)); break; default: FAILURE(InvalidArgument, "Weird IO size"); } } /** * Write one byte to a VGA I/O port. **/ void CS3Trio64::io_write_b(u32 address, u8 data) { switch (address) { case 0x3c0: write_b_3c0(data); break; case 0x3c2: write_b_3c2(data); break; case 0x3c4: write_b_3c4(data); break; case 0x3c5: write_b_3c5(data); break; case 0x3c6: write_b_3c6(data); break; case 0x3c7: write_b_3c7(data); break; case 0x3c8: write_b_3c8(data); break; case 0x3c9: write_b_3c9(data); break; case 0x3ce: write_b_3ce(data); break; case 0x3cf: write_b_3cf(data); break; case 0x3b4: case 0x3d4: write_b_3d4(data); break; case 0x3b5: case 0x3d5: write_b_3d5(data); break; default: FAILURE_1(NotImplemented, "Unhandled port %x write", address); } } /** * Write to the attribute controller I/O port (0x3c0) * * The attribute controller registers are used to select the 16 color * and 64 color palettes used for EGA/CGA compatibility. * * The attribute registers are accessed in an indexed fashion. * The address register is read and written via port 3C0h. * The data register is written to port 3C0h and read from port 3C1h. * The index and the data are written to the same port, one after * another. A flip-flop inside the card keeps track of whether the * next write will be handled is an index or data. Because there is * no standard method of determining the state of this flip-flop, the * ability to reset the flip-flop such that the next write will be * handled as an index is provided. This is accomplished by reading * the Input Status #1 Register (normally port 3DAh) (the data * received is not important.) * * Attribute registers: * - Palette Index registers (index 0x00 - 0x0f) * - Attribute Mode Control register (index 0x10) * - Overscan Color register (index 0x11) * - Color Plane Enable register (index 0x12) * - Horizontal Pixel Panning register (index 0x13) * - Color Select register (index 0x14) * . * * \code * Attribute Address Register(3C0h) * +---+-+---------+ * | |5|4 3 2 1 0| * +---+-+---------+ * ^ ^ * | +-- 0..4: Attribute Address: This field specifies the index * | value of the attribute register to be read or written * +----------- 5: Palette Address Source: This bit is set to 0 to load * color values to the registers in the internal palette. * It is set to 1 for normal operation of the attribute * controller. * * Palette Index Registers (index 0x00 - 0x0f) * +---+-----------+ * | |5 4 3 2 1 0| * +---+-----------+ * ^ * +--- 0..5: Internal Palette Index: These 6-bit registers allow a * dynamic mapping between the text attribute or graphic * color input value and the display color on the CRT * screen. These internal palette values are sent off-chip * to the video DAC, where they serve as addresses into * the DAC registers. * * Attribute Mode Control Register (index 0x10) * +-+-+-+-+-+-+-+-+ * |7|6|5| |3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +- 0: ATGE - Attribute Controller Graphics Enable: * | | | | | | 0: Disables the graphics mode of operation. * | | | | | | 1: Selects the graphics mode of operation. * | | | | | +--- 1: MONO - Monochrome Emulation: This bit is present and * | | | | | programmable in all of the hardware but it apparently * | | | | | does nothing. * | | | | +----- 2: LGE - Line Graphics Enable: This field is used in 9 * | | | | bit wide character modes to provide continuity for the * | | | | horizontal line characters in the range C0h-DFh: * | | | | 0: the 9th column is replicated from the 8th column. * | | | | 1: the 9th column is set to the background. * | | | +------- 3: BLINK - Blink Enable: * | | | 0: Bit 7 of the attribute selects the background * | | | intensity (allows 16 colors for background). * | | | 1: Bit 7 of the attribute enables blinking. * | | +----------- 5: PPM -- Pixel Panning Mode: Allows the upper half of * | | the screen to pan independently of the lower screen. * | | 0: nothing special occurs during a successful line * | | compare (see the Line Compare field.) * | | 1: upon a successful line compare, the bottom portion * | | of the screen is displayed as if the Pixel Shift * | | Count and Byte Panning fields are set to 0. * | +------------- 6: 8BIT -- 8-bit Color Enable: * | 1: The video data is sampled so that eight bits are * | available to select a color in the 256-color mode. * | 0: All other modes. * +--------------- 7: P54S -- Palette Bits 5-4 Select: Selects the source for * the P5 and P4 video bits that act as inputs to the video * DAC. * 0: P5 and P4 are the outputs of the Internal Palette * registers. * 1: P5 and P4 are bits 1 and 0 of the Color Select * register. * * Overscan Color Register (index 0x11) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * +----- 0..7: Overscan Palette Index: Selects a color from one of the * DAC registers for the border. * * Color Plane Enable Register (index 0x12) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Plane Enable: Setting a bit to 1 enables the * corresponding display-memory color plane. * * Horizontal Pixel Panning Register (index 0x13) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Pixel Shift Count: These bits select the number of pels * that the video data is shifted to the left. * * Color Select Register (index 0x14) * +-------+---+---+ * | |3 2|1 0| * +-------+---+---+ * ^ ^ * | +- 0..1: Color Select 5-4: These bits can be used in place of * | the P4 and P5 bits from the Internal Palette registers * | to form the 8-bit digital color value to the video *DAC. | Selecting these bits is done in the Attribute Mode | Control *register (index 0x10). * +----- 2..3: Color Select 7-6: In modes other than mode 0x13 * (256-color VGA), these are the two most-significant *bits of the 8-bit digital color value to the video DAC. \endcode **/ void CS3Trio64::write_b_3c0(u8 value) { // Variables to save old state (to detect transitions) bool prev_video_enabled; bool prev_line_graphics; bool prev_int_pal_size; /* The flip-flop determines whether the write goes to the index-register (address) or the data-register. */ if (state.attribute_ctrl.flip_flop == 0) { // Write goes to the index-register. /* The index register also has a bit that controls whether video output is enabled or not. We check this bit, and compare it to it's previous state, to determine whether we need to perform an enable or disable transition. */ prev_video_enabled = state.attribute_ctrl.video_enabled; state.attribute_ctrl.video_enabled = (value >> 5) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c0: video_enabled = %u \n", (unsigned)state.attribute_ctrl.video_enabled); #endif if (state.attribute_ctrl.video_enabled == 0) { if (prev_video_enabled) { #if defined(DEBUG_VGA) printf("found disable transition \n"); #endif // Video output has been disabled. Clear the screen. bx_gui->lock(); bx_gui->clear_screen(); bx_gui->unlock(); } } else if (!prev_video_enabled) { #if defined(DEBUG_VGA) printf("found enable transition \n"); #endif // Video output has been enabled. Draw the screen. redraw_area(0, 0, old_iWidth, old_iHeight); } // Determine what register should be addressed. value &= 0x1f; /* address = bits 0..4 */ state.attribute_ctrl.address = value; /* Registers 0x00..0x0f are palette selection registers. Write a debugging message for all other registers. */ #if defined(DEBUG_VGA) if (value > 0x0f) printf("io write 3c0: address mode reg=%u \n", (unsigned)value); #endif } else { // Write should go to the data-register. // Registers 0x00..0x0f are palette selection registers. if (state.attribute_ctrl.address <= 0x0f) { // Update palette selection only of there is a change. if (value != state.attribute_ctrl.palette_reg[state.attribute_ctrl.address]) { // Update the palette selection. state.attribute_ctrl.palette_reg[state.attribute_ctrl.address] = value; // Requires redrawing the screen. redraw_area(0, 0, old_iWidth, old_iHeight); } } else { switch (state.attribute_ctrl.address) { // Mode control register case 0x10: prev_line_graphics = state.attribute_ctrl.mode_ctrl.enable_line_graphics; prev_int_pal_size = state.attribute_ctrl.mode_ctrl.internal_palette_size; state.attribute_ctrl.mode_ctrl.graphics_alpha = (value >> 0) & 0x01; state.attribute_ctrl.mode_ctrl.display_type = (value >> 1) & 0x01; state.attribute_ctrl.mode_ctrl.enable_line_graphics = (value >> 2) & 0x01; state.attribute_ctrl.mode_ctrl.blink_intensity = (value >> 3) & 0x01; state.attribute_ctrl.mode_ctrl.pixel_panning_compat = (value >> 5) & 0x01; state.attribute_ctrl.mode_ctrl.pixel_clock_select = (value >> 6) & 0x01; state.attribute_ctrl.mode_ctrl.internal_palette_size = (value >> 7) & 0x01; if (((value >> 2) & 0x01) != prev_line_graphics) { bx_gui->lock(); bx_gui->set_text_charmap( &state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } if (((value >> 7) & 0x01) != prev_int_pal_size) { redraw_area(0, 0, old_iWidth, old_iHeight); } #if defined(DEBUG_VGA) printf("io write 3c0: mode control: %02x h \n", (unsigned)value); #endif break; // Overscan Color Register case 0x11: /* We don't do anything with this. Our display doesn't show the overscan part of the normal monitor. */ state.attribute_ctrl.overscan_color = (value & 0x3f); #if defined(DEBUG_VGA) printf("io write 3c0: overscan color = %02x \n", (unsigned)value); #endif break; // Color Plane Enable Register case 0x12: state.attribute_ctrl.color_plane_enable = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: color plane enable = %02x \n", (unsigned)value); #endif break; // Horizontal Pixel Panning Register case 0x13: state.attribute_ctrl.horiz_pel_panning = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: horiz pel panning = %02x \n", (unsigned)value); #endif break; // Color Select Register case 0x14: state.attribute_ctrl.color_select = (value & 0x0f); redraw_area(0, 0, old_iWidth, old_iHeight); #if defined(DEBUG_VGA) printf("io write 3c0: color select = %02x \n", (unsigned)state.attribute_ctrl.color_select); #endif break; default: FAILURE_1(NotImplemented, "io write 3c0: data-write mode %02x h", (unsigned)state.attribute_ctrl.address); } } } // Flip the flip-flop state.attribute_ctrl.flip_flop = !state.attribute_ctrl.flip_flop; } /** * Write to the VGA Miscellaneous Output Register (0x3c2) * * \code * +-+-+-+-+---+-+-+ * |7|6|5| |3 2|1|0| * +-+-+-+-+---+-+-+ * ^ ^ ^ ^ ^ ^ * | | | | | +- 0: I/OAS -- Input/Output Address Select: Selects the CRT * | | | | | controller addresses. * | | | | | 0: Compatibility with monochrome adapter * | | | | | (0x3b4,0x3b5,0x03ba) * | | | | | 1: Compatibility with color graphics adapter (CGA) * | | | | | (0x3d4,0x3d5,0x03da) * | | | | +--- 1: RAM Enable: Controls access from the system: * | | | | 0: Disables access to the display buffer * | | | | 1: Enables access to the display buffer * | | | +--- 2..3: Clock Select: Controls the selection of the dot clocks * | | | used in driving the display timing: * | | | 00: Select 25 Mhz clock (320/640 pixel wide modes) * | | | 01: Select 28 Mhz clock (360/720 pixel wide modes) * | | | 10: Undefined (possible external clock) * | | | 11: Undefined (possible external clock) * | | +----------- 5: Odd/Even Page Select: Selects the upper/lower 64K page * | | of memory when the system is in an even/odd mode. * | | 0: Selects the low page. * | | 1: Selects the high page. * | +------------- 6: Horizontal Sync Polarity * | 0: Positive sync pulse. * | 1: Negative sync pulse. * +--------------- 7: Vertical Sync Polarity * 0: Positive sync pulse. * 1: Negative sync pulse. * \endcode **/ void CS3Trio64::write_b_3c2(u8 value) { state.misc_output.color_emulation = (value >> 0) & 0x01; state.misc_output.enable_ram = (value >> 1) & 0x01; state.misc_output.clock_select = (value >> 2) & 0x03; state.misc_output.select_high_bank = (value >> 5) & 0x01; state.misc_output.horiz_sync_pol = (value >> 6) & 0x01; state.misc_output.vert_sync_pol = (value >> 7) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c2: \n"); printf(" color_emulation = %u \n", (unsigned)state.misc_output.color_emulation); printf(" enable_ram = %u \n", (unsigned)state.misc_output.enable_ram); printf(" clock_select = %u \n", (unsigned)state.misc_output.clock_select); printf(" select_high_bank = %u \n", (unsigned)state.misc_output.select_high_bank); printf(" horiz_sync_pol = %u \n", (unsigned)state.misc_output.horiz_sync_pol); printf(" vert_sync_pol = %u \n", (unsigned)state.misc_output.vert_sync_pol); #endif } /** * Write to the VGA sequencer index register (0x3c4) * * The Sequencer registers control how video data is sent to the DAC. * * The Sequencer registers are accessed in an indexed fashion. By writing a byte * to the Sequencer Index Register (0x3c4) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the Sequencer Data Register (0x3c5). * * Sequencer registers: * - Reset register (index 0x00) * - Clocking Mode register (index 0x01) * - Map Mask register (index 0x02) * - Character Map Select register (index 0x03) * - Memory Mode register (index 0x04) * . * * \code * Reset register (index 0x00) * +-----------+-+-+ * | |1|0| * +-----------+-+-+ * ^ ^ * | +- 0: Asynchronous Reset: * | 0: Commands the sequencer to asynchronously clear and * | halt. Resetting the sequencer with this bit can * | cause loss of video data. * | 1: Allows the sequencer to function normally. * +--- 1: Sychnronous Reset: * 0: Commands the sequencer to synchronously clear and * halt. * 1: Allows the sequencer to function normally. * Bits 1 and 0 must be 1 to allow the sequencer to operate. * To prevent the loss of data, bit 1 must be set to 0 during the active display * interval before changing the clock selection. The clock is changed through *the Clocking Mode register or the Miscellaneous Output register. * * Clocking Mode register (index 0x01) * +---+-+-+-+-+-+-+ * | |5|4|3|2| |0| * +---+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ * | | | | +- 0: 9/8 Dot Mode: Selects whether a character is 8 or 9 dots * | | | | wide. This can be used to select between 720 and 640 * | | | | pixel modes (or 360 and 320) and also is used to provide * | | | | 9 bit wide character fonts in text mode: * | | | | 0: Selects 9 dots per character. * | | | | 1: Selects 8 dots per character. * | | | +----- 2: Shift/Load Rate: * | | | 0: Video serializers are loaded every character clock. * | | | 1: Video serializers are loaded every other character * | | | clock, which is useful when 16 bits are fetched per * | | | cycle and chained together in the shift registers. * | | +------- 3: Dot Clock Rate: * | | 0: Selects the normal dot clocks derived from the * | | sequencer master clock input. * | | 1: The master clock will be divided by 2 to generate * | | the dot clock. All other timings are affected * | | because they are derived from the dot clock. The *dot | | clock divided by 2 is used for 320 and 360 horizontal * | | PEL modes. * | +--------- 4: Shift Four Enable: * | 0: Video serializers are loaded every character clock. * | 1: Video serializers are loaded every fourth character * | clock, which is useful when 32 bits are fetched per * | cycle and chained together in the shift registers. * +----------- 5: Screen Disable: * 0: Display enabled. * 1: Display blanked. Maximum memory bandwidth assigned *to the system. * * Map Mask register (index 0x02) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Memory Plane Write Enable: If a bit is set, then write * operations will modify the respective plane of display * memory. If a bit is not set then write operations will *not affect the respective plane of display memory. * * Character Map Select register (index 0x03) * +---+-+-+---+---+ * | |5|4|3 2|1 0| * +---+-+-+---+---+ * ^ ^ ^ ^ * | +--|---+- 0..1,4: Character Set B Select: This field is used to select *the | | font that is used in text mode when bit 3 of the *attribute | | byte for a character is set to 0. (*) * +----+----- 2..3,5: Character Set A Select: This field is used to select *the font that is used in text mode when bit 3 of the attribute byte for a *character is set to 1. (*) * * (*) Note that this field is not contiguous in order to provide EGA *compatibility. The font selected resides in plane 2 of display memory at the *address specified by this field, as follows: * +------+---------------+ * | val | font at | * +------+---------------+ * | 000b | 0000h - 1FFFh | * | 001b | 4000h - 5FFFh | * | 010b | 8000h - 9FFFh | * | 011b | C000h - DFFFh | * | 100b | 2000h - 3FFFh | * | 101b | 6000h - 7FFFh | * | 110b | A000h - BFFFh | * | 111b | E000h - FFFFh | * +------+---------------+ * * Memory Mode register (index 0x04) * +-------+-+-+-+-+ * | |3|2|1| | * +-------+-+-+-+-+ * ^ ^ ^ * | | +--- 1: Extended Memory: * | | 0: 64 KB of video memory enabled * | | 1: 256 KB of video memory enabled. This bit must be *set to 1 to | | enable the character map selection described for *the | | previous register. | +----- 2: Odd/Even Host Memory Write *Adressing Disable: | 0: Even system addresses access maps 0 and 2, *while odd system | addresses access maps 1 and 3. | 1: System *addresses sequentially access data within a bit map, | and the *maps are accessed according to the value in the Map | Mask *register (index 0x02). * +------- 3: Chain 4 Enable: This bit controls the map selected *during system read operations. 0: Enables system addresses to sequentially *access data within a bit map by using the Map Mask register. 1: Causes the two *low-order bits to select the map accessed as shown below: * +----+----+--------------+ * | A0 | A1 | Map Selected | * +----+----+--------------+ * | 0 | 0 | 0 | * | 0 | 1 | 1 | * | 1 | 0 | 2 | * | 1 | 1 | 3 | * +----+----+--------------+ * \endcode **/ void CS3Trio64::write_b_3c4(u8 value) { state.sequencer.index = value; } /** * Write to the VGA sequencer data register (0x3c5) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ void CS3Trio64::write_b_3c5(u8 value) { unsigned i; u8 charmap1; u8 charmap2; switch (state.sequencer.index) { // Sequencer: reset register case 0: #if defined(DEBUG_VGA) printf("write 0x3c5: sequencer reset: value=0x%02x \n", (unsigned)value); #endif if (state.sequencer.reset1 && ((value & 0x01) == 0)) { state.sequencer.char_map_select = 0; state.charmap_address = 0; bx_gui->lock(); bx_gui->set_text_charmap(&state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } state.sequencer.reset1 = (value >> 0) & 0x01; state.sequencer.reset2 = (value >> 1) & 0x01; break; // Sequencer: clocking mode register case 1: #if defined(DEBUG_VGA) printf("io write 3c5=%02x: clocking mode reg: ignoring \n", (unsigned)value); #endif state.sequencer.reg1 = value & 0x3f; state.x_dotclockdiv2 = ((value & 0x08) > 0); break; // Sequencer: map mask register case 2: state.sequencer.map_mask = (value & 0x0f); for (i = 0; i < 4; i++) state.sequencer.map_mask_bit[i] = (value >> i) & 0x01; break; // Sequencer: character map select register case 3: state.sequencer.char_map_select = value; charmap1 = value & 0x13; if (charmap1 > 3) charmap1 = (charmap1 & 3) + 4; charmap2 = (value & 0x2C) >> 2; if (charmap2 > 3) charmap2 = (charmap2 & 3) + 4; if (state.CRTC.reg[0x09] > 0) { state.charmap_address = (charmap1 << 13); bx_gui->lock(); bx_gui->set_text_charmap(&state.memory[0x20000 + state.charmap_address]); bx_gui->unlock(); state.vga_mem_updated = 1; } if (charmap2 != charmap1) printf("char map select: #2=%d (unused) \n", charmap2); break; // Sequencer: memory mode register case 4: state.sequencer.extended_mem = (value >> 1) & 0x01; state.sequencer.odd_even = (value >> 2) & 0x01; state.sequencer.chain_four = (value >> 3) & 0x01; #if defined(DEBUG_VGA) printf("io write 3c5: index 4: \n"); printf(" extended_mem %u \n", (unsigned)state.sequencer.extended_mem); printf(" odd_even %u \n", (unsigned)state.sequencer.odd_even); printf(" chain_four %u \n", (unsigned)state.sequencer.chain_four); #endif break; default: FAILURE_1(NotImplemented, "io write 3c5: index %u unhandled", (unsigned)state.sequencer.index); } } /** * Write to VGA DAC Pixel Mask register (0x3c6) * * The pixel inputs (R, G and B) are anded with this value. Set to FFh * for normal operation. **/ void CS3Trio64::write_b_3c6(u8 value) { state.pel.mask = value; #if defined(DEBUG_VGA) if (state.pel.mask != 0xff) printf("io write 3c6: PEL mask=0x%02x != 0xFF \n", value); #endif // state.pel.mask should be and'd with final value before // indexing into color register state.pel.data[] } /** * Write VGA DAC Address Read Mode register (0x3c7) * * The Color Registers in the standard VGA provide a mapping between the * palette of between 2 and 256 colors to a larger 18-bit color space. * This capability allows for efficient use of video memory while * providing greater flexibility in color choice. The standard VGA has * 256 palette entries containing six bits each of red, green, and blue * values. The palette RAM is accessed via a pair of address registers * and a data register. * * To write a palette entry, output the palette entry's index value to * the DAC Address Write Mode Register (0x3c8) then perform 3 writes to * the DAC Data Register (0x3c9), loading the red, green, then blue * values into the palette RAM. The internal write address automatically * advances allowing the next value's RGB values to be loaded without * having to reprogram the DAC Address Write Mode Register. This allows * the entire palette to be loaded in one write operation. * * To read a palette entry, output the palette entry's index to the DAC * Address Read Mode Register (0x3c7). Then perform 3 reads from the DAC * Data Register (0x3c9), loading the red, green, then blue values from * palette RAM. The internal read address automatically advances * allowing the next RGB values to be read without having to reprogram * the DAC Address Read Mode Register. * * The data values are 6-bits each. **/ void CS3Trio64::write_b_3c7(u8 value) { state.pel.read_data_register = value; state.pel.read_data_cycle = 0; state.pel.dac_state = 0x03; } /** * Write VGA DAC Address Write Mode register (0x3c8) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ void CS3Trio64::write_b_3c8(u8 value) { state.pel.write_data_register = value; state.pel.write_data_cycle = 0; state.pel.dac_state = 0x00; } /** * Write VGA DAC Data register (0x3c9) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ void CS3Trio64::write_b_3c9(u8 value) { switch (state.pel.write_data_cycle) { case 0: state.pel.data[state.pel.write_data_register].red = value; break; case 1: state.pel.data[state.pel.write_data_register].green = value; break; case 2: { state.pel.data[state.pel.write_data_register].blue = value; // Palette write complete. Check if value has changed bx_gui->lock(); bool changed = bx_gui->palette_change( state.pel.write_data_register, state.pel.data[state.pel.write_data_register].red << 2, state.pel.data[state.pel.write_data_register].green << 2, state.pel.data[state.pel.write_data_register].blue << 2); bx_gui->unlock(); // If palette value has changed, redraw the screen. if (changed) redraw_area(0, 0, old_iWidth, old_iHeight); } break; } // Move on to next RGB component state.pel.write_data_cycle++; // palette entry complete, move on to next one if (state.pel.write_data_cycle >= 3) { // BX_INFO(("state.pel.data[%u] {r=%u, g=%u, b=%u}", // (unsigned) state.pel.write_data_register, // (unsigned) state.pel.data[state.pel.write_data_register].red, // (unsigned) state.pel.data[state.pel.write_data_register].green, // (unsigned) state.pel.data[state.pel.write_data_register].blue); state.pel.write_data_cycle = 0; state.pel.write_data_register++; } } /** * Write to VGA Graphics Controller Index Register (0x3ce) * * The Graphics Controller registers control how the system accesses video RAM. * * The Graphics registers are accessed in an indexed fashion. By writing a byte * to the Graphics Index Register (0x3ce) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the Graphics Data Register (0x3cf). * * Graphics registers: * - Set/Reset register (index 0x00) * - Enable Set/Reset register (index 0x01) * - Color Compare register (index 0x02) * - Data Rotate register (index 0x03) * - Read Map Select register (index 0x04) * - Graphics Mode register (index 0x05) * - Miscellaneous Graphics register (index 0x06) * - Color Don't Care register (index 0x07) * - Bit Mask register (index 0x08) * . * * \code * Set/Reset register (index 0x00) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Set/Reset: Bits 3-0 of this field represent planes 3-0 *of the VGA display memory. This field is used by Write Mode 0 and Write Mode 3 *(See the Write Mode field.) In Write Mode 0, if the corresponding bit in the *Enable Set/Reset field is set, and in Write Mode 3 regardless of the Enable * Set/Reset field, the value of the bit in this field is * expanded to 8 bits and substituted for the data of the * respective plane and passed to the next stage in the * graphics pipeline, which for Write Mode 0 is the Logical * Operation unit and for Write Mode 3 is the Bit Mask *unit. * * Enable Set/Reset Register (index 0x01) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Enable Set/Reset: Bits 3-0 of this field represent *planes 3-0 of the VGA display memory. This field is used in Write Mode 0 (See *the Write Mode field) to select whether data for each plane is derived from *host data or from expansion of the respective bit in the Set/Reset field. * * Color Compare Register (index 0x02) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Compare: Bits 3-0 of this field represent planes *3-0 of the VGA display memory. This field holds a reference color that is used *by Read Mode 1 (See the Read Mode field.) Read Mode 1 returns the result of *the comparison between this value and a location of display memory, modified *by the Color Don't Care field. * * Data Rotate Register (index 0x03) * +-----+---+-----+ * | |4 3|2 1 0| * +-----+---+-----+ * ^ ^ * | +- 0..2: Rotate Count: * | This field is used in Write Mode 0 and Write Mode 3 *(See | the Write Mode field.) In these modes, the host data is | *rotated to the right by the value specified by the value of | this *field. A rotation operation consists of moving bits | 7-1 right *one position to bits 6-0, simultaneously | wrapping bit 0 around *to bit 7, and is repeated the number | of times specified by this *field. * +------ 3..4: Logical Operation: * This field is used in Write Mode 0 and Write Mode 2 *(See the Write Mode field.) The logical operation stage of the graphics *pipeline is 32 bits wide (1 byte * 4 planes) and performs the operations on *its inputs from the previous stage in the graphics pipeline and the latch *register. The latch register remains unchanged and the result is passed on to *the next stage in the pipeline. The results based on the value of this field *are: 00: Result is input from previous stage unmodified. 01: Result is input *from previous stage logical ANDed with latch register. 10: Result is input *from previous stage logical ORed with latch register. 11: Result is input from *previous stage logical XORed with latch register. * * Read Map Select register (index 0x04) * +-----------+---+ * | |1 0| * +-----------+---+ * ^ * +- 0..1: Read Map Select: The value of this field is used in *Read Mode 0 (see the Read Mode field) to specify the display memory plane to *transfer data from. Due to the arrangement of video memory, this field must be *modified four times to read one or more pixels values in the planar video *modes. * * Graphics Mode Register (index 0x05) * +-+-+-+-+-+-+---+ * | |6|5|4|3| |1 0| * +-+-+-+-+-+-+---+ * ^ ^ ^ ^ ^ * | | | | +- 0..1: Write Mode * | | | | This field selects between four write modes, simply *known | | | | as Write Modes 0-3, based upon the value of this *field: | | | | 00: Write Mode 0: In this mode, the host data is *first | | | | rotated as per the Rotate Count field, then *the | | | | Enable Set/Reset mechanism selects data from *this or | | | | the Set/Reset field. Then the selected *Logical | | | | Operation is performed on the resulting data *and the | | | | data in the latch register. Then the Bit *Mask field | | | | is used to select which bits come from *the resulting | | | | data and which come from the latch *register. Finally, | | | | only the bit planes enabled by *the Memory Plane Write | | | | Enable field are written to *memory. | | | | 01: Write Mode 1: In this mode, data is *transferred directly | | | | from the 32 bit latch register *to display memory, | | | | affected only by the Memory Plane *Write Enable field. | | | | The host data is not used in *this mode. | | | | 10: Write Mode 2: In this mode, the bits 3-0 *of the host | | | | data are replicated across all 8 bits of *their | | | | respective planes. Then the selected Logical *Operation | | | | is performed on the resulting data and the *data in the | | | | latch register. Then the Bit Mask field *is used to | | | | select which bits come from the resulting *data and which | | | | come from the latch register. *Finally, only the bit | | | | planes enabled by the Memory *Plane Write Enable field | | | | are written to memory. | | *| | 11: Write Mode 3: In this mode, the data in the Set/Reset | *| | | field is used as if the Enable Set/Reset field were *set | | | | to 1111b. Then the host data is first rotated as *per the | | | | Rotate Count field, then logical ANDed with *the value of | | | | the Bit Mask field. The resulting value *is used on the | | | | data obtained from the Set/Reset *field in the same way | | | | that the Bit Mask field would *ordinarily be used. to | | | | select which bits come from *the expansion of the | | | | Set/Reset field and which come *from the latch register. | | | | Finally, only the bit *planes enabled by the Memory Plane | | | | Write Enable *field are written to memory. | | | +--------- 3: Read Mode: | | | This field *selects between two read modes, simply known as Read | | | Mode *0, and Read Mode 1, based upon the value of this field: | | | 0: Read Mode 0: *In this mode, a byte from one of the four | | | planes is *returned on read operations. The plane from | | | which the *data is returned is determined by the value of | | | the *Read Map Select field. | | | 1: Read Mode 1: In this mode, a *comparison is made between | | | display memory and a *reference color defined by the Color | | | Compare field. *Bit planes not set in the Color Don't Care | | | field then *the corresponding color plane is not considered | | | in *the comparison. Each bit in the returned result | | | represents one *comparison between the reference color, with | | | the bit *being set if the comparison is true. | | +----------- 4: Host Odd/Even Memory *Read Addressing Enable: | | 0: Selects the standard *addressing mode. | | 1: Selects the odd/even addressing mode *used by the IBM CGA | | Adapter. | | Normally, the value *here follows the value of Memory Mode | | register bit 2 in *the sequencer." | +------------- 5: Shift Register Interleave Mode: | 1: *Directs the shift registers in the graphics controller to | format the serial *data stream with even-numbered bits from | both maps on *even-numbered maps, and odd-numbered bits from | both *maps on the odd-numbered maps. This bit is used for | modes 4 and 5. * +--------------- 6: 256-Color Shift Mode: * 0: Allows bit 5 to control the loading of the shift *registers. 1: Causes the shift registers to be loaded in a manner that * supports the 256-color mode. * * Miscellaneous Graphics register (index 0x06) * +-------+---+-+-+ * | |3 2|1|0| * +-------+---+-+-+ * ^ ^ ^ * | | +- 0: Alphanumeric Mode Disable: * | | This bit controls alphanumeric mode addressing. * | | 0: Text mode. * | | 1: Graphics modes, disables character generator *latches. | +--- 1: Chain Odd/Even Enable | 1: Directs the system *address bit, A0, to be replaced by a | higher-order bit. The odd *map is then selected when A0 is 1, | and the even map when A0 is *0. * +--- 2..3: Memory Map Select * This field specifies the range of host memory addresses *that is decoded by the VGA hardware and mapped into display memory accesses. *The values of this field and their corresponding host memory ranges are: 00: *A0000h-BFFFFh (128K region) 01: A0000h-AFFFFh (64K region) 10: B0000h-B7FFFh *(32K region) 11: B8000h-BFFFFh (32K region) * * Color Don't Care register (index 0x07) * +-------+-------+ * | |3 2 1 0| * +-------+-------+ * ^ * +- 0..3: Color Don't Care: Bits 3-0 of this field represent *planes 3-0 of the VGA display memory. This field selects the planes that are *used in the comparisons made by Read Mode 1 (See the Read Mode field.) Read *Mode 1 returns the result of the comparison between the value of the Color * Compare field and a location of display memory. If a bit * in this field is set, then the corresponding display * plane is considered in the comparison. If it is not set, * then that plane is ignored for the results of the * comparison. * * Bit Mask register (index 0x08) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * +----- 0..7: Bit Mask: This field is used in Write Modes 0, 2, and 3 * (See the Write Mode field.) It it is applied to one byte * of data in all four display planes. If a bit is set, * then the value of corresponding bit from the previous * stage in the graphics pipeline is selected; otherwise * the value of the corresponding bit in the latch register * is used instead. In Write Mode 3, the incoming data *byte, after being rotated is logical ANDed with this byte and the resulting *value is used in the same way this field would normally be used by itself. * \endcode **/ void CS3Trio64::write_b_3ce(u8 value) { #if defined(DEBUG_VGA) if (value > 0x08) /* ??? */ printf("io write: 3ce: value > 8 \n"); #endif state.graphics_ctrl.index = value; } /** * Write to VGA Graphics Controller Data Register (0x3cf) * * For a description of the Graphics registers, see CCirrus::write_b_3ce **/ void CS3Trio64::write_b_3cf(u8 value) { u8 prev_memory_mapping; bool prev_graphics_alpha; bool prev_chain_odd_even; /* Graphics Controller Registers 00..08 */ switch (state.graphics_ctrl.index) { case 0: /* Set/Reset */ state.graphics_ctrl.set_reset = value & 0x0f; break; case 1: /* Enable Set/Reset */ state.graphics_ctrl.enable_set_reset = value & 0x0f; break; case 2: /* Color Compare */ state.graphics_ctrl.color_compare = value & 0x0f; break; case 3: /* Data Rotate */ state.graphics_ctrl.data_rotate = value & 0x07; /* ??? is this bits 3..4 or 4..5 */ state.graphics_ctrl.raster_op = (value >> 3) & 0x03; /* ??? */ break; case 4: /* Read Map Select */ state.graphics_ctrl.read_map_select = value & 0x03; #if defined(DEBUG_VGA) printf("io write to 03cf = %02x (RMS) \n", (unsigned)value); #endif break; case 5: /* Mode */ state.graphics_ctrl.write_mode = value & 0x03; state.graphics_ctrl.read_mode = (value >> 3) & 0x01; state.graphics_ctrl.odd_even = (value >> 4) & 0x01; state.graphics_ctrl.shift_reg = (value >> 5) & 0x03; #if defined(DEBUG_VGA) if (state.graphics_ctrl.odd_even) printf("io write: 3cf: reg 05: value = %02xh \n", (unsigned)value); if (state.graphics_ctrl.shift_reg) printf("io write: 3cf: reg 05: value = %02xh \n", (unsigned)value); #endif break; case 6: /* Miscellaneous */ prev_graphics_alpha = state.graphics_ctrl.graphics_alpha; prev_chain_odd_even = state.graphics_ctrl.chain_odd_even; prev_memory_mapping = state.graphics_ctrl.memory_mapping; state.graphics_ctrl.graphics_alpha = value & 0x01; state.graphics_ctrl.chain_odd_even = (value >> 1) & 0x01; state.graphics_ctrl.memory_mapping = (value >> 2) & 0x03; #if defined(DEBUG_VGA) printf("memory_mapping set to %u \n", (unsigned)state.graphics_ctrl.memory_mapping); printf("graphics mode set to %u \n", (unsigned)state.graphics_ctrl.graphics_alpha); printf("odd_even mode set to %u \n", (unsigned)state.graphics_ctrl.odd_even); printf("io write: 3cf: reg 06: value = %02xh \n", (unsigned)value); #endif if (prev_memory_mapping != state.graphics_ctrl.memory_mapping) { redraw_area(0, 0, old_iWidth, old_iHeight); } if (prev_graphics_alpha != state.graphics_ctrl.graphics_alpha) { redraw_area(0, 0, old_iWidth, old_iHeight); old_iHeight = 0; } break; case 7: /* Color Don't Care */ state.graphics_ctrl.color_dont_care = value & 0x0f; break; case 8: /* Bit Mask */ state.graphics_ctrl.bitmask = value; break; default: /* ??? */ FAILURE_1(NotImplemented, "io write: 3cf: index %u unhandled", (unsigned)state.graphics_ctrl.index); } } /** * Write to VGA CRTC Index Register (0x3b4 or 0x3d4) * * The VGA CRTC Registers control how the video is output to the display. * * The CRTC registers are accessed in an indexed fashion. By writing a byte * to the CRTC Index Register (0x3d4) equal to the index of the particular * sub-register you wish to access, one can address the data pointed to by that * index by reading and writing the CRTC Data Register (0x3d5). * * CRTC registers: * - Horizontal Total Register (index 0x00) * - End Horizontal Display Register (index 0x01) * - Start Horizontal Blanking Register (index 0x02) * - End Horizontal Blanking Register (index 0x03) * - Start Horizontal Retrace Register (index 0x04) * - End Horizontal Retrace Register (index 0x05) * - Vertical Total Register (index 0x06) * - Overflow Register (index 0x07) * - Preset Row Scan Register (index 0x08) * - Maximum Scan Line Register (index 0x09) * - Cursor Start Register (index 0x0a) * - Cursor End Register (index 0x0b) * - Start Address High Register (index 0x0c) * - Start Address Low Register (index 0x0d) * - Cursor Location High Register (index 0x0e) * - Cursor Location Low Register (index 0x0f) * - Vertical Retrace Start Register (index 0x10) * - Vertical Retrace End Register (index 0x11) * - Vertical Display End Register (index 0x12) * - Offset Register (index 0x13) * - Underline Location Register (index 0x14) * - Start Vertical Blanking Register (index 0x15) * - End Vertical Blanking (index 0x16) * - CRTC Mode Control Register (index 0x17) * - Line Compare Register (index 0x18) * . * * \code * Horizontal Total register (index 0x00) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Horizontal Total: * This field is used to specify the number of character clocks per scan line. * This field, along with the dot rate selected, controls the horizontal * refresh rate of the VGA by specifying the amount of time one scan line * takes. This field is not programmed with the actual number of character * clocks, however. Due to timing factors of the VGA hardware (which, for * compatibility purposes has been emulated by VGA compatible chipsets), the * actual horizontal total is 5 character clocks more than the value stored in * this field, thus one needs to subtract 5 from the actual horizontal total * value desired before programming it into this register. * * End Horizontal Display register (index 0x01) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: End Horizontal Display: * This field is used to control the point that the sequencer stops outputting * pixel values from display memory, and sequences the pixel value specified by * the Overscan Palette Index field for the remainder of the scan line. The * overscan begins the character clock after the the value programmed into this * field. This register should be programmed with the number of character * clocks in the active display - 1. Note that the active display may be * affected by the Display Enable Skew field. * * Start Horizontal Blanking register (index 0x02) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Start Horizontal Blanking: * This field is used to specify the character clock at which the horizontal * blanking period begins. During the horizontal blanking period, the VGA * hardware forces the DAC into a blanking state, where all of the intensities * output are at minimum value, no matter what color information the attribute * controller is sending to the DAC. This field works in conjunction with the * End Horizontal Blanking field to specify the horizontal blanking period. * Note that the horizontal blanking can be programmed to appear anywhere within * the scan line, as well as being programmed to a value greater than the * Horizontal Total field preventing the horizontal blanking from occurring at * all. * * End Horizontal Blanking register (index 0x03) * +-+---+---------+ * |7|6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ ^ * | | +-- 0..4: End Horizontal Blanking: * | | Contains bits 4-0 of the End Horizontal Blanking field * | | which specifies the end of the horizontal blanking * | | period. Bit 5 is located in bit 7 of the End Horizontal * | | Retrace register (index 0x05). After the period has * | | begun as specified by the Start Horizontal Blanking * | | field, the 6-bit value of this field is compared against * | | the lower 6 bits of the character clock. When a match * | | occurs, the horizontal blanking signal is disabled. This * | | provides from 1 to 64 character clocks although some * | | implementations may match in the character clock * | | specified by the Start Horizontal Blanking field, in *which | | case the range is 0 to 63. Note that if blanking *extends | | past the end of the scan line, it will end on the *first | | match of this field on the next scan line. | *+--------- 5..6: Display Enable Skew: | This field affects *the timings of the display enable | circuitry in the VGA. *The value of this field is the number | of character clocks *that the display enable "signal" is | delayed. In all known *VGA cards, this field is always | programmed to 0. *Programming it to non-zero values results | in the overscan *being displayed over the number of | characters programmed *into this field at the beginning of | the scan line, as well *as the end of the active display | being shifted the number *of characters programmed into this | field. The characters *that extend past the normal end of the | active display can *be garbled in certain circumstances that | is dependent on *the particular VGA implementation. According | to *documentation from IBM, "This skew control is needed to | provide sufficient *time for the CRT controller to read a | character and *attribute code from the video buffer, to gain | access to *the character generator, and go through the | Horizontal PEL *Panning register in the attribute controller. | Each access *requires the 'display enable' signal to be | skewed one *character clock so that the video output is | synchronized *with the horizontal and vertical retrace | signals." as well *as "Note: Character skew is not adjustable | on the Type 2 *video and the bits are ignored; however, | programs should *set these bits for the appropriate skew to | maintain *compatibility." This may be required for some early | IBM *VGA implementations or may be simply an unused "feature" | carried over along *with its register description from the IBM | EGA *implementations that require the use of this field. * +--------------- 7: Enable Vertical Retrace Access: * This field was used in the IBM EGA to provide access to *the light pen input values as the light pen registers were mapped over CRTC *indexes 10h-11h. The VGA lacks capability for light pen input, thus this field *is normally forced to 1 (although always writing it as 1 might be a good idea *for compatibility), which in the EGA would enable access to the vertical *retrace fields instead of the light pen fields. * * Start Horizontal Retrace register (index 0x04) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Start Horizontal Retrace: * This field specifies the character clock at which the VGA begins sending the * horizontal synchronization pulse to the display which signals the monitor to *retrace back to the left side of the screen. The end of this pulse is *controlled by the End Horizontal Retrace field. This pulse may appear anywhere *in the scan line, as well as set to a position beyond the Horizontal Total *field which effectively disables the horizontal synchronization pulse. * * End Horizontal Retrace register (index 0x05) * +-+---+---------+ * |7|6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ ^ * | | +-- 0..4: End Horizontal Retrace: * | | This field specifies the end of the horizontal retrace *period, | | which begins at the character clock specified in *the Start | | Horizontal Retrace field. The horizontal *retrace signal is | | enabled until the lower 5 bits of the *character counter match | | the 5 bits of this field. This *provides for a horizontal | | retrace period from 1 to 32 *character clocks. Note that some | | implementations may *match immediately instead of 32 clocks | | away, making the *effective range 0 to 31 character clocks. | +--------- 5..6: Horizontal *Retrace Skew: | This field delays the start of the *horizontal retrace period | by the number of character *clocks equal to the value of this | field. From *observation, this field is programmed to 0, with | the *exception of the 40 column text modes where this field is | set to 1. The VGA *hardware simply acts as if this value is | added to the *Start Horizontal Retrace field. According to IBM | documentation, "For certain *modes, the 'horizontal retrace' | signal takes up the entire *blanking interval. Some internal | timings are generated by *the falling edge of the 'horizontal | retrace' signal. To *ensure that the signals are latched | properly, the *'retrace' signal is started before the end of | the 'display *enable' signal and then skewed several character | clock *times to provide the proper screen centering." This does | not appear to be *the case, leading me to believe this is yet | another *holdout from the IBM EGA implementations that do | require *the use of this field. * +--------------- 7: End Horizontal Blanking (bit 5): * This contains bit 5 of the End Horizontal Blanking field *in the End Horizontal Blanking register (index 0x03). * * Vertical Total register (index 0x06) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Vertical Total * This contains the lower 8 bits of the Vertical Total field. Bits 9-8 of this *field are located in the Overflow Register (index 0x07). This field determines *the number of scanlines in the active display and thus the length of each *vertical retrace. This field contains the value of the scanline counter at the *beginning of the last scanline in the vertical period. * * Overflow register (index 0x07) * +-+-+-+-+-+-+-+-+ * |7|6|5|4|3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ ^ * | | +-|-|-|-|-+- 0,5: Bit 8,9 of Vertical Total (index 0x06) * | +---|-|-|-+--- 1,6: Bit 8,9 of Vertical Display End (index 0x12) * +-----|-|-+----- 2,7: Bit 8,9 of Vertical Retrace Start (index 0x10) * | +--------- 3: Bit 8 of Start Vertical Blanking (index 0x15) * +----------- 4: Bit 8 of Line Compare (index 0x18) * * Preset Row Scan register (index 0x08) * +-+---+---------+ * | |6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ * | +-- 0..4: Preset Row Scan: * | This field is used when using text mode or any mode with *a non-zero | Maximum Scan Line field (index 0x09) to provide *for more precise | vertical scrolling than the Start Address *Register provides. The | value of this field specifies how many *scan lines to scroll the | display upwards. Valid values range *from 0 to the value of the | Maximum Scan Line field. Invalid *values may cause undesired effects | and seem to be dependent *upon the particular VGA implementation. * +--------- 5..6: Byte Panning: * The value of this field is added to the Start Address *Register when calculating the display memory address for the upper left hand *pixel or character of the screen. This allows for a maximum shift of 15, 31, *or 35 pixels without having to reprogram the Start Address Register. * * Maximum Scan Line register (index 0x09) * +-+-+-+---------+ * |7|6|5|4 3 2 1 0| * +-+-+-+---------+ * ^ ^ ^ ^ * | | | +-- 0..4: Maximum Scan Line: * | | | In text modes, this field is programmed with the *character height - 1 | | | (scan line numbers are zero based.) *In graphics modes, a non-zero | | | value in this field will *cause each scan line to be repeated by the | | | value of this *field + 1. | | +----------- 5: Bit 9 of Start Vertical Blanking (index 0x15) * | +------------- 6: Bit 9 of Line Compare (index 0x18) * +--------------- 7: Scan Doubling: * When this bit is set to 1, 200-scan-line video data is *converted to 400-scan-line output. To do this, the clock in the row scan *counter is divided by 2, which allows the 200-line modes to be displayed as *400 lines on the display (this is called double scanning; each line is * displayed twice). When this bit is set to 0, the clock *to the row scan counter is equal to the horizontal scan rate. * * Cursor Start Register (index 0x0a) * +---+-+---------+ * | |5|4 3 2 1 0| * +---+-+---------+ * ^ ^ * | +-- 0..4: Cursor Scan Line Start: * | This field controls the appearance of the text-mode *cursor by | specifying the scan line location within a character *cell at which | the cursor should begin, with the top-most scan *line in a character | cell being 0 and the bottom being with the *value of the Maximum Scan | Line field. * +------------5: Cursor Disable: * This field controls whether or not the text-mode cursor *is displayed: 0: Cursor Enabled. 1: Cursor Disabled. * * Cursor End Register (index 0x0b) * +-+---+---------+ * | |6 5|4 3 2 1 0| * +-+---+---------+ * ^ ^ * | +-- 0..4: Cursor Scan Line End: * | This field controls the appearance of the text-mode *cursor by | specifying the scan line location within a *character cell at which | the cursor should end, with the *top-most scan line in a character | cell being 0 and the bottom *being with the value of the Maximum Scan | Line field. If this *field is less than the Cursor Scan Line Start | field, the *cursor is not drawn. Some graphics adapters, such as the | IBM *EGA display a split-block cursor instead. * +------------ 5: Cursor Skew: * This field was necessary in the EGA to synchronize the *cursor with internal timing. In the VGA it basically is added to the cursor * location. In some cases when this value is non-zero and *the cursor is near the left or right edge of the screen, the cursor will not *appear at all, or a second cursor above and to the left of the actual one may * appear. This behavior may not be the same on all VGA *compatible adapter cards. * * Start Address High register (index 0x0c) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 8..15 of the Start Address. * Bits 0..7 are in the Start Address Low register (index 0x0d). The Start *Address field specifies the display memory address of the upper left pixel or *character of the screen. Because the standard VGA has a maximum of 256K of *memory, and memory is accessed 32 bits at a time, this 16-bit field is *sufficient to allow the screen to start at any memory address. Normally this *field is programmed to 0h, except when using virtual resolutions, paging, * and/or split-screen operation. Note that the VGA display will wrap around in *display memory if the starting address is too high. (This may or may not be *desirable, depending on your intentions.) * * Start Address Low register (index 0x0d) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of the Start Address. See Start Address High register (index *0x0c) * * Cursor Location High register (index 0x0e) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 8..15 of the Cursor Location. * Bits 0..7 are in the Cursor Location Low register (index 0x0d). When the VGA *hardware is displaying text mode and the text-mode cursor is enabled, the *hardware compares the address of the character currently being displayed with *sum of value of this field and the sum of the Cursor Skew field. If the values *equal then the scan lines in that character specified by the Cursor Scan Line *Start field and the Cursor Scan Line End field are replaced with the * foreground color. * * Cursor Location Low register (index 0x0f) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of the Cursor Location. See Cursor Location High register *(index 0x0f) * * Vertical Retrace Start register (index 0x10) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Vertical Retrace Start * Bits 8 and 9 are in the Overflow Register (index 0x07). This field controls *the start of the vertical retrace pulse which signals the display to move up *to the beginning of the active display. This field contains the value of the *vertical scanline counter at the beginning of the first scanline where the *vertical retrace signal is asserted. * * Vertical Retrace End register (index 0x11) * +-+-+---+-------+ * |7|6|5|4|3 2 1 0| * +-+-+---+-------+ * ^ ^ ^ ^ ^ * | | | | +-- 0..3: Vertical Retrace End: * | | | | This field determines the end of the vertical retrace *pulse, and thus its | | | | length. This field contains the lower *four bits of the vertical scanline | | | | counter at the *beginning of the scanline immediately after the last | | | | scanline where *the vertical retrace signal is asserted. | | | +--------- 4: End Vertical *Interrupt | | +----------- 5: Enable Vertical Interrupt | +------------- 6: *Memory Refresh Bandwidth: | Nearly all video chipsets *include a few registers that control memory, bus, | or other *timings not directly related to the output of the video card. Most | VGA/SVGA *implementations ignore the value of this field; however, in the | least, IBM *VGA adapters do utilize it and thus for compatibility with these | chipsets *this field should be programmed. This register is used in the IBM | VGA *hardware to control the number of DRAM refresh cycles per scan line. | The *three refresh cycles per scanline is appropriate for the IBM VGA | horizontal *frequency of approximately 31.5 kHz. For horizontal frequencies | greater than *this, this setting will work as the DRAM will be refreshed more | often. *However, refreshing not often enough for the DRAM can cause memory | loss. *Thus at some point slower than 31.5 kHz the five refresh cycle setting | *should be used. At which particular point this should occur, would require | *better knowledge of the IBM VGA's schematics than I have available. | *According to IBM documentation, "Selecting five refresh cycles allows use of * | the VGA chip with 15.75 kHz displays." which isn't *really enough to go by | unless the mode you are defining *has a 15.75 kHz horizontal frequency. * +--------------- 7: CRTC Registers Protect Enable: * This field is used to protect the video timing registers *from being changed by programs written for earlier graphics chipsets that *attempt to program these registers with values unsuitable for VGA timings. *When this field is set to 1, the CRTC register indexes 00h-07h ignore write *access, with the exception of bit 4 of the Overflow Register, which holds bit *8 of the Line Compare field. * * Vertical Display End register (index 0x12) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Vertical Display End * Bits 8 and 9 are in the Overflow Register (index 0x07). This field contains *the value of the vertical scanline counter at the beggining of the scanline *immediately after the last scanline of active display. * * Offset register (index 0x13) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Offset: * This field specifies the address difference between consecutive scan lines or *two lines of characters. Beginning with the second scan line, the starting *scan line is increased by twice the value of this register multiplied by the *current memory address size (byte = 1, word = 2, double-word = 4) each line. *For text modes the following equation is used: Offset = Width / ( *MemoryAddressSize * 2 ) and in graphics mode, the following equation is used: * Offset = Width / ( PixelsPerAddress * MemoryAddressSize * 2 ) * where Width is the width in pixels of the screen. This register can be *modified to provide for a virtual resolution, in which case Width is the width *is the width in pixels of the virtual screen. PixelsPerAddress is the number *of pixels stored in one display memory address, and MemoryAddressSize is the *current memory addressing size. * * Underline Location register (index 0x14) * +-+-+-+---------+ * | |6|5|4 3 2 1 0| * +-+-+-+---------+ * ^ ^ ^ * | | +-- 0..4: Underline Location * | | These bits specify the horizontal scan line of a *character row on which an | | underline occurs. The value *programmed is the scan line desired minus 1. | +----------- 5: Divide Memory *Address Clock by 4: | 1: The memory-address counter is *clocked with the character clock divided | by 4, which is *used when doubleword addresses are used. * +------------- 6: Double-Word Addressing: * 1: Memory addresses are doubleword addresses. See the *description of the word/byte mode bit (bit 6) in the CRT Mode Control Register *(index 0x17) * * Start Vertical Blanking register (index 0x15) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Start Vertical Blanking * Bit 8 is in the Overflow Register (index 0x07), and bit 9 is in the Maximum *Scan Line register (index 0x09). This field determines when the vertical *blanking period begins, and contains the value of the vertical scanline *counter at the beginning of the first vertical scanline of blanking. * * End Vertical Blanking register (index 0x16) * +-+-------------+ * | |6 5 4 3 2 1 0| * +-+-------------+ * ^ * +---- 0..6: End Vertical Blanking: * This field determines when the vertical blanking period *ends, and contains the value of the vertical scanline counter at the beginning *of the vertical scanline immediately after the last scanline of blanking. * * CRTC Mode Control Register (index 0x17) * +-+-+-+-+-+-+-+-+ * |7|6|5| |3|2|1|0| * +-+-+-+-+-+-+-+-+ * ^ ^ ^ ^ ^ ^ ^ * | | | | | | +- 0: Map Display Address 13: * | | | | | | This bit selects the source of bit 13 of the output *multiplexer: | | | | | | 0: Bit 0 of the row scan counter is the *source. | | | | | | 1: Bit 13 of the address counter is the source. * | | | | | | The CRT controller used on the IBM Color/Graphics *Adapter was capable of | | | | | | using 128 horizontal scan-line *addresses. For the VGA to obtain 640-by-200 | | | | | | graphics *resolution, the CRT controller is programmed for 100 horizontal | | | | | | *scan lines with two scan-line addresses per character row. Row scan address | *| | | | | bit 0 becomes the most-significant address bit to the *display buffer. | | | | | | Successive scan lines of the display image *are displaced in 8KB of memory. | | | | | | This bit allows *compatibility with the graphics modes of earlier adapters. | | | | | +--- 1: *Map Display Address 14: | | | | | This bit selects the source of bit *14 of the output multiplexer: | | | | | 0: Bit 1 of the row scan *counter is the source. | | | | | 1: Bit 14 of the address counter *is the source. | | | | +----- 2: Divide Scan Line clock by 2: | | | | This *bit selects the clock that controls the vertical timing counter: | | | | 0: *The horizontal retrace clock. | | | | 1: The horizontal retrace *clock divided by 2. | | | | Dividing the clock effectively doubles *the vertical resolution of the CRT | | | | controller. The *vertical counter has a maximum resolution of 1024 scan lines | | | | because *the vertical total value is 10-bits wide. If the vertical counter is | | | | *clocked with the horizontal retrace divided by 2, the vertical resolution is * | | | | doubled to 2048 scan lines." * | | | +------- 3: Divide Memory Address clock by 2: * | | | This bit selects the clock that controlls the address *counter: | | | 0: The character clock. | | | 1: The character *clock divided by 2. | | | This bit is used to create either a *byte or word refresh address for the | | | display buffer. | | *+----------- 5: Address Wrap Select: | | This bit selects the *memory-address bit, bit MA 13 or MA 15, that appears on | | the output pin MA *0, in the word address mode. If the VGA is not in the word | | address mode, *bit 0 from the address counter appears on the output pin, MA 0. | | 0: Selects *MA 13. Used in applications where only 64KB of video memory is | | present. | *| 1: Selects MA 15. In odd/even mode, this bit should be set *to 1 because | | 256KB of video memory is installed on *the system board. | | This function maintains compatibility *with the IBM Color/Graphics Monitor | | Adapter. | *+------------- 6: Word/Byte Mode Select: | 0: Selects the *word address mode. The word mode shifts the memory-address | counter bits to *the left by one bit; the most-significant bit of the | counter appears on the *least-significant bit of the memory address | outputs. * | 1: Selects the byte address mode. * | The doubleword bit in the Underline Location register *(index 0x14) also | controls the addressing. When the *doubleword bit is 0, the word/byte bit | selects the mode. *When the doubleword bit is set to 1, the addressing is | shifted by two bits. * +--------------- 7: Sync Enable: * 0: Disables the horizontal and vertical retrace *signals and forces them to an inactive level. 1: Enables the horizontal and *vertical retrace signals. This bit does not reset any other registers or *signal outputs. * * Line Compare register (index 0x18) * +---------------+ * |7 6 5 4 3 2 1 0| * +---------------+ * ^ * 0..7: Bits 0..7 of Line Compare * Bit 8 is in the Overflow Register (index 0x07), and bit 9 is in the Maximum *Scan Line register (index 0x09). The Line Compare field specifies the scan *line at which a horizontal division can occur, providing for split-screen *operation. If no horizontal division is required, this field should be set to *3FFh. When the scan line counter reaches the value in the Line Compare field, *the current scan line address is reset to 0 and the Preset Row Scan is *presumed to be 0. If the Pixel Panning Mode field is set to 1 then the Pixel *Shift Count and Byte Panning fields are reset to 0 for the remainder of the *display cycle. \endcode **/ void CS3Trio64::write_b_3d4(u8 value) { state.CRTC.address = value & 0x7f; #if defined(DEBUG_VGA) if (state.CRTC.address > 0x18) printf("write: invalid CRTC register 0x%02x selected", (unsigned)state.CRTC.address); #endif } /** * Write to VGA CRTC Data Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ void CS3Trio64::write_b_3d5(u8 value) { /* CRTC Registers */ if (state.CRTC.address > 0x18) { #if defined(DEBUG_VGA) printf("write: invalid CRTC register 0x%02x ignored", (unsigned)state.CRTC.address); #endif return; } if (state.CRTC.write_protect && (state.CRTC.address < 0x08)) { if (state.CRTC.address == 0x07) { state.CRTC.reg[state.CRTC.address] &= ~0x10; state.CRTC.reg[state.CRTC.address] |= (value & 0x10); state.line_compare &= 0x2ff; if (state.CRTC.reg[0x07] & 0x10) state.line_compare |= 0x100; redraw_area(0, 0, old_iWidth, old_iHeight); return; } else { return; } } if (value != state.CRTC.reg[state.CRTC.address]) { state.CRTC.reg[state.CRTC.address] = value; switch (state.CRTC.address) { case 0x07: state.vertical_display_end &= 0xff; if (state.CRTC.reg[0x07] & 0x02) state.vertical_display_end |= 0x100; if (state.CRTC.reg[0x07] & 0x40) state.vertical_display_end |= 0x200; state.line_compare &= 0x2ff; if (state.CRTC.reg[0x07] & 0x10) state.line_compare |= 0x100; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x08: // Vertical pel panning change redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x09: state.y_doublescan = ((value & 0x9f) > 0); state.line_compare &= 0x1ff; if (state.CRTC.reg[0x09] & 0x40) state.line_compare |= 0x200; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x0A: case 0x0B: case 0x0E: case 0x0F: // Cursor size / location change state.vga_mem_updated = 1; break; case 0x0C: case 0x0D: // Start address change if (state.graphics_ctrl.graphics_alpha) { redraw_area(0, 0, old_iWidth, old_iHeight); } else { state.vga_mem_updated = 1; } break; case 0x12: state.vertical_display_end &= 0x300; state.vertical_display_end |= state.CRTC.reg[0x12]; break; case 0x13: case 0x14: case 0x17: // Line offset change state.line_offset = state.CRTC.reg[0x13] << 1; if (state.CRTC.reg[0x14] & 0x40) state.line_offset <<= 2; else if ((state.CRTC.reg[0x17] & 0x40) == 0) state.line_offset <<= 1; redraw_area(0, 0, old_iWidth, old_iHeight); break; case 0x18: state.line_compare &= 0x300; state.line_compare |= state.CRTC.reg[0x18]; redraw_area(0, 0, old_iWidth, old_iHeight); break; } } } /** * Read from the attribute controller index register (0x3c0) * * For a description of the attribute controller registers, see *CCirrus::write_b_3c0. **/ u8 CS3Trio64::read_b_3c0() { if (state.attribute_ctrl.flip_flop == 0) { // BX_INFO(("io read: 0x3c0: flip_flop = 0")); return (state.attribute_ctrl.video_enabled << 5) | state.attribute_ctrl.address; } else { FAILURE(NotImplemented, "io read: 0x3c0: flip_flop != 0"); } } /** * Read from the attribute controller data register (0x3c1) * * For a description of the attribute controller registers, see *CCirrus::write_b_3c0. **/ u8 CS3Trio64::read_b_3c1() { u8 retval; switch (state.attribute_ctrl.address) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: retval = state.attribute_ctrl.palette_reg[state.attribute_ctrl.address]; return (retval); break; case 0x10: /* mode control register */ retval = (state.attribute_ctrl.mode_ctrl.graphics_alpha << 0) | (state.attribute_ctrl.mode_ctrl.display_type << 1) | (state.attribute_ctrl.mode_ctrl.enable_line_graphics << 2) | (state.attribute_ctrl.mode_ctrl.blink_intensity << 3) | (state.attribute_ctrl.mode_ctrl.pixel_panning_compat << 5) | (state.attribute_ctrl.mode_ctrl.pixel_clock_select << 6) | (state.attribute_ctrl.mode_ctrl.internal_palette_size << 7); return (retval); break; case 0x11: /* overscan color register */ return (state.attribute_ctrl.overscan_color); break; case 0x12: /* color plane enable */ return (state.attribute_ctrl.color_plane_enable); break; case 0x13: /* horizontal PEL panning register */ return (state.attribute_ctrl.horiz_pel_panning); break; case 0x14: /* color select register */ return (state.attribute_ctrl.color_select); break; default: FAILURE_1(NotImplemented, "io read: 0x3c1: unknown register 0x%02x", (unsigned)state.attribute_ctrl.address); } } /** * Read from the VGA Input Status register (0x3c2) * * \code * +-----+-+-------+ * | |4| | * +-----+-+-------+ * ^ * +--------- 4: Switch Sense: * Returns the status of the four sense switches as *selected by the Clock Select field of the Miscellaneous Output Register (See * CCirrus::write_b_3c2) * \endcode **/ u8 CS3Trio64::read_b_3c2() { return 0; // input status register } /** * Read from the VGA Enable register (0x3c3) * * (Not sure where this comes from; doesn't seem to be in the VGA specs.) **/ u8 CS3Trio64::read_b_3c3() { return state.vga_enabled; } /** * Read from the VGA sequencer index register (0x3c4) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ u8 CS3Trio64::read_b_3c4() { return state.sequencer.index; } /** * Read from the VGA sequencer data register (0x3c5) * * For a description of the Sequencer registers, see CCirrus::write_b_3c4 **/ u8 CS3Trio64::read_b_3c5() { switch (state.sequencer.index) { case 0: /* sequencer: reset */ #if defined(DEBUG_VGA) BX_DEBUG(("io read 0x3c5: sequencer reset")); #endif return (state.sequencer.reset1 ? 1 : 0) | (state.sequencer.reset2 ? 2 : 0); break; case 1: /* sequencer: clocking mode */ #if defined(DEBUG_VGA) BX_DEBUG(("io read 0x3c5: sequencer clocking mode")); #endif return state.sequencer.reg1; break; case 2: /* sequencer: map mask register */ return state.sequencer.map_mask; break; case 3: /* sequencer: character map select register */ return state.sequencer.char_map_select; break; case 4: /* sequencer: memory mode register */ return (state.sequencer.extended_mem << 1) | (state.sequencer.odd_even << 2) | (state.sequencer.chain_four << 3); break; default: FAILURE_1(NotImplemented, "io read 0x3c5: index %u unhandled", (unsigned)state.sequencer.index); } } /** * Read from VGA DAC Data register (0x3c9) * * For a description of DAC registers see CCirrus::write_b_3c7 **/ u8 CS3Trio64::read_b_3c9() { u8 retval; if (state.pel.dac_state == 0x03) { switch (state.pel.read_data_cycle) { case 0: retval = state.pel.data[state.pel.read_data_register].red; break; case 1: retval = state.pel.data[state.pel.read_data_register].green; break; case 2: retval = state.pel.data[state.pel.read_data_register].blue; break; default: retval = 0; // keep compiler happy } state.pel.read_data_cycle++; if (state.pel.read_data_cycle >= 3) { state.pel.read_data_cycle = 0; state.pel.read_data_register++; } } else { retval = 0x3f; } return retval; } /** * Read from the VGA Feature Control register (index 0x3ca) * * \code * +-----------+-+-+ * | |1|0| * +-----------+-+-+ * ^ ^ * | +- 0: Feature Control 0 (reserved) * +--- 1: Feature Control 1 (reserved) * \endcode **/ u8 CS3Trio64::read_b_3ca() { return 0; } /** * Write to the VGA Miscellaneous Output Register (0x3cc) * * For a description of the Miscellaneous Output register, see *CCirrus::write_b_3c2 **/ u8 CS3Trio64::read_b_3cc() { /* Miscellaneous Output / Graphics 1 Position ??? */ return ((state.misc_output.color_emulation & 0x01) << 0) | ((state.misc_output.enable_ram & 0x01) << 1) | ((state.misc_output.clock_select & 0x03) << 2) | ((state.misc_output.select_high_bank & 0x01) << 5) | ((state.misc_output.horiz_sync_pol & 0x01) << 6) | ((state.misc_output.vert_sync_pol & 0x01) << 7); } /** * Read from VGA Graphics Controller Data Register (0x3cf) * * For a description of the Graphics registers, see CCirrus::write_b_3ce **/ u8 CS3Trio64::read_b_3cf() { u8 retval; switch (state.graphics_ctrl.index) { case 0: /* Set/Reset */ return (state.graphics_ctrl.set_reset); break; case 1: /* Enable Set/Reset */ return (state.graphics_ctrl.enable_set_reset); break; case 2: /* Color Compare */ return (state.graphics_ctrl.color_compare); break; case 3: /* Data Rotate */ retval = ((state.graphics_ctrl.raster_op & 0x03) << 3) | ((state.graphics_ctrl.data_rotate & 0x07) << 0); return (retval); break; case 4: /* Read Map Select */ return (state.graphics_ctrl.read_map_select); break; case 5: /* Mode */ retval = ((state.graphics_ctrl.shift_reg & 0x03) << 5) | ((state.graphics_ctrl.odd_even & 0x01) << 4) | ((state.graphics_ctrl.read_mode & 0x01) << 3) | ((state.graphics_ctrl.write_mode & 0x03) << 0); #if defined(DEBUG_VGA) if (state.graphics_ctrl.odd_even || state.graphics_ctrl.shift_reg) BX_DEBUG(("io read 0x3cf: reg 05 = 0x%02x", (unsigned)retval)); #endif return (retval); break; case 6: /* Miscellaneous */ return ((state.graphics_ctrl.memory_mapping & 0x03) << 2) | ((state.graphics_ctrl.odd_even & 0x01) << 1) | ((state.graphics_ctrl.graphics_alpha & 0x01) << 0); break; case 7: /* Color Don't Care */ return (state.graphics_ctrl.color_dont_care); break; case 8: /* Bit Mask */ return (state.graphics_ctrl.bitmask); break; default: FAILURE_1(NotImplemented, "io read: 0x3cf: index %u unhandled", (unsigned)state.graphics_ctrl.index); } } /** * Read from VGA CRTC Index Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ u8 CS3Trio64::read_b_3d4() { return state.CRTC.address; } /** * Read from VGA CRTC Data Register (0x3b5 or 0x3d5) * * For a description of CRTC Registers, see CCirrus::write_b_3d4. **/ u8 CS3Trio64::read_b_3d5() { if (state.CRTC.address > 0x18) { FAILURE_1(NotImplemented, "io read: invalid CRTC register 0x%02x \n", (unsigned)state.CRTC.address); } return state.CRTC.reg[state.CRTC.address]; } /** * Read from the VGA Input Status 1 register (0x3ba or 0x3da) * * \code * +-------+-+---+-+ * | |3| |0| * +-------+-+---+-+ * ^ ^ * | +- 0: Display Disabled: * | 1: Indicates a horizontal or vertical retrace *interval. This | bit is the real-time status of the inverted *'display | enable' signal. Programs have used this status bit *to | restrict screen updates to the inactive display intervals * | in order to reduce screen flicker. The video *subsystem is | designed to eliminate this software requirement; *screen | updates may be made at any time without screen *degradation. * +------- 1: Vertical Retrace: * 1: Indicates that the display is in a vertical retrace *interval. This bit can be programmed, through the Vertical Retrace End * register, to generate an interrupt at the start of *the vertical retrace. \endcode **/ u8 CS3Trio64::read_b_3da() { /* Input Status 1 (color emulation modes) */ u8 retval = 0; // bit3: Vertical Retrace // 0 = display is in the display mode // 1 = display is in the vertical retrace mode // bit0: Display Enable // 0 = display is in the display mode // 1 = display is not in the display mode; either the // horizontal or vertical retrace period is active // using 72 Hz vertical frequency /*** TO DO ??? *** usec = bx_pc_system.time_usec(); switch ( ( state.misc_output.vert_sync_pol << 1) | state.misc_output.horiz_sync_pol ) { case 0: vertres = 200; break; case 1: vertres = 400; break; case 2: vertres = 350; break; default: vertres = 480; break; } if ((usec % 13888) < 70) { vert_retrace = 1; } if ((usec % (13888 / vertres)) == 0) { horiz_retrace = 1; } if (horiz_retrace || vert_retrace) retval = 0x01; if (vert_retrace) retval |= 0x08; *** TO DO ??? ***/ /* reading this port resets the flip-flop to address mode */ state.attribute_ctrl.flip_flop = 0; return retval; } u8 CS3Trio64::get_actl_palette_idx(u8 index) { return state.attribute_ctrl.palette_reg[index]; } void CS3Trio64::redraw_area(unsigned x0, unsigned y0, unsigned width, unsigned height) { unsigned xti; unsigned yti; unsigned xt0; unsigned xt1; unsigned yt0; unsigned yt1; unsigned xmax; unsigned ymax; if ((width == 0) || (height == 0)) { return; } state.vga_mem_updated = 1; if (state.graphics_ctrl.graphics_alpha) { // graphics mode xmax = old_iWidth; ymax = old_iHeight; xt0 = x0 / X_TILESIZE; yt0 = y0 / Y_TILESIZE; if (x0 < xmax) { xt1 = (x0 + width - 1) / X_TILESIZE; } else { xt1 = (xmax - 1) / X_TILESIZE; } if (y0 < ymax) { yt1 = (y0 + height - 1) / Y_TILESIZE; } else { yt1 = (ymax - 1) / Y_TILESIZE; } for (yti = yt0; yti <= yt1; yti++) { for (xti = xt0; xti <= xt1; xti++) { SET_TILE_UPDATED(xti, yti, 1); } } } else { // text mode memset(state.text_snapshot, 0, sizeof(state.text_snapshot)); } } void CS3Trio64::update(void) { unsigned iHeight; unsigned iWidth; /* no screen update necessary */ if (state.vga_mem_updated == 0) return; /* skip screen update when vga/video is disabled or the sequencer is in reset * mode */ if (!state.vga_enabled || !state.attribute_ctrl.video_enabled || !state.sequencer.reset2 || !state.sequencer.reset1) return; // fields that effect the way video memory is serialized into screen output: // GRAPHICS CONTROLLER: // state.graphics_ctrl.shift_reg: // 0: output data in standard VGA format or CGA-compatible 640x200 2 color // graphics mode (mode 6) // 1: output data in CGA-compatible 320x200 4 color graphics mode // (modes 4 & 5) // 2: output data 8 bits at a time from the 4 bit planes // (mode 13 and variants like modeX) // if (state.vga_mem_updated==0 || state.attribute_ctrl.video_enabled == 0) if (state.graphics_ctrl.graphics_alpha) { u8 color; unsigned bit_no; unsigned r; unsigned c; unsigned x; unsigned y; unsigned long byte_offset; unsigned long start_addr; unsigned xc; unsigned yc; unsigned xti; unsigned yti; start_addr = (state.CRTC.reg[0x0c] << 8) | state.CRTC.reg[0x0d]; // BX_DEBUG(("update: shiftreg=%u, chain4=%u, mapping=%u", // (unsigned) state.graphics_ctrl.shift_reg, // (unsigned) state.sequencer.chain_four, // (unsigned) state.graphics_ctrl.memory_mapping); determine_screen_dimensions(&iHeight, &iWidth); if ((iWidth != old_iWidth) || (iHeight != old_iHeight) || (state.last_bpp > 8)) { bx_gui->dimension_update(iWidth, iHeight); old_iWidth = iWidth; old_iHeight = iHeight; state.last_bpp = 8; } switch (state.graphics_ctrl.shift_reg) { case 0: u8 attribute, palette_reg_val, DAC_regno; unsigned long line_compare; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; if (state.graphics_ctrl.memory_mapping == 3) { // CGA 640x200x2 for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; /* 0 or 0x2000 */ byte_offset = start_addr + ((y & 1) << 13); /* to the start of the line */ byte_offset += (320 / 4) * (y / 2); /* to the byte start */ byte_offset += (x / 8); bit_no = 7 - (x % 8); palette_reg_val = (((state.memory[byte_offset]) >> bit_no) & 1); DAC_regno = state.attribute_ctrl.palette_reg[palette_reg_val]; state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } else { // output data in serial fashion with each display plane // output on its associated serial output. Standard EGA/VGA format plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; line_compare = state.line_compare; if (state.y_doublescan) line_compare >>= 1; for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; if (state.x_dotclockdiv2) x >>= 1; bit_no = 7 - (x % 8); if (y > line_compare) { byte_offset = x / 8 + ((y - line_compare - 1) * state.line_offset); } else { byte_offset = start_addr + x / 8 + (y * state.line_offset); } attribute = (((plane0[byte_offset] >> bit_no) & 0x01) << 0) | (((plane1[byte_offset] >> bit_no) & 0x01) << 1) | (((plane2[byte_offset] >> bit_no) & 0x01) << 2) | (((plane3[byte_offset] >> bit_no) & 0x01) << 3); attribute &= state.attribute_ctrl.color_plane_enable; // undocumented feature ???: colors 0..7 high intensity, // colors 8..15 blinking using low/high intensity. Blinking is // not implemented yet. if (state.attribute_ctrl.mode_ctrl.blink_intensity) attribute ^= 0x08; palette_reg_val = state.attribute_ctrl.palette_reg[attribute]; if (state.attribute_ctrl.mode_ctrl.internal_palette_size) { // use 4 lower bits from palette register // use 4 higher bits from color select register // 16 banks of 16-color registers DAC_regno = (palette_reg_val & 0x0f) | (state.attribute_ctrl.color_select << 4); } else { // use 6 lower bits from palette register // use 2 higher bits from color select register // 4 banks of 64-color registers DAC_regno = (palette_reg_val & 0x3f) | ((state.attribute_ctrl.color_select & 0x0c) << 4); } // DAC_regno &= video DAC mask register ??? state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } break; // case 0 case 1: // output the data in a CGA-compatible 320x200 4 color graphics // mode. (modes 4 & 5) /* CGA 320x200x4 start */ for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { y = yc + r; if (state.y_doublescan) y >>= 1; for (c = 0; c < X_TILESIZE; c++) { x = xc + c; if (state.x_dotclockdiv2) x >>= 1; /* 0 or 0x2000 */ byte_offset = start_addr + ((y & 1) << 13); /* to the start of the line */ byte_offset += (320 / 4) * (y / 2); /* to the byte start */ byte_offset += (x / 4); attribute = 6 - 2 * (x % 4); palette_reg_val = (state.memory[byte_offset]) >> attribute; palette_reg_val &= 3; DAC_regno = state.attribute_ctrl.palette_reg[palette_reg_val]; state.tile[r * X_TILESIZE + c] = DAC_regno; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } /* CGA 320x200x4 end */ break; // case 1 case 2: // output the data eight bits at a time from the 4 bit plane // (format for VGA mode 13 hex) case 3: // FIXME: is this really the same ??? if (state.sequencer.chain_four) { unsigned long pixely; unsigned long pixelx; unsigned long plane; if (state.misc_output.select_high_bank != 1) { FAILURE(NotImplemented, "update: select_high_bank != 1 \n"); } for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { pixely = yc + r; if (state.y_doublescan) pixely >>= 1; for (c = 0; c < X_TILESIZE; c++) { pixelx = (xc + c) >> 1; plane = (pixelx % 4); byte_offset = start_addr + (plane * 65536) + (pixely * state.line_offset) + (pixelx & ~0x03); color = state.memory[byte_offset]; state.tile[r * X_TILESIZE + c] = color; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } else { // chain_four == 0, modeX unsigned long pixely; // chain_four == 0, modeX unsigned long pixelx; // chain_four == 0, modeX unsigned long plane; for (yc = 0, yti = 0; yc < iHeight; yc += Y_TILESIZE, yti++) { for (xc = 0, xti = 0; xc < iWidth; xc += X_TILESIZE, xti++) { if (GET_TILE_UPDATED(xti, yti)) { for (r = 0; r < Y_TILESIZE; r++) { pixely = yc + r; if (state.y_doublescan) pixely >>= 1; for (c = 0; c < X_TILESIZE; c++) { pixelx = (xc + c) >> 1; plane = (pixelx % 4); byte_offset = (plane * 65536) + (pixely * state.line_offset) + (pixelx >> 2); color = state.memory[start_addr + byte_offset]; state.tile[r * X_TILESIZE + c] = color; } } SET_TILE_UPDATED(xti, yti, 0); bx_gui->graphics_tile_update(state.tile, xc, yc); } } } } break; // case 2 default: FAILURE_1(NotImplemented, "update: shift_reg == %u \n", (unsigned)state.graphics_ctrl.shift_reg); } state.vga_mem_updated = 0; return; } else { // text mode unsigned long start_address; unsigned long cursor_address; unsigned long cursor_x; unsigned long cursor_y; bx_vga_tminfo_t tm_info; unsigned VDE; unsigned MSL; unsigned cols; unsigned rows; unsigned cWidth; tm_info.start_address = 2 * ((state.CRTC.reg[12] << 8) + state.CRTC.reg[13]); tm_info.cs_start = state.CRTC.reg[0x0a] & 0x3f; tm_info.cs_end = state.CRTC.reg[0x0b] & 0x1f; tm_info.line_offset = state.CRTC.reg[0x13] << 2; tm_info.line_compare = state.line_compare; tm_info.h_panning = state.attribute_ctrl.horiz_pel_panning & 0x0f; tm_info.v_panning = state.CRTC.reg[0x08] & 0x1f; tm_info.line_graphics = state.attribute_ctrl.mode_ctrl.enable_line_graphics; tm_info.split_hpanning = state.attribute_ctrl.mode_ctrl.pixel_panning_compat; if ((state.sequencer.reg1 & 0x01) == 0) { if (tm_info.h_panning >= 8) tm_info.h_panning = 0; else tm_info.h_panning++; } else { tm_info.h_panning &= 0x07; } // Verticle Display End: find out how many lines are displayed VDE = state.vertical_display_end; // Maximum Scan Line: height of character cell MSL = state.CRTC.reg[0x09] & 0x1f; if (MSL == 0) { #if defined(DEBUG_VGA) BX_ERROR(("character height = 1, skipping text update")); #endif return; } cols = state.CRTC.reg[1] + 1; if ((MSL == 1) && (VDE == 399)) { // emulated CGA graphics mode 160x100x16 colors MSL = 3; } rows = (VDE + 1) / (MSL + 1); if (rows > BX_MAX_TEXT_LINES) { BX_PANIC(("text rows>%d: %d", BX_MAX_TEXT_LINES, rows)); return; } cWidth = ((state.sequencer.reg1 & 0x01) == 1) ? 8 : 9; iWidth = cWidth * cols; iHeight = VDE + 1; if ((iWidth != old_iWidth) || (iHeight != old_iHeight) || (MSL != old_MSL) || (state.last_bpp > 8)) { bx_gui->dimension_update(iWidth, iHeight, MSL + 1, cWidth); old_iWidth = iWidth; old_iHeight = iHeight; old_MSL = MSL; state.last_bpp = 8; } // pass old text snapshot & new VGA memory contents start_address = 2 * ((state.CRTC.reg[12] << 8) + state.CRTC.reg[13]); cursor_address = 2 * ((state.CRTC.reg[0x0e] << 8) + state.CRTC.reg[0x0f]); if (cursor_address < start_address) { cursor_x = 0xffff; cursor_y = 0xffff; } else { cursor_x = ((cursor_address - start_address) / 2) % (iWidth / cWidth); cursor_y = ((cursor_address - start_address) / 2) / (iWidth / cWidth); } bx_gui->text_update(state.text_snapshot, &state.memory[start_address], cursor_x, cursor_y, tm_info, rows); // screen updated, copy new VGA memory contents into text snapshot memcpy(state.text_snapshot, &state.memory[start_address], 2 * cols * rows); state.vga_mem_updated = 0; } } void CS3Trio64::determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth) { int ai[0x20]; int i; int h; int v; for (i = 0; i < 0x20; i++) ai[i] = state.CRTC.reg[i]; h = (ai[1] + 1) * 8; v = (ai[18] | ((ai[7] & 0x02) << 7) | ((ai[7] & 0x40) << 3)) + 1; if (state.graphics_ctrl.shift_reg == 0) { *piWidth = 640; *piHeight = 480; if (state.CRTC.reg[6] == 0xBF) { if (state.CRTC.reg[23] == 0xA3 && state.CRTC.reg[20] == 0x40 && state.CRTC.reg[9] == 0x41) { *piWidth = 320; *piHeight = 240; } else { if (state.x_dotclockdiv2) h <<= 1; *piWidth = h; *piHeight = v; } } else if ((h >= 640) && (v >= 480)) { *piWidth = h; *piHeight = v; } } else if (state.graphics_ctrl.shift_reg == 2) { if (state.sequencer.chain_four) { *piWidth = h; *piHeight = v; } else { *piWidth = h; *piHeight = v; } } else { if (state.x_dotclockdiv2) h <<= 1; *piWidth = h; *piHeight = v; } } u8 CS3Trio64::vga_mem_read(u32 addr) { u32 offset; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; u8 retval = 0; switch (state.graphics_ctrl.memory_mapping) { case 1: // 0xA0000 .. 0xAFFFF if (addr > 0xAFFFF) return 0xff; offset = addr & 0xFFFF; break; case 2: // 0xB0000 .. 0xB7FFF if ((addr < 0xB0000) || (addr > 0xB7FFF)) return 0xff; offset = addr & 0x7FFF; break; case 3: // 0xB8000 .. 0xBFFFF if (addr < 0xB8000) return 0xff; offset = addr & 0x7FFF; break; default: // 0xA0000 .. 0xBFFFF offset = addr & 0x1FFFF; } if (state.sequencer.chain_four) { // Mode 13h: 320 x 200 256 color mode: chained pixel representation return state.memory[(offset & ~0x03) + (offset % 4) * 65536]; } plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; /* addr between 0xA0000 and 0xAFFFF */ if (state.graphics_ctrl.read_mode) { u8 color_compare; u8 color_dont_care; u8 latch0; u8 latch1; u8 latch2; u8 latch3; color_compare = state.graphics_ctrl.color_compare & 0x0f; color_dont_care = state.graphics_ctrl.color_dont_care & 0x0f; latch0 = state.graphics_ctrl.latch[0] = plane0[offset]; latch1 = state.graphics_ctrl.latch[1] = plane1[offset]; latch2 = state.graphics_ctrl.latch[2] = plane2[offset]; latch3 = state.graphics_ctrl.latch[3] = plane3[offset]; latch0 ^= ccdat[color_compare][0]; latch1 ^= ccdat[color_compare][1]; latch2 ^= ccdat[color_compare][2]; latch3 ^= ccdat[color_compare][3]; latch0 &= ccdat[color_dont_care][0]; latch1 &= ccdat[color_dont_care][1]; latch2 &= ccdat[color_dont_care][2]; latch3 &= ccdat[color_dont_care][3]; retval = ~(latch0 | latch1 | latch2 | latch3); } else { state.graphics_ctrl.latch[0] = plane0[offset]; state.graphics_ctrl.latch[1] = plane1[offset]; state.graphics_ctrl.latch[2] = plane2[offset]; state.graphics_ctrl.latch[3] = plane3[offset]; retval = state.graphics_ctrl.latch[state.graphics_ctrl.read_map_select]; } return retval; } /** * Write to Legacy VGA Memory **/ void CS3Trio64::vga_mem_write(u32 addr, u8 value) { u32 offset; u8 new_val[4]; unsigned start_addr; u8 *plane0; u8 *plane1; u8 *plane2; u8 *plane3; /* The memory_mapping bits of the graphics controller determine * what window of VGA memory is available. * * 00: 0xA0000 .. 0xBFFFF (128K) * 01: 0xA0000 .. 0xAFFFF (64K) (also used for VGA text mode) * 02: 0xB0000 .. 0xB7FFF (32K) * 03: 0xB8000 .. 0xBFFFF (32K) (also used for CGA text mode) */ switch (state.graphics_ctrl.memory_mapping) { // 0xA0000 .. 0xAFFFF case 1: if (addr > 0xAFFFF) return; offset = addr - 0xA0000; break; // 0xB0000 .. 0xB7FFF case 2: if ((addr < 0xB0000) || (addr > 0xB7FFF)) return; offset = addr - 0xB0000; break; // 0xB8000 .. 0xBFFFF case 3: if (addr < 0xB8000) return; offset = addr - 0xB8000; break; // 0xA0000 .. 0xBFFFF default: offset = addr - 0xA0000; } start_addr = (state.CRTC.reg[0x0c] << 8) | state.CRTC.reg[0x0d]; if (state.graphics_ctrl.graphics_alpha) { if (state.graphics_ctrl.memory_mapping == 3) { // Text mode, and memory 0xB8000 .. 0xBFFFF selected => CGA text mode unsigned x_tileno; unsigned x_tileno2; unsigned y_tileno; /* CGA 320x200x4 / 640x200x2 start */ state.memory[offset] = value; offset -= start_addr; if (offset >= 0x2000) { y_tileno = offset - 0x2000; y_tileno /= (320 / 4); y_tileno <<= 1; // 2 * y_tileno; y_tileno++; x_tileno = (offset - 0x2000) % (320 / 4); x_tileno <<= 2; //*= 4; } else { y_tileno = offset / (320 / 4); y_tileno <<= 1; // 2 * y_tileno; x_tileno = offset % (320 / 4); x_tileno <<= 2; //*=4; } x_tileno2 = x_tileno; if (state.graphics_ctrl.shift_reg == 0) { x_tileno *= 2; x_tileno2 += 7; } else { x_tileno2 += 3; } if (state.x_dotclockdiv2) { x_tileno /= (X_TILESIZE / 2); x_tileno2 /= (X_TILESIZE / 2); } else { x_tileno /= X_TILESIZE; x_tileno2 /= X_TILESIZE; } if (state.y_doublescan) { y_tileno /= (Y_TILESIZE / 2); } else { y_tileno /= Y_TILESIZE; } state.vga_mem_updated = 1; SET_TILE_UPDATED(x_tileno, y_tileno, 1); if (x_tileno2 != x_tileno) { SET_TILE_UPDATED(x_tileno2, y_tileno, 1); } return; /* CGA 320x200x4 / 640x200x2 end */ } if (state.graphics_ctrl.memory_mapping != 1) { FAILURE_1(NotImplemented, "mem_write: graphics: mapping = %u \n", (unsigned)state.graphics_ctrl.memory_mapping); } if (state.sequencer.chain_four) { unsigned x_tileno; unsigned y_tileno; // 320 x 200 256 color mode: chained pixel representation state.memory[(offset & ~0x03) + (offset % 4) * 65536] = value; if (state.line_offset > 0) { offset -= start_addr; x_tileno = (offset % state.line_offset) / (X_TILESIZE / 2); if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } state.vga_mem_updated = 1; SET_TILE_UPDATED(x_tileno, y_tileno, 1); } return; } } /* addr between 0xA0000 and 0xAFFFF */ plane0 = &state.memory[0 << 16]; plane1 = &state.memory[1 << 16]; plane2 = &state.memory[2 << 16]; plane3 = &state.memory[3 << 16]; switch (state.graphics_ctrl.write_mode) { unsigned i; // Write mode 0 case 0: { /* Write Mode 0 is the standard and most general write mode. * While the other write modes are designed to perform a specific * task, this mode can be used to perform most tasks as all five * operations are performed on the data: * - The data byte from the host is first rotated as specified * by the Rotate Count field, then is replicated across all * four planes. * - Then the Enable Set/Reset field selects which planes will * receive their values from the host data and which will * receive their data from that plane's Set/Reset field * location. * - Then the operation specified by the Logical Operation * field is performed on the resulting data and the data in * the read latches. * - The Bit Mask field is then used to select between the * resulting data and data from the latch register. * - Finally, the resulting data is written to the display * memory planes enabled in the Memory Plane Write Enable * field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask; const u8 set_reset = state.graphics_ctrl.set_reset; const u8 enable_set_reset = state.graphics_ctrl.enable_set_reset; /* perform rotate on CPU data in case its needed */ if (state.graphics_ctrl.data_rotate) { value = (value >> state.graphics_ctrl.data_rotate) | (value << (8 - state.graphics_ctrl.data_rotate)); } new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // replace new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? bitmask : 0) : (value & bitmask)); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? bitmask : 0) : (value & bitmask)); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? bitmask : 0) : (value & bitmask)); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? bitmask : 0) : (value & bitmask)); break; case 1: // AND new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? (state.graphics_ctrl.latch[0] & bitmask) : 0) : (value & state.graphics_ctrl.latch[0]) & bitmask); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? (state.graphics_ctrl.latch[1] & bitmask) : 0) : (value & state.graphics_ctrl.latch[1]) & bitmask); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? (state.graphics_ctrl.latch[2] & bitmask) : 0) : (value & state.graphics_ctrl.latch[2]) & bitmask); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? (state.graphics_ctrl.latch[3] & bitmask) : 0) : (value & state.graphics_ctrl.latch[3]) & bitmask); break; case 2: // OR new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? bitmask : (state.graphics_ctrl.latch[0] & bitmask)) : ((value | state.graphics_ctrl.latch[0]) & bitmask)); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? bitmask : (state.graphics_ctrl.latch[1] & bitmask)) : ((value | state.graphics_ctrl.latch[1]) & bitmask)); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? bitmask : (state.graphics_ctrl.latch[2] & bitmask)) : ((value | state.graphics_ctrl.latch[2]) & bitmask)); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? bitmask : (state.graphics_ctrl.latch[3] & bitmask)) : ((value | state.graphics_ctrl.latch[3]) & bitmask)); break; case 3: // XOR new_val[0] |= ((enable_set_reset & 1) ? ((set_reset & 1) ? (~state.graphics_ctrl.latch[0] & bitmask) : (state.graphics_ctrl.latch[0] & bitmask)) : (value ^ state.graphics_ctrl.latch[0]) & bitmask); new_val[1] |= ((enable_set_reset & 2) ? ((set_reset & 2) ? (~state.graphics_ctrl.latch[1] & bitmask) : (state.graphics_ctrl.latch[1] & bitmask)) : (value ^ state.graphics_ctrl.latch[1]) & bitmask); new_val[2] |= ((enable_set_reset & 4) ? ((set_reset & 4) ? (~state.graphics_ctrl.latch[2] & bitmask) : (state.graphics_ctrl.latch[2] & bitmask)) : (value ^ state.graphics_ctrl.latch[2]) & bitmask); new_val[3] |= ((enable_set_reset & 8) ? ((set_reset & 8) ? (~state.graphics_ctrl.latch[3] & bitmask) : (state.graphics_ctrl.latch[3] & bitmask)) : (value ^ state.graphics_ctrl.latch[3]) & bitmask); break; default: FAILURE_1(NotImplemented, "vga_mem_write: write mode 0: op = %u", (unsigned)state.graphics_ctrl.raster_op); } } break; // Write mode 1 case 1: /* Write Mode 1 is used to transfer the data in the latches * register directly to the screen, affected only by the * Memory Plane Write Enable field. This can facilitate * rapid transfer of data on byte boundaries from one area * of video memory to another or filling areas of the * display with a pattern of 8 pixels. * When Write Mode 0 is used with the Bit Mask field set to * 00000000b the operation of the hardware is identical to * this mode. */ for (i = 0; i < 4; i++) { new_val[i] = state.graphics_ctrl.latch[i]; } break; // Write mode 2 case 2: { /* Write Mode 2 is used to unpack a pixel value packed into * the lower 4 bits of the host data byte into the 4 display * planes: * - In the byte from the host, the bit representing each * plane will be replicated across all 8 bits of the * corresponding planes. * - Then the operation specified by the Logical Operation * field is performed on the resulting data and the data * in the read latches. * - The Bit Mask field is then used to select between the * resulting data and data from the latch register. * - Finally, the resulting data is written to the display * memory planes enabled in the Memory Plane Write Enable * field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask; new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // write new_val[0] |= (value & 1) ? bitmask : 0; new_val[1] |= (value & 2) ? bitmask : 0; new_val[2] |= (value & 4) ? bitmask : 0; new_val[3] |= (value & 8) ? bitmask : 0; break; case 1: // AND new_val[0] |= (value & 1) ? (state.graphics_ctrl.latch[0] & bitmask) : 0; new_val[1] |= (value & 2) ? (state.graphics_ctrl.latch[1] & bitmask) : 0; new_val[2] |= (value & 4) ? (state.graphics_ctrl.latch[2] & bitmask) : 0; new_val[3] |= (value & 8) ? (state.graphics_ctrl.latch[3] & bitmask) : 0; break; case 2: // OR new_val[0] |= (value & 1) ? bitmask : (state.graphics_ctrl.latch[0] & bitmask); new_val[1] |= (value & 2) ? bitmask : (state.graphics_ctrl.latch[1] & bitmask); new_val[2] |= (value & 4) ? bitmask : (state.graphics_ctrl.latch[2] & bitmask); new_val[3] |= (value & 8) ? bitmask : (state.graphics_ctrl.latch[3] & bitmask); break; case 3: // XOR new_val[0] |= (value & 1) ? (~state.graphics_ctrl.latch[0] & bitmask) : (state.graphics_ctrl.latch[0] & bitmask); new_val[1] |= (value & 2) ? (~state.graphics_ctrl.latch[1] & bitmask) : (state.graphics_ctrl.latch[1] & bitmask); new_val[2] |= (value & 4) ? (~state.graphics_ctrl.latch[2] & bitmask) : (state.graphics_ctrl.latch[2] & bitmask); new_val[3] |= (value & 8) ? (~state.graphics_ctrl.latch[3] & bitmask) : (state.graphics_ctrl.latch[3] & bitmask); break; } } break; // Write mode 3 case 3: { /* Write Mode 3 is used when the color written is fairly * constant but the Bit Mask field needs to be changed * frequently, such as when drawing single color lines or * text: * - The value of the Set/Reset field is expanded as if * the Enable Set/Reset field were set to 1111b, * regardless of its actual value. * - The host data is first rotated as specified by the * Rotate Count field, then is ANDed with the Bit * Mask field. * - The resulting value is used where the Bit Mask * field normally would be used, selecting data from * either the expansion of the Set/Reset field or the * latch register. * - Finally, the resulting data is written to the * display memory planes enabled in the Memory Plane * Write Enable field. * . */ const u8 bitmask = state.graphics_ctrl.bitmask & value; const u8 set_reset = state.graphics_ctrl.set_reset; /* perform rotate on CPU data */ if (state.graphics_ctrl.data_rotate) { value = (value >> state.graphics_ctrl.data_rotate) | (value << (8 - state.graphics_ctrl.data_rotate)); } new_val[0] = state.graphics_ctrl.latch[0] & ~bitmask; new_val[1] = state.graphics_ctrl.latch[1] & ~bitmask; new_val[2] = state.graphics_ctrl.latch[2] & ~bitmask; new_val[3] = state.graphics_ctrl.latch[3] & ~bitmask; value &= bitmask; switch (state.graphics_ctrl.raster_op) { case 0: // write new_val[0] |= (set_reset & 1) ? value : 0; new_val[1] |= (set_reset & 2) ? value : 0; new_val[2] |= (set_reset & 4) ? value : 0; new_val[3] |= (set_reset & 8) ? value : 0; break; case 1: // AND new_val[0] |= ((set_reset & 1) ? value : 0) & state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) & state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) & state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) & state.graphics_ctrl.latch[3]; break; case 2: // OR new_val[0] |= ((set_reset & 1) ? value : 0) | state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) | state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) | state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) | state.graphics_ctrl.latch[3]; break; case 3: // XOR new_val[0] |= ((set_reset & 1) ? value : 0) ^ state.graphics_ctrl.latch[0]; new_val[1] |= ((set_reset & 2) ? value : 0) ^ state.graphics_ctrl.latch[1]; new_val[2] |= ((set_reset & 4) ? value : 0) ^ state.graphics_ctrl.latch[2]; new_val[3] |= ((set_reset & 8) ? value : 0) ^ state.graphics_ctrl.latch[3]; break; } } break; default: FAILURE_1(NotImplemented, "vga_mem_write: write mode %u ?", (unsigned)state.graphics_ctrl.write_mode); } // state.sequencer.map_mask determines which bitplanes the write should // actually go to if (state.sequencer.map_mask & 0x0f) { state.vga_mem_updated = 1; if (state.sequencer.map_mask & 0x01) plane0[offset] = new_val[0]; if (state.sequencer.map_mask & 0x02) plane1[offset] = new_val[1]; if (state.sequencer.map_mask & 0x04) { // Plane 2 contains the character map if ((offset & 0xe000) == state.charmap_address) { // printf("Updating character map %04x with %02x...\n ", (offset & // 0x1fff), new_val[2]); bx_gui->lock(); bx_gui->set_text_charbyte((u16)(offset & 0x1fff), new_val[2]); bx_gui->unlock(); } plane2[offset] = new_val[2]; } if (state.sequencer.map_mask & 0x08) plane3[offset] = new_val[3]; unsigned x_tileno; unsigned y_tileno; if (state.graphics_ctrl.shift_reg == 2) { offset -= start_addr; x_tileno = (offset % state.line_offset) * 4 / (X_TILESIZE / 2); if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } else { if (state.line_compare < state.vertical_display_end) { if (state.line_offset > 0) { if (state.x_dotclockdiv2) { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 16); } else { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 8); } if (state.y_doublescan) { y_tileno = ((offset / state.line_offset) * 2 + state.line_compare + 1) / Y_TILESIZE; } else { y_tileno = ((offset / state.line_offset) + state.line_compare + 1) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } } if (offset >= start_addr) { offset -= start_addr; if (state.line_offset > 0) { if (state.x_dotclockdiv2) { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 16); } else { x_tileno = (offset % state.line_offset) / (X_TILESIZE / 8); } if (state.y_doublescan) { y_tileno = (offset / state.line_offset) / (Y_TILESIZE / 2); } else { y_tileno = (offset / state.line_offset) / Y_TILESIZE; } SET_TILE_UPDATED(x_tileno, y_tileno, 1); } } } } } ================================================ FILE: src/S3Trio64.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_S3Trio64_H_) #define INCLUDED_S3Trio64_H_ #include "VGA.hpp" #include "gui/vga.hpp" /* video card has 4M of ram */ #define VIDEO_RAM_SIZE 22 #define CRTC_MAX 0x57 /** * \brief S3 Trio 64 Video Card * * Documentation consulted: * - VGADOC4b * (http://home.worldonline.dk/~finth/) * . **/ class CS3Trio64 : public CVGA { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); virtual void WriteMem_Legacy(int index, u32 address, int dsize, u32 data); virtual u32 ReadMem_Legacy(int index, u32 address, int dsize); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); CS3Trio64(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CS3Trio64(); void update(void); void run(void); virtual u8 get_actl_palette_idx(u8 index); virtual void redraw_area(unsigned x0, unsigned y0, unsigned width, unsigned height); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: u32 mem_read(u32 address, int dsize); void mem_write(u32 address, int dsize, u32 data); u32 io_read(u32 address, int dsize); void io_write(u32 address, int dsize, u32 data); void io_write_b(u32 address, u8 data); void write_b_3c0(u8 data); void write_b_3c2(u8 data); void write_b_3c4(u8 data); void write_b_3c5(u8 data); void write_b_3c6(u8 data); void write_b_3c7(u8 data); void write_b_3c8(u8 data); void write_b_3c9(u8 data); void write_b_3ce(u8 data); void write_b_3cf(u8 data); void write_b_3d4(u8 data); void write_b_3d5(u8 data); u8 read_b_3c0(); u8 read_b_3c1(); u8 read_b_3c2(); u8 read_b_3c3(); u8 read_b_3c4(); u8 read_b_3c5(); u8 read_b_3c9(); u8 read_b_3ca(); u8 read_b_3cc(); u8 read_b_3cf(); u8 read_b_3d4(); u8 read_b_3d5(); u8 read_b_3da(); u32 legacy_read(u32 address, int dsize); void legacy_write(u32 address, int dsize, u32 data); u32 rom_read(u32 address, int dsize); void determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth); char bios_message[200]; int bios_message_size; void vga_mem_write(u32 addr, u8 value); u8 vga_mem_read(u32 addr); std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; bool StopThread; /// The state structure contains all elements that need to be saved to the /// statefile. struct SS3_state { bool vga_enabled; bool vga_mem_updated; u16 charmap_address; bool x_dotclockdiv2; bool y_doublescan; unsigned line_offset; unsigned line_compare; unsigned vertical_display_end; u8 text_snapshot[32 * 1024]; // current text snapshot bool vga_tile_updated[BX_NUM_X_TILES][BX_NUM_Y_TILES]; u8 *memory; u32 memsize; u8 last_bpp; u8 tile[X_TILESIZE * Y_TILESIZE * 4]; /**< Currently allocates the tile as large as needed. */ unsigned x_tilesize; unsigned y_tilesize; struct SS3_attr { bool flip_flop; /* 0 = address, 1 = data-write */ unsigned address; /* register number */ bool video_enabled; u8 palette_reg[16]; u8 overscan_color; u8 color_plane_enable; u8 horiz_pel_panning; u8 color_select; struct SS3_mode { bool graphics_alpha; bool display_type; bool enable_line_graphics; bool blink_intensity; bool pixel_panning_compat; bool pixel_clock_select; bool internal_palette_size; } mode_ctrl; } attribute_ctrl; struct SS3_misc { bool color_emulation; // 1=color emulation, base address = 3Dx // 0=mono emulation, base address = 3Bx bool enable_ram; // enable CPU access to video memory if set u8 clock_select; // 0=25Mhz 1=28Mhz bool select_high_bank; // when in odd/even modes, select // high 64k bank if set bool horiz_sync_pol; // bit6: negative if set bool vert_sync_pol; // bit7: negative if set // bit7,bit6 represent number of lines on display: // 0 = reserved // 1 = 400 lines // 2 = 350 lines // 3 - 480 lines } misc_output; struct SS3_seq { u8 index; u8 map_mask; bool map_mask_bit[4]; bool reset1; bool reset2; u8 reg1; u8 char_map_select; bool extended_mem; bool odd_even; bool chain_four; } sequencer; struct SS3_pel { u8 write_data_register; u8 write_data_cycle; /* 0, 1, 2 */ u8 read_data_register; u8 read_data_cycle; /* 0, 1, 2 */ u8 dac_state; struct SS3_pel_data { u8 red; u8 green; u8 blue; } data[256]; u8 mask; } pel; struct SS3_gfx { u8 index; u8 set_reset; u8 enable_set_reset; u8 color_compare; u8 data_rotate; u8 raster_op; u8 read_map_select; u8 write_mode; bool read_mode; bool odd_even; bool chain_odd_even; u8 shift_reg; bool graphics_alpha; u8 memory_mapping; /* 0 = use A0000-BFFFF * 1 = use A0000-AFFFF EGA/VGA graphics modes * 2 = use B0000-B7FFF Monochrome modes * 3 = use B8000-BFFFF CGA modes */ u8 color_dont_care; u8 bitmask; u8 latch[4]; } graphics_ctrl; struct SS3_crtc { u8 address; u8 reg[0x20]; bool write_protect; } CRTC; } state; }; #endif // !defined(INCLUDED_S3Trio64_H_) ================================================ FILE: src/SCSIBus.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "SCSIBus.hpp" #include "StdAfx.hpp" /** * \brief Constructor. * * Initialize all disks to non-existing. **/ CSCSIBus::CSCSIBus(CConfigurator *cfg, CSystem *c) : CSystemComponent(cfg, c) { int i; for (i = 0; i < 16; i++) targets[i] = 0; state.phase = SCSI_PHASE_FREE; state.target = -1; state.initiator = -1; } /** * \brief Destructor. **/ CSCSIBus::~CSCSIBus(void) {} /** * \brief Register a SCSI device. * * Register a SCSI device as responding to a specific SCSI id. **/ void CSCSIBus::scsi_register(CSCSIDevice *dev, int bus, int target) { if (targets[target] && targets[target] != dev) FAILURE(IllegalState, "More than one SCSI device at the same ID"); targets[target] = dev; target_bus_no[target] = bus; } /** * \brief Unregister a SCSI device. * * Unregister a SCSI device as responding to a specific SCSI id. Needed * for some controllers if software changes it's SCSI id. **/ void CSCSIBus::scsi_unregister(CSCSIDevice *dev, int target) { if (targets[target] != dev) FAILURE(IllegalState, "Attempt to unregister other SCSI device"); targets[target] = 0; } /** * \brief Arbitrate for the SCSI bus. * * Returns true on succesful arbitration. * Arbitration succeeds if the bus is free. **/ bool CSCSIBus::arbitrate(int initiator) { if (state.phase != SCSI_PHASE_FREE && state.initiator != initiator) { printf("Could not arbitrate for the SCSI bus.\n"); return false; } state.initiator = initiator; state.phase = SCSI_PHASE_ARBITRATION; return true; } /** * \brief Select a device as a target. * * Returns true on succesful selection (= if the device responds to the select * by changing the SCSI phase. * The initiator calling this function should have succesfully arbitrated * for the SCSI bus first. **/ bool CSCSIBus::select(int initiator, int target) { if (state.phase != SCSI_PHASE_ARBITRATION || state.initiator != initiator) FAILURE(IllegalState, "Attempt to select while the device has not won SCSI arbitration"); if (!targets[target]) return false; state.target = target; targets[target]->scsi_select_me(target_bus_no[target]); return (state.phase >= 0); } /** * \brief Change the SCSI phase. * * Only the selected target can do this. **/ void CSCSIBus::set_phase(int target, int phase) { if (targets[target] != targets[state.target]) FAILURE(IllegalState, "Attempt to set phase while the device has not been selected"); state.phase = phase; } /** * \brief Release the SCSI bus. * * In the Arbitration phase, only the initiator can do this; otherwise, * only the selected target can do this. **/ void CSCSIBus::free_bus(int initiator) { // nothing to do if (state.phase == SCSI_PHASE_FREE) return; if (state.phase == SCSI_PHASE_ARBITRATION) { if (initiator != state.initiator) FAILURE(IllegalState, "Attempt to free the scsi bus"); } else { if (targets[initiator] != targets[state.target]) FAILURE(IllegalState, "Attempt to free the scsi bus"); } state.phase = SCSI_PHASE_FREE; } static u32 scsi_magic1 = 0x5C510123; static u32 scsi_magic2 = 0x32105c51; /** * Save state to a Virtual Machine State file. **/ int CSCSIBus::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&scsi_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&scsi_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CSCSIBus::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != scsi_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != scsi_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } ================================================ FILE: src/SCSIBus.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__SCSIBUS__H__) #define __SCSIBUS__H__ #include "SCSIDevice.hpp" #include "StdAfx.hpp" #include "SystemComponent.hpp" /** * \brief Emulated SCSI bus. * * connects SCSI Devices (class CSCSIDevice) together; SCSI devices are * disks and controllers. **/ class CSCSIBus : public CSystemComponent { public: CSCSIBus(CConfigurator *cfg, CSystem *c); ~CSCSIBus(void); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void scsi_register(CSCSIDevice *dev, int bus, int target); void scsi_unregister(CSCSIDevice *dev, int target); bool arbitrate(int initiator); bool select(int initiator, int target); void set_phase(int target, int phase); int get_phase() { return state.phase; }; /**< Get current SCSI bus phase **/ void free_bus(int initiator); CSCSIDevice *targets[16]; /**< pointers to the SCSI devices that respond to the 15 possible target id's. **/ int target_bus_no[16]; /**< indicates what bus this is for each connected SCSI device. always 0 for disks, but controllers could have multiple SCSI busses. **/ /// The state structure contains all elements that need to be saved to the /// statefile struct SSCSI_state { int initiator; /**< SCSI id of the initiator. **/ int target; /**< SCSI id of the target. **/ int phase; /**< SCSI bus phase. **/ } state; }; #define SCSI_PHASE_FREE -2 #define SCSI_PHASE_ARBITRATION -1 #define SCSI_PHASE_DATA_OUT 0 #define SCSI_PHASE_DATA_IN 1 #define SCSI_PHASE_COMMAND 2 #define SCSI_PHASE_STATUS 3 #define SCSI_PHASE_MSG_OUT 6 #define SCSI_PHASE_MSG_IN 7 #endif ================================================ FILE: src/SCSIDevice.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "SCSIDevice.hpp" #include "SCSIBus.hpp" /** * \brief Constructor. * * Set this device as unregistered with any SCSI bus. **/ CSCSIDevice::CSCSIDevice(void) { int i; for (i = 0; i < 10; i++) { scsi_bus[i] = 0; scsi_initiator_id[i] = -1; } } /** * \brief Destructor. **/ CSCSIDevice::~CSCSIDevice(void) {} /** * \brief Register this device with a SCSI bus. **/ void CSCSIDevice::scsi_register(int busno, class CSCSIBus *with, int target) { scsi_bus[busno] = with; scsi_initiator_id[busno] = target; scsi_bus[busno]->scsi_register(this, busno, target); } /** * \brief Arbitrate for the SCSI bus. * * See CSCSIBus::arbitrate for a description. **/ bool CSCSIDevice::scsi_arbitrate(int bus) { return scsi_bus[bus]->arbitrate(scsi_initiator_id[bus]); } /** * \brief Select a target on the SCSI bus. * * See CSCSIBus::select for a description. **/ bool CSCSIDevice::scsi_select(int bus, int target) { return scsi_bus[bus]->select(scsi_initiator_id[bus], target); } /** * \brief Called when this device is selected. * * Override this to allow this device to be selected. Overrided * functions should at least call scsi_set_phase to set * the SCSI bus phase to a valid phase. **/ void CSCSIDevice::scsi_select_me(int bus) { FAILURE(NotImplemented, "selected device doesn't implement scsi_select_me"); } /** * \brief Change the SCSI bus phase. * * See CSCSIBus::set_phase for a description. **/ void CSCSIDevice::scsi_set_phase(int bus, int phase) { scsi_bus[bus]->set_phase(scsi_initiator_id[bus], phase); } /** * \brief Get the current SCSI bus phase. * * See CSCSIBus::get_phase for a description. **/ int CSCSIDevice::scsi_get_phase(int bus) { return scsi_bus[bus]->get_phase(); } /** * \brief Release the SCSI bus. * * See CSCSIBus::free_bus for a description. **/ void CSCSIDevice::scsi_free(int bus) { return scsi_bus[bus]->free_bus(scsi_initiator_id[bus]); } /** * \brief Return the number of bytes expected or available. * * Override this to return the number of bytes the device * expects to receive from the initiator, or has available * for the initiator, in the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ size_t CSCSIDevice::scsi_expected_xfer_me(int bus) { FAILURE(NotImplemented, "selected device doesn't implement scsi_expected_xfer_me"); } /** * \brief Return the number of bytes expected or available. * * Returns the number of bytes the currently selected target * expects to receive from the initiator, or has available * for the initiator, in the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ size_t CSCSIDevice::scsi_expected_xfer(int bus) { return scsi_bus[bus] ->targets[scsi_bus[bus]->state.target] ->scsi_expected_xfer_me( scsi_bus[bus]->target_bus_no[scsi_bus[bus]->state.target]); } /** * \brief Return a pointer where the initiator can read or write data. * * Override this to return a pointer to where the initiator * can read or write data in the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ void *CSCSIDevice::scsi_xfer_ptr_me(int bus, size_t bytes) { FAILURE(NotImplemented, "selected device doesn't implement scsi_xfer_ptr_me"); } /** * \brief Return a pointer where target data can be read or written. * * Returns a pointer to where the initiator can read or * write data from/to the currentlt selected target in * the current SCSI phase. * * The initiator should do the following for each transfer: * - Check the current phase using CSCSIDevice::scsi_get_phase. * - Check the amount of data to transfer using * CSCSIDevice::scsi_expected_xfer. * - Signal intent to transfer data, and obtain a pointer * using SCSIDevice::scsi_xfer_ptr. * - Transfer data using the returned pointer. * - Call CSCSIDevice::scsi_xfer_done to the the target * process the data and/or transfer to a new phase. * . **/ void *CSCSIDevice::scsi_xfer_ptr(int bus, size_t bytes) { return scsi_bus[bus]->targets[scsi_bus[bus]->state.target]->scsi_xfer_ptr_me( scsi_bus[bus]->target_bus_no[scsi_bus[bus]->state.target], bytes); } /** * \brief Process data written or read. * * Override this to process data read or written in * the current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ void CSCSIDevice::scsi_xfer_done_me(int bus) { FAILURE(NotImplemented, "selected device doesn't implement scsi_xfer_done_me"); } /** * \brief Mark data transfer done. * * Let the target know that data has been transfered for the * current SCSI phase. * * For an overview of data transfer during a SCSI bus phase, * see SCSIDevice::scsi_xfer_ptr. **/ void CSCSIDevice::scsi_xfer_done(int bus) { scsi_bus[bus]->targets[scsi_bus[bus]->state.target]->scsi_xfer_done_me( scsi_bus[bus]->target_bus_no[scsi_bus[bus]->state.target]); } ================================================ FILE: src/SCSIDevice.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__SCSIDEVICE__H__) #define __SCSIDEVICE__H__ #include "StdAfx.hpp" /** * \brief Base class for emulated SCSI devices. * * Connects to one or more SCSI busses (class CSCSIBus) together; derived * SCSI devices are disks and controllers. **/ class CSCSIDevice { public: CSCSIDevice(void); virtual ~CSCSIDevice(void); void scsi_register(int busno, class CSCSIBus *with, int target); bool scsi_arbitrate(int bus); bool scsi_select(int bus, int target); virtual void scsi_select_me(int bus); void scsi_set_phase(int bus, int phase); int scsi_get_phase(int bus); void scsi_free(int bus); virtual size_t scsi_expected_xfer_me(int bus); size_t scsi_expected_xfer(int bus); virtual void *scsi_xfer_ptr_me(int bus, size_t bytes); void *scsi_xfer_ptr(int bus, size_t bytes); virtual void scsi_xfer_done_me(int bus); void scsi_xfer_done(int bus); protected: class CSCSIBus *scsi_bus[10]; /**< SCSI busses this device connects to. Disks connect to 1 bus only, controllers can have several SCSI busses. **/ int scsi_initiator_id[10]; /**< Main SCSI id of this device on each of the busses. **/ }; #endif ================================================ FILE: src/Serial.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "Serial.hpp" #include "AliM1543C.hpp" #include "StdAfx.hpp" #include "System.hpp" #include "lockstep.hpp" #define RECV_TICKS 10 int iCounter = 0; #define FIFO_SIZE 1024 //#define DEBUG_SERIAL 1 /** * Constructor. **/ CSerial::CSerial(CConfigurator *cfg, CSystem *c, u16 number) : CSystemComponent(cfg, c) { state.iNumber = number; breakHit = false; } /** * Initialize the Serial port device. **/ void CSerial::init() { listenPort = (int)myCfg->get_num_value("port", false, 8000 + state.iNumber); if (!(listenAddress = myCfg->get_text_value("address"))) { listenAddress = "0.0.0.0"; } cSystem->RegisterMemory(this, 0, U64(0x00000801fc0003f8) - (0x100 * state.iNumber), 8); // Start Telnet server #if defined(_WIN32) // Windows Sockets only work after calling WSAStartup. WSADATA wsa; WSAStartup(0x0101, &wsa); #endif // defined (_WIN32) struct sockaddr_in Address; listenSocket = (int)socket(AF_INET, SOCK_STREAM, 0); if (listenSocket == INVALID_SOCKET) { printf("Could not open socket to listen on!\n"); } inet_aton(listenAddress, (in_addr *) &Address.sin_addr.s_addr); Address.sin_port = htons((u16)(listenPort)); Address.sin_family = AF_INET; int optval = 1; setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)); bind(listenSocket, (struct sockaddr *)&Address, sizeof(Address)); listen(listenSocket, 1); printf("%s: Waiting for connection on port %d.\n", devid_string, listenPort); WaitForConnection(); #if defined(IDB) && defined(LS_MASTER) struct sockaddr_in dest_addr; int result = -1; throughSocket = socket(AF_INET, SOCK_STREAM, 0); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons((u16)(base + number)); dest_addr.sin_addr.s_addr = inet_addr(ls_IP); printf("%s: Waiting to initiate remote connection to %s.\n", devid_string, ls_IP); while (result == -1) { result = connect(throughSocket, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); } #endif state.rcvW = 0; state.rcvR = 0; state.bLCR = 0x00; state.bLSR = 0x60; // THRE, TSRE state.bMSR = 0x30; // CTS, DSR state.bIIR = 0x01; // no interrupt state.irq_active = false; printf("%s: $Id: Serial.cpp,v 1.51 2008/06/03 09:07:56 iamcamiel Exp $\n", devid_string); } void CSerial::start_threads() { char buffer[5]; if (!myThread) { sprintf(buffer, "srl%d", state.iNumber); printf(" %s", buffer); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); } } void CSerial::stop_threads() { char buffer[5]; StopThread = true; if (myThread) { sprintf(buffer, "srl%d", state.iNumber); printf(" %s", buffer); if (!acceptingSocket) { myThread->join(); } myThread = nullptr; } } /** * Destructor. **/ CSerial::~CSerial() { stop_threads(); } u64 CSerial::ReadMem(int index, u64 address, int dsize) { u8 d; switch (address) { case 0: // data buffer if (state.bLCR & 0x80) { return state.bBRB_LSB; } else { if (state.rcvR != state.rcvW) { state.bRDR = state.rcvBuffer[state.rcvR]; state.rcvR++; if (state.rcvR == FIFO_SIZE) state.rcvR = 0; TRC_DEV4("Read character %02x (%c) on serial port %d\n", state.bRDR, printable(state.bRDR), state.iNumber); #if defined(DEBUG_SERIAL) printf("Read character %02x (%c) on serial port %d\n", state.bRDR, printable(state.bRDR), state.iNumber); #endif } else { TRC_DEV2("Read past FIFO on serial port %d\n", state.iNumber); #if defined(DEBUG_SERIAL) printf("Read past FIFO on serial port %d\n", state.iNumber); #endif } return state.bRDR; } case 1: if (state.bLCR & 0x80) { return state.bBRB_MSB; } else { return state.bIER; } case 2: // interrupt cause d = state.bIIR; state.bIIR = 0x01; return d; case 3: return state.bLCR; case 4: return state.bMCR; case 5: // serialization state if (state.rcvR != state.rcvW) state.bLSR = 0x61; // THRE, TSRE, RxRD else state.bLSR = 0x60; // THRE, TSRE return state.bLSR; case 6: return state.bMSR; default: return state.bSPR; } } void CSerial::WriteMem(int index, u64 address, int dsize, u64 data) { u8 d; char s[5]; d = (u8)data; switch (address) { case 0: if (state.bLCR & 0x80) // divisor latch access bit set { // LSB of divisor latch state.bBRB_LSB = d; } else { // Transmit Hold Register sprintf(s, "%c", d); write(s); TRC_DEV4("Write character %02x (%c) on serial port %d\n", d, printable(d), state.iNumber); #if defined(DEBUG_SERIAL) printf("Write character %02x (%c) on serial port %d\n", d, printable(d), state.iNumber); #endif eval_interrupts(); } break; case 1: if (state.bLCR & 0x80) // divisor latch access bit set { // MSB of divisor latch state.bBRB_MSB = d; } else { // Interrupt Enable Register state.bIER = d; eval_interrupts(); } break; case 2: state.bFCR = d; break; case 3: state.bLCR = d; break; case 4: state.bMCR = d; break; default: state.bSPR = d; } } void CSerial::eval_interrupts() { state.bIIR = 0x01; // no interrupt if ((state.bIER & 0x01) && (state.rcvR != state.rcvW)) state.bIIR = 0x04; else if (state.bIER & 0x2) // transmitter buffer empty enabled? state.bIIR = 0x02; // transmitter buffer empty else state.bIIR = 0x01; // no interrupt if (state.bIIR > 0x01) { if (!state.irq_active) theAli->pic_interrupt(0, 4 - state.iNumber); } else { if (state.irq_active) theAli->pic_deassert(0, 4 - state.iNumber); } } void CSerial::write(const char *s) { send(connectSocket, s, (int)strlen(s) + 1, 0); } void CSerial::receive(const char *data) { char *x; x = (char *)data; while (*x) { state.rcvBuffer[state.rcvW++] = *x; if (state.rcvW == FIFO_SIZE) state.rcvW = 0; x++; eval_interrupts(); } } /** * Thread entry point. **/ void CSerial::run() { try { for (;;) { if (StopThread) return; execute(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); } } catch (CException &e) { printf("Exception in Serial thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** * Check if threads are still running. * * Enter serial port menu if was received. **/ void CSerial::check_state() { if (breakHit) serial_menu(); if (myThreadDead.load()) FAILURE(Thread, "Serial thread has died"); } void CSerial::serial_menu() { fd_set readset; unsigned char buffer[FIFO_SIZE + 1]; ssize_t size; struct timeval tv; bool exitLoop = false; cSystem->stop_threads(); write("\r\n received. What do you want to do?\r\n"); write(" 0. Continue\r\n"); #if defined(IDB) write(" 1. End run\r\n"); #else write(" 1. Exit emulator gracefully\r\n"); write(" 2. Abort emulator (no changes saved)\r\n"); write(" 3. Save state to autosave.axp and continue\r\n"); write(" 4. Load state from autosave.axp and continue\r\n"); #endif while (!exitLoop) { FD_ZERO(&readset); FD_SET(connectSocket, &readset); tv.tv_sec = 60; tv.tv_usec = 0; if (select(connectSocket + 1, &readset, NULL, NULL, &tv) <= 0) { write("%SRL-I-TIMEOUT: no timely answer received. Continuing " "emulation.\r\n"); break; // leave loop } #if defined(_WIN32) || defined(__VMS) size = recv(connectSocket, (char *)buffer, FIFO_SIZE, 0); #else size = read(connectSocket, &buffer, FIFO_SIZE); #endif switch (buffer[0]) { case '0': write("%SRL-I-CONTINUE: continuing emulation.\r\n"); exitLoop = true; break; case '1': write("%SRL-I-EXIT: exiting emulation gracefully.\r\n"); FAILURE(Graceful, "Graceful exit"); exitLoop = true; break; case '2': write("%SRL-I-ABORT: aborting emulation.\r\n"); FAILURE(Abort, "Aborting"); exitLoop = true; break; case '3': write("%SRL-I-SAVESTATE: Saving state to autosave.axp.\r\n"); cSystem->SaveState("autosave.axp"); write("%SRL-I-CONTINUE: continuing emulation.\r\n"); exitLoop = true; break; case '4': write("%SRL-I-LOADSTATE: Loading state from autosave.axp.\r\n"); cSystem->RestoreState("autosave.axp"); write("%SRL-I-CONTINUE: continuing emulation.\r\n"); exitLoop = true; break; default: write("%SRL-W-INVALID: Not a valid answer.\r\n"); } } breakHit = false; cSystem->start_threads(); } void CSerial::execute() { fd_set readset; unsigned char buffer[FIFO_SIZE + 1]; unsigned char cbuffer[FIFO_SIZE + 1]; // cooked buffer unsigned char *b; // cooked buffer unsigned char *c; ssize_t size; struct timeval tv; state.serial_cycles++; if (state.serial_cycles >= RECV_TICKS) { FD_ZERO(&readset); FD_SET(connectSocket, &readset); tv.tv_sec = 0; tv.tv_usec = 0; if (select(connectSocket + 1, &readset, NULL, NULL, &tv) > 0) { #if defined(_WIN32) || defined(__VMS) // Windows Sockets has no direct equivalent of BSD's read size = recv(connectSocket, (char *)buffer, FIFO_SIZE, 0); #else size = read(connectSocket, &buffer, FIFO_SIZE); #endif extern int got_sigint; if (size == 0 && !got_sigint) { printf("%%SRL-W-DISCONNECT: Write socket closed on other end for " "serial port %d.\n", state.iNumber); printf("-SRL-I-WAITFOR: Waiting for a new connection on port %d.\n", listenPort); WaitForConnection(); return; } buffer[size + 1] = 0; // force null termination. b = buffer; c = cbuffer; while ((ssize_t)(b - buffer) < size) { if (*b == 0x0a) { b++; // skip LF continue; } if (*b == IAC) { if (*(b + 1) == IAC) { // escaped IAC. b++; } else if (*(b + 1) >= WILL) { // will/won't/do/don't b += 3; // skip this byte, and following two. (telnet escape) continue; } else if (*(b + 1) == SB) { // skip until IAC SE b += 2; // now we're at start of subnegotiation. while (*b != IAC && *(b + 1) != SE) b++; b += 2; continue; } else if (*(b + 1) == BREAK) { // break (== halt button?) b += 2; breakHit = true; } else if (*(b + 1) == AYT) { // are you there? } else { // misc single byte command. b += 2; continue; } } *c = *b; c++; b++; } *c = 0; // null terminate it. this->receive((const char *)&cbuffer); } state.serial_cycles = 0; } eval_interrupts(); } static u32 srl_magic1 = 0x5A15A15A; static u32 srl_magic2 = 0x1A51A51A; /** * Save state to a Virtual Machine State file. **/ int CSerial::SaveState(FILE *f) { long ss = sizeof(state); fwrite(&srl_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&srl_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CSerial::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; size_t r; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != srl_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != srl_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } void CSerial::WaitForConnection() { struct sockaddr_in Address; socklen_t nAddressSize = sizeof(struct sockaddr_in); const char *telnet_options = "%c%c%c"; char buffer[8]; char s[1000]; char *nargv = s; int i = 0; #if !defined(LS_SLAVE) char s2[200]; char *argv[20]; strncpy(s, myCfg->get_text_value("action", ""), 999); s[999] = '\0'; // printf("%s: Specified : %s\n",devid_string,s); if (strcmp(s, "")) { // spawn external program (telnet client)... while (*nargv) { argv[i] = nargv; if (nargv[0] == '\"') nargv = strchr(nargv + 1, '\"'); if (nargv) nargv = strchr(nargv, ' '); if (!nargv) break; *nargv++ = '\0'; i++; argv[i] = NULL; } argv[i + 1] = NULL; strcpy(s2, argv[0]); nargv = s2; if (nargv[0] == '\"') { nargv++; *(strchr(nargv, '\"')) = '\0'; } // printf("%s: Starting %s\n", devid_string,nargv); #if defined(_WIN32) if (_spawnvp(_P_NOWAIT, nargv, argv) < 0) FAILURE_1(Runtime, "Exec of '%s' has failed.\n", argv[0]); #elif !defined(__VMS) pid_t child; int status; if (!(child = fork())) { execvp(argv[0], argv); FAILURE_1(Runtime, "Exec of '%s' failed.\n", argv[0]); } else { sleep(1); // give it a chance to start up. waitpid(child, &status, WNOHANG); // reap it, if needed. if (kill(child, 0) < 0) { // uh oh, no kiddo. FAILURE_1(Runtime, "Exec of '%s' has failed.\n", argv[0]); } } #endif } #endif Address.sin_addr.s_addr = INADDR_ANY; Address.sin_port = htons((u16)listenPort); Address.sin_family = AF_INET; // Wait until we have a connection connectSocket = INVALID_SOCKET; while (connectSocket == INVALID_SOCKET) { acceptingSocket = true; connectSocket = (int)accept(listenSocket, (struct sockaddr *)&Address, &nAddressSize); acceptingSocket = false; } state.serial_cycles = 0; // Send some control characters to the telnet client to handle // character-at-a-time mode. sprintf(buffer, telnet_options, IAC, DO, TELOPT_ECHO); this->write(buffer); sprintf(buffer, telnet_options, IAC, DO, TELOPT_NAWS); write(buffer); sprintf(buffer, telnet_options, IAC, DO, TELOPT_LFLOW); this->write(buffer); sprintf(buffer, telnet_options, IAC, WILL, TELOPT_ECHO); this->write(buffer); sprintf(buffer, telnet_options, IAC, WILL, TELOPT_SGA); this->write(buffer); sprintf(s, "This is serial port #%d on ES40 Emulator\r\n", state.iNumber); this->write(s); } ================================================ FILE: src/Serial.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_SERIAL_H) #define INCLUDED_SERIAL_H #include "SystemComponent.hpp" #include "telnet.hpp" /** * \brief Emulated serial port. * * The serial port is translated to a telnet port. **/ class CSerial : public CSystemComponent { public: void write(const char *s); virtual void WriteMem(int index, u64 address, int dsize, u64 data); virtual u64 ReadMem(int index, u64 address, int dsize); CSerial(CConfigurator *cfg, CSystem *c, u16 number); virtual ~CSerial(); void receive(const char *data); virtual void check_state(); virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); void eval_interrupts(); void WaitForConnection(); void run(); void execute(); virtual void init(); virtual void start_threads(); virtual void stop_threads(); private: void serial_menu(); std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; bool StopThread = false; bool acceptingSocket = false; bool breakHit; /// The state structure contains all elements that need to be saved to the /// statefile. struct SSrl_state { u8 bTHR; /**< Transmit Hold Register */ u8 bRDR; /**< Received Data Register */ u8 bBRB_LSB; u8 bBRB_MSB; u8 bIER; /**< Interrupt Enable Register */ u8 bIIR; /**< Interrupt Identification Register */ u8 bFCR; u8 bLCR; /**< Line Control Register (Data Format Register) */ u8 bMCR; /**< Modem Control Register */ u8 bLSR; /**< Line Status Register */ u8 bMSR; /**< Modem Status Register */ u8 bSPR; /**< Scratch Pad Register */ int serial_cycles; char rcvBuffer[1024]; int rcvW; int rcvR; int iNumber; bool irq_active; } state; int listenPort; const char *listenAddress; int listenSocket; int connectSocket; #if defined(IDB) && defined(LS_MASTER) int throughSocket; #endif }; #endif // !defined(INCLUDED_SERIAL_H) ================================================ FILE: src/ShrinkingChoiceQuestion.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /** * Question class implementing a "Shrinking Choice" question. * * A shrinking choice question is a multiple choice question * where each answer given is removed from the list of valid * answers. **/ class ShrinkingChoiceQuestion : public MultipleChoiceQuestion { /** * Overridden to remove the answer that was given from the * allowed answer set. **/ virtual void haveChosen(string choice) { dropChoice(choice); } }; ================================================ FILE: src/StdAfx.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_STDAFX_H) #define INCLUDED_STDAFX_H // Include generated file with debugging flags (defines) #include "config_debug.hpp" #include "config.hpp" #include "datatypes.hpp" #ifdef _WIN32 #pragma comment( lib, "ws2_32.lib") #endif /* _WIN32 */ #if defined(HAVE_WINDOWS_H) #include "windows.h" #endif #if !defined(HAVE_STRCASECMP) #if defined(HAVE__STRICMP) #define strcasecmp(a, b) _stricmp(a, b) #else #ifdef _MSC_VER #define strcasecmp _stricmp #else #error "Need strcasecmp" #endif #endif #endif // !defined(HAVE_STRCASECMP) #if !defined(HAVE_STRNCASECMP) #if defined(HAVE__STRNICMP) #define strncasecmp(a, b, c) _strnicmp(a, b, c) #else #ifdef _MSC_VER #define strncasecmp _strnicmp #else #error "Need strncasecmp" #endif #endif #endif // !defined(HAVE_STRNCASECMP) #if defined(HAVE_PROCESS_H) #include #endif #if !defined(HAVE__STRDUP) #if defined(HAVE_STRDUP) #define _strdup(a) strdup(a) #else #error "Need strdup" #endif #endif // !defined(HAVE__STRDUP) #if defined(HAVE_SYS_TIME_H) #include #endif #if defined(HAVE_UNISTD_H) #include #endif #if defined(HAVE_PTHREAD_H) #include #endif #if defined(HAVE_SIGNAL_H) #include #endif #if defined(HAVE_SYS_WAIT_H) #include #endif #if defined(HAVE_STDLIB_H) #include #endif #if defined(HAVE_STDIO_H) #include #endif #if defined(HAVE_STDDEF_H) #include #endif #if defined(HAVE_STRING_H) #include #endif #if defined(HAVE_MALLOC_H) #include #endif #if defined(HAVE_TIME_H) #include #endif #if defined(HAVE_CTYPE_H) #include #endif #if !defined(HAVE_GMTIME_S) inline void gmtime_s(struct tm *t1, time_t *t2) { struct tm *t3; t3 = gmtime(t2); memcpy(t1, t3, sizeof(struct tm)); } #endif #if !defined(HAVE_LOCALTIME_S) #ifdef _WIN32 inline struct tm *localtime_s(struct tm *buf, time_t *timer) #else inline struct tm *localtime_s(time_t *timer, struct tm *buf) #endif { struct tm *tmp; tmp = localtime(timer); return (struct tm*) memcpy(buf, tmp, sizeof(struct tm)); } #endif #if !defined(HAVE_ISBLANK) inline bool isblank(char c) { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') return true; return false; } #endif inline char printable(char c) { if (isprint((unsigned char)c)) return c; return '.'; } #if defined(HAVE__FSEEKI64) #define fseek_large _fseeki64 #elif defined(HAVE_FSEEKO64) #define fseek_large fseeko64 #elif defined(HAVE_FSEEKO) #define fseek_large fseeko #elif defined(HAVE_FSEEK) #define fseek_large fseek #else #error "Need fseek" #endif #if defined(HAVE__FTELLI64) #define ftell_large _ftelli64 #define off_t_large __int64 #elif defined(HAVE_FTELLO64) #define ftell_large ftello64 #define off_t_large off64_t #elif defined(HAVE_FTELLO) #define ftell_large ftello #define off_t_large off_t #elif defined(HAVE_FTELL) #define ftell_large ftell #define off_t_large off_t #else #error "Need ftell" #endif #include #include #include #include #define POCO_NO_UNWINDOWS #include "base/Mutex.hpp" #include "base/RWLock.hpp" #include "base/Semaphore.hpp" #include "base/Timestamp.hpp" #include "es40_debug.hpp" #include "es40_endian.hpp" #if __cplusplus < 201402L #ifndef _WIN32 #include "make_unique.hpp" #endif #endif #endif // !defined(INCLUDED_STDAFX_H) ================================================ FILE: src/Sym53C810.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(DEBUG_SYM) #define DEBUG_SYM_REGS #define DEBUG_SYM_SCRIPTS #endif #include "Sym53C810.hpp" #include "Disk.hpp" #include "SCSIBus.hpp" #include "StdAfx.hpp" #include "System.hpp" /// Register 00: SCNTL0: SCSI Control 0 #define R_SCNTL0 0x00 #define R_SCNTL0_ARB1 0x80 #define R_SCNTL0_ARB0 0x40 #define R_SCNTL0_START 0x20 #define R_SCNTL0_WATN 0x10 #define R_SCNTL0_EPC 0x08 #define R_SCNTL0_AAP 0x02 #define R_SCNTL0_TRG 0x01 #define SCNTL0_MASK 0xFB /// Register 01: SCNTL1: SCSI Control 1 #define R_SCNTL1 0x01 #define R_SCNTL1_CON 0x10 #define R_SCNTL1_RST 0x08 #define R_SCNTL1_IARB 0x02 /// Register 02: SCNTL2: SCSI Control 2 #define R_SCNTL2 0x02 #define R_SCNTL2_SDU 0x80 #define SCNTL2_MASK 0x80 /// Register 03: SCNTL3: SCSI Control 3 #define R_SCNTL3 0x03 #define SCNTL3_MASK 0x77 /// Register 04: SCID: SCSI Chip ID #define R_SCID 0x04 #define R_SCID_ID 0x07 #define SCID_MASK 0x67 /// Register 05: SXFER: SCSI Transfer #define R_SXFER 0x05 /// Register 06: SDID: SCSI Destination ID #define R_SDID 0x06 #define R_SDID_ID 0x07 #define SDID_MASK 0x07 /// Register 07: GPREG: General Purpose #define R_GPREG 0x07 #define GPREG_MASK 0x03 /// Register 08: SFBR: SCSI First Byte REceived #define R_SFBR 0x08 /// Register 09: SOCL: SCSI Output Control Latch #define R_SOCL 0x09 #define R_SOCL_ACK 0x40 #define R_SOCL_ATN 0x20 /// Register 0A: SSID: SCSI Selector ID #define R_SSID 0x0A #define R_SSID_VAL 0x80 #define R_SSID_ID 0x07 /// Register 0B: SBCL: SCSI Bus Control Lines #define R_SBCL 0x0B #define R_SBCL_REQ 0x80 #define R_SBCL_ACK 0x40 #define R_SBCL_BSY 0x20 #define R_SBCL_SEL 0x10 #define R_SBCL_ATN 0x08 #define R_SBCL_MSG 0x04 #define R_SBCL_CD 0x02 #define R_SBCL_IO 0x01 #define R_SBCL_PHASE 0x07 /// Register 0C: DSTAT: DMA Status #define R_DSTAT 0x0C #define R_DSTAT_DFE 0x80 #define R_DSTAT_MDPE 0x40 #define R_DSTAT_BF 0x20 #define R_DSTAT_ABRT 0x10 #define R_DSTAT_SSI 0x08 #define R_DSTAT_SIR 0x04 #define R_DSTAT_IID 0x01 #define DSTAT_RC 0x7D #define DSTAT_FATAL 0x7D /// Register 0D: SSTAT0: SCSI Status 0 #define R_SSTAT0 0x0D #define R_SSTAT0_RST 0x02 #define R_SSTAT0_SDP0 0x01 /// Register 0E: SSTAT1: SCSI Status 1 #define R_SSTAT1 0x0E #define R_SSTAT1_SDP1 0x01 /// Register 0F: SSTAT2: SCSI Status 2 #define R_SSTAT2 0x0F #define R_SSTAT2_LDSC 0x02 /// Register 10..13: DSA: Data Structure Address #define R_DSA 0x10 /// Register 14: ISTAT: Interrupt Status #define R_ISTAT 0x14 #define R_ISTAT_ABRT 0x80 #define R_ISTAT_SRST 0x40 #define R_ISTAT_SIGP 0x20 #define R_ISTAT_SEM 0x10 #define R_ISTAT_CON 0x08 #define R_ISTAT_INTF 0x04 #define R_ISTAT_SIP 0x02 #define R_ISTAT_DIP 0x01 #define ISTAT_MASK 0xF0 #define ISTAT_W1C 0x04 /// Register 18: CTEST0: Chip Test 0 #define R_CTEST0 0x18 /// Register 19: CTEST1: Chip Test 1 #define R_CTEST1 0x19 #define R_CTEST1_FMT 0xF0 #define R_CTEST1_FFL 0x0F /// Register 1A: CTEST2: Chip Test 2 #define R_CTEST2 0x1A #define R_CTEST2_DDIR 0x80 #define R_CTEST2_SIGP 0x40 #define R_CTEST2_CIO 0x20 #define R_CTEST2_CM 0x10 #define R_CTEST2_TEOP 0x04 #define R_CTEST2_DREQ 0x02 #define R_CTEST2_DACK 0x01 /// Register 1B: CTEST3: Chip Test 3 #define R_CTEST3 0x1B #define R_CTEST3_REV 0xf0 #define R_CTEST3_FLF 0x08 #define R_CTEST3_CLF 0x04 #define R_CTEST3_FM 0x02 #define CTEST3_MASK 0x0B /// Register 1C..1F: TEMP: Temporary #define R_TEMP 0x1C /// Register 20: DFIFO: DMA FIFO #define R_DFIFO 0x20 /// Register 21: CTEST4: Chip Test 4 #define R_CTEST4 0x21 /// Register 22: CTEST5: Chip Test 5 #define R_CTEST5 0x22 #define R_CTEST5_ADCK 0x80 #define R_CTEST5_BBCK 0x40 #define CTEST5_MASK 0x18 /// Register 24..26: DBC: DMA Byte Counter #define R_DBC 0x24 /// Register 27: DCMD: DMA Command #define R_DCMD 0x27 /// Register 28..2B: DNAD: DMA Next Address #define R_DNAD 0x28 /// Register 2C..2F: DSP: DMA SCRIPTS Pointer #define R_DSP 0x2C /// Register 30..33: DSPS: DMA SCRIPTS Pointer Save #define R_DSPS 0x30 /// Register 34..37: SCRATCHA: Scratch Register A #define R_SCRATCHA 0x34 /// Register 38: DMODE: DMA Mode #define R_DMODE 0x38 #define R_DMODE_MAN 0x01 /// Register 39: DIEN: DMA Interrupt Enable #define R_DIEN 0x39 #define DIEN_MASK 0x7D /// Register 3A: SBR: Scratch Byte Register #define R_SBR 0x3A /// Register 3B: DCNTL: DMA Control #define R_DCNTL 0x3B #define R_DCNTL_SSM 0x10 #define R_DCNTL_STD 0x04 #define R_DCNTL_IRQD 0x02 #define R_DCNTL_COM 0x01 #define DCNTL_MASK 0xFB /// Register 3C..37: ADDER: Adder Sum Output #define R_ADDER 0x3C /// Register 40: SIEN0: SCSI Interrupt Enable 0 #define R_SIEN0 0x40 #define SIEN0_MASK 0xFF /// Register 41: SIEN1: SCSI Interrupt Enable 1 #define R_SIEN1 0x41 #define SIEN1_MASK 0x07 /// Register 42: SIST0: SCSI Interrupt Status 0 #define R_SIST0 0x42 #define R_SIST0_MA 0x80 #define R_SIST0_CMP 0x40 #define R_SIST0_SEL 0x20 #define R_SIST0_RSL 0x10 #define R_SIST0_SGE 0x08 #define R_SIST0_UDC 0x04 #define R_SIST0_RST 0x02 #define R_SIST0_PAR 0x01 #define SIST0_RC 0xFF #define SIST0_FATAL 0x8F /// Register 43: SIST1: SCSI Interrupt Status 1 #define R_SIST1 0x43 #define R_SIST1_STO 0x04 #define R_SIST1_GEN 0x02 #define R_SIST1_HTH 0x01 #define SIST1_RC 0x07 #define SIST1_FATAL 0x04 /// Register 46: MACNTL: Memory Access Control #define R_MACNTL 0x46 #define MACNTL_MASK 0x0F /// Register 47: GPCNTL: General Purpose Pin Control #define R_GPCNTL 0x47 /// Register 48: STIME0: SCSI Timer 0 #define R_STIME0 0x48 /// Register 49: STIME1: SCSI Timer 1 #define R_STIME1 0x49 #define R_STIME1_GEN 0x0F #define STIME1_MASK 0x0F /// Register 4A: RESPID: SCSI Response ID #define R_RESPID 0x4A /// Register 4C: STEST0: SCSI Test 0 #define R_STEST0 0x4C /// Register 4D: STEST1: SCSI Test 1 #define R_STEST1 0x4D #define STEST1_MASK 0xC0 /// Register 4E: STEST2: SCSI Test 2 #define R_STEST2 0x4E #define R_STEST2_SCE 0x80 #define R_STEST2_ROF 0x40 #define R_STEST2_SLB 0x10 #define R_STEST2_SZM 0x08 #define R_STEST2_EXT 0x02 #define R_STEST2_LOW 0x01 #define STEST2_MASK 0x9B /// Register 4F: STEST3: SCSI Test 3 #define R_STEST3 0x4F #define R_STEST3_TE 0x80 #define R_STEST3_STR 0x40 #define R_STEST3_HSC 0x20 #define R_STEST3_DSI 0x10 #define R_STEST3_TTM 0x04 #define R_STEST3_CSF 0x02 #define R_STEST3_STW 0x01 #define STEST3_MASK 0xF7 /// Register 58: SBDL: SCSI Bus Data Lines #define R_SBDL 0x58 /// Registers 5C..5F: SCRATCHB: Scratch Register B #define R_SCRATCHB 0x5C /// Acces an 8-byte register #define R8(a) state.regs.reg8[R_##a] /// Acces a 16-byte register #define R16(a) state.regs.reg16[R_##a / 2] /// Access a 32-byte register #define R32(a) state.regs.reg32[R_##a / 4] /** * Test bit in register * * \param a is the name of the register * \param b is the name of the bit **/ #define TB_R8(a, b) ((R8(a) & R_##a##_##b) == R_##a##_##b) /** * Set bit in register. * * \param a is the name of the register * \param b is the name of the bit * \param c is the value for the bit **/ #define SB_R8(a, b, c) R8(a) = (R8(a) & ~R_##a##_##b) | (c ? R_##a##_##b : 0) /** * Write to a register, using a mask * * \param a is the name of the register * \param b is the value to write. * * Only those bits that are set to 1 in _MASK will be changed. **/ #define WRM_R8(a, b) R8(a) = (R8(a) & ~a##_MASK) | ((b)&a##_MASK) /** * Write to a register, using a mask, and using write-1-to-clear bits * * \param a is the name of the register * \param b is the value to write. * * Only those bits that are set to 1 in _MASK will be changed. * In addition, bits that are set to 1 in _W1C will be cleared * in the register if they are set to 1 in the value. **/ #define WRMW1C_R8(a, b) \ R8(a) = \ (R8(a) & ~a##_MASK & ~a##_W1C) | ((b)&a##_MASK) | (R8(a) & ~(b)&a##_W1C) /** * Raise an interrupt * * \param a is the name of the interrupt register * \param b is the name of the bit to set **/ #define RAISE(a, b) set_interrupt(R_##a, R_##a##_##b) /** * Clear read-to-clear-bits * * \param a is the name of the register * * Clear bits that are set to 1 in _RC **/ #define RDCLR_R8(a) R8(a) &= ~a##_RC /** * Get the SCSI destination ID from the SDID register **/ #define GET_DEST() (R8(SDID) & R_SCID_ID) /** * Set the SCSI destination ID in the SDID register **/ #define SET_DEST(a) R8(SDID) = (a)&R_SCID_ID /** * Get the value of the DBC register (24-bits) **/ #define GET_DBC() (R32(DBC) & 0x00ffffff) /** * Set the value of the DBC register (24-bits) **/ #define SET_DBC(a) R32(DBC) = (R32(DBC) & 0xff000000) | ((a)&0x00ffffff) /** * PCI Configuration Data Block **/ static u32 osym_cfg_data[64] = { /*00*/ 0x00011000, // CFID: vendor + device /*04*/ 0x02000001, // CFCS: command + status /*08*/ 0x01000001, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000001, // BAR0: IO Space /*14*/ 0x00000000, // BAR1: Memory space /*18*/ 0x00000000, // BAR2: RAM space /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x401101ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * PCI Configuration Mask Block **/ static u32 osym_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x00000157, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xffffff00, // BAR0: IO space (256 bytes) /*14*/ 0xffffff00, // BAR1: Memory space (256 bytes) /*18*/ 0x00000000, // BAR2: RAM space (4KB) /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Thread entry point. * * Repeat: * - Waiting until the semaphore is set * - Executing SCRIPTS code until execution ends. * . **/ void CSym53C810::run() { try { for (;;) { mySemaphore.wait(); if (StopThread) return; while (state.executing) { MUTEX_LOCK(myRegLock); execute(); MUTEX_UNLOCK(myRegLock); } } } catch (CException &e) { printf("Exception in SYM thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** * Constructor. * * Set up the SCSI bus, and defer the rest of initialization to * CSym53C895::init. **/ CSym53C810::CSym53C810(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev), CDiskController(1, 7), mySemaphore(0, 1) { // create scsi bus CSCSIBus *a = new CSCSIBus(cfg, c); scsi_register(0, a, 7); // scsi id 7 by default // initialize state memset(&state, 1, sizeof(struct SSym_state)); } /** * Initialize the Symbios device. * * Reset PCI structures, reset the chipset, and set up locks. **/ void CSym53C810::init() { add_function(0, osym_cfg_data, osym_cfg_mask); ResetPCI(); chip_reset(); myRegLock = new CMutex("sym-reg"); printf("%s: $Id: Sym53C810.cpp,v 1.14 2008/05/31 15:47:13 iamcamiel Exp $\n", devid_string); } /** * Create the thread, and start executing it. **/ void CSym53C810::start_threads() { if (!myThread) { printf(" sym"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); if (state.executing) mySemaphore.set(); } } /** * Stop and destroy the thread. **/ void CSym53C810::stop_threads() { StopThread = true; if (myThread) { printf(" sym"); mySemaphore.set(); myThread->join(); myThread = nullptr; } } /** * Destructor. * * Kill thread if still running. * Note: SCSI bus is destroyed when destroying the System. **/ CSym53C810::~CSym53C810() { stop_threads(); } /** * Reset the chipset. * * Initialize all registers to their default values. **/ void CSym53C810::chip_reset() { state.executing = false; state.wait_reselect = false; state.irq_asserted = false; state.gen_timer = 0; memset(state.regs.reg32, 0, sizeof(state.regs.reg32)); R8(SCNTL0) = R_SCNTL0_ARB1 | R_SCNTL0_ARB0; // 810 R8(DSTAT) = R_DSTAT_DFE; // DMA FIFO empty // 810 // R8(SSTAT2) = R_SSTAT2_LDSC; // 810 R8(CTEST1) = R_CTEST1_FMT; // 810 R8(CTEST2) = R_CTEST2_DACK; // 810 R8(CTEST3) = (u8)(pci_state.config_data[0][2] << 4) & R_CTEST3_REV; // Chip rev. R8(MACNTL) = 0x40; // 810 type ID R8(GPCNTL) = 0x0F; // 810 R8(STEST0) = 0x03; // 810 } /** * Register a disk * * Attach the disk to the SCSI bus. **/ void CSym53C810::register_disk(class CDisk *dsk, int bus, int dev) { CDiskController::register_disk(dsk, bus, dev); dsk->scsi_register(0, scsi_bus[0], dev); } static u32 sym_magic1 = 0x53C810CC; static u32 sym_magic2 = 0xCC53C810; /** * Save state to a Virtual Machine State file. **/ int CSym53C810::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&sym_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&sym_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CSym53C810::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != sym_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != sym_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * write data to one of the PCI BAR (relocatable) address ranges. **/ void CSym53C810::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { void *p; switch (bar) { case 0: case 1: address &= 0x7f; switch (dsize) { case 8: MUTEX_LOCK(myRegLock); #if defined(DEBUG_SYM_REGS) printf("SYM: Write to register %02x: %02x. \n", address, data); #endif if (address >= R_SCRATCHB) { state.regs.reg8[address] = (u8)data; MUTEX_UNLOCK(myRegLock); break; } switch (address) { // SIMPLE CASES: JUST WRITE case R_SXFER: // 05 case R_DSA: // 10 case R_DSA + 1: // 11 case R_DSA + 2: // 12 case R_DSA + 3: // 13 case R_CTEST0: // 18 case R_TEMP: // 1C case R_TEMP + 1: // 1D case R_TEMP + 2: // 1E case R_TEMP + 3: // 1F case R_DSP: // 2C case R_DSP + 1: // 2D case R_DSP + 2: // 2E case R_DSPS: // 30 case R_DSPS + 1: // 31 case R_DSPS + 2: // 32 case R_DSPS + 3: // 33 case R_SCRATCHA: // 34 case R_SCRATCHA + 1: // 35 case R_SCRATCHA + 2: // 36 case R_SCRATCHA + 3: // 37 case R_DMODE: // 38 case R_SBR: // 3A // 810 case R_GPCNTL: // 47 case R_STIME0: // 48 case R_RESPID: // 4A case R_STEST0: // 4C state.regs.reg8[address] = (u8)data; break; case R_SCNTL0: // 00 // side effects: start arbitration bit write_b_scntl0((u8)data); break; case R_SCNTL1: // 01 // side effects: start immediate arbitration bit write_b_scntl1((u8)data); break; case R_SCNTL2: // 02 WRM_R8(SCNTL2, (u8)data); break; case R_SCNTL3: // 03 // side effects: clearing EWS WRM_R8(SCNTL3, (u8)data); break; case R_SCID: // 04 WRM_R8(SCID, (u8)data); break; case R_SDID: // 06 WRM_R8(SDID, (u8)data); break; case R_GPREG: // 07 WRM_R8(GPREG, (u8)data); break; case R_ISTAT: // 14 write_b_istat((u8)data); break; case R_CTEST3: // 1B write_b_ctest3((u8)data); break; case R_CTEST4: // 21 write_b_ctest4((u8)data); break; case R_CTEST5: // 22 write_b_ctest5((u8)data); break; case R_DSP + 3: // 2F state.regs.reg8[address] = (u8)data; post_dsp_write(); break; case R_DIEN: // 39 WRM_R8(DIEN, (u8)data); eval_interrupts(); break; case R_DCNTL: // 3B write_b_dcntl((u8)data); break; case R_SIEN0: // 40 R8(SIEN0) = (u8)data; eval_interrupts(); break; case R_SIEN1: // 41 WRM_R8(SIEN1, (u8)data); eval_interrupts(); break; case R_MACNTL: // 46 // 810 WRM_R8(MACNTL, (u8)data); break; case R_STIME1: // 49 WRM_R8(STIME1, (u8)data); state.gen_timer = (R8(STIME1) & R_STIME1_GEN) * 30; break; case R_STEST1: // 4D WRM_R8(STEST1, (u8)data); break; case R_STEST2: // 4E write_b_stest2((u8)data); break; case R_STEST3: // 4F write_b_stest3((u8)data); break; case R_DSTAT: // 0C case R_SSTAT0: // 0D case R_SSTAT1: // 0E case R_SSTAT2: // 0F case R_CTEST2: // 1A // printf("SYM: Write to read-only register at %02x. FreeBSD driver // cache test.\n", address); break; case 0x4b: // ??? Linux wants this // printf("SYM: Write to non-existing register at %02x. Linux generic // driver.\n", address); break; default: FAILURE_2(NotImplemented, "SYM: Write to unknown register at %02x with %08x.\n", address, data); } MUTEX_UNLOCK(myRegLock); break; case 16: WriteMem_Bar(0, 1, address + 0, 8, (data >> 0) & 0xff); WriteMem_Bar(0, 1, address + 1, 8, (data >> 8) & 0xff); break; case 32: WriteMem_Bar(0, 1, address + 0, 8, (data >> 0) & 0xff); WriteMem_Bar(0, 1, address + 1, 8, (data >> 8) & 0xff); WriteMem_Bar(0, 1, address + 2, 8, (data >> 16) & 0xff); WriteMem_Bar(0, 1, address + 3, 8, (data >> 24) & 0xff); break; } break; case 2: p = (u8 *)state.ram + address; switch (dsize) { case 8: *((u8 *)p) = (u8)data; break; case 16: *((u16 *)p) = (u16)data; break; case 32: *((u32 *)p) = (u32)data; break; } break; } } /** * Read data from one of the PCI BAR (relocatable) address ranges. **/ u32 CSym53C810::ReadMem_Bar(int func, int bar, u32 address, int dsize) { u32 data = 0; void *p; switch (bar) { case 0: case 1: address &= 0x7f; switch (dsize) { case 8: MUTEX_LOCK(myRegLock); if (address >= R_SCRATCHB) { data = state.regs.reg8[address]; MUTEX_UNLOCK(myRegLock); break; } switch (address) { case R_SCNTL0: // 00 case R_SCNTL1: // 01 case R_SCNTL2: // 02 case R_SCNTL3: // 03 case R_SCID: // 04 case R_SXFER: // 05 case R_SDID: // 06 case R_GPREG: // 07 case R_SFBR: // 08 case R_SSID: // 0A case R_SBCL: // 0B case R_SSTAT0: // 0D case R_SSTAT1: // 0E case R_SSTAT2: // 0F case R_DSA: // 10 case R_DSA + 1: // 11 case R_DSA + 2: // 12 case R_DSA + 3: // 13 case R_ISTAT: // 14 case R_CTEST0: // 18 case R_CTEST1: // 19 case R_CTEST3: // 1B case R_TEMP: // 1C case R_TEMP + 1: // 1D case R_TEMP + 2: // 1E case R_TEMP + 3: // 1F case R_CTEST4: // 21 case R_CTEST5: // 22 case R_DBC: // 24 // 810 case R_DBC + 1: // 25 // 810 case R_DBC + 2: // 26 // 810 case R_DCMD: // 27 // 810 case R_DNAD: // 28 // 810 case R_DNAD + 1: // 29 // 810 case R_DNAD + 2: // 2A // 810 case R_DNAD + 3: // 2B // 810 case R_DSP: // 2C case R_DSP + 1: // 2D case R_DSP + 2: // 2E case R_DSP + 3: // 2F case R_DSPS: // 30 case R_DSPS + 1: // 31 case R_DSPS + 2: // 32 case R_DSPS + 3: // 33 case R_SCRATCHA: // 34 case R_SCRATCHA + 1: // 35 case R_SCRATCHA + 2: // 36 case R_SCRATCHA + 3: // 37 case R_DMODE: // 38 case R_DIEN: // 39 case R_SBR: // 3A // 810 case R_DCNTL: // 3B case R_SIEN0: // 40 case R_SIEN1: // 41 case R_MACNTL: // 46 // 810 case R_GPCNTL: // 47 case R_STIME0: // 48 case R_STIME1: // 49 case R_RESPID: // 4A case R_STEST0: // 4C case R_STEST1: // 4D case R_STEST2: // 4E case R_STEST3: // 4F case R_SBDL: // 58 data = state.regs.reg8[address]; break; case R_DSTAT: // 0C data = read_b_dstat(); break; case R_CTEST2: // 1A data = read_b_ctest2(); break; case R_DFIFO: // 20 data = R8(DBC) & 0x7f; // 810 - fake the DFIFO count break; case R_SIST0: // 42 case R_SIST1: // 43 data = read_b_sist(address - R_SIST0); break; case 0x17: // ??? Linux wants this. case 0x4b: // ??? Linux wants this case 0x52: // ??? Linux wants this. case 0x59: // ??? Linux wants this. // printf("SYM: Read from non-existing register at %02x. Linux generic // driver.\n", address); data = 0; break; default: FAILURE_2( NotImplemented, "SYM: Attempt to read %d bytes from unknown register at %" PRIx32 "\n", dsize, address); } MUTEX_UNLOCK(myRegLock); #if defined(DEBUG_SYM_REGS) printf("SYM: Read frm register %02x: %02x. \n", address, data); #endif break; case 16: data = (ReadMem_Bar(0, 1, address + 0, 8) << 0) & 0x00ff; data |= (ReadMem_Bar(0, 1, address + 1, 8) << 8) & 0xff00; break; case 32: data = (ReadMem_Bar(0, 1, address + 0, 8) << 0) & 0x000000ff; data |= (ReadMem_Bar(0, 1, address + 1, 8) << 8) & 0x0000ff00; data |= (ReadMem_Bar(0, 1, address + 2, 8) << 16) & 0x00ff0000; data |= (ReadMem_Bar(0, 1, address + 3, 8) << 24) & 0xff000000; break; } break; case 2: p = (u8 *)state.ram + address; switch (dsize) { case 8: return *((u8 *)p); case 16: return *((u16 *)p); case 32: return *((u32 *)p); } break; } return data; } /** * Override PCI Configuration Space read action. * * Lower 80 bytes are normal, upper 80 bytes reflect into the * register space. **/ u32 CSym53C810::config_read_custom(int func, u32 address, int dsize, u32 data) { if (address >= 0x80) return ReadMem_Bar(func, 1, address - 0x80, dsize); else return data; } /** * Override PCI Configuration Space write action. * * Lower 80 bytes are normal, upper 80 bytes reflect into the * register space. **/ void CSym53C810::config_write_custom(int func, u32 address, int dsize, u32 old_data, u32 new_data, u32 data) { if (address >= 0x80) WriteMem_Bar(func, 1, address - 0x80, dsize, data); } /** * Write a byte to the SCSI Control 0 register. * * This is a normal masked write operation; implemented as a separate * function, because there are some bits in here (START and TRG) that * we should do something with if a driver sets these, but that we * don't implement. * * START: When this bit is set, the controller should start the * arbitration seqence indicated by the arbitration mode bits. Used * only in low-level mode. UNIMPLEMENTED. * * TRG: When this bit is set, the controller is a target device. * UNIMPLEMENTED. **/ void CSym53C810::write_b_scntl0(u8 value) { bool old_start = TB_R8(SCNTL0, START); WRM_R8(SCNTL0, value); if (TB_R8(SCNTL0, START) && !old_start) FAILURE(NotImplemented, "SYM: Don't know how to start arbitration sequence"); if (TB_R8(SCNTL0, TRG)) FAILURE(NotImplemented, "SYM: Don't know how to operate in target mode"); } /** * Write a byte to the SCSI Control 1 register. * * This is implemented as a separate function, because there are * quite a few side-effects that occur when writing to this register * * CON (Connected): This bit is automatically set any time the * controller is connected to the SCSI bus. The CPU can force a * connection or disconnection by setting or clearing this bit. * UNIMPLEMENTED. * * RST: Asserts the SCSI RST/ signal. Has the "side-effect" of * resetting the SCSI bus. This effects a couple of other registers. * * \todo: Implement real reset of the SCSI bus. **/ void CSym53C810::write_b_scntl1(u8 value) { bool old_rst = TB_R8(SCNTL1, RST); R8(SCNTL1) = value; // if (TB_R8(SCNTL1,CON) != old_con) // printf("SYM: Don't know how to forcibly connect or disconnect\n"); if (TB_R8(SCNTL1, RST) != old_rst) { SB_R8(SSTAT0, SDP0, false); SB_R8(SSTAT1, SDP1, false); R16(SBDL) = 0; R8(SBCL) = 0; SB_R8(SSTAT0, RST, !old_rst); // printf("SYM: %s SCSI bus reset.\n",old_rst?"end":"start"); if (!old_rst) RAISE(SIST0, RST); } } /** * Write a byte to the SCSI Interrupt Status. * * This is implemented as a separate function, because there are * quite a few side-effects that occur when writing to this register * * ABRT (Abort Operation): Aborts the currently executing SCRIP, and * generate an interrupt. * * SRST (Software Reset): Resets the SCSI chipset. * * SIGP (Signal Process): Aborts a Wait for (Re)Selection instruction * by jumping to the alternate address immediately. * * Since interrupt state is affected, call eval_interrupts. **/ void CSym53C810::write_b_istat(u8 value) { bool old_srst = TB_R8(ISTAT, SRST); WRMW1C_R8(ISTAT, value); if (TB_R8(ISTAT, ABRT)) { // printf("SYM: Aborting on request.\n"); RAISE(DSTAT, ABRT); } if (TB_R8(ISTAT, SRST) && !old_srst) { // printf("SYM: Resetting on request.\n"); chip_reset(); } // if (TB_R8(ISTAT,SEM) != old_sem) // printf("SYM: SEM %s.\n",old_sem?"reset":"set"); // if (TB_R8(ISTAT,SIGP) != old_sigp) // printf("SYM: SIGP %s.\n",old_sigp?"reset":"set"); if (TB_R8(ISTAT, SIGP)) { if (state.wait_reselect) { // printf("SYM: SIGP while wait_reselect. Jumping...\n"); R32(DSP) = state.wait_jump; state.wait_reselect = false; state.executing = true; mySemaphore.set(); } } eval_interrupts(); } /** * Reads a byte from the Chip Test 2 register. * * This is implemented as a separate function, because: * - The SIGP flag read by this register comes from the ISTAT * register. * - The CIO (configured as I/O) and CM (configured as memory) * flags are determined from PCI Configuration space. * - Reading this register has the side effect of clearing the * SIGP flag. * . **/ u8 CSym53C810::read_b_ctest2() { SB_R8(CTEST2, CIO, pci_state.config_data[0][4] != 0); SB_R8(CTEST2, CM, pci_state.config_data[0][5] != 0); SB_R8(CTEST2, SIGP, TB_R8(ISTAT, SIGP)); SB_R8(ISTAT, SIGP, false); // printf("SYM: SIGP cleared by CTEST2 read.\n"); return R8(CTEST2); } /** * Write a byte to the Chip Test 3 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * FM (Fetch Pin Mode): When set, this bit causes the FETCH/ pin to * deassert during indirect and table indirect read operations. * FETCH/ will only be active during the op codde portion of an * instruction fetch. This allows SCRIPTS to be stored in a PROM * while data tables are stored in RAM. UNIMPLEMENTED. **/ void CSym53C810::write_b_ctest3(u8 value) { WRM_R8(CTEST3, value); // if ((value>>3) & 1) // printf("SYM: Don't know how to flush DMA FIFO\n"); // if ((value>>2) & 1) // printf("SYM: Don't know how to clear DMA FIFO\n"); if ((value >> 1) & 1) FAILURE(NotImplemented, "SYM: Don't know how to handle FM mode"); } /** * Write a byte to the Chip Test 4 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * SRTM: Shadow Register Test Mode. Access shadow copies of TEMP and * DSA. Used for manufacturing diagnostics only. UNIMPLEMENTED. **/ void CSym53C810::write_b_ctest4(u8 value) { R8(CTEST4) = value; if ((value >> 4) & 1) FAILURE(NotImplemented, "SYM: Don't know how to handle SRTM mode"); } /** * Write a byte to the Chip Test 5 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * ADCK (Clock Address Incrementor): Setting this bit increments the * DNAD register. The DNAD register is incremented based on the DNAD * contents and the current DBC value. This bit automatically clears * itself after incrementing the DNAD register. UNIMPLEMENTED. * * BBCK (Clock Byte Counter): Setting this bit decrements the byte * count contained in the 24-bit DBC register. It is decremented * based on the DBC contents and the current DNAD value. This bit * automatically clears itself after decrementing the DBC register. * UNIMPLEMENTED. **/ void CSym53C810::write_b_ctest5(u8 value) { WRM_R8(CTEST5, value); if ((value >> 7) & 1) FAILURE(NotImplemented, "SYM: Don't know how to do Clock Address increment"); if ((value >> 6) & 1) FAILURE(NotImplemented, "SYM: Don't know how to do Clock Byte Counter decrement"); } /** * Read a byte from the DSTAT register. * * This is implemented as a separate function, because it requires * interrupt re-evaluation. **/ u8 CSym53C810::read_b_dstat() { u8 retval = R8(DSTAT); RDCLR_R8(DSTAT); // printf("Read DSTAT --> eval int\n"); eval_interrupts(); // printf("Read DSTAT <-- eval int; retval: %02x; dstat: // %02x.\n",retval,R8(DSTAT)); return retval; } /** * Read a byte from the SIST0 or SIST1 register. * * This is implemented as a separate function, because it requires * interrupt re-evaluation. **/ u8 CSym53C810::read_b_sist(int id) { u8 retval = state.regs.reg8[R_SIST0 + id]; if (id) RDCLR_R8(SIST1); else RDCLR_R8(SIST0); eval_interrupts(); return retval; } /** * Write a byte to the DMA Control register. * * This is implemented as a separate function, because there are * some side-effects. * * STD (Start DMA Operation): Start executing SCSI SCRIPT. Needs to * wake the thread. * * IRQD (IRQ Disable): disables the IRQ pin. Requires interrupt * re-evaluation. **/ void CSym53C810::write_b_dcntl(u8 value) { WRM_R8(DCNTL, value); // start operation if (value & R_DCNTL_STD) { state.executing = true; mySemaphore.set(); } // IRQD bit... eval_interrupts(); } /** * Write a byte to the SCSI Test 2 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * LOW (Low-level-mode). Switches the SCSI controller to low-level * mode operation. No SCRIPTS processor, but raw manipulation of * SCSI registers. Yuck. UNIMPLEMENTED. **/ void CSym53C810::write_b_stest2(u8 value) { WRM_R8(STEST2, value); // if (value & R_STEST2_ROF) // printf("SYM: Don't know how to reset SCSI offset!\n"); if (TB_R8(STEST2, LOW)) FAILURE(NotImplemented, "SYM: I don't like LOW level mode"); } /** * Write a byte to the SCSI Test 3 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. **/ void CSym53C810::write_b_stest3(u8 value) { WRM_R8(STEST3, value); // if (value & R_STEST3_CSF) // printf("SYM: Don't know how to clear the SCSI fifo.\n"); } /** * Called after the DMA Scripts Pointer register has been written. * * Start executing SCSI SCRIPT. Needs to wake the thread. **/ void CSym53C810::post_dsp_write() { if (!TB_R8(DMODE, MAN)) { state.executing = true; mySemaphore.set(); // printf("SYM: Execution started @ %08x.\n",R32(DSP)); } } /** * Check if threads are still running. **/ void CSym53C810::check_state() { if (myThreadDead.load()) FAILURE(Thread, "SYM thread has died"); if (state.gen_timer) { state.gen_timer--; if (!state.gen_timer) { state.gen_timer = (R8(STIME1) & R_STIME1_GEN) * 30; RAISE(SIST1, GEN); return; } } /** if (state.wait_reselect && PT.disconnected) { state.executing = true; state.wait_reselect = false; PT.disconnected = false; //PT.disconnect_priv = false; //PT.will_disconnect = false; PT.reselected = true; state.phase = 7; // msg in //PT.disconnect_phase; R8(SSID) = GET_DEST() | R_SSID_VAL; // valid scsi selector id if (TB_R8(DCNTL,COM)) R8(SFBR) = GET_DEST(); // don't expect a disconnect. SB_R8(SCNTL2,SDU,true); //RAISE(SIST0,RSL); return 0; } **/ if (state.disconnected) { if (!TB_R8(SCNTL2, SDU)) { // disconnect expected // printf("SYM: Disconnect expected. stopping disconnect timer at // %d.\n",state.disconnected); state.disconnected = 0; return; } state.disconnected--; if (!state.disconnected) { // printf("SYM: Disconnect unexpected. raising interrupt!\n"); // printf(">"); // getchar(); RAISE(SIST0, UDC); return; } } } /** * Check SCSI Bus Phase. * * Returns -1 on timeout or similar, 0 on different phase, and 1 on same phase **/ int CSym53C810::check_phase(int chk_phase) { int real_phase = scsi_get_phase(0); if (real_phase == SCSI_PHASE_ARBITRATION) { #if defined(DEBUG_SYM_SCRIPTS) printf("Phase check... selection time-out!\n"); #endif RAISE(SIST1, STO); // select time-out scsi_free(0); state.select_timeout = false; return -1; } if (real_phase == SCSI_PHASE_FREE && state.disconnected) { #if defined(DEBUG_SYM_SCRIPTS) printf("Phase check... disconnected!\n"); #endif state.disconnected = 1; R32(DSP) -= 8; return -1; } if (real_phase == chk_phase) return 1; else return 0; } /** * Execute one SCRIPTS Block Move instruction * * The Block Move instruction moves data between system memory and the * SCSI Bus. This is a two DWORD instruction. The instruction format in * the DCMD register is as follows: * * \code * +---+-+-+-+-----+ * |7 6|5|4|3|2 1 0| DCMD Register * +---+-+-+-+-----+ * | | | | +- 0..2: SCSI Phase (I/O, C/D and MSG/ signals): * | | | | The data transfer only occurs if these bits * | | | | match the actual SCSI bus phase. * | | | +- 3: Op Code: IGNORED * | | +- 4: Table Indirect Adressing: * | | 0: The DSPS register contains the address of the data, * | | and the DBC register contains the number of bytes to * | | transfer. * | | 1: The DSPS register contains a 24-bit signed offset * | | that is added to the DSA register to get a pointer * | | to a data structure that contains the address and * | | byte count. This structure looks as follows: * | | +----+------------+ * | | DSA+DSPS: | 00 | Byte Count | * | | +----+------------+ * | | DSA+DSPS+4: | Data Address | * | | +-----------------+ * | +- 5: Indirect Addressing: * | 0: The DSPS register contains the address of the data * | 1: The DSPS register contains the address of a 32-bit * | pointer to the data. * +- 6..7: Instruction Type: 00 = Block Move * \endcode **/ void CSym53C810::execute_bm_op() { bool indirect = (R8(DCMD) >> 5) & 1; bool table_indirect = (R8(DCMD) >> 4) & 1; int scsi_phase = (R8(DCMD) >> 0) & 7; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = Block Move (i %d, t %d, opc %d, phase %d\n", indirect, table_indirect, opcode, scsi_phase); #endif // Compare phase int phase_ok = check_phase(scsi_phase); if (phase_ok == 0) { RAISE(SIST0, MA); return; } if (phase_ok > 0) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Ready for transfer.\n"); #endif u32 start; u32 count; if (table_indirect) { u32 add = R32(DSA) + sext_u32_24(R32(DSPS)); add &= ~0x03; // 810 #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Reading table at DSA(%08x)+DSPS(%08x) = %08x.\n", R32(DSA), R32(DSPS), add); #endif do_pci_read(add, &count, 4, 1); count &= 0x00ffffff; do_pci_read(add + 4, &start, 4, 1); } else if (indirect) { FAILURE(NotImplemented, "SYM: Unsupported: indirect addressing"); } else { start = R32(DSPS); count = GET_DBC(); } #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: MOVE Start/count %x, %x\n", R32(DSP) - 8, start, count); #endif R32(DNAD) = start; SET_DBC(count); if (count == 0) { // printf("SYM: Count equals zero!\n"); RAISE(DSTAT, IID); // page 5-32 return; } for (;;) { size_t expected = scsi_expected_xfer(0); u32 remaining = GET_DBC(); u32 xfer = remaining; if ((size_t)xfer > expected) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: xfer %d bytes, max %zu expected, in phase %d.\n", xfer, expected, scsi_phase); #endif xfer = (u32)expected; } if (xfer == 0) { RAISE(SIST0, MA); return; } u8 *scsi_data_ptr = (u8 *)scsi_xfer_ptr(0, xfer); u8 *org_sdata_ptr = scsi_data_ptr; switch (scsi_phase) { case SCSI_PHASE_COMMAND: case SCSI_PHASE_DATA_OUT: case SCSI_PHASE_MSG_OUT: do_pci_read(R32(DNAD), scsi_data_ptr, 1, xfer); R32(DNAD) += xfer; break; case SCSI_PHASE_STATUS: case SCSI_PHASE_DATA_IN: case SCSI_PHASE_MSG_IN: do_pci_write(R32(DNAD), scsi_data_ptr, 1, xfer); R32(DNAD) += xfer; break; } SET_DBC(remaining - xfer); R8(SFBR) = *org_sdata_ptr; scsi_xfer_done(0); if (GET_DBC() == 0) return; phase_ok = check_phase(scsi_phase); if (phase_ok <= 0) { if (phase_ok == 0) { RAISE(SIST0, MA); } return; } } return; } } /* Execute one SCRIPTS I/O instruction * * The I/O instructions perform common SCSI hardware sequences, like * Selection and reselection. The instruction format in * the DCMD and DBC register is as follows: * * \code * +---+-----+-+-+-+ * |7 6|5 4 3|2|1|0| DCMD Register * +---+-----+-+-+-+ * | | | | +- 0: Select with ATN/: * | | | | Valid only for Select instruction. Assert ATN/ during * | | | | selection * | | | +- 1: Table Indirect Mode: * | | | 0: All information is taken from the instruction, * | | | and the contents of the SCNTL3 and SXFER registers. * | | | 1: The DSPS register contains a 24-bit signed offset * | | | that is added to the DSA register to get a pointer * | | | to a data structure that contains the destination * | | | ID, SCNTL3 bits, and SXFER bits. This structure * | | | is 32 bits long and looks as follows: * | | | +--------+--------+--------+--------+ * | | | DSA+DSPS: | SCNTL3 | ID | SXFER | | * | | | +--------+--------+--------+--------+ * | | +- 2: Relative Addrressing: * | | 0: The value in the DNAD register is an absolute address. * | | 1: The value in the DNAD register is a 24-bit signed * | | displacement from the current DSP address. * | +- 3..5: Op Code * +- 6..7 Instruction Type: 01 = I/O * * +-------+-------++--------+--+-+-++-+-+---+-+-----+ * | |19 16|| |10|9| || |6| |3| | DBC Register * +-------+-------++--------+--+-+-++-+-+---+-+-----+ * | | | | +- 3: Set/Clear ATN * | | | +- 6: Set/Clear ACK * | | +- 9: Set/Clear Target Mode * | +- 10: Set/Clear Carry * +- 16..19: Destination ID * \endcode * * The Opcode determines the actual instruction: * \code * +-----+-----------------+-------------+ * | OPC | Initiator mode | Target Mode | * +-----+-----------------+-------------+ * | 000 | Select | Reselect | * | 001 | Wait Disconnect | Disconnect | * | 010 | Wait Reselect | Wait Select | * | 011 | Set | Set | * | 100 | Clear | Clear | * +-----+-----------------+-------------+ * \endcode * * Select: * - Arbitrate for the SCSI bus until arbitration is won. * - If arbitration is won, try to select the destination ID * - If the controller is selected or reselected before winning * arbitration, jump to the address in the DNAD register. * . * * Wait Disconnect: * - Wait for the target to disconnect from the SCSI bus. * . * * Wait Reselect: * - If the controller is reselected, go to the next instruction. * - If the controller is selected before being reselected, or if * the CPU sets the SIGP flag, jump to the address in the DNAD * register * . * * Set/Clear: * - Set or Clear the flags whose Set/Clear bits are set in the * instruction. * . **/ void CSym53C810::execute_io_op() { int opcode = (R8(DCMD) >> 3) & 7; bool relative = (R8(DCMD) >> 2) & 1; bool table_indirect = (R8(DCMD) >> 1) & 1; int destination = (GET_DBC() >> 16) & 0x0f; bool sc_carry = (GET_DBC() >> 10) & 1; bool sc_target = (GET_DBC() >> 9) & 1; bool sc_ack = (GET_DBC() >> 6) & 1; bool sc_atn = (GET_DBC() >> 3) & 1; R32(DNAD) = R32(DSPS); u32 dest_addr = R32(DNAD); if (relative) dest_addr = R32(DSP) + sext_u32_24(R32(DNAD)); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = I/O (opc %d, r %d, t %d, a %d, dest %d, sc %d%d%d%d\n", opcode, relative, table_indirect, atn, destination, sc_carry, sc_target, sc_ack, sc_atn); #endif if (table_indirect) { u32 io_addr = R32(DSA) + sext_u32_24(GET_DBC()); io_addr &= ~3; // 810 #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Reading table at DSA(%08x)+DBC(%08x) = %08x.\n", R32(DSA), sext_u32_24(GET_DBC()), io_addr); #endif u32 io_struc; do_pci_read(io_addr, &io_struc, 4, 1); destination = (io_struc >> 16) & 0x0f; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: table indirect. io_struct = %08x, new dest = %d.\n", io_struc, destination); #endif } switch (opcode) { case 0: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: SELECT %d.\n", R32(DSP) - 8, destination); #endif SET_DEST(destination); if (!scsi_arbitrate(0)) { // scsi bus busy, try again next clock... printf("scsi bus busy...\n"); R32(DSP) -= 8; return; } state.select_timeout = !scsi_select(0, destination); if (!state.select_timeout) // select ok SB_R8(SCNTL2, SDU, true); // don't expect a disconnect return; case 1: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: WAIT DISCONNECT\n", R32(DSP) - 8); #endif // maybe we need to do more?? scsi_free(0); return; case 2: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: WAIT RESELECT\n", R32(DSP) - 8); #endif if (TB_R8(ISTAT, SIGP)) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: SIGP set before wait reselect; jumping!\n"); #endif R32(DSP) = dest_addr; } else { state.wait_reselect = true; state.wait_jump = dest_addr; state.executing = false; } return; case 3: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: SET %s%s%s%s\n", R32(DSP) - 8, sc_carry ? "carry " : "", sc_target ? "target " : "", sc_ack ? "ack " : "", sc_atn ? "atn " : ""); #endif if (sc_ack) SB_R8(SOCL, ACK, true); if (sc_atn) { if (!TB_R8(SOCL, ATN)) { SB_R8(SOCL, ATN, true); // printf("SET ATN.\n"); // printf(">"); // getchar(); } } if (sc_target) SB_R8(SCNTL0, TRG, true); if (sc_carry) state.alu.carry = true; return; case 4: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: CLEAR %s%s%s%s\n", R32(DSP) - 8, sc_carry ? "carry " : "", sc_target ? "target " : "", sc_ack ? "ack " : "", sc_atn ? "atn " : ""); #endif if (sc_ack) SB_R8(SOCL, ACK, false); if (sc_atn) { if (TB_R8(SOCL, ATN)) { SB_R8(SOCL, ATN, false); // printf("RESET ATN.\n"); // printf(">"); // getchar(); } } if (sc_target) SB_R8(SCNTL0, TRG, false); if (sc_carry) state.alu.carry = false; return; break; } } /** * Execute one SCRIPTS R/W instruction * * The R/W instructions perform arithmetic or logic operations on * registers. The instruction format in the DCMD and DBC register is as follows: * * \code * +---+-----+-----+ * |7 6|5 4 3|2 1 0| DCMD Register * +---+-----+-----+ * | | +- 2..0: operator: * | | 000: data8 * | | 001: reg << 1 * | | 010: reg | data8 * | | 011: reg ^ data8 * | | 100: reg & data8 * | | 101: reg >> 1 * | | 110: reg + data8 * | | 111: reg + data8 + carry * | +- 3..5: Op Code * | 101: regA = operator(SFBR, data8) * | 110: SFBR = operator(RegA, data8) * | 111: regA = operator(RegA, data8) * +- 6..7 Instruction Type: 01 = R/W * * +--+------------++---------------++-+-------------+ * |23|22 16||15 8||7| | DBC Register * +--+------------++---------------++-+-------------+ * | A6--------A0 | A7 * | +--------------|---------+- 7,22..16: RegA address * | | * | +- 15..8: Immediate data * +- 23: Use data8/SFBR * 0: data8 = Immediate data * 1: data8 = SFBR * \endcode */ void CSym53C810::execute_rw_op() { int opcode = (R8(DCMD) >> 3) & 7; int oper = (R8(DCMD) >> 0) & 7; bool use_data8_sfbr = (GET_DBC() >> 23) & 1; int reg_address = ((GET_DBC() >> 16) & 0x7f); //| (GET_DBC() & 0x80); // manual is unclear about bit 7. u8 imm_data = (u8)(GET_DBC() >> 8) & 0xff; u8 op_data; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = R/W (opc %d, oper %d, use %d, add %d, imm %02x\n", opcode, oper, use_data8_sfbr, reg_address, imm_data); #endif if (use_data8_sfbr) imm_data = R8(SFBR); if (oper != 0) { if (opcode == 5 || reg_address == 0x08) { op_data = R8(SFBR); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: sfbr (%02x) ", R32(DSP) - 8, op_data); #endif } else { op_data = (u8)ReadMem_Bar(0, 1, reg_address, 8); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: reg%02x (%02x) ", R32(DSP) - 8, reg_address, op_data); #endif } } u16 tmp16; switch (oper) { case 0: op_data = imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: %02x ", R32(DSP) - 8, imm_data); #endif break; case 1: tmp16 = (op_data << 1) + (state.alu.carry ? 1 : 0); state.alu.carry = (tmp16 >> 8) & 1; op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("<< 1 = %02x ", op_data); #endif break; case 2: op_data |= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("| %02x = %02x ", imm_data, op_data); #endif break; case 3: op_data ^= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("^ %02x = %02x ", imm_data, op_data); #endif break; case 4: op_data &= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("& %02x = %02x ", imm_data, op_data); #endif break; case 5: tmp16 = (op_data >> 1) + (state.alu.carry ? 0x80 : 0x00); state.alu.carry = op_data & 1; op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf(">> 1 = %02x ", op_data); #endif break; case 6: tmp16 = op_data + imm_data; state.alu.carry = (tmp16 > 0xff); op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("+ %02x = %02x (carry %d) ", imm_data, op_data, state.alu.carry); #endif break; case 7: tmp16 = op_data + imm_data + (state.alu.carry ? 1 : 0); state.alu.carry = (tmp16 > 0xff); op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("+ %02x (w/carry) = %02x (carry %d) ", imm_data, op_data, state.alu.carry); #endif break; } if (opcode == 6 || reg_address == 0x08) { #if defined(DEBUG_SYM_SCRIPTS) printf("-> sfbr.\n"); #endif R8(SFBR) = op_data; } else { #if defined(DEBUG_SYM_SCRIPTS) printf("-> reg%02x.\n", reg_address); #endif WriteMem_Bar(0, 1, reg_address, 8, op_data); } } /** * Execute one SCRIPTS Transfer Control instruction * * The Transfer Control instructions perform conditional jumps, calls, * returns and interrupts. The instruction format in the DCMD and DBC * registers is as follows: * * \code * +---+-----+-----+ * |7 6|5 4 3|2 1 0| DCMD Register * +---+-----+-----+ * | | +- 0..2: SCSI Phase (I/O, C/D and MSG/ signals): * | | The actual SCSI phase is compared against these * | | bits. * | +- 3..5: Op Code * | 000: Jump * | 001: Call * | 010: Return * | 011: Interrupt * | 1xx: reserved * +- 6..7 Instruction Type: 10 = Transfer Control * * +--+-+--+--+--+--+--+--++---------------++---------------+ * |23| |21|20|19|18|17|16||15 8||7 0| DBC Register * +--+-+--+--+--+--+--+--++---------------++---------------+ * | | | | | | | | +- 7..0: Data to compare * | | | | | | | | against SFBR * | | | | | | | +- 15..8: Mask that determines what bits * | | | | | | | to compare against SFBR. * | | | | | | +- 16: Wait for valid SCSI phase * | | | | | +- 17: Compare Phase * | | | | +- 18: Compare SFBR data * | | | +- Jump if: * | | | 0: Jump/Call/return/Interrupt if the equation is true * | | | 0: Jump/Call/return/Interrupt if the equation is false * | | +- Interrupt on the Fly * | +- Carry Test * +- relative Addressing: * 0: The value in the DSPS register is an absolute address. * 1: The value in the DSPS register is a 24-bit signed * displacement from the current DSP address. * \endcode * * The equation evaluated is one of the following: * - the value of the carry bit (if the Carry Test bit is set) * - equality comparisons of SCSI phase and/or SFBR register data * - true (if none of the compare/carry test bits are set) * . * * An action is taken when the equation evaluates to either true or false * as determined by the "Jump if" bit. * * Jump: * - Jump to the instruction addressed by the DSPS register. * . * * Call: * - Store the current DSP register value to the TEMP register. * - Jump to the instruction addressed by the DSPS register. * . * * Return: * - Jump to the instruction addressed by the TEMP register. * . * * Interrupt: * - If the Interrupt on the Fly bit is set, raise the INTF interrupt. * - Otherwise: * - Raise the SIR interrupt * - Terminate SCRIPTS execution * - The DSPS value is used as an interrupt vector for the driver. * . * . **/ void CSym53C810::execute_tc_op() { int opcode = (R8(DCMD) >> 3) & 7; int scsi_phase = (R8(DCMD) >> 0) & 7; bool relative = (GET_DBC() >> 23) & 1; bool carry_test = (GET_DBC() >> 21) & 1; bool interrupt_fly = (GET_DBC() >> 20) & 1; bool jump_if = (GET_DBC() >> 19) & 1; bool cmp_data = (GET_DBC() >> 18) & 1; bool cmp_phase = (GET_DBC() >> 17) & 1; int cmp_mask = (GET_DBC() >> 8) & 0xff; int cmp_dat = (GET_DBC() >> 0) & 0xff; u32 dest_addr; // wait_valid can be safely ignored, phases are always valid in this ideal // world... bool wait_valid = (GET_DBC()>>16) & 1; // We'll keep modifying this variable until we know what the result of the // comparisons is. bool do_it; // Relative jump or not? (no effect on Return or Interrupt) if (relative) dest_addr = R32(DSP) + sext_u32_24(R32(DSPS)); else dest_addr = R32(DSPS); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: if (", R32(DSP) - 8); #endif if (carry_test) { // All we need to check is the CARRY flag #if defined(DEBUG_SYM_SCRIPTS) printf("(%scarry)", jump_if ? "" : "!"); #endif do_it = (state.alu.carry == jump_if); } else if (cmp_data || cmp_phase) { // We need to compare data and/or phase do_it = true; if (cmp_data) { // compare data #if defined(DEBUG_SYM_SCRIPTS) printf("((data & 0x%02x) %s 0x%02x)", (~cmp_mask) & 0xff, jump_if ? "==" : "!=", cmp_dat & ~cmp_mask); #endif if (((R8(SFBR) & ~cmp_mask) == (cmp_dat & ~cmp_mask)) != jump_if) do_it = false; #if defined(DEBUG_SYM_SCRIPTS) if (cmp_phase) printf(" && "); #endif } if (cmp_phase) { // Compare phase #if defined(DEBUG_SYM_SCRIPTS) printf("(phase %s %d)", jump_if ? "==" : "!=", scsi_phase); #endif if ((check_phase(scsi_phase) > 0) != jump_if) do_it = false; } } else { // no comparison do_it = jump_if; } #if defined(DEBUG_SYM_SCRIPTS) printf(") "); #endif switch (opcode) { case 0: #if defined(DEBUG_SYM_SCRIPTS) printf("jump %x\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Jumping %08x...\n", dest_addr); #endif R32(DSP) = dest_addr; } return; break; case 1: #if defined(DEBUG_SYM_SCRIPTS) printf("call %d\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Calling %08x...\n", dest_addr); #endif R32(TEMP) = R32(DSP); R32(DSP) = dest_addr; } return; break; case 2: #if defined(DEBUG_SYM_SCRIPTS) printf("return %d\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Returning %08x...\n", R32(TEMP)); #endif R32(DSP) = R32(TEMP); } return; break; case 3: #if defined(DEBUG_SYM_SCRIPTS) printf("interrupt%s.\n", interrupt_fly ? " on the fly" : ""); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Interrupt with vector %x...\n", R32(DSPS)); #endif if (interrupt_fly) RAISE(ISTAT, INTF); else RAISE(DSTAT, SIR); } return; break; default: FAILURE_1(NotImplemented, "SYM: Transfer Control Instruction with opcode %d is RESERVED.\n", opcode); } } /** * Execute one SCRIPTS Load/Store instruction * * The Load/Store instruction moves data between registers and memory. * The memory range could very well map back to the registers through * the PCI bus! The instruction format in the DCMD and DBC registers * is as follows: * * \code * +-----+-+---+-+-+ * |7 6 5|4| |1|0| DCMD Register * +-----+-+---+-+-+ * | | | +- 0: Load/Store * | | | 0: Store (register -> memory) * | | | 1: Load (memory -> register) * | | +- 1: No Flush (no effect) * | +- 4: DSA Relative * | 0: The value in the DSPS register is absolute. * | 1: The value in the DSPS register is a 24-bit * | signed offset from DSA. * +- 7..5 Instruction Type: 111 = Load/Store * * +---------------++---------------++---------------+ * |23 16|| || |2 0| DBC Register * +---------------++---------------++---------------+ * | +- 2..0: Byte Count * +- 23..16: Register Address * \endcode * * This instructions moves up to 4 bytes between registers and memory. **/ void CSym53C810::execute_ls_op() { bool is_load = (R8(DCMD) >> 0) & 1; bool dsa_relative = (R8(DCMD) >> 4) & 1; int regaddr = (GET_DBC() >> 16) & 0x7f; int byte_count = (GET_DBC() >> 0) & 7; u32 memaddr; // Relative Addressing if (dsa_relative) memaddr = R32(DSA) + sext_u32_24(R32(DSPS)); else memaddr = R32(DSPS); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: dsa_rel: %d, DSA: %04x, DSPS: %04x, mem %04x.\n", dsa_relative, R32(DSA), R32(DSPS), memaddr); #endif if (is_load) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Load reg%02x", R32(DSP) - 8, regaddr); if (byte_count > 1) printf("..%02x", regaddr + byte_count - 1); printf("from %x.\n", memaddr); #endif // Perform Load Operation for (int i = 0; i < byte_count; i++) { u8 dat; do_pci_read(memaddr + i, &dat, 1, 1); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %02x -> reg%02x\n", dat, regaddr + i); #endif WriteMem_Bar(0, 1, regaddr + i, 8, dat); } } else { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Store reg%02x", R32(DSP) - 8, regaddr); if (byte_count > 1) printf("..%02x", regaddr + byte_count - 1); printf("to %x.\n", memaddr); #endif // Perform Store Operation for (int i = 0; i < byte_count; i++) { u8 dat = (u8)ReadMem_Bar(0, 1, regaddr + i, 8); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %02x <- reg%02x\n", dat, regaddr + i); #endif do_pci_write(memaddr + i, &dat, 1, 1); } } } /** * Execute one SCRIPTS Memory Move instruction * * The Memory Move instruction is used to transfer data from one * region in host memory to another region through the SCSI * controller's. DMA. The instruction format in the DCMD register * is as follows: * * \code * +-----+-------+-+ * |7 6 5| |0| DCMD Register * +-----+-------+-+ * | +- No Flush (no effect) * +- 7..5 Instruction Type: 110 = Memory Move * \endcode * * This is a 3-DWORD instruction. * * The DBC register holds the number of bytes to be moved. * The DSPS register holds the source address. * The TEMP register holds the destination address. */ void CSym53C810::execute_mm_op() { u32 temp_shadow; do_pci_read(R32(DSP), &temp_shadow, 4, 1); R32(DSP) += 4; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Memory Move %06x bytes from %08x to %08x.\n", R32(DSP) - 12, GET_DBC(), R32(DSPS), temp_shadow); #endif // To speed things up, we set up a buffer and read all data // at once, followed by writing all data at once. void *buf = malloc(GET_DBC()); do_pci_read(R32(DSPS), buf, 1, GET_DBC()); do_pci_write(temp_shadow, buf, 1, GET_DBC()); free(buf); return; } /** * Execute one SCRIPTS instruction. * * DSP (DMA Scripts Pointer) contains the address of the next instruction. * For each instruction, two DWORDS are read into the 8-bit DCMD (DMA Command), * 24-bit DBC (DMA Byte Counter), and 32-bit DSPS (DMA Scripts Pointer Save) * registers. For some commands, a third DWORD is read into the 32-bit TEMP * register: * * \code * +--------+------------------------+ * DSP : | DCMD | DBC | * +--------+------------------------+ * DSP+4: | DSPS | * +---------------------------------+ * DSP+8: |/ / / / / / / TEMP / / / / / / /| * +---------------------------------+ * \endcode **/ void CSym53C810::execute() { int optype; int opcode; bool is_load_store; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS @ %x \n", R32(DSP)); #endif // Read 2 DWORDS into the DCMD, DBC and DSPS registers. do_pci_read(R32(DSP), &R32(DBC), 4, 1); do_pci_read(R32(DSP) + 4, &R32(DSPS), 4, 1); // Increase DSP to point to the next instruction R32(DSP) += 8; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = %x, %x, %x \n", R8(DCMD), GET_DBC(), R32(DSPS)); #endif /* The two most significant bits of the DCMD register determine the operation * type. These are: * 00: Block Move * 01: I/O or R/W * 10: Transfer Control * 11: Memory Move or Load And Store */ optype = (R8(DCMD) >> 6) & 3; switch (optype) { case 0: execute_bm_op(); break; case 1: opcode = (R8(DCMD) >> 3) & 7; if (opcode < 5) execute_io_op(); else execute_rw_op(); break; case 2: execute_tc_op(); break; case 3: is_load_store = (R8(DCMD) >> 5) & 1; if (is_load_store) execute_ls_op(); else execute_mm_op(); break; } // single step mode if (TB_R8(DCNTL, SSM)) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Single step...\n"); #endif RAISE(DSTAT, SSI); } } /** * Set an interrupt bit. * * This function checks if any other interrupt bits are active, if so, * the interrupt bit is set in the stacked interrupt registers. Otherwise * it goes straight to the respective interrupt register. * * According to the datasheet: * "The SYM53C895 stacks interrupts if they occur one after another. If the SIP *or DIP bits in the ISTAT register are set (first level), then there is already *at least one pending interrupt, and any future interrupts will be stacked in *extra registers behind the SIST0, SIST1, and DSTAT registers (second level). *When two interrupts have occurred and the two levels of the stack are full, *any further interrupts will set additional bits in the extra registers behind *SIST0, SIST1 and DSTAT. When the first level of interrupts are cleared, all *the interrupts that came in afterward will move into the SIST0, SIST1 and *DSTAT. After the first interrupt is cleared by reading the appropriate *register, the IRQ/ pin will be deasserted for a minimum of three CLKs; the *stacked interrupt(s) will move into the SIST0, SIST1 or DSTAT; and the IRQ/ *pin will be asserted once again. * * Since a masked non-fatal interrupt will not set the SIP or DIP bits, *interrupt stacking will not occur. A masked, non-fatal interrupt will still *post the interrupt in SIST0, but will not assert the IRQ/ pin. Since no *interrupt is generated, future interrupts will move right into the SIST0 or *SIST1 instead of being stacked behind another interrupt. When another *condition occurs that generates an interrupt, the bit corresponding to the *earlier masked non-fatal interrupt will still be set." **/ void CSym53C810::set_interrupt(int reg, u8 interrupt) { // printf("set interrupt %02x, %02x.\n",reg,interrupt); switch (reg) { case R_DSTAT: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.dstat_stack |= interrupt; // printf("DSTAT stacked.\n"); } else { R8(DSTAT) |= interrupt; // printf("DSTAT.\n"); } break; case R_SIST0: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.sist0_stack |= interrupt; // printf("SIST0 stacked.\n"); } else { R8(SIST0) |= interrupt; // printf("SIST0.\n"); } break; case R_SIST1: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.sist1_stack |= interrupt; // printf("SIST1 stacked.\n"); } else { R8(SIST1) |= interrupt; // printf("SIST1.\n"); } break; case R_ISTAT: // printf("ISTAT.\n"); R8(ISTAT) |= interrupt; break; default: FAILURE_1(NotImplemented, "set_interrupt reg %02x!!\n", reg); } // printf("--> eval int\n"); eval_interrupts(); // printf("<-- eval_int\n"); } /** * Evaluate interrupt status. * * Check interrupt registers, and determine if an interrupt should be generated. **/ void CSym53C810::eval_interrupts() { // will_assert: when this boolean value is true at the end of this function, // an interrupt will be signalled to the system. bool will_assert = false; // will_halt: when this boolean value is true at the end of this function, // program execution will be halted. (fatal interrupt) bool will_halt = false; // Check current interrupt status. If no interrupt is active, move interrupt // flags from the interrupt stack down. // // (When an interrupt is signalled, but another interrupt bit is already // active, the interrupt doesn't go to the interrupt register, but to the // interrupt stack. The interrupt stack, however, doesn't keep track of the // order in which interrupts come in, so when the interrupt stack is moved // down into the interrupt registers, multiple interrupt bits may become // active.) if (!R8(SIST0) && !R8(SIST1) && !R8(DSTAT)) { R8(SIST0) |= state.sist0_stack; R8(SIST1) |= state.sist1_stack; R8(DSTAT) |= state.dstat_stack; state.sist0_stack = 0; state.sist1_stack = 0; state.dstat_stack = 0; } // Check for DMA interrupts. if (R8(DSTAT) & DSTAT_FATAL) { // DMA interrupt conditions always halt execution (always fatal) will_halt = true; // printf(" will halt(DSTAT).\n"); // Set the DMA interrupt pending bit. SB_R8(ISTAT, DIP, true); // If the interrupt is also enabled in the DIEN register, it will // be signalled to the system. if (R8(DSTAT) & R8(DIEN) & DSTAT_FATAL) { will_assert = true; // printf(" will assert(DSTAT).\n"); } } else { // Reset the DMA interrupt pending bit. (It may still be set). SB_R8(ISTAT, DIP, false); } // Check for SCSI engine interrupts if (R8(SIST0) || R8(SIST1)) { // Set the SCSI interrupt pending bit. SB_R8(ISTAT, SIP, true); // Check if the interrupt is either fatal, or enabled. if ((R8(SIST0) & (SIST0_FATAL | R8(SIEN0))) || (R8(SIST1) & (SIST1_FATAL | R8(SIEN1)))) { // In either case, stop execution. will_halt = true; // printf(" will halt(SIST).\n"); // If the interrupt is enabled, signal it to the system. if ((R8(SIST0) & R8(SIEN0)) || (R8(SIST1) & R8(SIEN1))) { will_assert = true; // printf(" will assert(SIST).\n"); } } } else { // Reset the SCSI interrupt pending bit (It may still be set). SB_R8(ISTAT, SIP, false); } // Check the interrupt on the fly bit. if (TB_R8(ISTAT, INTF)) { // Signal this to the system will_assert = true; // printf(" will assert(INTF).\n"); } // If interrupts are disabled, don't signal any interrupt to the system. if (TB_R8(DCNTL, IRQD)) { will_assert = false; // printf(" won't assert(IRQD).\n"); } // Halt execution if will_halt is true. if (will_halt) state.executing = false; // Assert or de-assert the interrupt line as needed if (will_assert != state.irq_asserted) { // printf(" doing...%d\n",will_assert); do_pci_interrupt(0, will_assert); state.irq_asserted = will_assert; } } ================================================ FILE: src/Sym53C810.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_SYM53C810_H_) #define INCLUDED_SYM53C810_H_ #include "DiskController.hpp" #include "PCIDevice.hpp" #include "SCSIDevice.hpp" /** * \brief Symbios Sym53C810 SCSI disk controller. * * \bug Exception below ASTDEL during OpenVMS boot when booting from SCSI. * * Documentation consulted: * - SCSI 2 (http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf) * - SCSI 3 Multimedia Commands (MMC) *(http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf) * - SYM53C810A PCI-SCSI I/O Processor *(http://ftp.netbsd.org/pub/NetBSD/arch/bebox/doc/810a.pdf) * - Symbios SCSI SCRIPTS Processors Programming Guide *(http://la.causeuse.org/hauke/macbsd/symbios_53cXXX_doc/lsilogic-53cXXX-scripts.pdf) * . **/ class CSym53C810 : public CPCIDevice, public CDiskController, public CSCSIDevice { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); void run(); virtual void init(); virtual void start_threads(); virtual void stop_threads(); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); virtual u32 config_read_custom(int func, u32 address, int dsize, u32 data); virtual void config_write_custom(int func, u32 address, int dsize, u32 old_data, u32 new_data, u32 data); virtual void register_disk(class CDisk *dsk, int bus, int dev); CSym53C810(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CSym53C810(); private: void write_b_scntl0(u8 value); void write_b_scntl1(u8 value); void write_b_istat(u8 value); u8 read_b_ctest2(); void write_b_ctest3(u8 value); void write_b_ctest4(u8 value); void write_b_ctest5(u8 value); void write_b_stest2(u8 value); void write_b_stest3(u8 value); u8 read_b_dstat(); u8 read_b_sist(int id); void write_b_dcntl(u8 value); void post_dsp_write(); int check_phase(int chk_phase); void execute_io_op(); void execute_rw_op(); void execute_ls_op(); void execute_mm_op(); void execute_tc_op(); void execute_bm_op(); void execute(); void eval_interrupts(); void set_interrupt(int reg, u8 interrupt); void chip_reset(); std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; CSemaphore mySemaphore; CMutex *myRegLock; bool StopThread; /// The state structure contains all elements that need to be saved to the /// statefile. struct SSym_state { bool irq_asserted; union USym_regs { u8 reg8[128]; u16 reg16[64]; u32 reg32[64]; } regs; struct SSym_alu { bool carry; } alu; u8 ram[4096]; bool executing; bool wait_reselect; bool select_timeout; int disconnected; u32 wait_jump; u8 dstat_stack; u8 sist0_stack; u8 sist1_stack; long gen_timer; // int phase; } state; }; #endif // !defined(INCLUDED_SYM_H) ================================================ FILE: src/Sym53C895.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(DEBUG_SYM) #define DEBUG_SYM_REGS #define DEBUG_SYM_SCRIPTS #endif #include "Sym53C895.hpp" #include "Disk.hpp" #include "SCSIBus.hpp" #include "StdAfx.hpp" #include "System.hpp" /// Register 00: SCNTL0: SCSI Control 0 #define R_SCNTL0 0x00 #define R_SCNTL0_ARB1 0x80 #define R_SCNTL0_ARB0 0x40 #define R_SCNTL0_START 0x20 #define R_SCNTL0_WATN 0x10 #define R_SCNTL0_EPC 0x08 #define R_SCNTL0_AAP 0x02 #define R_SCNTL0_TRG 0x01 #define SCNTL0_MASK 0xFB /// Register 01: SCNTL1: SCSI Control 1 #define R_SCNTL1 0x01 #define R_SCNTL1_CON 0x10 #define R_SCNTL1_RST 0x08 #define R_SCNTL1_IARB 0x02 /// Register 02: SCNTL2: SCSI Control 2 #define R_SCNTL2 0x02 #define R_SCNTL2_SDU 0x80 #define R_SCNTL2_CHM 0x40 #define R_SCNTL2_SLPMD 0x20 #define R_SCNTL2_SLPHBEN 0x10 #define R_SCNTL2_WSS 0x08 #define R_SCNTL2_VUE0 0x04 #define R_SCNTL2_VUE1 0x02 #define R_SCNTL2_WSR 0x01 #define SCNTL2_MASK 0xF2 #define SCNTL2_W1C 0x09 /// Register 03: SCNTL3: SCSI Control 3 #define R_SCNTL3 0x03 #define R_SCNTL3_EWS 0x08 /// Register 04: SCID: SCSI Chip ID #define R_SCID 0x04 #define R_SCID_ID 0x0F #define SCID_MASK 0x6F /// Register 05: SXFER: SCSI Transfer #define R_SXFER 0x05 /// Register 06: SDID: SCSI Destination ID #define R_SDID 0x06 #define R_SDID_ID 0x0F #define SDID_MASK 0x0F /// Register 07: GPREG: General Purpose #define R_GPREG 0x07 #define GPREG_MASK 0x1F /// Register 08: SFBR: SCSI First Byte REceived #define R_SFBR 0x08 /// Register 09: SOCL: SCSI Output Control Latch #define R_SOCL 0x09 #define R_SOCL_ACK 0x40 #define R_SOCL_ATN 0x20 /// Register 0A: SSID: SCSI Selector ID #define R_SSID 0x0A #define R_SSID_VAL 0x80 #define R_SSID_ID 0x0F /// Register 0B: SBCL: SCSI Bus Control Lines #define R_SBCL 0x0B #define R_SBCL_REQ 0x80 #define R_SBCL_ACK 0x40 #define R_SBCL_BSY 0x20 #define R_SBCL_SEL 0x10 #define R_SBCL_ATN 0x08 #define R_SBCL_MSG 0x04 #define R_SBCL_CD 0x02 #define R_SBCL_IO 0x01 #define R_SBCL_PHASE 0x07 /// Register 0C: DSTAT: DMA Status #define R_DSTAT 0x0C #define R_DSTAT_DFE 0x80 #define R_DSTAT_MDPE 0x40 #define R_DSTAT_BF 0x20 #define R_DSTAT_ABRT 0x10 #define R_DSTAT_SSI 0x08 #define R_DSTAT_SIR 0x04 #define R_DSTAT_IID 0x01 #define DSTAT_RC 0x7D #define DSTAT_FATAL 0x7D /// Register 0D: SSTAT0: SCSI Status 0 #define R_SSTAT0 0x0D #define R_SSTAT0_RST 0x02 #define R_SSTAT0_SDP0 0x01 /// Register 0E: SSTAT1: SCSI Status 1 #define R_SSTAT1 0x0E #define R_SSTAT1_SDP1 0x01 /// Register 0F: SSTAT2: SCSI Status 2 #define R_SSTAT2 0x0F #define R_SSTAT2_LDSC 0x02 /// Register 10..13: DSA: Data Structure Address #define R_DSA 0x10 /// Register 14: ISTAT: Interrupt Status #define R_ISTAT 0x14 #define R_ISTAT_ABRT 0x80 #define R_ISTAT_SRST 0x40 #define R_ISTAT_SIGP 0x20 #define R_ISTAT_SEM 0x10 #define R_ISTAT_CON 0x08 #define R_ISTAT_INTF 0x04 #define R_ISTAT_SIP 0x02 #define R_ISTAT_DIP 0x01 #define ISTAT_MASK 0xF0 #define ISTAT_W1C 0x04 /// Register 18: CTEST0: Chip Test 0 #define R_CTEST0 0x18 /// Register 19: CTEST1: Chip Test 1 #define R_CTEST1 0x19 #define R_CTEST1_FMT 0xF0 #define R_CTEST1_FFL 0x0F /// Register 1A: CTEST2: Chip Test 2 #define R_CTEST2 0x1A #define R_CTEST2_DDIR 0x80 #define R_CTEST2_SIGP 0x40 #define R_CTEST2_CIO 0x20 #define R_CTEST2_CM 0x10 #define R_CTEST2_SRTCH 0x08 #define R_CTEST2_TEOP 0x04 #define R_CTEST2_DREQ 0x02 #define R_CTEST2_DACK 0x01 #define CTEST2_MASK 0x08 /// Register 1B: CTEST3: Chip Test 3 #define R_CTEST3 0x1B #define R_CTEST3_REV 0xf0 #define R_CTEST3_FLF 0x08 #define R_CTEST3_CLF 0x04 #define R_CTEST3_FM 0x02 #define CTEST3_MASK 0x0B /// Register 1C..1F: TEMP: Temporary #define R_TEMP 0x1C /// Register 20: DFIFO: DMA FIFO #define R_DFIFO 0x20 /// Register 21: CTEST4: Chip Test 4 #define R_CTEST4 0x21 /// Register 22: CTEST5: Chip Test 5 #define R_CTEST5 0x22 #define R_CTEST5_ADCK 0x80 #define R_CTEST5_BBCK 0x40 #define CTEST5_MASK 0x3F /// Register 23: CTEST6: Chip Test 6 #define R_CTEST6 0x23 /// Register 24..26: DBC: DMA Byte Counter #define R_DBC 0x24 /// Register 27: DCMD: DMA Command #define R_DCMD 0x27 /// Register 28..2B: DNAD: DMA Next Address #define R_DNAD 0x28 /// Register 2C..2F: DSP: DMA SCRIPTS Pointer #define R_DSP 0x2C /// Register 30..33: DSPS: DMA SCRIPTS Pointer Save #define R_DSPS 0x30 /// Register 34..37: SCRATCHA: Scratch Register A #define R_SCRATCHA 0x34 /// Register 38: DMODE: DMA Mode #define R_DMODE 0x38 #define R_DMODE_MAN 0x01 /// Register 39: DIEN: DMA Interrupt Enable #define R_DIEN 0x39 #define DIEN_MASK 0x7D /// Register 3A: SBR: Scratch Byte Register #define R_SBR 0x3A /// Register 3B: DCNTL: DMA Control #define R_DCNTL 0x3B #define R_DCNTL_SSM 0x10 #define R_DCNTL_STD 0x04 #define R_DCNTL_IRQD 0x02 #define R_DCNTL_COM 0x01 #define DCNTL_MASK 0xFB /// Register 3C..37: ADDER: Adder Sum Output #define R_ADDER 0x3C /// Register 40: SIEN0: SCSI Interrupt Enable 0 #define R_SIEN0 0x40 #define SIEN0_MASK 0xFF /// Register 41: SIEN1: SCSI Interrupt Enable 1 #define R_SIEN1 0x41 #define SIEN1_MASK 0x17 /// Register 42: SIST0: SCSI Interrupt Status 0 #define R_SIST0 0x42 #define R_SIST0_MA 0x80 #define R_SIST0_CMP 0x40 #define R_SIST0_SEL 0x20 #define R_SIST0_RSL 0x10 #define R_SIST0_SGE 0x08 #define R_SIST0_UDC 0x04 #define R_SIST0_RST 0x02 #define R_SIST0_PAR 0x01 #define SIST0_RC 0xFF #define SIST0_FATAL 0x8F /// Register 43: SIST1: SCSI Interrupt Status 1 #define R_SIST1 0x43 #define R_SIST1_SBMC 0x10 #define R_SIST1_STO 0x04 #define R_SIST1_GEN 0x02 #define R_SIST1_HTH 0x01 #define SIST1_RC 0x17 #define SIST1_FATAL 0x14 /// Register 44: SLPAR: SCSI Longitudinal Parity #define R_SLPAR 0x44 /// Register 45: SWIDE: SCSI Wide Residue #define R_SWIDE 0x45 /// Register 46: MACNTL: Memory Access Control #define R_MACNTL 0x46 #define MACNTL_MASK 0x0F /// Register 47: GPCNTL: General Purpose Pin Control #define R_GPCNTL 0x47 /// Register 48: STIME0: SCSI Timer 0 #define R_STIME0 0x48 /// Register 49: STIME1: SCSI Timer 1 #define R_STIME1 0x49 #define R_STIME1_GEN 0x0F #define STIME1_MASK 0x7F /// Register 4A..4B: RESPID: SCSI Response ID #define R_RESPID 0x4A /// Register 4C: STEST0: SCSI Test 0 #define R_STEST0 0x4C /// Register 4D: STEST1: SCSI Test 1 #define R_STEST1 0x4D #define STEST1_MASK 0xCC /// Register 4E: STEST2: SCSI Test 2 #define R_STEST2 0x4E #define R_STEST2_SCE 0x80 #define R_STEST2_ROF 0x40 #define R_STEST2_DIF 0x20 #define R_STEST2_SLB 0x10 #define R_STEST2_SZM 0x08 #define R_STEST2_AWS 0x04 #define R_STEST2_EXT 0x02 #define R_STEST2_LOW 0x01 #define STEST2_MASK 0xBF /// Register 4F: STEST3: SCSI Test 3 #define R_STEST3 0x4F #define R_STEST3_TE 0x80 #define R_STEST3_STR 0x40 #define R_STEST3_HSC 0x20 #define R_STEST3_DSI 0x10 #define R_STEST3_S16 0x08 #define R_STEST3_TTM 0x04 #define R_STEST3_CSF 0x02 #define R_STEST3_STW 0x01 #define STEST3_MASK 0xFF /// Register 50..51: SIDL: SCSI Input Data Latch #define R_SIDL 0x50 /// Register 52: STEST4: SCSI Test 4 #define R_STEST4 0x52 /// Register 54..55: SODL: SCSI Output Data Latch #define R_SODL 0x54 /// Register 58..59: SBDL: SCSI Bus Data Lines #define R_SBDL 0x58 /// Registers 5C..5F: SCRATCHB: Scratch Register B #define R_SCRATCHB 0x5C /// Register 60..7F: SCRATCHC..SCRATCHJ: Scratch Register C..J #define R_SCRATCHC 0x60 /// Acces an 8-byte register #define R8(a) state.regs.reg8[R_##a] /// Acces a 16-byte register #define R16(a) state.regs.reg16[R_##a / 2] /// Access a 32-byte register #define R32(a) state.regs.reg32[R_##a / 4] /** * Test bit in register * * \param a is the name of the register * \param b is the name of the bit **/ #define TB_R8(a, b) ((R8(a) & R_##a##_##b) == R_##a##_##b) /** * Set bit in register. * * \param a is the name of the register * \param b is the name of the bit * \param c is the value for the bit **/ #define SB_R8(a, b, c) R8(a) = (R8(a) & ~R_##a##_##b) | (c ? R_##a##_##b : 0) /** * Write to a register, using a mask * * \param a is the name of the register * \param b is the value to write. * * Only those bits that are set to 1 in _MASK will be changed. **/ #define WRM_R8(a, b) R8(a) = (R8(a) & ~a##_MASK) | ((b)&a##_MASK) /** * Write to a register, using a mask, and using write-1-to-clear bits * * \param a is the name of the register * \param b is the value to write. * * Only those bits that are set to 1 in _MASK will be changed. * In addition, bits that are set to 1 in _W1C will be cleared * in the register if they are set to 1 in the value. **/ #define WRMW1C_R8(a, b) \ R8(a) = \ (R8(a) & ~a##_MASK & ~a##_W1C) | ((b)&a##_MASK) | (R8(a) & ~(b)&a##_W1C) /** * Raise an interrupt * * \param a is the name of the interrupt register * \param b is the name of the bit to set **/ #define RAISE(a, b) set_interrupt(R_##a, R_##a##_##b) /** * Clear read-to-clear-bits * * \param a is the name of the register * * Clear bits that are set to 1 in _RC **/ #define RDCLR_R8(a) R8(a) &= ~a##_RC /** * Get the SCSI destination ID from the SDID register **/ #define GET_DEST() (R8(SDID) & R_SCID_ID) /** * Set the SCSI destination ID in the SDID register **/ #define SET_DEST(a) R8(SDID) = (a)&R_SCID_ID /** * Get the value of the DBC register (24-bits) **/ #define GET_DBC() (R32(DBC) & 0x00ffffff) /** * Set the value of the DBC register (24-bits) **/ #define SET_DBC(a) R32(DBC) = (R32(DBC) & 0xff000000) | ((a)&0x00ffffff) /** * PCI Configuration Data Block **/ u32 sym_cfg_data[64] = { /*00*/ 0x000c1000, // CFID: vendor + device /*04*/ 0x02000001, // CFCS: command + status /*08*/ 0x01000000, // CFRV: class + revision /*0c*/ 0x00000000, // CFLT: latency timer + cache line size /*10*/ 0x00000001, // BAR0: IO Space /*14*/ 0x00000000, // BAR1: Memory space /*18*/ 0x00000000, // BAR2: RAM space /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x401101ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * PCI Configuration Mask Block **/ u32 sym_cfg_mask[64] = { /*00*/ 0x00000000, // CFID: vendor + device /*04*/ 0x00000157, // CFCS: command + status /*08*/ 0x00000000, // CFRV: class + revision /*0c*/ 0x0000ffff, // CFLT: latency timer + cache line size /*10*/ 0xffffff00, // BAR0: IO space (256 bytes) /*14*/ 0xffffff00, // BAR1: Memory space (256 bytes) /*18*/ 0xfffff000, // BAR2: RAM space (4KB) /*1c*/ 0x00000000, // BAR3: /*20*/ 0x00000000, // BAR4: /*24*/ 0x00000000, // BAR5: /*28*/ 0x00000000, // CCIC: CardBus /*2c*/ 0x00000000, // CSID: subsystem + vendor /*30*/ 0x00000000, // BAR6: expansion rom base /*34*/ 0x00000000, // CCAP: capabilities pointer /*38*/ 0x00000000, /*3c*/ 0x000000ff, // CFIT: interrupt configuration 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /** * Thread entry point. * * Repeat: * - Waiting until the semaphore is set * - Executing SCRIPTS code until execution ends. * . **/ void CSym53C895::run() { try { for (;;) { mySemaphore.wait(); if (StopThread) return; while (state.executing) { MUTEX_LOCK(myRegLock); execute(); MUTEX_UNLOCK(myRegLock); } } } catch (CException &e) { printf("Exception in SYM thread: %s.\n", e.displayText().c_str()); myThreadDead.store(true); // Let the thread die... } } /** * Constructor. * * Set up the SCSI bus, and defer the rest of initialization to * CSym53C895::init. **/ CSym53C895::CSym53C895(CConfigurator *cfg, CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev), CDiskController(1, 16), mySemaphore(0, 1) { // create scsi bus CSCSIBus *a = new CSCSIBus(cfg, c); scsi_register(0, a, 7); // scsi id 7 by default } /** * Initialize the Symbios device. * * Reset PCI structures, reset the chipset, and set up locks. **/ void CSym53C895::init() { add_function(0, sym_cfg_data, sym_cfg_mask); ResetPCI(); chip_reset(); myRegLock = new CMutex("sym-reg"); printf("%s: $Id: Sym53C895.cpp,v 1.35 2008/05/31 15:47:14 iamcamiel Exp $\n", devid_string); } /** * Create the thread, and start executing it. **/ void CSym53C895::start_threads() { if (!myThread) { printf(" sym"); StopThread = false; myThread = std::make_unique([this](){ this->run(); }); if (state.executing) mySemaphore.set(); } } /** * Stop and destroy the thread. **/ void CSym53C895::stop_threads() { StopThread = true; if (myThread) { printf(" sym"); mySemaphore.set(); myThread->join(); myThread = nullptr; } } /** * Destructor. * * Kill thread if still running, and destroy the SCSI bus. **/ CSym53C895::~CSym53C895() { stop_threads(); delete scsi_bus[0]; } /** * Reset the chipset. * * Initialize all registers to their default values. **/ void CSym53C895::chip_reset() { state.executing = false; state.wait_reselect = false; state.irq_asserted = false; state.gen_timer = 0; memset(state.regs.reg32, 0, sizeof(state.regs.reg32)); R8(SCNTL0) = R_SCNTL0_ARB1 | R_SCNTL0_ARB0; // 810 R8(DSTAT) = R_DSTAT_DFE; // DMA FIFO empty // 810 // R8(SSTAT2) = R_SSTAT2_LDSC; // 810 R8(CTEST1) = R_CTEST1_FMT; // 810 R8(CTEST2) = R_CTEST2_DACK; // 810 R8(CTEST3) = (u8)(pci_state.config_data[0][2] << 4) & R_CTEST3_REV; // Chip rev. R8(MACNTL) = 0xD0; // 895 type ID R8(GPCNTL) = 0x0F; // 810 R8(STEST0) = 0x03; // 810 } /** * Register a disk * * Attach the disk to the SCSI bus. **/ void CSym53C895::register_disk(class CDisk *dsk, int bus, int dev) { CDiskController::register_disk(dsk, bus, dev); dsk->scsi_register(0, scsi_bus[0], dev); } /// Magic number 1 for save/restore state static u32 sym_magic1 = 0x53C895CC; /// Magic number 2 for save/restore state static u32 sym_magic2 = 0xCC53C895; /** * Save state to a Virtual Machine State file. **/ int CSym53C895::SaveState(FILE *f) { long ss = sizeof(state); int res; if ((res = CPCIDevice::SaveState(f))) return res; fwrite(&sym_magic1, sizeof(u32), 1, f); fwrite(&ss, sizeof(long), 1, f); fwrite(&state, sizeof(state), 1, f); fwrite(&sym_magic2, sizeof(u32), 1, f); printf("%s: %d bytes saved.\n", devid_string, (int)ss); return 0; } /** * Restore state from a Virtual Machine State file. **/ int CSym53C895::RestoreState(FILE *f) { long ss; u32 m1; u32 m2; int res; size_t r; if ((res = CPCIDevice::RestoreState(f))) return res; r = fread(&m1, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m1 != sym_magic1) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } r = fread(&ss, sizeof(long), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (ss != sizeof(state)) { printf("%s: STRUCT SIZE does not match!\n", devid_string); return -1; } r = fread(&state, sizeof(state), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } r = fread(&m2, sizeof(u32), 1, f); if (r != 1) { printf("%s: unexpected end of file!\n", devid_string); return -1; } if (m2 != sym_magic2) { printf("%s: MAGIC 1 does not match!\n", devid_string); return -1; } printf("%s: %d bytes restored.\n", devid_string, (int)ss); return 0; } /** * write data to one of the PCI BAR (relocatable) address ranges. **/ void CSym53C895::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data) { void *p; switch (bar) { case 0: case 1: address &= 0x7f; switch (dsize) { case 8: MUTEX_LOCK(myRegLock); #if defined(DEBUG_SYM_REGS) printf("SYM: Write to register %02x: %02x. \n", address, data); #endif if (address >= R_SCRATCHC) { state.regs.reg8[address] = (u8)data; MUTEX_UNLOCK(myRegLock); break; } switch (address) { // SIMPLE CASES: JUST WRITE case R_SXFER: // 05 case R_DSA: // 10 case R_DSA + 1: // 11 case R_DSA + 2: // 12 case R_DSA + 3: // 13 case R_CTEST0: // 18 case R_TEMP: // 1C case R_TEMP + 1: // 1D case R_TEMP + 2: // 1E case R_TEMP + 3: // 1F case R_DSP: // 2C case R_DSP + 1: // 2D case R_DSP + 2: // 2E case R_DSPS: // 30 case R_DSPS + 1: // 31 case R_DSPS + 2: // 32 case R_DSPS + 3: // 33 case R_SCRATCHA: // 34 case R_SCRATCHA + 1: // 35 case R_SCRATCHA + 2: // 36 case R_SCRATCHA + 3: // 37 case R_DMODE: // 38 case R_SBR: // 3A // 810 case R_GPCNTL: // 47 case R_STIME0: // 48 case R_RESPID: // 4A case R_RESPID + 1: // 4B case R_STEST0: // 4C case R_SCRATCHB: // 5C case R_SCRATCHB + 1: // 5D case R_SCRATCHB + 2: // 5E case R_SCRATCHB + 3: // 5F state.regs.reg8[address] = (u8)data; break; case R_SCNTL0: // 00 // side effects: start arbitration bit write_b_scntl0((u8)data); break; case R_SCNTL1: // 01 // side effects: start immediate arbitration bit write_b_scntl1((u8)data); break; case R_SCNTL2: // 02 WRMW1C_R8(SCNTL2, (u8)data); break; case R_SCNTL3: // 03 // side effects: clearing EWS write_b_scntl3((u8)data); break; case R_SCID: // 04 WRM_R8(SCID, (u8)data); break; case R_SDID: // 06 WRM_R8(SDID, (u8)data); break; case R_GPREG: // 07 WRM_R8(GPREG, (u8)data); break; case R_ISTAT: // 14 write_b_istat((u8)data); break; case R_CTEST2: // 1A WRM_R8(CTEST2, (u8)data); break; case R_CTEST3: // 1B write_b_ctest3((u8)data); break; case R_CTEST4: // 21 write_b_ctest4((u8)data); break; case R_CTEST5: // 22 write_b_ctest5((u8)data); break; case R_DSP + 3: // 2F state.regs.reg8[address] = (u8)data; post_dsp_write(); break; case R_DIEN: // 39 WRM_R8(DIEN, (u8)data); eval_interrupts(); break; case R_DCNTL: // 3B write_b_dcntl((u8)data); break; case R_SIEN0: // 40 R8(SIEN0) = (u8)data; eval_interrupts(); break; case R_SIEN1: // 41 WRM_R8(SIEN1, (u8)data); eval_interrupts(); break; case R_MACNTL: // 46 // 810 WRM_R8(MACNTL, (u8)data); break; case R_STIME1: // 49 WRM_R8(STIME1, (u8)data); state.gen_timer = (R8(STIME1) & R_STIME1_GEN) * 30; break; case R_STEST1: // 4D WRM_R8(STEST1, (u8)data); break; case R_STEST2: // 4E write_b_stest2((u8)data); break; case R_STEST3: // 4F write_b_stest3((u8)data); break; case R_DSTAT: // 0C case R_SSTAT0: // 0D case R_SSTAT1: // 0E case R_SSTAT2: // 0F // printf("SYM: Write to read-only memory at %02x. FreeBSD driver cache // test.\n" ,address); break; default: FAILURE_2(NotImplemented, "SYM: Write to unknown register at %02x with %08x.\n", address, data); } MUTEX_UNLOCK(myRegLock); break; case 16: WriteMem_Bar(0, 1, address + 0, 8, (data >> 0) & 0xff); WriteMem_Bar(0, 1, address + 1, 8, (data >> 8) & 0xff); break; case 32: WriteMem_Bar(0, 1, address + 0, 8, (data >> 0) & 0xff); WriteMem_Bar(0, 1, address + 1, 8, (data >> 8) & 0xff); WriteMem_Bar(0, 1, address + 2, 8, (data >> 16) & 0xff); WriteMem_Bar(0, 1, address + 3, 8, (data >> 24) & 0xff); break; } break; case 2: p = (u8 *)state.ram + address; switch (dsize) { case 8: *((u8 *)p) = (u8)data; break; case 16: *((u16 *)p) = (u16)data; break; case 32: *((u32 *)p) = (u32)data; break; } break; } } /** * Read data from one of the PCI BAR (relocatable) address ranges. **/ u32 CSym53C895::ReadMem_Bar(int func, int bar, u32 address, int dsize) { u32 data = 0; void *p; switch (bar) { case 0: case 1: address &= 0x7f; switch (dsize) { case 8: MUTEX_LOCK(myRegLock); if (address >= R_SCRATCHC) { data = state.regs.reg8[address]; MUTEX_UNLOCK(myRegLock); break; } switch (address) { case R_SCNTL0: // 00 case R_SCNTL1: // 01 case R_SCNTL2: // 02 case R_SCNTL3: // 03 case R_SCID: // 04 case R_SXFER: // 05 case R_SDID: // 06 case R_GPREG: // 07 case R_SFBR: // 08 case R_SSID: // 0A case R_SBCL: // 0B case R_SSTAT0: // 0D case R_SSTAT1: // 0E case R_SSTAT2: // 0F case R_DSA: // 10 case R_DSA + 1: // 11 case R_DSA + 2: // 12 case R_DSA + 3: // 13 case R_ISTAT: // 14 case R_CTEST0: // 18 case R_CTEST1: // 19 case R_CTEST3: // 1B case R_TEMP: // 1C case R_TEMP + 1: // 1D case R_TEMP + 2: // 1E case R_TEMP + 3: // 1F case R_CTEST4: // 21 case R_CTEST5: // 22 case R_DBC: // 24 // 810 case R_DBC + 1: // 25 // 810 case R_DBC + 2: // 26 // 810 case R_DCMD: // 27 // 810 case R_DNAD: // 28 // 810 case R_DNAD + 1: // 29 // 810 case R_DNAD + 2: // 2A // 810 case R_DNAD + 3: // 2B // 810 case R_DSP: // 2C case R_DSP + 1: // 2D case R_DSP + 2: // 2E case R_DSP + 3: // 2F case R_DSPS: // 30 case R_DSPS + 1: // 31 case R_DSPS + 2: // 32 case R_DSPS + 3: // 33 case R_DMODE: // 38 case R_DIEN: // 39 case R_SBR: // 3A // 810 case R_DCNTL: // 3B case R_SIEN0: // 40 case R_SIEN1: // 41 case R_MACNTL: // 46 // 810 case R_GPCNTL: // 47 case R_STIME0: // 48 case R_STIME1: // 49 case R_RESPID: // 4A case R_RESPID + 1: // 4B case R_STEST0: // 4C case R_STEST1: // 4D case R_STEST2: // 4E case R_STEST3: // 4F case R_STEST4: // 52 case R_SBDL: // 58 case R_SBDL + 1: // 59 data = state.regs.reg8[address]; break; case R_DSTAT: // 0C data = read_b_dstat(); break; case R_CTEST2: // 1A data = read_b_ctest2(); break; case R_DFIFO: // 20 data = R8(DBC) & 0x7f; // 810 - fake the DFIFO count break; case R_SCRATCHA: // 34 case R_SCRATCHA + 1: // 35 case R_SCRATCHA + 2: // 36 case R_SCRATCHA + 3: // 37 data = read_b_scratcha(address - R_SCRATCHA); break; case R_SIST0: // 42 case R_SIST1: // 43 data = read_b_sist(address - R_SIST0); break; case R_SCRATCHB: // 5C case R_SCRATCHB + 1: // 5D case R_SCRATCHB + 2: // 5E case R_SCRATCHB + 3: // 5F data = read_b_scratchb(address - R_SCRATCHB); break; default: FAILURE_2( NotImplemented, "SYM: Attempt to read %d bytes from unknown register at %" PRIx32 "\n", dsize, address); } MUTEX_UNLOCK(myRegLock); #if defined(DEBUG_SYM_REGS) printf("SYM: Read frm register %02x: %02x. \n", address, data); #endif break; case 16: data = (ReadMem_Bar(0, 1, address + 0, 8) << 0) & 0x00ff; data |= (ReadMem_Bar(0, 1, address + 1, 8) << 8) & 0xff00; break; case 32: data = (ReadMem_Bar(0, 1, address + 0, 8) << 0) & 0x000000ff; data |= (ReadMem_Bar(0, 1, address + 1, 8) << 8) & 0x0000ff00; data |= (ReadMem_Bar(0, 1, address + 2, 8) << 16) & 0x00ff0000; data |= (ReadMem_Bar(0, 1, address + 3, 8) << 24) & 0xff000000; break; } break; case 2: p = (u8 *)state.ram + address; switch (dsize) { case 8: return *((u8 *)p); case 16: return *((u16 *)p); case 32: return *((u32 *)p); } break; } return data; } /** * Override PCI Configuration Space read action. * * Lower 80 bytes are normal, upper 80 bytes reflect into the * register space. **/ u32 CSym53C895::config_read_custom(int func, u32 address, int dsize, u32 data) { if (address >= 0x80) return ReadMem_Bar(func, 1, address - 0x80, dsize); else return data; } /** * Override PCI Configuration Space write action. * * Lower 80 bytes are normal, upper 80 bytes reflect into the * register space. **/ void CSym53C895::config_write_custom(int func, u32 address, int dsize, u32 old_data, u32 new_data, u32 data) { if (address >= 0x80) WriteMem_Bar(func, 1, address - 0x80, dsize, data); } /** * Write a byte to the SCSI Control 0 register. * * This is a normal masked write operation; implemented as a separate * function, because there are some bits in here (START and TRG) that * we should do something with if a driver sets these, but that we * don't implement. * * START: When this bit is set, the controller should start the * arbitration seqence indicated by the arbitration mode bits. Used * only in low-level mode. UNIMPLEMENTED. * * TRG: When this bit is set, the controller is a target device. * UNIMPLEMENTED. **/ void CSym53C895::write_b_scntl0(u8 value) { bool old_start = TB_R8(SCNTL0, START); WRM_R8(SCNTL0, value); if (TB_R8(SCNTL0, START) && !old_start) FAILURE(NotImplemented, "SYM: Don't know how to start arbitration sequence"); if (TB_R8(SCNTL0, TRG)) FAILURE(NotImplemented, "SYM: Don't know how to operate in target mode"); } /** * Write a byte to the SCSI Control 1 register. * * This is implemented as a separate function, because there are * quite a few side-effects that occur when writing to this register * * CON (Connected): This bit is automatically set any time the * controller is connected to the SCSI bus. The CPU can force a * connection or disconnection by setting or clearing this bit. * UNIMPLEMENTED. * * RST: Asserts the SCSI RST/ signal. Has the "side-effect" of * resetting the SCSI bus. This effects a couple of other registers. * * \todo: Implement real reset of the SCSI bus. **/ void CSym53C895::write_b_scntl1(u8 value) { bool old_rst = TB_R8(SCNTL1, RST); R8(SCNTL1) = value; if (TB_R8(SCNTL1, RST) != old_rst) { SB_R8(SSTAT0, SDP0, false); SB_R8(SSTAT1, SDP1, false); R16(SBDL) = 0; R8(SBCL) = 0; SB_R8(SSTAT0, RST, !old_rst); // printf("SYM: %s SCSI bus reset.\n",old_rst?"end":"start"); if (!old_rst) RAISE(SIST0, RST); } } /** * Write a byte to the SCSI Control 3 register. * * The Enable Wide SCSI bit has a side-effect on the SCSI Control 2 * register. **/ void CSym53C895::write_b_scntl3(u8 value) { R8(SCNTL3) = value; /* If Wide SCSI is disabled, the Wide SCSI Receive flag can't be set. */ if (!TB_R8(SCNTL3, EWS)) SB_R8(SCNTL2, WSR, false); } /** * Write a byte to the SCSI Interrupt Status. * * This is implemented as a separate function, because there are * quite a few side-effects that occur when writing to this register * * ABRT (Abort Operation): Aborts the currently executing SCRIP, and * generate an interrupt. * * SRST (Software Reset): Resets the SCSI chipset. * * SIGP (Signal Process): Aborts a Wait for (Re)Selection instruction * by jumping to the alternate address immediately. * * Since interrupt state is affected, call eval_interrupts. **/ void CSym53C895::write_b_istat(u8 value) { bool old_srst = TB_R8(ISTAT, SRST); WRMW1C_R8(ISTAT, value); if (TB_R8(ISTAT, ABRT)) { // printf("SYM: Aborting on request.\n"); RAISE(DSTAT, ABRT); } if (TB_R8(ISTAT, SRST) && !old_srst) { // printf("SYM: Resetting on request.\n"); chip_reset(); } if (TB_R8(ISTAT, SIGP)) { if (state.wait_reselect) { // printf("SYM: SIGP while wait_reselect. Jumping...\n"); R32(DSP) = state.wait_jump; state.wait_reselect = false; state.executing = true; mySemaphore.set(); } } eval_interrupts(); } /** * Reads a byte from the Chip Test 2 register. * * This is implemented as a separate function, because: * - The SIGP flag read by this register comes from the ISTAT * register. * - The CIO (configured as I/O) and CM (configured as memory) * flags are determined from PCI Configuration space. * - Reading this register has the side effect of clearing the * SIGP flag. * . **/ u8 CSym53C895::read_b_ctest2() { SB_R8(CTEST2, CIO, pci_state.config_data[0][4] != 0); SB_R8(CTEST2, CM, pci_state.config_data[0][5] != 0); SB_R8(CTEST2, SIGP, TB_R8(ISTAT, SIGP)); SB_R8(ISTAT, SIGP, false); // printf("SYM: SIGP cleared by CTEST2 read.\n"); return R8(CTEST2); } /** * Write a byte to the Chip Test 3 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * FM (Fetch Pin Mode): When set, this bit causes the FETCH/ pin to * deassert during indirect and table indirect read operations. * FETCH/ will only be active during the op codde portion of an * instruction fetch. This allows SCRIPTS to be stored in a PROM * while data tables are stored in RAM. UNIMPLEMENTED. **/ void CSym53C895::write_b_ctest3(u8 value) { WRM_R8(CTEST3, value); // if ((value>>3) & 1) // printf("SYM: Don't know how to flush DMA FIFO\n"); // if ((value>>2) & 1) // printf("SYM: Don't know how to clear DMA FIFO\n"); if ((value >> 1) & 1) FAILURE(NotImplemented, "SYM: Don't know how to handle FM mode"); } /** * Write a byte to the Chip Test 4 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * SRTM: Shadow Register Test Mode. Access shadow copies of TEMP and * DSA. Used for manufacturing diagnostics only. UNIMPLEMENTED. **/ void CSym53C895::write_b_ctest4(u8 value) { R8(CTEST4) = value; if ((value >> 4) & 1) FAILURE(NotImplemented, "SYM: Don't know how to handle SRTM mode"); } /** * Write a byte to the Chip Test 5 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * ADCK (Clock Address Incrementor): Setting this bit increments the * DNAD register. The DNAD register is incremented based on the DNAD * contents and the current DBC value. This bit automatically clears * itself after incrementing the DNAD register. UNIMPLEMENTED. * * BBCK (Clock Byte Counter): Setting this bit decrements the byte * count contained in the 24-bit DBC register. It is decremented * based on the DBC contents and the current DNAD value. This bit * automatically clears itself after decrementing the DBC register. * UNIMPLEMENTED. **/ void CSym53C895::write_b_ctest5(u8 value) { WRM_R8(CTEST5, value); if ((value >> 7) & 1) FAILURE(NotImplemented, "SYM: Don't know how to do Clock Address increment"); if ((value >> 6) & 1) FAILURE(NotImplemented, "SYM: Don't know how to do Clock Byte Counter decrement"); } /** * Read a byte from the DSTAT register. * * This is implemented as a separate function, because it requires * interrupt re-evaluation. **/ u8 CSym53C895::read_b_dstat() { u8 retval = R8(DSTAT); RDCLR_R8(DSTAT); // printf("Read DSTAT --> eval int\n"); eval_interrupts(); // printf("Read DSTAT <-- eval int; retval: %02x; dstat: // %02x.\n",retval,R8(DSTAT)); return retval; } /** * Read a byte from the SIST0 or SIST1 register. * * This is implemented as a separate function, because it requires * interrupt re-evaluation. **/ u8 CSym53C895::read_b_sist(int id) { u8 retval = state.regs.reg8[R_SIST0 + id]; if (id) RDCLR_R8(SIST1); else RDCLR_R8(SIST0); eval_interrupts(); return retval; } /** * Write a byte to the DMA Control register. * * This is implemented as a separate function, because there are * some side-effects. * * STD (Start DMA Operation): Start executing SCSI SCRIPT. Needs to * wake the thread. * * IRQD (IRQ Disable): disables the IRQ pin. Requires interrupt * re-evaluation. **/ void CSym53C895::write_b_dcntl(u8 value) { WRM_R8(DCNTL, value); // start operation if (value & R_DCNTL_STD) { state.executing = true; mySemaphore.set(); } // IRQD bit... eval_interrupts(); } /** * Read a byte from the Scratch Register A. * * This is implemented as a separate function, because depending on * the SRTCH bit in CTEST2, the data comes from either the Scratch * Register A, or the memory mapped base address of the registers. **/ u8 CSym53C895::read_b_scratcha(int reg) { if (TB_R8(CTEST2, SRTCH)) { // printf("SYM: SCRATCHA from PCI\n"); return (u8)(pci_state.config_data[0][4] >> (reg * 8)) & 0xff; } else return state.regs.reg8[R_SCRATCHA + reg]; } /** * Read a byte from the Scratch Register B. * * This is implemented as a separate function, because depending on * the SRTCH bit in CTEST2, the data comes from either the Scratch * Register B, or the memory mapped base address of the internal RAM. **/ u8 CSym53C895::read_b_scratchb(int reg) { if (TB_R8(CTEST2, SRTCH)) { // printf("SYM: SCRATCHB from PCI\n"); return (u8)(pci_state.config_data[0][5] >> (reg * 8)) & 0xff; } else return state.regs.reg8[R_SCRATCHB + reg]; } /** * Write a byte to the SCSI Test 2 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. * * LOW (Low-level-mode). Switches the SCSI controller to low-level * mode operation. No SCRIPTS processor, but raw manipulation of * SCSI registers. Yuck. UNIMPLEMENTED. **/ void CSym53C895::write_b_stest2(u8 value) { WRM_R8(STEST2, value); // if (value & R_STEST2_ROF) // printf("SYM: Don't know how to reset SCSI offset!\n"); if (TB_R8(STEST2, LOW)) FAILURE(NotImplemented, "SYM: I don't like LOW level mode"); } /** * Write a byte to the SCSI Test 3 register. * * This is implemented as a separate function, because there are * some unimplemented bits that probably should have a function if * a driver ever decides to use these. **/ void CSym53C895::write_b_stest3(u8 value) { WRM_R8(STEST3, value); // if (value & R_STEST3_CSF) // printf("SYM: Don't know how to clear the SCSI fifo.\n"); } /** * Called after the DMA Scripts Pointer register has been written. * * Start executing SCSI SCRIPT. Needs to wake the thread. **/ void CSym53C895::post_dsp_write() { if (!TB_R8(DMODE, MAN)) { state.executing = true; mySemaphore.set(); // printf("SYM: Execution started @ %08x.\n",R32(DSP)); } } /** * Check if threads are still running. **/ void CSym53C895::check_state() { if (myThreadDead.load()) FAILURE(Thread, "SYM thread has died"); if (state.gen_timer) { state.gen_timer--; if (!state.gen_timer) { state.gen_timer = (R8(STIME1) & R_STIME1_GEN) * 30; RAISE(SIST1, GEN); return; } } /** if (state.wait_reselect && PT.disconnected) { state.executing = true; state.wait_reselect = false; PT.disconnected = false; //PT.disconnect_priv = false; //PT.will_disconnect = false; PT.reselected = true; state.phase = 7; // msg in //PT.disconnect_phase; R8(SSID) = GET_DEST() | R_SSID_VAL; // valid scsi selector id if (TB_R8(DCNTL,COM)) R8(SFBR) = GET_DEST(); // don't expect a disconnect. SB_R8(SCNTL2,SDU,true); //RAISE(SIST0,RSL); return 0; } **/ if (state.disconnected) { if (!TB_R8(SCNTL2, SDU)) { // disconnect expected // printf("SYM: Disconnect expected. stopping disconnect timer at // %d.\n",state.disconnected); state.disconnected = 0; return; } state.disconnected--; if (!state.disconnected) { // printf("SYM: Disconnect unexpected. raising interrupt!\n"); // printf(">"); // getchar(); RAISE(SIST0, UDC); return; } } } /** * Check SCSI Bus Phase. * * Returns -1 on timeout or similar, 0 on different phase, and 1 on same phase **/ int CSym53C895::check_phase(int chk_phase) { int real_phase = scsi_get_phase(0); if (real_phase == SCSI_PHASE_ARBITRATION) { #if defined(DEBUG_SYM_SCRIPTS) printf("Phase check... selection time-out!\n"); #endif RAISE(SIST1, STO); // select time-out scsi_free(0); state.select_timeout = false; return -1; } if (real_phase == SCSI_PHASE_FREE && state.disconnected) { #if defined(DEBUG_SYM_SCRIPTS) printf("Phase check... disconnected!\n"); #endif state.disconnected = 1; R32(DSP) -= 8; return -1; } if (real_phase == chk_phase) return 1; else return 0; } /** * Execute one SCRIPTS Block Move instruction * * The Block Move instruction moves data between system memory and the * SCSI Bus. This is a two DWORD instruction. The instruction format in * the DCMD register is as follows: * * \code * +---+-+-+-+-----+ * |7 6|5|4|3|2 1 0| DCMD Register * +---+-+-+-+-----+ * | | | | +- 0..2: SCSI Phase (I/O, C/D and MSG/ signals): * | | | | The data transfer only occurs if these bits * | | | | match the actual SCSI bus phase. * | | | +- 3: Op Code: IGNORED * | | +- 4: Table Indirect Adressing: * | | 0: The DSPS register contains the address of the data, * | | and the DBC register contains the number of bytes to * | | transfer. * | | 1: The DSPS register contains a 24-bit signed offset * | | that is added to the DSA register to get a pointer * | | to a data structure that contains the address and * | | byte count. This structure looks as follows: * | | +----+------------+ * | | DSA+DSPS: | 00 | Byte Count | * | | +----+------------+ * | | DSA+DSPS+4: | Data Address | * | | +-----------------+ * | +- 5: Indirect Addressing: * | 0: The DSPS register contains the address of the data * | 1: The DSPS register contains the address of a 32-bit * | pointer to the data. * +- 6..7: Instruction Type: 00 = Block Move * \endcode **/ void CSym53C895::execute_bm_op() { bool indirect = (R8(DCMD) >> 5) & 1; bool table_indirect = (R8(DCMD) >> 4) & 1; int scsi_phase = (R8(DCMD) >> 0) & 7; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = Block Move (i %d, t %d, opc %d, phase %d\n", indirect, table_indirect, opcode, scsi_phase); #endif // Compare phase if (check_phase(scsi_phase) > 0) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Ready for transfer.\n"); #endif u32 start; u32 count; if (table_indirect) { u32 add = R32(DSA) + sext_u32_24(R32(DSPS)); add &= ~0x03; // 810 #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Reading table at DSA(%08x)+DSPS(%08x) = %08x.\n", R32(DSA), R32(DSPS), add); #endif do_pci_read(add, &count, 4, 1); count &= 0x00ffffff; do_pci_read(add + 4, &start, 4, 1); } else if (indirect) { FAILURE(NotImplemented, "SYM: Unsupported: indirect addressing"); } else { start = R32(DSPS); count = GET_DBC(); } #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: MOVE Start/count %x, %x\n", R32(DSP) - 8, start, count); #endif R32(DNAD) = start; SET_DBC(count); // page 5-32 if (count == 0) { // printf("SYM: Count equals zero!\n"); RAISE(DSTAT, IID); // page 5-32 return; } if ((size_t)count > scsi_expected_xfer(0)) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: xfer %d bytes, max %d expected, in phase %d.\n", count, scsi_expected_xfer(0), scsi_phase); #endif count = (u32)scsi_expected_xfer(0); } u8 *scsi_data_ptr = (u8 *)scsi_xfer_ptr(0, count); u8 *org_sdata_ptr = scsi_data_ptr; switch (scsi_phase) { case SCSI_PHASE_COMMAND: case SCSI_PHASE_DATA_OUT: case SCSI_PHASE_MSG_OUT: do_pci_read(R32(DNAD), scsi_data_ptr, 1, count); R32(DNAD) += count; break; case SCSI_PHASE_STATUS: case SCSI_PHASE_DATA_IN: case SCSI_PHASE_MSG_IN: do_pci_write(R32(DNAD), scsi_data_ptr, 1, count); R32(DNAD) += count; break; } R8(SFBR) = *org_sdata_ptr; scsi_xfer_done(0); return; } } /* Execute one SCRIPTS I/O instruction * * The I/O instructions perform common SCSI hardware sequences, like * Selection and reselection. The instruction format in * the DCMD and DBC register is as follows: * * \code * +---+-----+-+-+-+ * |7 6|5 4 3|2|1|0| DCMD Register * +---+-----+-+-+-+ * | | | | +- 0: Select with ATN/: * | | | | Valid only for Select instruction. Assert ATN/ during * | | | | selection * | | | +- 1: Table Indirect Mode: * | | | 0: All information is taken from the instruction, * | | | and the contents of the SCNTL3 and SXFER registers. * | | | 1: The DSPS register contains a 24-bit signed offset * | | | that is added to the DSA register to get a pointer * | | | to a data structure that contains the destination * | | | ID, SCNTL3 bits, and SXFER bits. This structure * | | | is 32 bits long and looks as follows: * | | | +--------+--------+--------+--------+ * | | | DSA+DSPS: | SCNTL3 | ID | SXFER | | * | | | +--------+--------+--------+--------+ * | | +- 2: Relative Addrressing: * | | 0: The value in the DNAD register is an absolute address. * | | 1: The value in the DNAD register is a 24-bit signed * | | displacement from the current DSP address. * | +- 3..5: Op Code * +- 6..7 Instruction Type: 01 = I/O * * +-------+-------++--------+--+-+-++-+-+---+-+-----+ * | |19 16|| |10|9| || |6| |3| | DBC Register * +-------+-------++--------+--+-+-++-+-+---+-+-----+ * | | | | +- 3: Set/Clear ATN * | | | +- 6: Set/Clear ACK * | | +- 9: Set/Clear Target Mode * | +- 10: Set/Clear Carry * +- 16..19: Destination ID * \endcode * * The Opcode determines the actual instruction: * \code * +-----+-----------------+-------------+ * | OPC | Initiator mode | Target Mode | * +-----+-----------------+-------------+ * | 000 | Select | Reselect | * | 001 | Wait Disconnect | Disconnect | * | 010 | Wait Reselect | Wait Select | * | 011 | Set | Set | * | 100 | Clear | Clear | * +-----+-----------------+-------------+ * \endcode * * Select: * - Arbitrate for the SCSI bus until arbitration is won. * - If arbitration is won, try to select the destination ID * - If the controller is selected or reselected before winning * arbitration, jump to the address in the DNAD register. * . * * Wait Disconnect: * - Wait for the target to disconnect from the SCSI bus. * . * * Wait Reselect: * - If the controller is reselected, go to the next instruction. * - If the controller is selected before being reselected, or if * the CPU sets the SIGP flag, jump to the address in the DNAD * register * . * * Set/Clear: * - Set or Clear the flags whose Set/Clear bits are set in the * instruction. * . **/ void CSym53C895::execute_io_op() { int opcode = (R8(DCMD) >> 3) & 7; bool relative = (R8(DCMD) >> 2) & 1; bool table_indirect = (R8(DCMD) >> 1) & 1; int destination = (GET_DBC() >> 16) & 0x0f; bool sc_carry = (GET_DBC() >> 10) & 1; bool sc_target = (GET_DBC() >> 9) & 1; bool sc_ack = (GET_DBC() >> 6) & 1; bool sc_atn = (GET_DBC() >> 3) & 1; R32(DNAD) = R32(DSPS); u32 dest_addr = R32(DNAD); if (relative) dest_addr = R32(DSP) + sext_u32_24(R32(DNAD)); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = I/O (opc %d, r %d, t %d, a %d, dest %d, sc %d%d%d%d\n", opcode, relative, table_indirect, atn, destination, sc_carry, sc_target, sc_ack, sc_atn); #endif if (table_indirect) { u32 io_addr = R32(DSA) + sext_u32_24(GET_DBC()); io_addr &= ~3; // 810 #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Reading table at DSA(%08x)+DBC(%08x) = %08x.\n", R32(DSA), sext_u32_24(GET_DBC()), io_addr); #endif u32 io_struc; do_pci_read(io_addr, &io_struc, 4, 1); destination = (io_struc >> 16) & 0x0f; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: table indirect. io_struct = %08x, new dest = %d.\n", io_struc, destination); #endif } switch (opcode) { case 0: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: SELECT %d.\n", R32(DSP) - 8, destination); #endif SET_DEST(destination); if (!scsi_arbitrate(0)) { // scsi bus busy, try again next clock... printf("scsi bus busy...\n"); R32(DSP) -= 8; return; } state.select_timeout = !scsi_select(0, destination); if (!state.select_timeout) // select ok SB_R8(SCNTL2, SDU, true); // don't expect a disconnect return; case 1: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: WAIT DISCONNECT\n", R32(DSP) - 8); #endif // maybe we need to do more?? scsi_free(0); return; case 2: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: WAIT RESELECT\n", R32(DSP) - 8); #endif if (TB_R8(ISTAT, SIGP)) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: SIGP set before wait reselect; jumping!\n"); #endif R32(DSP) = dest_addr; } else { state.wait_reselect = true; state.wait_jump = dest_addr; state.executing = false; } return; case 3: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: SET %s%s%s%s\n", R32(DSP) - 8, sc_carry ? "carry " : "", sc_target ? "target " : "", sc_ack ? "ack " : "", sc_atn ? "atn " : ""); #endif if (sc_ack) SB_R8(SOCL, ACK, true); if (sc_atn) { if (!TB_R8(SOCL, ATN)) { SB_R8(SOCL, ATN, true); // printf("SET ATN.\n"); // printf(">"); // getchar(); } } if (sc_target) SB_R8(SCNTL0, TRG, true); if (sc_carry) state.alu.carry = true; return; case 4: #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: CLEAR %s%s%s%s\n", R32(DSP) - 8, sc_carry ? "carry " : "", sc_target ? "target " : "", sc_ack ? "ack " : "", sc_atn ? "atn " : ""); #endif if (sc_ack) SB_R8(SOCL, ACK, false); if (sc_atn) { if (TB_R8(SOCL, ATN)) { SB_R8(SOCL, ATN, false); // printf("RESET ATN.\n"); // printf(">"); // getchar(); } } if (sc_target) SB_R8(SCNTL0, TRG, false); if (sc_carry) state.alu.carry = false; return; break; } } /** * Execute one SCRIPTS R/W instruction * * The R/W instructions perform arithmetic or logic operations on * registers. The instruction format in the DCMD and DBC register is as follows: * * \code * +---+-----+-----+ * |7 6|5 4 3|2 1 0| DCMD Register * +---+-----+-----+ * | | +- 2..0: operator: * | | 000: data8 * | | 001: reg << 1 * | | 010: reg | data8 * | | 011: reg ^ data8 * | | 100: reg & data8 * | | 101: reg >> 1 * | | 110: reg + data8 * | | 111: reg + data8 + carry * | +- 3..5: Op Code * | 101: regA = operator(SFBR, data8) * | 110: SFBR = operator(RegA, data8) * | 111: regA = operator(RegA, data8) * +- 6..7 Instruction Type: 01 = R/W * * +--+------------++---------------++-+-------------+ * |23|22 16||15 8||7| | DBC Register * +--+------------++---------------++-+-------------+ * | A6--------A0 | A7 * | +--------------|---------+- 7,22..16: RegA address * | | * | +- 15..8: Immediate data * +- 23: Use data8/SFBR * 0: data8 = Immediate data * 1: data8 = SFBR * \endcode */ void CSym53C895::execute_rw_op() { int opcode = (R8(DCMD) >> 3) & 7; int oper = (R8(DCMD) >> 0) & 7; bool use_data8_sfbr = (GET_DBC() >> 23) & 1; int reg_address = ((GET_DBC() >> 16) & 0x7f); //| (GET_DBC() & 0x80); // manual is unclear about bit 7. u8 imm_data = (u8)(GET_DBC() >> 8) & 0xff; u8 op_data; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = R/W (opc %d, oper %d, use %d, add %d, imm %02x\n", opcode, oper, use_data8_sfbr, reg_address, imm_data); #endif if (use_data8_sfbr) imm_data = R8(SFBR); if (oper != 0) { if (opcode == 5 || reg_address == 0x08) { op_data = R8(SFBR); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: sfbr (%02x) ", R32(DSP) - 8, op_data); #endif } else { op_data = (u8)ReadMem_Bar(0, 1, reg_address, 8); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: reg%02x (%02x) ", R32(DSP) - 8, reg_address, op_data); #endif } } u16 tmp16; switch (oper) { case 0: op_data = imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: %02x ", R32(DSP) - 8, imm_data); #endif break; case 1: tmp16 = (op_data << 1) + (state.alu.carry ? 1 : 0); state.alu.carry = (tmp16 >> 8) & 1; op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("<< 1 = %02x ", op_data); #endif break; case 2: op_data |= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("| %02x = %02x ", imm_data, op_data); #endif break; case 3: op_data ^= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("^ %02x = %02x ", imm_data, op_data); #endif break; case 4: op_data &= imm_data; #if defined(DEBUG_SYM_SCRIPTS) printf("& %02x = %02x ", imm_data, op_data); #endif break; case 5: tmp16 = (op_data >> 1) + (state.alu.carry ? 0x80 : 0x00); state.alu.carry = op_data & 1; op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf(">> 1 = %02x ", op_data); #endif break; case 6: tmp16 = op_data + imm_data; state.alu.carry = (tmp16 > 0xff); op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("+ %02x = %02x (carry %d) ", imm_data, op_data, state.alu.carry); #endif break; case 7: tmp16 = op_data + imm_data + (state.alu.carry ? 1 : 0); state.alu.carry = (tmp16 > 0xff); op_data = tmp16 & 0xff; #if defined(DEBUG_SYM_SCRIPTS) printf("+ %02x (w/carry) = %02x (carry %d) ", imm_data, op_data, state.alu.carry); #endif break; } if (opcode == 6 || reg_address == 0x08) { #if defined(DEBUG_SYM_SCRIPTS) printf("-> sfbr.\n"); #endif R8(SFBR) = op_data; } else { #if defined(DEBUG_SYM_SCRIPTS) printf("-> reg%02x.\n", reg_address); #endif WriteMem_Bar(0, 1, reg_address, 8, op_data); } } /** * Execute one SCRIPTS Transfer Control instruction * * The Transfer Control instructions perform conditional jumps, calls, * returns and interrupts. The instruction format in the DCMD and DBC * registers is as follows: * * \code * +---+-----+-----+ * |7 6|5 4 3|2 1 0| DCMD Register * +---+-----+-----+ * | | +- 0..2: SCSI Phase (I/O, C/D and MSG/ signals): * | | The actual SCSI phase is compared against these * | | bits. * | +- 3..5: Op Code * | 000: Jump * | 001: Call * | 010: Return * | 011: Interrupt * | 1xx: reserved * +- 6..7 Instruction Type: 10 = Transfer Control * * +--+-+--+--+--+--+--+--++---------------++---------------+ * |23| |21|20|19|18|17|16||15 8||7 0| DBC Register * +--+-+--+--+--+--+--+--++---------------++---------------+ * | | | | | | | | +- 7..0: Data to compare * | | | | | | | | against SFBR * | | | | | | | +- 15..8: Mask that determines what bits * | | | | | | | to compare against SFBR. * | | | | | | +- 16: Wait for valid SCSI phase * | | | | | +- 17: Compare Phase * | | | | +- 18: Compare SFBR data * | | | +- Jump if: * | | | 0: Jump/Call/return/Interrupt if the equation is true * | | | 0: Jump/Call/return/Interrupt if the equation is false * | | +- Interrupt on the Fly * | +- Carry Test * +- relative Addressing: * 0: The value in the DSPS register is an absolute address. * 1: The value in the DSPS register is a 24-bit signed * displacement from the current DSP address. * \endcode * * The equation evaluated is one of the following: * - the value of the carry bit (if the Carry Test bit is set) * - equality comparisons of SCSI phase and/or SFBR register data * - true (if none of the compare/carry test bits are set) * . * * An action is taken when the equation evaluates to either true or false * as determined by the "Jump if" bit. * * Jump: * - Jump to the instruction addressed by the DSPS register. * . * * Call: * - Store the current DSP register value to the TEMP register. * - Jump to the instruction addressed by the DSPS register. * . * * Return: * - Jump to the instruction addressed by the TEMP register. * . * * Interrupt: * - If the Interrupt on the Fly bit is set, raise the INTF interrupt. * - Otherwise: * - Raise the SIR interrupt * - Terminate SCRIPTS execution * - The DSPS value is used as an interrupt vector for the driver. * . * . **/ void CSym53C895::execute_tc_op() { int opcode = (R8(DCMD) >> 3) & 7; int scsi_phase = (R8(DCMD) >> 0) & 7; bool relative = (GET_DBC() >> 23) & 1; bool carry_test = (GET_DBC() >> 21) & 1; bool interrupt_fly = (GET_DBC() >> 20) & 1; bool jump_if = (GET_DBC() >> 19) & 1; bool cmp_data = (GET_DBC() >> 18) & 1; bool cmp_phase = (GET_DBC() >> 17) & 1; int cmp_mask = (GET_DBC() >> 8) & 0xff; int cmp_dat = (GET_DBC() >> 0) & 0xff; u32 dest_addr; // wait_valid can be safely ignored, phases are always valid in this ideal // world... bool wait_valid = (GET_DBC()>>16) & 1; // We'll keep modifying this variable until we know what the result of the // comparisons is. bool do_it; // Relative jump or not? (no effect on Return or Interrupt) if (relative) dest_addr = R32(DSP) + sext_u32_24(R32(DSPS)); else dest_addr = R32(DSPS); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: if (", R32(DSP) - 8); #endif if (carry_test) { // All we need to check is the CARRY flag #if defined(DEBUG_SYM_SCRIPTS) printf("(%scarry)", jump_if ? "" : "!"); #endif do_it = (state.alu.carry == jump_if); } else if (cmp_data || cmp_phase) { // We need to compare data and/or phase do_it = true; if (cmp_data) { // compare data #if defined(DEBUG_SYM_SCRIPTS) printf("((data & 0x%02x) %s 0x%02x)", (~cmp_mask) & 0xff, jump_if ? "==" : "!=", cmp_dat & ~cmp_mask); #endif if (((R8(SFBR) & ~cmp_mask) == (cmp_dat & ~cmp_mask)) != jump_if) do_it = false; #if defined(DEBUG_SYM_SCRIPTS) if (cmp_phase) printf(" && "); #endif } if (cmp_phase) { // Compare phase #if defined(DEBUG_SYM_SCRIPTS) printf("(phase %s %d)", jump_if ? "==" : "!=", scsi_phase); #endif if ((check_phase(scsi_phase) > 0) != jump_if) do_it = false; } } else { // no comparison do_it = jump_if; } #if defined(DEBUG_SYM_SCRIPTS) printf(") "); #endif switch (opcode) { case 0: #if defined(DEBUG_SYM_SCRIPTS) printf("jump %x\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Jumping %08x...\n", dest_addr); #endif R32(DSP) = dest_addr; } return; break; case 1: #if defined(DEBUG_SYM_SCRIPTS) printf("call %d\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Calling %08x...\n", dest_addr); #endif R32(TEMP) = R32(DSP); R32(DSP) = dest_addr; } return; break; case 2: #if defined(DEBUG_SYM_SCRIPTS) printf("return %d\n", R32(DSPS)); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Returning %08x...\n", R32(TEMP)); #endif R32(DSP) = R32(TEMP); } return; break; case 3: #if defined(DEBUG_SYM_SCRIPTS) printf("interrupt%s.\n", interrupt_fly ? " on the fly" : ""); #endif if (do_it) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Interrupt with vector %x...\n", R32(DSPS)); #endif if (interrupt_fly) RAISE(ISTAT, INTF); else RAISE(DSTAT, SIR); } return; break; default: FAILURE_1(NotImplemented, "SYM: Transfer Control Instruction with opcode %d is RESERVED.\n", opcode); } } /** * Execute one SCRIPTS Load/Store instruction * * The Load/Store instruction moves data between registers and memory. * The memory range could very well map back to the registers through * the PCI bus! The instruction format in the DCMD and DBC registers * is as follows: * * \code * +-----+-+---+-+-+ * |7 6 5|4| |1|0| DCMD Register * +-----+-+---+-+-+ * | | | +- 0: Load/Store * | | | 0: Store (register -> memory) * | | | 1: Load (memory -> register) * | | +- 1: No Flush (no effect) * | +- 4: DSA Relative * | 0: The value in the DSPS register is absolute. * | 1: The value in the DSPS register is a 24-bit * | signed offset from DSA. * +- 7..5 Instruction Type: 111 = Load/Store * * +---------------++---------------++---------------+ * |23 16|| || |2 0| DBC Register * +---------------++---------------++---------------+ * | +- 2..0: Byte Count * +- 23..16: Register Address * \endcode * * This instructions moves up to 4 bytes between registers and memory. **/ void CSym53C895::execute_ls_op() { bool is_load = (R8(DCMD) >> 0) & 1; bool dsa_relative = (R8(DCMD) >> 4) & 1; int regaddr = (GET_DBC() >> 16) & 0x7f; int byte_count = (GET_DBC() >> 0) & 7; u32 memaddr; // Relative Addressing if (dsa_relative) memaddr = R32(DSA) + sext_u32_24(R32(DSPS)); else memaddr = R32(DSPS); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: dsa_rel: %d, DSA: %04x, DSPS: %04x, mem %04x.\n", dsa_relative, R32(DSA), R32(DSPS), memaddr); #endif if (is_load) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Load reg%02x", R32(DSP) - 8, regaddr); if (byte_count > 1) printf("..%02x", regaddr + byte_count - 1); printf("from %x.\n", memaddr); #endif // Perform Load Operation for (int i = 0; i < byte_count; i++) { u8 dat; do_pci_read(memaddr + i, &dat, 1, 1); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %02x -> reg%02x\n", dat, regaddr + i); #endif WriteMem_Bar(0, 1, regaddr + i, 8, dat); } } else { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Store reg%02x", R32(DSP) - 8, regaddr); if (byte_count > 1) printf("..%02x", regaddr + byte_count - 1); printf("to %x.\n", memaddr); #endif // Perform Store Operation for (int i = 0; i < byte_count; i++) { u8 dat = (u8)ReadMem_Bar(0, 1, regaddr + i, 8); #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %02x <- reg%02x\n", dat, regaddr + i); #endif do_pci_write(memaddr + i, &dat, 1, 1); } } } /** * Execute one SCRIPTS Memory Move instruction * * The Memory Move instruction is used to transfer data from one * region in host memory to another region through the SCSI * controller's. DMA. The instruction format in the DCMD register * is as follows: * * \code * +-----+-------+-+ * |7 6 5| |0| DCMD Register * +-----+-------+-+ * | +- No Flush (no effect) * +- 7..5 Instruction Type: 110 = Memory Move * \endcode * * This is a 3-DWORD instruction. * * The DBC register holds the number of bytes to be moved. * The DSPS register holds the source address. * The TEMP register holds the destination address. */ void CSym53C895::execute_mm_op() { u32 temp_shadow; do_pci_read(R32(DSP), &temp_shadow, 4, 1); R32(DSP) += 4; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: %08x: Memory Move %06x bytes from %08x to %08x.\n", R32(DSP) - 12, GET_DBC(), R32(DSPS), temp_shadow); #endif // To speed things up, we set up a buffer and read all data // at once, followed by writing all data at once. void *buf = malloc(GET_DBC()); do_pci_read(R32(DSPS), buf, 1, GET_DBC()); do_pci_write(temp_shadow, buf, 1, GET_DBC()); free(buf); return; } /** * Execute one SCRIPTS instruction. * * DSP (DMA Scripts Pointer) contains the address of the next instruction. * For each instruction, two DWORDS are read into the 8-bit DCMD (DMA Command), * 24-bit DBC (DMA Byte Counter), and 32-bit DSPS (DMA Scripts Pointer Save) * registers. For some commands, a third DWORD is read into the 32-bit TEMP * register: * * \code * +--------+------------------------+ * DSP : | DCMD | DBC | * +--------+------------------------+ * DSP+4: | DSPS | * +---------------------------------+ * DSP+8: |/ / / / / / / TEMP / / / / / / /| * +---------------------------------+ * \endcode **/ void CSym53C895::execute() { int optype; int opcode; bool is_load_store; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS @ %x \n", R32(DSP)); #endif // Read 2 DWORDS into the DCMD, DBC and DSPS registers. do_pci_read(R32(DSP), &R32(DBC), 4, 1); do_pci_read(R32(DSP) + 4, &R32(DSPS), 4, 1); // Increase DSP to point to the next instruction R32(DSP) += 8; #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: INS = %x, %x, %x \n", R8(DCMD), GET_DBC(), R32(DSPS)); #endif /* The two most significant bits of the DCMD register determine the operation * type. These are: * 00: Block Move * 01: I/O or R/W * 10: Transfer Control * 11: Memory Move or Load And Store */ optype = (R8(DCMD) >> 6) & 3; switch (optype) { case 0: execute_bm_op(); break; case 1: opcode = (R8(DCMD) >> 3) & 7; if (opcode < 5) execute_io_op(); else execute_rw_op(); break; case 2: execute_tc_op(); break; case 3: is_load_store = (R8(DCMD) >> 5) & 1; if (is_load_store) execute_ls_op(); else execute_mm_op(); break; } // single step mode if (TB_R8(DCNTL, SSM)) { #if defined(DEBUG_SYM_SCRIPTS) printf("SYM: Single step...\n"); #endif RAISE(DSTAT, SSI); } } /** * Set an interrupt bit. * * This function checks if any other interrupt bits are active, if so, * the interrupt bit is set in the stacked interrupt registers. Otherwise * it goes straight to the respective interrupt register. * * According to the datasheet: * "The SYM53C895 stacks interrupts if they occur one after another. If the SIP *or DIP bits in the ISTAT register are set (first level), then there is already *at least one pending interrupt, and any future interrupts will be stacked in *extra registers behind the SIST0, SIST1, and DSTAT registers (second level). *When two interrupts have occurred and the two levels of the stack are full, *any further interrupts will set additional bits in the extra registers behind *SIST0, SIST1 and DSTAT. When the first level of interrupts are cleared, all *the interrupts that came in afterward will move into the SIST0, SIST1 and *DSTAT. After the first interrupt is cleared by reading the appropriate *register, the IRQ/ pin will be deasserted for a minimum of three CLKs; the *stacked interrupt(s) will move into the SIST0, SIST1 or DSTAT; and the IRQ/ *pin will be asserted once again. * * Since a masked non-fatal interrupt will not set the SIP or DIP bits, *interrupt stacking will not occur. A masked, non-fatal interrupt will still *post the interrupt in SIST0, but will not assert the IRQ/ pin. Since no *interrupt is generated, future interrupts will move right into the SIST0 or *SIST1 instead of being stacked behind another interrupt. When another *condition occurs that generates an interrupt, the bit corresponding to the *earlier masked non-fatal interrupt will still be set." **/ void CSym53C895::set_interrupt(int reg, u8 interrupt) { // printf("set interrupt %02x, %02x.\n",reg,interrupt); switch (reg) { case R_DSTAT: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.dstat_stack |= interrupt; // printf("DSTAT stacked.\n"); } else { R8(DSTAT) |= interrupt; // printf("DSTAT.\n"); } break; case R_SIST0: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.sist0_stack |= interrupt; // printf("SIST0 stacked.\n"); } else { R8(SIST0) |= interrupt; // printf("SIST0.\n"); } break; case R_SIST1: if (TB_R8(ISTAT, DIP) || TB_R8(ISTAT, SIP)) { state.sist1_stack |= interrupt; // printf("SIST1 stacked.\n"); } else { R8(SIST1) |= interrupt; // printf("SIST1.\n"); } break; case R_ISTAT: // printf("ISTAT.\n"); R8(ISTAT) |= interrupt; break; default: FAILURE_1(NotImplemented, "set_interrupt reg %02x!!\n", reg); } // printf("--> eval int\n"); eval_interrupts(); // printf("<-- eval_int\n"); } /** * Evaluate interrupt status. * * Check interrupt registers, and determine if an interrupt should be generated. **/ void CSym53C895::eval_interrupts() { // will_assert: when this boolean value is true at the end of this function, // an interrupt will be signalled to the system. bool will_assert = false; // will_halt: when this boolean value is true at the end of this function, // program execution will be halted. (fatal interrupt) bool will_halt = false; // Check current interrupt status. If no interrupt is active, move interrupt // flags from the interrupt stack down. // // (When an interrupt is signalled, but another interrupt bit is already // active, the interrupt doesn't go to the interrupt register, but to the // interrupt stack. The interrupt stack, however, doesn't keep track of the // order in which interrupts come in, so when the interrupt stack is moved // down into the interrupt registers, multiple interrupt bits may become // active.) if (!R8(SIST0) && !R8(SIST1) && !R8(DSTAT)) { R8(SIST0) |= state.sist0_stack; R8(SIST1) |= state.sist1_stack; R8(DSTAT) |= state.dstat_stack; state.sist0_stack = 0; state.sist1_stack = 0; state.dstat_stack = 0; } // Check for DMA interrupts. if (R8(DSTAT) & DSTAT_FATAL) { // DMA interrupt conditions always halt execution (always fatal) will_halt = true; // printf(" will halt(DSTAT).\n"); // Set the DMA interrupt pending bit. SB_R8(ISTAT, DIP, true); // If the interrupt is also enabled in the DIEN register, it will // be signalled to the system. if (R8(DSTAT) & R8(DIEN) & DSTAT_FATAL) { will_assert = true; // printf(" will assert(DSTAT).\n"); } } else { // Reset the DMA interrupt pending bit. (It may still be set). SB_R8(ISTAT, DIP, false); } // Check for SCSI engine interrupts if (R8(SIST0) || R8(SIST1)) { // Set the SCSI interrupt pending bit. SB_R8(ISTAT, SIP, true); // Check if the interrupt is either fatal, or enabled. if ((R8(SIST0) & (SIST0_FATAL | R8(SIEN0))) || (R8(SIST1) & (SIST1_FATAL | R8(SIEN1)))) { // In either case, stop execution. will_halt = true; // printf(" will halt(SIST).\n"); // If the interrupt is enabled, signal it to the system. if ((R8(SIST0) & R8(SIEN0)) || (R8(SIST1) & R8(SIEN1))) { will_assert = true; // printf(" will assert(SIST).\n"); } } } else { // Reset the SCSI interrupt pending bit (It may still be set). SB_R8(ISTAT, SIP, false); } // Check the interrupt on the fly bit. if (TB_R8(ISTAT, INTF)) { // Signal this to the system will_assert = true; // printf(" will assert(INTF).\n"); } // If interrupts are disabled, don't signal any interrupt to the system. if (TB_R8(DCNTL, IRQD)) { will_assert = false; // printf(" won't assert(IRQD).\n"); } // Halt execution if will_halt is true. if (will_halt) state.executing = false; // Assert or de-assert the interrupt line as needed if (will_assert != state.irq_asserted) { // printf(" doing...%d\n",will_assert); do_pci_interrupt(0, will_assert); state.irq_asserted = will_assert; } } ================================================ FILE: src/Sym53C895.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_SYM53C895_H_) #define INCLUDED_SYM53C895_H_ #include "DiskController.hpp" #include "PCIDevice.hpp" #include "SCSIDevice.hpp" /** * \brief Symbios Sym53C895 SCSI disk controller. * * \bug PROCGONE bugcheck during OpenVMS boot, probably because proper option *ROM is missing. * * Documentation consulted: * - SCSI 2 (http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf) * - SCSI 3 Multimedia Commands (MMC) *(http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf) * - SYM53C895 PCI-Ultra2 SCSI I/O Processor *(http://www.datasheet4u.com/html/S/Y/M/SYM53C895_LSILogic.pdf.html) * - Symbios SCSI SCRIPTS Processors Programming Guide *(http://la.causeuse.org/hauke/macbsd/symbios_53cXXX_doc/lsilogic-53cXXX-scripts.pdf) * . **/ class CSym53C895 : public CPCIDevice, public CDiskController, public CSCSIDevice { public: virtual int SaveState(FILE *f); virtual int RestoreState(FILE *f); virtual void check_state(); void run(); virtual void init(); virtual void start_threads(); virtual void stop_threads(); virtual void WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data); virtual u32 ReadMem_Bar(int func, int bar, u32 address, int dsize); virtual u32 config_read_custom(int func, u32 address, int dsize, u32 data); virtual void config_write_custom(int func, u32 address, int dsize, u32 old_data, u32 new_data, u32 data); virtual void register_disk(class CDisk *dsk, int bus, int dev); CSym53C895(CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); virtual ~CSym53C895(); private: void write_b_scntl0(u8 value); void write_b_scntl1(u8 value); void write_b_scntl3(u8 value); void write_b_istat(u8 value); u8 read_b_ctest2(); void write_b_ctest3(u8 value); void write_b_ctest4(u8 value); void write_b_ctest5(u8 value); void write_b_stest2(u8 value); void write_b_stest3(u8 value); u8 read_b_dstat(); u8 read_b_sist(int id); void write_b_dcntl(u8 value); u8 read_b_scratcha(int reg); u8 read_b_scratchb(int reg); void post_dsp_write(); int check_phase(int chk_phase); void execute_io_op(); void execute_rw_op(); void execute_ls_op(); void execute_mm_op(); void execute_tc_op(); void execute_bm_op(); void execute(); void eval_interrupts(); void set_interrupt(int reg, u8 interrupt); void chip_reset(); std::unique_ptr myThread; std::atomic_bool myThreadDead{false}; CSemaphore mySemaphore; CMutex *myRegLock; bool StopThread; /// The state structure contains all elements that need to be saved to the /// statefile. struct SSym_state { bool irq_asserted; union USym_regs { u8 reg8[128]; u16 reg16[64]; u32 reg32[64]; } regs; struct SSym_alu { bool carry; } alu; u8 ram[4096]; bool executing; bool wait_reselect; bool select_timeout; int disconnected; u32 wait_jump; u8 dstat_stack; u8 sist0_stack; u8 sist1_stack; long gen_timer; // int phase; } state; }; #endif // !defined(INCLUDED_SYM_H) ================================================ FILE: src/System.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "System.hpp" #include "AlphaCPU.hpp" #include "DPR.hpp" #include "StdAfx.hpp" #include "lockstep.hpp" #include #include #include #define CLOCK_RATIO 10000 #if defined(LS_MASTER) || defined(LS_SLAVE) char debug_string[10000] = ""; char *dbg_strptr = debug_string; #endif /** * Constructor. **/ CSystem::CSystem(CConfigurator *cfg) { int i; if (theSystem != 0) FAILURE(Configuration, "More than one system"); theSystem = this; myCfg = cfg; iNumComponents = 0; iNumMemories = 0; iNumCPUs = 0; iNumMemoryBits = (int)myCfg->get_num_value("memory.bits", false, 27); // iNumConfig = 0; #if defined(IDB) iSingleStep = 0; iSSCycles = 0; #endif for (i = 0; i < 4; i++) state.cchip.dim[i] = 0; state.cchip.drir = 0; state.cchip.misc = U64(0x0000000800000000); state.cchip.csc = U64(0x3142444014157803); state.dchip.drev = 0x01; state.dchip.dsc = 0x43; state.dchip.dsc2 = 0x03; state.dchip.str = 0x25; // initialize pchip data for (i = 0; i < 2; i++) { memset(&state.pchip[i], 0, sizeof(struct SSys_state::SSys_pchip)); state.pchip[i].wsba[3] = 2; } state.pchip[0].pctl = U64(0x0000104401440081); state.pchip[1].pctl = U64(0x0000504401440081); state.tig.FwWrite = 0; state.tig.HaltA = 0; state.tig.HaltB = 0; state.cpu_lock_flags = 0; if (iNumMemoryBits > 30) { // size_t may not be big enough, and makes 2^31 negative, so the // alloc fails. We're going to allocate the memory in // 2^(iNumMemoryBits-10) chunks of 2^10. CHECK_ALLOCATION(memory = calloc(1 << (iNumMemoryBits - 10), 1 << 10)); } else CHECK_ALLOCATION(memory = calloc(1 << iNumMemoryBits, 1)); cpu_lock_mutex = new CFastMutex("cpu-locking-lock"); printf("%s(%s): $Id: System.cpp,v 1.79 2008/06/12 07:29:44 iamcamiel Exp $\n", cfg->get_myName(), cfg->get_myValue()); } /** * Destructor. Calls the destructors for registered devices, and * frees used memory. **/ CSystem::~CSystem() { int i; printf("Freeing memory in use by system...\n"); for (i = 0; i < iNumComponents; i++) delete acComponents[i]; for (i = 0; i < iNumMemories; i++) free(asMemories[i]); free(memory); } /** * free memory, and allocate and clear new memory. **/ void CSystem::ResetMem(unsigned int membits) { free(memory); iNumMemoryBits = membits; CHECK_ALLOCATION(memory = calloc(1 << iNumMemoryBits, 1)); } /** * Register a device. **/ void CSystem::RegisterComponent(CSystemComponent *component) { acComponents[iNumComponents] = component; iNumComponents++; } void CSystem::UnregisterComponent(CSystemComponent *component) { iNumComponents--; } /** * Get the number of bits that corresponds to the amount of RAM installed. * (e.g. 28 = 256 MB, 29 = 512 MB, 30 = 1 GB) **/ unsigned int CSystem::get_memory_bits() { return iNumMemoryBits; } /** * Obtain a pointer to system memory. **/ char *CSystem::PtrToMem(u64 address) { if (address >> iNumMemoryBits) // Non Memory return 0; return &(((char *)memory)[(int)address]); } /** * Register a device as being a CPU. Return the CPU number. **/ int CSystem::RegisterCPU(class CAlphaCPU *cpu) { if (iNumCPUs >= 4) return -1; acCPUs[iNumCPUs] = cpu; iNumCPUs++; return iNumCPUs - 1; } /** * Reserve a range of the 64-bit system address space for a device. **/ int CSystem::RegisterMemory(CSystemComponent *component, int index, u64 base, u64 length) { struct SMemoryUser *m; int i; #if defined(CHECK_MEM_RANGES) for (i = 0; i < iNumMemories; i++) { if (component == asMemories[i]->component) continue; // check for overlaps if (base >= asMemories[i]->base && base <= (asMemories[i]->base + asMemories[i]->length - 1)) { printf( "WARNING: Start address for %s/%d (%016" PRIx64 "-%016" PRIx64 ")\n" " is within memory range of %s/%d (%016" PRIx64 "-%016" PRIx64 ").\n", component->devid_string, index, base, base + length - 1, asMemories[i]->component->devid_string, asMemories[i]->index, asMemories[i]->base, asMemories[i]->base + asMemories[i]->length - 1); } if (base + length - 1 >= asMemories[i]->base && base + length - 1 <= (asMemories[i]->base + asMemories[i]->length - 1)) { printf("WARNING: End address for %s/%d (%016" PRIx64 "-%016" PRIx64 ")\n" " is within memory range of %s/%d (%016" PRIx64 "-%016" PRIx64 ").\n", component->devid_string, index, base, base + length - 1, asMemories[i]->component->devid_string, asMemories[i]->index, asMemories[i]->base, asMemories[i]->base + asMemories[i]->length - 1); } } #endif // defined(CHECK_MEM_RANGES) for (i = 0; i < iNumMemories; i++) { if ((asMemories[i]->component == component) && (asMemories[i]->index == index)) { asMemories[i]->base = base; asMemories[i]->length = length; return 0; } } CHECK_ALLOCATION( m = (struct SMemoryUser *)malloc(sizeof(struct SMemoryUser))); m->component = component; m->base = base; m->length = length; m->index = index; asMemories[iNumMemories] = m; iNumMemories++; return 0; } int got_sigint = 0; /** * Handle a SIGINT by setting a flag that terminates the emulator. **/ void sigint_handler(int signum) { got_sigint = 1; } /** * Run the system by clocking the CPU(s) and devices. **/ void CSystem::Run() { int i; int k; #if defined(DUMP_MEMMAP) printf("ES40 Memory Map\n"); printf("Physical Address Size Device/Index\n"); printf("---------------- -------- -------------------------\n"); for (i = 0; i < iNumMemories; i++) { printf("%016" PRIx64 " %8x %s/%d\n", asMemories[i]->base, asMemories[i]->length, asMemories[i]->component->devid_string, asMemories[i]->index); } #endif // defined(DUMP_MEMMAP) /* catch CTRL-C and shutdown gracefully */ signal(SIGINT, &sigint_handler); start_threads(); for (k = 0;; k++) { if (got_sigint) FAILURE(Graceful, "CTRL-C detected"); std::this_thread::sleep_for(std::chrono::milliseconds(100)); for (i = 0; i < iNumComponents; i++) acComponents[i]->check_state(); #if !defined(HIDE_COUNTER) #if defined(PROFILE) printf("%d | %016" PRIx64 " | %" PRId64 " profiled instructions. \r", k, acCPUs[0]->get_pc(), profiled_insts); #else // defined(PROFILE) printf("%d | %016" PRIx64 "\r", k, acCPUs[0]->get_pc()); #endif // defined(PROFILE) #endif // defined(HIDE_COUNTER) } // printf ("%%SYS-W-SHUTDOWN: CTRL-C or Device Failed\n"); // return 1; } /** * Do one clock tick. The cpu(s) will execute one single instruction, and * some devices may be clocked. **/ int CSystem::SingleStep() { for (int i = 0; i < iNumCPUs; i++) if (!acCPUs[i]->get_waiting()) acCPUs[i]->execute(); // iSingleStep++; #if defined(LS_MASTER) || defined(LS_SLAVE) if (!(iSingleStep % 50)) { lockstep_sync_m2s("sync1"); *dbg_strptr = '\0'; lockstep_compare(debug_string); dbg_strptr = debug_string; *dbg_strptr = '\0'; } #endif // defined(LS_MASTER) || defined(LS_SLAVE) // if (iSingleStep >= CLOCK_RATIO) // { // iSingleStep = 0; // for(i=0;iDoClock(); // if (result) // return result; // } //#ifdef IDB // iSSCycles++; //#if !defined(LS_SLAVE) // if (bHashing) //#endif // printf("%d | %016" PRIx64 "\r",iSSCycles,acCPUs[0]->get_pc()); //#endif // } return 0; } #if defined(DEBUG_PORTACCESS) u64 lastport; #endif // defined(DEBUG_PORTACCESS) void CSystem::cpu_lock(int cpuid, u64 address) { SCOPED_FM_LOCK(cpu_lock_mutex); // printf("cpu%d: lock %" PRIx64 ". \n",cpuid,address); state.cpu_lock_flags |= (1 << cpuid); state.cpu_lock_address[cpuid] = address; } bool CSystem::cpu_unlock(int cpuid) { SCOPED_FM_LOCK(cpu_lock_mutex); bool retval; retval = state.cpu_lock_flags & (1 << cpuid); // printf("cpu%d: unlock (%s). \n",cpuid,retval?"ok":"failed"); state.cpu_lock_flags &= ~(1 << cpuid); return retval; } void CSystem::cpu_break_lock(int cpuid, CSystemComponent *source) { SCOPED_FM_LOCK(cpu_lock_mutex); printf("cpu%d: lock broken by %s. \n", cpuid, source->devid_string); state.cpu_lock_flags &= ~(1 << cpuid); } /** * \brief Write 8, 4, 2 or 1 byte(s) to a 64-bit system address. This could be *memory, internal chipset registers, nothing or some device. * * Source: HRM, 10.1.1: * * System Space and Address Map * * The system address space is divided into two parts: system memory and PIO. *This division is indicated by physical memory bit <43> = 1 for PIO accesses *from the CPU, and by the PTP bit in the window registers for PTP accesses from *the Pchip. While the operating system may choose bit <40> instead of bit <43> *to represent PIO space, bit <43> is used throughout this chapter. In general, *bits <42:35> are don�t cares if bit <43> is asserted. * * There is 16GB of PIO space available on the 21272 chipset with 8GB assigned *to each Pchip. The Pchip supports up to bit <34> (35 bits total) of system *address. However, the Version 1 Cchip only supports 4GB of system memory (32 *bits total). As described in Chapter 6, the CAPbus protocol between the Pchip *and Cchip does support up to bit <34>, as does the Cchip�s interface to the *CPU. The Typhoon Cchip supports 32GB of system memory (35 bits total). * * The system address space is divided as shown in the following table: * * \code * +-------------------+--------+-------------------------------+---------------------------------+ * | Space | Size | System Address <43:0> | Comments | * +-------------------+--------+-------------------------------+---------------------------------+ * | System memory | 4GB | 000.0000.0000 - 000.FFFF.FFFF | Cacheable and *prefetchable. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 8188GB | 001.0000.0000 - 7FF.FFFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI memory | 4GB | 800.0000.0000 - 800.FFFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | TIGbus | 1GB | 801.0000.0000 - 801.3FFF.FFFF | addr<5:0> = 0. *Single byte | | | | | valid in quadword access. *| | | | | 16MB *accessible. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 1GB | 801.4000.0000 - 801.7FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 CSRs | 256MB | 801.8000.0000 - 801.8FFF.FFFF | addr<5:0> = 0. *Quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 256MB | 801.9000.0000 - 801.9FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Cchip CSRs | 256MB | 801.A000.0000 - 801.AFFF.FFFF | addr<5:0> = 0. *Quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Dchip CSRs | 256MB | 801.B000.0000 - 801.BFFF.FFFF | addr<5:0> = 0. *All eight bytes | | | | | in quadword access must be *| | | | | identical. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 768MB | 801.C000.0000 - 801.EFFF.FFFF | � | | Reserved *| 128MB | 801.F000.0000 - 801.F7FF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip 0 PCI IACK | 64MB | 801.F800.0000 - 801.FBFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI I/O | 32MB | 801.FC00.0000 - 801.FDFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI conf | 16MB | 801.FE00.0000 - 801.FEFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 16MB | 801.FF00.0000 - 801.FFFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI memory | 4GB | 802.0000.0000 - 802.FFFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 2GB | 803.0000.0000 - 803.7FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 CSRs | 256MB | 803.8000.0000 - 803.8FFF.FFFF | addr<5:0> = 0, *quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 1536MB | 803.9000.0000 - 803.EFFF.FFFF | � | | Reserved *| 128MB | 803.F000.0000 - 803.F7FF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip 1 PCI IACK | 64MB | 803.F800.0000 - 803.FBFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI I/O | 32MB | 803.FC00.0000 - 803.FDFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI conf | 16MB | 803.FE00.0000 - 803.FEFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 16MB | 803.FF00.0000 - 803.FFFF.FFFF | � | | Reserved *| 8172GB | 804.0000.0000 - FFF.FFFF.FFFF | Bits <42:35> are don�t cares if | * | | | | bit <43> is *asserted. | * +-------------------+--------+-------------------------------+---------------------------------+ * \endcode **/ void CSystem::WriteMem(u64 address, int dsize, u64 data, CSystemComponent *source) { u64 a; int i; u8 *p; #if defined(ALIGN_MEM_ACCESS) u64 t64; u32 t32; u16 t16; #endif // defined(ALIGN_MEM_ACCESS) if (state.cpu_lock_flags) { for (i = 0; i < iNumCPUs; i++) { if ((state.cpu_lock_flags & (1 << i)) && (!((state.cpu_lock_address[i] ^ address) & U64(0x00000807ffffff00))) && (source != acCPUs[i])) cpu_break_lock(i, source); } } a = address & U64(0x00000807ffffffff); if (a >> iNumMemoryBits) // non-memory { // check registered device memory ranges for (i = 0; i < iNumMemories; i++) { if ((a >= asMemories[i]->base) && (a < asMemories[i]->base + asMemories[i]->length)) { asMemories[i]->component->WriteMem( asMemories[i]->index, a - asMemories[i]->base, dsize, data); return; } } if ((a == U64(0x00000801FC000CF8)) && (dsize == 32)) { state.cf8_address[0] = (u32)data & 0x00ffffff; return; } if ((a == U64(0x00000803FC000CF8)) && (dsize == 32)) { state.cf8_address[1] = (u32)data & 0x00ffffff; return; } if ((a == U64(0x00000801FC000CFC)) && (dsize == 32)) { printf("PCI 0 config space write through CF8/CFC mechanism. \n"); getc(stdin); WriteMem(U64(0x00000801FE000000) | state.cf8_address[0], dsize, data, source); return; } if ((a == U64(0x00000803FC000CFC)) && (dsize == 32)) { printf("PCI 1 config space write through CF8/CFC mechanism. \n"); getc(stdin); WriteMem(U64(0x00000803FE000000) | state.cf8_address[1], dsize, data, source); return; } if (a >= U64(0x00000801A0000000) && a <= U64(0x00000801AFFFFFFF)) { cchip_csr_write((u32)a & 0xFFFFFFF, data, source); return; } if (a >= U64(0x0000080180000000) && a <= U64(0x000008018FFFFFFF)) { pchip_csr_write(0, (u32)a & 0xFFFFFFF, data); return; } if (a >= U64(0x0000080380000000) && a <= U64(0x000008038FFFFFFF)) { pchip_csr_write(1, (u32)a & 0xFFFFFFF, data); return; } if (a >= U64(0x00000801B0000000) && a <= U64(0x00000801BFFFFFFF)) { dchip_csr_write((u32)a & 0xFFFFFFF, (u8)data & 0xff); return; } if (a >= U64(0x0000080100000000) && a <= U64(0x000008013FFFFFFF)) { tig_write((u32)a & 0x3FFFFFFF, (u8)data); return; } if (a >= U64(0x801fc000000) && a < U64(0x801fe000000)) { // Unused PCI I/O space // if (source) // printf("Write to unknown IO port %" LL"x on PCI 0 from %s \n",a // & U64(0x1ffffff),source->devid_string); // else // printf("Write to unknown IO port %" LL"x on PCI 0 \n",a & // U64(0x1ffffff)); return; } if (a >= U64(0x803fc000000) && a < U64(0x803fe000000)) { // Unused PCI I/O space if (source) { printf("Write to unknown IO port %" PRIx64 " on PCI 1 from %s \n", a & U64(0x1ffffff), source->devid_string); } else printf("Write to unknown IO port %" PRIx64 " on PCI 1 \n", a & U64(0x1ffffff)); return; } if (a >= U64(0x80000000000) && a < U64(0x80100000000)) { // Unused PCI memory space u64 paddr = a & U64(0xffffffff); if (paddr > 0xb8fff || paddr < 0xb8000) { // skip legacy video if (source) { printf("Write to unknown memory %" PRIx64 " on PCI 0 from %s \n", a & U64(0xffffffff), source->devid_string); } else printf("Write to unknown memory %" PRIx64 " on PCI 0 \n", a & U64(0xffffffff)); } } if (a >= U64(0x80200000000) && a < U64(0x80300000000)) { // Unused PCI memory space if (source) { printf("Write to unknown memory %" PRIx64 " on PCI 1 from %s \n", a & U64(0xffffffff), source->devid_string); } else printf("Write to unknown memory %" PRIx64 " on PCI 1 \n", a & U64(0xffffffff)); return; } #ifdef DEBUG_UNKMEM if (source) printf("Write to unknown memory %" PRIx64 " from %s \n", a, source->devid_string); else printf("Write to unknown memory %" PRIx64 " \n", a); #endif // defined(DEBUG_UNKMEM) return; } p = (u8 *)memory + a; switch (dsize) { case 8: *((u8 *)p) = (u8)data; break; case 16: *((u16 *)p) = endian_16((u16)data); break; case 32: *((u32 *)p) = endian_32((u32)data); break; default: *((u64 *)p) = endian_64((u64)data); } } /** * \brief Read 8, 4, 2 or 1 byte(s) from a 64-bit system address. This could be *memory, internal chipset registers, nothing or some device. * * Source: HRM, 10.1.1: * * System Space and Address Map * * The system address space is divided into two parts: system memory and PIO. *This division is indicated by physical memory bit <43> = 1 for PIO accesses *from the CPU, and by the PTP bit in the window registers for PTP accesses from *the Pchip. While the operating system may choose bit <40> instead of bit <43> *to represent PIO space, bit <43> is used throughout this chapter. In general, *bits <42:35> are don�t cares if bit <43> is asserted. * * There is 16GB of PIO space available on the 21272 chipset with 8GB assigned *to each Pchip. The Pchip supports up to bit <34> (35 bits total) of system *address. However, the Version 1 Cchip only supports 4GB of system memory (32 *bits total). As described in Chapter 6, the CAPbus protocol between the Pchip *and Cchip does support up to bit <34>, as does the Cchip�s interface to the *CPU. The Typhoon Cchip supports 32GB of system memory (35 bits total). * * The system address space is divided as shown in the following table: * * \code * +-------------------+--------+-------------------------------+---------------------------------+ * | Space | Size | System Address <43:0> | Comments | * +-------------------+--------+-------------------------------+---------------------------------+ * | System memory | 4GB | 000.0000.0000 - 000.FFFF.FFFF | Cacheable and *prefetchable. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 8188GB | 001.0000.0000 - 7FF.FFFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI memory | 4GB | 800.0000.0000 - 800.FFFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | TIGbus | 1GB | 801.0000.0000 - 801.3FFF.FFFF | addr<5:0> = 0. *Single byte | | | | | valid in quadword access. *| | | | | 16MB *accessible. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 1GB | 801.4000.0000 - 801.7FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 CSRs | 256MB | 801.8000.0000 - 801.8FFF.FFFF | addr<5:0> = 0. *Quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 256MB | 801.9000.0000 - 801.9FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Cchip CSRs | 256MB | 801.A000.0000 - 801.AFFF.FFFF | addr<5:0> = 0. *Quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Dchip CSRs | 256MB | 801.B000.0000 - 801.BFFF.FFFF | addr<5:0> = 0. *All eight bytes | | | | | in quadword access must be *| | | | | identical. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 768MB | 801.C000.0000 - 801.EFFF.FFFF | � | | Reserved *| 128MB | 801.F000.0000 - 801.F7FF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip 0 PCI IACK | 64MB | 801.F800.0000 - 801.FBFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI I/O | 32MB | 801.FC00.0000 - 801.FDFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip0 PCI conf | 16MB | 801.FE00.0000 - 801.FEFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 16MB | 801.FF00.0000 - 801.FFFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI memory | 4GB | 802.0000.0000 - 802.FFFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 2GB | 803.0000.0000 - 803.7FFF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 CSRs | 256MB | 803.8000.0000 - 803.8FFF.FFFF | addr<5:0> = 0, *quadword access. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 1536MB | 803.9000.0000 - 803.EFFF.FFFF | � | | Reserved *| 128MB | 803.F000.0000 - 803.F7FF.FFFF | � | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip 1 PCI IACK | 64MB | 803.F800.0000 - 803.FBFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI I/O | 32MB | 803.FC00.0000 - 803.FDFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Pchip1 PCI conf | 16MB | 803.FE00.0000 - 803.FEFF.FFFF | Linear *addressing. | * +-------------------+--------+-------------------------------+---------------------------------+ * | Reserved | 16MB | 803.FF00.0000 - 803.FFFF.FFFF | � | | Reserved *| 8172GB | 804.0000.0000 - FFF.FFFF.FFFF | Bits <42:35> are don�t cares if | * | | | | bit <43> is *asserted. | * +-------------------+--------+-------------------------------+---------------------------------+ * \endcode **/ u64 CSystem::ReadMem(u64 address, int dsize, CSystemComponent *source) { u64 a; int i; u8 *p; a = address & U64(0x00000807ffffffff); if (a >> iNumMemoryBits) // Non Memory { // check registered device memory ranges for (i = 0; i < iNumMemories; i++) { if ((a >= asMemories[i]->base) && (a < asMemories[i]->base + asMemories[i]->length)) return asMemories[i]->component->ReadMem( asMemories[i]->index, a - asMemories[i]->base, dsize); } if ((a == U64(0x00000801FC000CFC)) && (dsize == 32)) { printf("PCI 0 config space read through CF8/CFC mechanism. \n"); getc(stdin); return ReadMem(U64(0x00000801FE000000) | state.cf8_address[0], dsize, source); } if ((a == U64(0x00000803FC000CFC)) && (dsize == 32)) { printf("PCI 1 config space read through CF8/CFC mechanism. \n"); getc(stdin); return ReadMem(U64(0x00000803FE000000) | state.cf8_address[1], dsize, source); } if (a >= U64(0x00000801A0000000) && a <= U64(0x00000801AFFFFFFF)) return cchip_csr_read((u32)a & 0xFFFFFFF, source); if (a >= U64(0x0000080180000000) && a <= U64(0x000008018FFFFFFF)) return pchip_csr_read(0, (u32)a & 0xFFFFFFF); if (a >= U64(0x0000080380000000) && a <= U64(0x000008038FFFFFFF)) return pchip_csr_read(1, (u32)a & 0xFFFFFFF); if (a >= U64(0x00000801B0000000) && a <= U64(0x00000801BFFFFFFF)) return dchip_csr_read((u32)a & 0xFFFFFFF) * U64(0x0101010101010101); if (a >= U64(0x0000080100000000) && a <= U64(0x000008013FFFFFFF)) return tig_read((u32)a & 0x3FFFFFFF); if ((a >= U64(0x801fe000000) && a < U64(0x801ff000000)) || (a >= U64(0x803fe000000) && a < U64(0x803ff000000))) { // Unused PCI configuration space switch (dsize) { case 8: return X64_BYTE; case 16: return X64_WORD; case 32: return X64_LONG; case 64: return X64_QUAD; } } if (a >= U64(0x800000c0000) && a < U64(0x801000e0000)) { // Unused PCI ROM BIOS space return 0; } if (a >= U64(0x801fc000000) && a < U64(0x801fe000000)) { // Unused PCI I/O space // if (source) // printf("Read from unknown IO port %" LL"x on PCI 0 from %s \n",a & // U64(0x1ffffff),source->devid_string); // else // printf("Read from unknown IO port %" LL"x on PCI 0 \n",a & // U64(0x1ffffff)); return 0; } if (a >= U64(0x803fc000000) && a < U64(0x803fe000000)) { // Unused PCI I/O space if (source) { printf("Read from unknown IO port %" PRIx64 " on PCI 1 from %s \n", a & U64(0x1ffffff), source->devid_string); } else printf("Read from unknown IO port %" PRIx64 " on PCI 1 \n", a & U64(0x1ffffff)); return 0; } if (a >= U64(0x80000000000) && a < U64(0x80100000000)) { // Unused PCI memory space u64 paddr = a & U64(0xffffffff); if (paddr > 0xb8fff || paddr < 0xb8000) { // skip legacy video if (source) { printf("Read from unknown memory %" PRIx64 " on PCI 0 from %s \n", a & U64(0xffffffff), source->devid_string); } else printf("Read from unknown memory %" PRIx64 " on PCI 0 \n", a & U64(0xffffffff)); } return 0; } if (a >= U64(0x80200000000) && a < U64(0x80300000000)) { // Unused PCI memory space if (source) { printf("Read from unknown memory %" PRIx64 " on PCI 1 from %s \n", a & U64(0xffffffff), source->devid_string); } else printf("Read from unknown memory %" PRIx64 " on PCI 1 \n", a & U64(0xffffffff)); return 0; } #if defined(DEBUG_UNKMEM) if (source) printf("Read from unknown memory %" PRIx64 " from %s \n", a, source->devid_string); else printf("Read from unknown memory %" PRIx64 " \n", a); #endif // defined(DEBUG_UNKMEM) return 0x00; // return 0x77; // 7f } p = (u8 *)memory + a; switch (dsize) { case 8: return *((u8 *)p); case 16: return endian_16(*((u16 *)p)); case 32: return endian_32(*((u32 *)p)); default: return endian_64(*((u64 *)p)); } } /** * \brief Read one of the PCHIP registers. * * Source: HRM, 10.2.5: * * \code * +-------------+---------------+------+----+ * |Register | Address | Type | ## | * +-------------+---------------+------+----+ * | P0-WSBA0 | 801.8000.0000 | RW | 00 | * | P0-WSBA1 | 801.8000.0040 | RW | 01 | * | P0-WSBA2 | 801.8000.0080 | RW | 02 | * | P0-WSBA3 | 801.8000.00C0 | RW | 03 | * +-------------+---------------+------+----+ * | P0-WSM0 | 801.8000.0100 | RW | 04 | * | P0-WSM1 | 801.8000.0140 | RW | 05 | * | P0-WSM2 | 801.8000.0180 | RW | 06 | * | P0-WSM3 | 801.8000.01C0 | RW | 07 | * +-------------+---------------+------+----+ * | P0-TBA0 | 801.8000.0200 | RW | 08 | * | P0-TBA1 | 801.8000.0240 | RW | 09 | * | P0-TBA2 | 801.8000.0280 | RW | 0A | * | P0-TBA3 | 801.8000.02C0 | RW | 0B | * +-------------+---------------+------+----+ * | P0-PCTL | 801.8000.0300 | RW | 0C | * +-------------+---------------+------+----+ * | P0-PLAT | 801.8000.0340 | RW | 0D | * +-------------+---------------+------+----+ * | P0-RES | 801.8000.0380 | RW | 0E | * +-------------+---------------+------+----+ * | P0-PERROR | 801.8000.03C0 | RW | 0F | * | P0-PERRMASK | 801.8000.0400 | RW | 10 | * | P0-PERRSET | 801.8000.0440 | WO | 11 | * +-------------+---------------+------+----+ * | P0-TLBIV | 801.8000.0480 | WO | 12 | * | P0-TLBIA | 801.8000.04C0 | WO | 13 | * +-------------+---------------+------+----+ * | P0-PMONCTL | 801.8000.0500 | RW | 14 | * | P0-PMONCNT | 801.8000.0540 | RO | 15 | * +-------------+---------------+------+----+ * | P0-SPRST | 801.8000.0800 | WO | 20 | * +-------------+---------------+------+----+ * \endcode * * Window Space Base Address Register (WSBAn - RW) * * Because the information in the WSBAn registers and WSMn registers * is used to compare against the PCI address, a clock-domain crossing (from * i_sysclk to i_pclko<7:0>) is made when these registers are written. *Therefore, for a period of several clock cycles, a window is disabled when its *contents are disabled. If PCI bus activity, which accesses the window in *question, is not stopped before updating that window, the Pchip might fail to *respond with b_devsel_l when it should. This would result in a master abort *condition on the PCI bus. Therefore, before a window (base or mask) is *updated, all PCI activity accessing that window must be stopped, even if only *some activity is being added or deleted. * * The contents of the window may be read back to confirm that the update has *taken place. Then PCI activity through that window can be resumed. * * \code * +-------+---------+---------+------+--------------------------+ * | Field | Bits | Type | Init | Description | * +-------+---------+---------+------+--------------------------+ * | RES | <63:40> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * | DAC | <39> | RW | 0 | DAC enable (WSBA3 only!) | * +-------+---------+---------+------+--------------------------+ * | RES | <38:32> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * | ADDR | <31:20> | RW | 0 | Base address (not used | * | | | | | DAC enable = 1) | * +-------+---------+---------+------+--------------------------+ * | RES | <19:2> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * | SG | <1> | RW | 0 | Scatter-gather | * +-------+---------+---------+------+--------------------------+ * | ENA | <0> | RW | 0 | Enable | * +-------+---------+---------+------+--------------------------+ * \endcode * * Window Space Mask Register (WSM0, WSM1, WSM2, WSM3 - RW) * * \code * +-------+---------+---------+------+--------------------------+ * | Field | Bits | Type | Init | Description | * +-------+---------+---------+------+--------------------------+ * | RES | <63:32> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * | AM | <31:20> | RW | 0 | Address mask | * +-------+---------+---------+------+--------------------------+ * | RES | <19:0> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * \endcode * * Translated Base Address Register (TBAn - RW) * * \code * +-------+---------+---------+------+--------------------------+ * | Field | Bits | Type | Init | Description | * +-------+---------+---------+------+--------------------------+ * | RES | <63:35> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * | ADDR | <34:10> | RW | 0 | Translated base address | * | | | | | (if DAC enable = 1, bits | * | | | | | <34:22> are the PT Origin| * | | | | | address <34:22> and bits | * | | | | | <21:10> are ignored) | * +-------+---------+---------+------+--------------------------+ * | RES | <9:0> | MBZ,RAZ | 0 | Reserved | * +-------+---------+---------+------+--------------------------+ * \encode * * Pchip Control Register (PCTL - RW) * * \code * +---------+---------+---------+------+-------------------------------------+ * | Field | Bits | Type | Init | Description | * +---------+---------+---------+------+-------------------------------------+ * | RES | <63:48> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | PID | <47:46> | RO | 1) | Pchip ID. | * +---------+---------+---------+------+-------------------------------------+ * | RPP | <45> | RO | 2) | Remote Pchip present. | * +---------+---------+---------+------+-------------------------------------+ * | PTEVRFY | <44> | RW | | PTE verify for DMA read. | * | | | | | Val Description | * | | | | | 0 If TLB miss, then make DMA | * | | | | | read request as soon as possi-| * | | | | | ble and discard data if PTE | * | | | | | was not valid - could cause | * | | | | | Cchip nonexistent mem. error. | * | | | | | 1 If TLB miss, then delay read | * | | | | | request until PTE is verified | * | | | | | as valid - no request if not | * | | | | | valid. | * +---------+---------+---------+------+-------------------------------------+ * | FDWDIS | <43> | RW | | Fast DMA read cache block wrap | * | | | | | request disable. | * | | | | | Val Description | * | | | | | 0 Normal operation. | * | | | | | 1 Reserved for testing purposes | * | | | | | only. | * +---------+---------+---------+------+-------------------------------------+ * | FDSDIS | <42> | RW | | Fast DMA start and SGTE request | * | | | | | disable. | * | | | | | Val Description | * | | | | | 0 Normal operation. | * | | | | | 1 Reserved for testing purposes | * | | | | | only. | * +---------+---------+---------+------+-------------------------------------+ * | PCLKX | <41:40> | RO | 3) | PCI clock frequency multiplier | * | | | | | Val Multiplier | * | | | | | 0 x6 | * | | | | | 1 x4 | * | | | | | 2 x5 | * | | | | | 3 Reserved | * +---------+---------+---------+------+-------------------------------------+ * | PTPMAX | <39:36> | RW | 2 | Maximum PTP requests to Cchip from | * | | | | | both Pchips until returned on | * | | | | | CAPbus, modulo 16 (minimum = 2) | * | | | | | (use 4 for pass 1 Cchip and Dchip). | * +---------+---------+---------+------+-------------------------------------+ * | CRQMAX | <35:32> | RW | 1 | Maximum requests to Cchip from both | * | | | | | Pchips until Ack, modulo 16 (use 4 | * | | | | | for Cchip). (Use 3 or less for | * | | | | | Typhoon because there is one less | * | | | | | skid buffer in the C4 chip.) | * +---------+---------+---------+------+-------------------------------------+ * | REV | <31:24> | RO | 0 | In conjunction with the state of | * | | | | | PMONCTL<0>, this field indicates | * | | | | | the revision of the Pchip. | * +---------+---------+---------+------+-------------------------------------+ * | CDQMAX | <23:20> | RW | 1 | Maximum data transfers to Dchips | * | | | | | from both Pchips until Ack, modulo | * | | | | | 16 (use 4 for Dchip). Must be same | * | | | | | as Cchip CSR CSC. | * +---------+---------+---------+------+-------------------------------------+ * | PADM | <19> | RW | 4) | PADbus mode. | * | | | | | Val Mode | * | | | | | 0 8-nibble, 8-check bit mode | * | | | | | 1 4-byte, 4-check bit mode | * +---------+---------+---------+------+-------------------------------------+ * | ECCEN | <18> | RW | 0 | ECC enable for DMA and SGTE access. | * +---------+---------+---------+------+-------------------------------------+ * | RES | <17:16> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | PPRI | <15> | | 0 | Arbiter prio group for the Pchip. | * +---------+---------+---------+------+-------------------------------------+ * | PRIGRP | <14:8> | RW | 0 | Arbiter prio group; one bit per PCI | * | | | | | slot with bits <14:8> corresponding | * | | | | | to input b_req_l<6:0>. | * | | | | | Val Group | * | | | | | 0 Low-priority group | * | | | | | 1 High-priority group | * +---------+---------+---------+------+-------------------------------------+ * | ARBENA | <7> | RW | 0 | Internal arbiter enable. | * +---------+---------+---------+------+-------------------------------------+ * | MWIN | <6> | RW | 0 | Monster window enable. | * +---------+---------+---------+------+-------------------------------------+ * | HOLE | <5> | RW | 0 | 512KB-to-1MB window hole enable. | * +---------+---------+---------+------+-------------------------------------+ * | TGTLAT | <4> | RW | 0 | Target latency timers enable. | * | | | | | Val Mode | * | | | | | 0 Retry/disconnect after 128 | * | | | | | PCI clocks without data. | * | | | | | 1 Retry initial request after | * | | | | | 32 PCI clocks without data; | * | | | | | disconnect subsequent trans- | * | | | | | fers after 8 PCI clocks | * | | | | | without data. | * +---------+---------+---------+------+-------------------------------------+ * | CHAINDIS| <3> | RW | 0 | Disable chaining. | * +---------+---------+---------+------+-------------------------------------+ * | THDIS | <2> | RW | 0 | Disable antithrash mechan. for TLB. | * | | | | | Val Mode | * | | | | | 0 Normal operation | * | | | | | 1 Testing purposes only | * +---------+---------+---------+------+-------------------------------------+ * | FBTB | <1> | RW | 0 | Fast back-to-back enable. | * +---------+---------+---------+------+-------------------------------------+ * | FDSC | <0> | RW | 0 | Fast discard enable. | * | | | | | Val Mode | * | | | | | 0 Discard data if no retry | * | | | | | after 215 PCI clocks. | * | | | | | 1 Discard data if no retry | * | | | | | after 210 PCI clocks. | * +---------+---------+---------+------+-------------------------------------+ * * 1) This field is initialized from the PID pins. * 2) This field is initialized from the assertion of CREQRMT_L pin at system *reset. 3) This field is initialized from the PCI i_pclkdiv<1:0> pins. 4) This *field is initialized from a decode of the b_cap<1:0> pins. \endcode * * Pchip Error Register (PERROR - RW) * * If any of bits <11:0> are set, then this entire register is frozen and the *Pchip output signal b_error is asserted. Only bit <0> can be set after that. *All other values will be held until all of bits <11:0> are clear. When an *error is detected and one of bits <11:0> becomes set, the associated *information is captured in bits <63:16> of this register. After the *information is captured, the INV bit is cleared, but the information is not *valid and should not be used if INV is set. * * In rare circumstances involving more than one error, INV may remain set *because the Pchip cannot correctly capture the SYN, CMD, or ADDR field. * * Furthermore, if software reads PERROR in a polling loop, or reads PERROR *before the Pchip�s error signal is reflected in the Cchip�s DRIR CSR, the INV *bit may also be set. * * To avoid the latter condition, read PERROR only after receiving an IRQ0 *interrupt, then read the Cchip DIR CSR to determine that this Pchip has *detected an error. * * \code * +---------+---------+---------+------+-------------------------------------+ * | Field | Bits | Type | Init | Description | * +---------+---------+---------+------+-------------------------------------+ * | SYN | <63:56> | RO | 0 | errors ECC syndrome if CRE or UECC. | * +---------+---------+---------+------+-------------------------------------+ * | CMD | <55:52> | RO | 0 | PCI command of transaction when | * | | | | | error detected if not CRE and not | * | | | | | UECC. If CRE or UECC, then: | * | | | | | Val Command | * | | | | | 0000 DMA read | * | | | | | 0001 DMA RMW | * | | | | | 0011 SGTE read | * | | | | | Others Reserved | * +---------+---------+---------+------+-------------------------------------+ * | INV | <51> | RO Rev1 | 0 | Info Not Valid - only meaningful | * | | | RAZ Rev0| | when one of bits <11:0> is set. | * | | | | | Indicates validity of , , | * | | | | | and fields. | * | | | | | Val Mode | * | | | | | 0 Info fields are valid. | * | | | | | 1 Info fields are not valid. | * +---------+---------+---------+------+-------------------------------------+ * | ADDR | <50:16> | RO | 0 | If CRE or UECC, then ADDR<50:19> = | * | | | | | system address <34:3> of erroneous | * | | | | | quadword and ADDR<18:16> = 0. | * | | | | | If not CRE and not UECC, then | * | | | | | ADDR<50:48> = 0; ADDR<47:18> = star-| * | | | | | ting PCI address <31:2> of trans- | * | | | | | action when error was detected; | * | | | | | ADDR<17:16> = 00 --> not a DAC | * | | | | | operation; | * | | | | | ADDR<17:16> = 01 --> via DAC SG | * | | | | | Window 3; | * | | | | | ADDR<17> = 1 --> via Monster Window | * +---------+---------+---------+------+-------------------------------------+ * | RES | <15:12> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | CRE | <11> | R,W1C | 0 | Correctable ECC error. | * +---------+---------+---------+------+-------------------------------------+ * | UECC | <10> | R,W1C | 0 | Uncorrectable ECC error. | * +---------+---------+---------+------+-------------------------------------+ * | RES | <9> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | NDS | <8> | R,W1C | 0 | No b_devsel_l as PCI master. | * +---------+---------+---------+------+-------------------------------------+ * | RDPE | <7> | R,W1C | 0 | PCI read data parity error as PCI | * | | | | | master. | * +---------+---------+---------+------+-------------------------------------+ * | TA | <6> | R,W1C | 0 | Target abort as PCI master. | * +---------+---------+---------+------+-------------------------------------+ * | APE | <5> | R,W1C | 0 | Address parity error detected as | * | | | | | potential PCI target. | * +---------+---------+---------+------+-------------------------------------+ * | SGE | <4> | R,W1C | 0 | Scatter-gather had invalid page | * | | | | | table entry. | * +---------+---------+---------+------+-------------------------------------+ * | DCRTO | <3> | R,W1C | 0 | Delayed completion retry timeout as | * | | | | | PCI target. | * +---------+---------+---------+------+-------------------------------------+ * | PERR | <2> | R,W1C | 0 | b_perr_l sampled asserted. | * +---------+---------+---------+------+-------------------------------------+ * | SERR | <1> | R,W1C | 0 | b_serr_l sampled asserted. | * +---------+---------+---------+------+-------------------------------------+ * | LOST | <0> | R,W1C | 0 | Lost an error because it was detec- | * | | | | | ted after this register was frozen, | * | | | | | or while in the process of clearing | * | | | | | this register. | * +---------+---------+---------+------+-------------------------------------+ * \endcode * * Pchip Error Mask Register (PERRMASK - RW) * * If any of the MASK bits have the value 0, they prevent the setting of the *corresponding bit in the PERROR register, regardless of the detection of *errors or writing to PERRSET. * * The default is for all errors to be disabled. * * Beside masking the reporting of errors in PERROR, certain bits of PERRMASK *have the following additional effects: * - If PERRMASK = 0, the Pchip ignores read data parity as the PCI *master. * - If PERRMASK = 0, the Pchip ignores write data parity as the PCI *target. * - If PERRMASK = 0, the Pchip ignores address parity. * . * * \code * +---------+---------+---------+------+-------------------------------------+ * | Field | Bits | Type | Init | Description | * +---------+---------+---------+------+-------------------------------------+ * | RES | <63:12> | MBZ,RAZ | 0 | Reserved | * +---------+---------+---------+------+-------------------------------------+ * | MASK | <11:0> | RW | 0 | PERROR register bit enables | * +---------+---------+---------+------+-------------------------------------+ * \endcode * * Pchip Master Latency Register (PLAT - RW) * * Bits <15:8> are the master latency timer. * * Translation Buffer Invalidate Virtual Register (TLBIV - WO) * * A write to this register invalidates all scatter-gather TLB entries that *correspond to PCI addresses whose bits <31:16> and bit 39 match the value *written in bits <19:4> and 27 respectively. This invalidates up to eight PTEs *at a time, which are the number that can be defined in one 21264 cache block *(64 bytes). Because a single TLB PCI tag covers four entries, at most two tags *are actually invalidated. PTE bits <22:4> correspond to system address bits *<34:16> - where PCI<34:32> must be zeros for scatter- gather window hits - in *generating the resulting system address, providing 8-page (8KB) granularity. * * Translation Buffer Invalidate All Register (TLBIA - WO) * * A write to this register invalidates the scatter-gather TLB. The value *written is ignored. **/ u64 CSystem::pchip_csr_read(int num, u32 a) { switch (a) { case 0x000: case 0x040: case 0x080: case 0x0c0: return state.pchip[num].wsba[(a >> 6) & 3]; case 0x100: case 0x140: case 0x180: case 0x1c0: return state.pchip[num].wsm[(a >> 6) & 3]; case 0x200: case 0x240: case 0x280: case 0x2c0: return state.pchip[num].tba[(a >> 6) & 3]; case 0x300: return state.pchip[num].pctl; case 0x3c0: return state.pchip[num].perr; case 0x400: return state.pchip[num].perrmask; case 0x480: // TLBIV case 0x4c0: // TLBIA return 0; case 0x800: // PCI reset return 0; default: printf("Unknown PCHIP %d CSR %07x read attempted.\n", num, a); return 0; } } /** * \brief Write one of the PCHIP registers. * * For a description of the PCHIP registers, see pchip_csr_read. **/ void CSystem::pchip_csr_write(int num, u32 a, u64 data) { switch (a) { case 0x000: case 0x040: case 0x080: state.pchip[num].wsba[(a >> 6) & 3] = data & U64(0x00000000fff00003); return; case 0x0c0: state.pchip[num].wsba[3] = (data & U64(0x00000080fff00001)) | 2; return; case 0x100: case 0x140: case 0x180: case 0x1c0: state.pchip[num].wsm[(a >> 6) & 3] = data & U64(0x00000000fff00000); return; case 0x200: case 0x240: case 0x280: case 0x2c0: state.pchip[num].tba[(a >> 6) & 3] = data & U64(0x00000007fffffc00); return; case 0x300: state.pchip[num].pctl &= U64(0xffffe300f0300000); state.pchip[num].pctl |= (data & U64(0x00001cff0fcfffff)); return; case 0x340: state.pchip[num].plat = data; return; case 0x3c0: // PERR return; case 0x400: state.pchip[num].perrmask = data; return; case 0x480: // TLBIV case 0x4c0: // TLBIA return; case 0x800: // PCI reset for (int i = 0; i < iNumComponents; i++) acComponents[i]->ResetPCI(); return; default: printf("Unknown PCHIP %d CSR %07x write with %016" PRIx64 " attempted.\n", num, a, data); } } u64 CSystem::cchip_csr_read(u32 a, CSystemComponent *source) { CAlphaCPU *cpu = (CAlphaCPU *)source; switch (a) { case 0x000: return state.cchip.csc; case 0x080: // printf("MISC: %016" PRIx64 " from CPU %d (@%" PRIx64 ") (other @ %" LL // "x).\n",state.cchip.misc | cpu->get_cpuid(),cpu->get_cpuid(), // cpu->get_pc()-4, acCPUs[1-cpu->get_cpuid()]->get_pc()); return state.cchip.misc | cpu->get_cpuid(); case 0x100: // WE PUT ALL OUR MEMORY IN A SINGLE ARRAY FOR NOW... return ((u64)(iNumMemoryBits - 23) << 12); // size case 0x140: case 0x180: case 0x1c0: // WE PUT ALL OUR MEMORY IN A SINGLE ARRAY FOR NOW... return 0; case 0x200: case 0x240: case 0x600: case 0x640: return state.cchip.dim[((a >> 10) & 2) | ((a >> 6) & 1)]; case 0x280: case 0x2c0: case 0x680: case 0x6c0: return state.cchip.drir & state.cchip.dim[((a >> 10) & 2) | ((a >> 6) & 1)]; case 0x300: return state.cchip.drir; default: printf("Unknown CCHIP CSR %07x read attempted.\n", a); return 0; } } void CSystem::cchip_csr_write(u32 a, u64 data, CSystemComponent *source) { CAlphaCPU *cpu = (CAlphaCPU *)source; switch (a) { case 0x000: // CSC state.cchip.csc &= ~U64(0x0777777fff3f0000); state.cchip.csc |= (data & U64(0x0777777fff3f0000)); return; case 0x080: // MISC state.cchip.misc |= (data & U64(0x00000f0000f00000)); // W1S state.cchip.misc &= ~(data & U64(0x0000000010000ff0)); // W1C if (data & U64(0x0000000001000000)) { state.cchip.misc &= ~U64(0x0000000000ff0000); // Arbitration Clear printf("Arbitration clear from CPU %d (@%" PRIx64 ").\n", cpu->get_cpuid(), cpu->get_pc() - 4); } if (data & U64(0x00000000000f0000)) { printf("Arbitration %016" PRIx64 " from CPU %d (@%" PRIx64 ")... ", data, cpu->get_cpuid(), cpu->get_pc() - 4); if (!(state.cchip.misc & U64(0x00000000000f0000))) { state.cchip.misc |= (data & U64(0x00000000000f0000)); // Arbitration won printf("won %016" PRIx64 "\n", state.cchip.misc); } else printf("lost %016" PRIx64 "\n", state.cchip.misc); } // stop interval timer interrupt if (data & U64(0x00000000000000f0)) { for (int i = 0; i < iNumCPUs; i++) { if (data & (U64(0x10) << i)) { acCPUs[i]->irq_h(2, false, 0); // printf("*** TIMER interrupt cleared for CPU %d\n",i); } } } // stop inter processor interrupt if (data & U64(0x0000000000000f00)) { for (int i = 0; i < iNumCPUs; i++) { if (data & (U64(0x100) << i)) { acCPUs[i]->irq_h(3, false, 0); printf("*** IP interrupt cleared for CPU %d from CPU %d(@ %" PRIx64 ").\n", i, cpu->get_cpuid(), cpu->get_pc() - 4); } } } // set inter processor interrupt if (data & U64(0x000000000000f000)) { for (int i = 0; i < iNumCPUs; i++) { if (data & (U64(0x1000) << i)) { state.cchip.misc |= U64(0x100) << i; acCPUs[i]->irq_h(3, true, 0); printf("*** IP interrupt set for CPU %d from CPU %d(@ %" PRIx64 ")\n", i, cpu->get_cpuid(), cpu->get_pc() - 4); // std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } } return; case 0x200: case 0x240: case 0x600: case 0x640: state.cchip.dim[((a >> 10) & 2) | ((a >> 6) & 1)] = data; return; default: printf("Unknown CCHIP CSR %07x write with %016" PRIx64 " attempted.\n", a, data); } } u8 CSystem::dchip_csr_read(u32 a) { switch (a) { case 0x800: // DSC return state.dchip.dsc; case 0x840: // STR return state.dchip.str; case 0x880: // DREV return state.dchip.drev; case 0x8c0: // DSC2 return state.dchip.dsc2; default: printf("Unknown DCHIP CSR %07x read attempted.\n", a); return 0; } } void CSystem::dchip_csr_write(u32 a, u8 data) { printf("Unknown DCHIP CSR %07x write with %02x attempted.\n", a, data); } /** * Read a byte from the TIGbus * * What information we have is sketchy at best. Following is extracted from T64, * Although we're not 100% sure that this is actually for ES40: * * \code * +-----------------+--------+ * | register | offset | * +-----------------+--------+ * | trr | 000 | * | smir | 040 | system management IR * | cpuir | 080 | CPU IR * | psir | 0c0 | powe supply IR * | mod_info | 100 | * | clk_info | 140 | * | chip_info | 180 | * | tpcr | 200 | * | pll_data | 280 | * | pll_clk | 2c0 | * | ev6_init | 300 | * | csleep | 340 | * | smcr | 380 | * | ttcr | 3c0 | * | clr_irq5 | 400 | * | clr_irq4 | 440 | * | clr_pwr_flt_det | 480 | * | clr_temp_warn | 4c0 | * | clr_temp_fail | 500 | * | ev6_halt | 5c0 | * | srcr0 | 600 | * | srcr1 | 640 | * | frar0 | 700 | * | frar1 | 740 | * | fwmr0 | 800 | * | fwmr1 | 840 | * | fwmr2 | 880 | * | fwmr3 | 8c0 | * | ipcr0 | a00 | inter-processor communications register for *arbiter (?) | ipcr1 | a40 | | ipcr2 | a80 | | ipcr3 *| ac0 | | ipcr4 | b00 | * +-----------------+--------+ * \endcode **/ u8 CSystem::tig_read(u32 a) { switch (a) { case 0x30000000: // trr return 0; case 0x30000040: // smir return state.tig.FwWrite; case 0x30000100: // mod_info return 0; case 0x300003c0: // ttcr return state.tig.HaltA; case 0x30000480: // clr_pwr_flt_det return 0; case 0x300005c0: // ev6_halt return state.tig.HaltB; case 0x38000180: // Arbiter revision return 0xfe; default: printf("Unknown TIG %08x read attempted.\n", a); return 0; } } void CSystem::tig_write(u32 a, u8 data) { switch (a) { case 0x30000000: // trr return; case 0x30000040: // smir state.tig.FwWrite = data; return; case 0x30000100: // mod_info printf("Soft reset: %02x\n", data); return; case 0x300003c0: // ttcr state.tig.HaltA = data; return; case 0x30000480: // clr_pwr_flt_det return; case 0x300005c0: // ev6_halt state.tig.HaltB = data; return; default: printf("Unknown TIG %07x write with %02x attempted.\n", a, data); } } /** * Load ROM contents from file. Try if the decompressed ROM image * is available, otherwise create it first. **/ int CSystem::LoadROM() { FILE *f; char *buffer; int i; int j; u64 temp; u32 scratch; f = fopen(myCfg->get_text_value("rom.decompressed", "decompressed.rom"), "rb"); if (!f) { f = fopen(myCfg->get_text_value("rom.srm", "cl67srmrom.exe"), "rb"); if (!f) FAILURE(Runtime, "No original or decompressed SRM ROM image found"); printf("%%SYS-I-READROM: Reading original ROM image from %s.\n", myCfg->get_text_value("rom.srm", "cl67srmrom.exe")); for (i = 0; i < 0x240; i++) { if (feof(f)) break; (void)!fread(&scratch, 1, 1, f); } if (feof(f)) FAILURE(Runtime, "File is too short to be a SRM ROM image"); buffer = PtrToMem(0x900000); while (!feof(f)) (void)!fread(buffer++, 1, 1, f); fclose(f); printf("%%SYS-I-DECOMP: Decompressing ROM image.\n0%%"); acCPUs[0]->set_pc(0x900001); acCPUs[0]->set_PAL_BASE(0x900000); acCPUs[0]->enable_icache(); j = 0; while (acCPUs[0]->get_clean_pc() > 0x200000) { for (i = 0; i < 1800000; i++) { SingleStep(); if (acCPUs[0]->get_clean_pc() < 0x200000) break; } j++; if (((j % 5) == 0) && (j < 50)) printf("%d%%", j * 2); else printf("."); fflush(stdout); } printf("100%%\n"); acCPUs[0]->restore_icache(); f = fopen(myCfg->get_text_value("rom.decompressed", "decompressed.rom"), "wb"); if (!f) { printf("%%SYS-W-NOWRITE: Couldn't write decompressed rom to %s.\n", myCfg->get_text_value("rom.decompressed", "decompressed.rom")); } else { printf("%%SYS-I-ROMWRT: Writing decompressed rom to %s.\n", myCfg->get_text_value("rom.decompressed", "decompressed.rom")); temp = endian_64(acCPUs[0]->get_pc()); fwrite(&temp, 1, sizeof(u64), f); temp = endian_64(acCPUs[0]->get_pal_base()); fwrite(&temp, 1, sizeof(u64), f); buffer = PtrToMem(0); fwrite(buffer, 1, 0x200000, f); fclose(f); } } else { printf("%%SYS-I-READROM: Reading decompressed ROM image from %s.\n", myCfg->get_text_value("rom.decompressed", "decompressed.rom")); (void)!fread(&temp, 1, sizeof(u64), f); for (int i = 0; i < iNumCPUs; i++) acCPUs[i]->set_pc(endian_64(temp)); (void)!fread(&temp, 1, sizeof(u64), f); for (int i = 0; i < iNumCPUs; i++) acCPUs[i]->set_PAL_BASE(endian_64(temp)); buffer = PtrToMem(0); (void)!fread(buffer, 1, 0x200000, f); fclose(f); } #if !defined(SRM_NO_SPEEDUPS) || !defined(SRM_NO_IDE) printf("%%SYM-I-PATCHROM: Patching ROM for speed.\n"); #endif #if !defined(SRM_NO_SPEEDUPS) WriteMem(U64(0x14248), 32, 0xe7e00000, 0); // e7e00000 = BEQ r31, +0 WriteMem(U64(0x14288), 32, 0xe7e00000, 0); WriteMem(U64(0x142c8), 32, 0xe7e00000, 0); WriteMem(U64(0x68320), 32, 0xe7e00000, 0); WriteMem(U64(0x8bb78), 32, 0xe7e00000, 0); // memory test (aa) WriteMem(U64(0x8bc0c), 32, 0xe7e00000, 0); // memory test (bb) WriteMem(U64(0x8bc94), 32, 0xe7e00000, 0); // memory test (00) // WriteMem(U64(0xb1158),32,0xe7e00000,0); // CPU sync? #endif printf("%%SYS-I-ROMLOADED: ROM Image loaded successfully!\n"); return 0; } /** * \brief Assert or deassert one of 64 possible interrupt lines on the Tsunami *chipset. * * Source: HRM, 6.3: * * TIGbus and Interrupts * * The TIGbus supports miscellaneous system logic such as flash ROM and * interrupt inputs. The Cchip TIG controller polls interrupts continuously * except when a read or write to flash is requested. The 64 possible interrupt *inputs are polled eight at a time by selecting a byte with the b_tia<2:0> *pins, and asserting b_toe_l to allow the selected byte to be driven onto *b_td<7:0>. Using the polled interrupts, the Cchip calculates the b_irq values *that should be delivered to the CPUs. When any change occurs in these b_irq *values, the Cchip drives the b_irq<3:0> data for both CPUs onto b_td<7:0>, and *asserts signal b_tis to strobe it into a register on the module. If there is *no flash read or write outstanding, the polling process is repeated. If there * is a flash read or write outstanding, it is serviced between interrupt reads *after any pending b_irq updates. Thus, the rounds of interrupt polling are not *atomic, but the b_irq values reflect the most recently polled interrupts. *Furthermore, b_irq<1> may be artificially suppressed for one full polling loop *using the CSR bit MISC. * [...] * * Device and Error Interrupt Delivery - b_irq<1:0> * * As interrupts are read into the Cchip through the TIGbus, the corresponding *bits are set in DRIR. These bits are ANDed with the mask bits in DIMn and then *placed in DIRn. If any bits are set in DIRn<55:0>, then CPUn is interrupted *using CPU pin b_irq<1>. * * Interrupt bits <62:58> cause b_irq<0> to be asserted and are intended for use *as error signals. Assertion of interrupt bits <62:58> causes b_irq<0> to be *asserted. Interrupt bits <62:61> can be used for Pchip 0 and Pchip 1 errors, *respectively. Interrupt bit <63> is special because it is not read from the *TIGbus, but is internally generated as the Cchip detected error interrupt *(currently used only for NXM requests). Assertion of interrupt bit <63> causes *b_irq<0> to be asserted. See Chapter 10 for descriptions of the *interrupt-related CSRs (DRIR, DIMn, DIRn, and MISC). A full mask register for * each CPU allows software to decide whether to send each of the 64 possible *interrupts to either or both CPUs. * * After handling all known outstanding interrupts, software may suppress *b_irq<1> device interrupts to allow the Cchip�s polling mechanism to detect *the updated (deasserted) value of the interrupt lines from the PCI devices and *thereby avoid giving the CPU �stale� interrupts, which require passive *release. The field MISC is provided for this purpose. When a CPU *writes a one to its bit in MISC the Cchip deasserts b_irq<1> to that *CPU (regardless of the value in the DIRn) until it has completed an entire *polling loop. When the Cchip has completed an entire polling loop, b_irq<1> *will again reflect the value of DIRn<55:00>. * * Interval Timer Interrupts - b_irq<2> * * The interval timer interrupts the Cchip through a dedicated pin, i_intim_l, *and is asserted low. When the Cchip sees an asserting (falling) edge of this *pin, it asserts MISC for both CPUs. Pin b_irq<2> remains asserted for *each CPU . When the CPU has finished handling the interrupt, it writes *a one to its MISC bit to clear it. Software can suppress interval *timer interrupts for n cycles by writing n into IICn. * * This table shows TIG Interrupts and IRQ Lines * * \code * +---------------+-----------------+--------+----------------------------------------+ * | TIG Interrupt | Assertion Level | irq | Use | * +---------------+-----------------+--------+----------------------------------------+ * | 63 | N/A | irq<0> | N/C (internally generated Cchip *error) | | | | | (currently NXM only) *| * +---------------+-----------------+--------+----------------------------------------+ * | 62:58 | High | irq<0> | Errors (Pchips, and so on) | | *| | | Recommended: | | | | | ** Bit <62> - Pchip0 error | | | | | ** Bit <61> - Pchip1 error | * +---------------+-----------------+--------+----------------------------------------+ * | 57:56 | N/A | N/A | Reserved | * +---------------+-----------------+--------+----------------------------------------+ * | 55:0 | Low | irq<1> | PCI devices (level sensitive) | * +---------------+-----------------+--------+----------------------------------------+ * \endcode * * It is not clear from the documentation how exactly the interval timer is *connected to the CChip. It looks like this is tied to the interrupt-line from *the real-time clock (TOY), as the periodic interval rate for the TOY is set to *1024 Hz by SRM. 1024 Hz is the frequency of the system timer interrupt *according to the OpenVMS Alpha Internals and Data Structures Handbook. * **/ void CSystem::interrupt(int number, bool assert) { int i; if (number == -1) { // timer int... state.cchip.misc |= 0xf0; for (i = 0; i < iNumCPUs; i++) acCPUs[i]->irq_h(2, true, 0); // timer interrupt is immediate } else if (assert) { // if (!(state.cchip.drir & (1i64<irq_h(1, true, 100); // device interrupts delayed by 100 clocks else acCPUs[i]->irq_h(1, false, 0); if (state.cchip.drir & state.cchip.dim[i] & U64(0xfc00000000000000)) acCPUs[i]->irq_h(0, true, 100); // device interrupts delayed by 100 clocks else acCPUs[i]->irq_h(0, false, 0); } } /** * \brief Translate a 32-bit address coming off the PCI bus into a * 64-bit system address. Used by PCI devices when accessing * memory (or other PCI devices) as bus master. * * Source: HRM, 10.1.4: * * DMA Address Translation (PCI-to-System) * The 21272 chipset supports some PCI commands as a target and does not support * (ignores) others as a target. The Pchip does not respond as a target when it *acts as a PCI master. * * The Pchip ignores all of the following commands as a target: * - Interrupt acknowledge * - Special cycle * - I/O read * - I/O write * - Configuration read * - Configuration write * . * * The Pchips may respond to the following commands as a target: * - Memory read * - Memory read line * - Memory write * - Memory write and invalidate * - Memory read multiple * - Dual-address cycle: This command is accepted by the Pchip when the address *lies inside the DMA monster window. * * There are two kinds of DMA address translation: direct mapped and *scatter-gather mapped. Each type starts by comparing the incoming PCI address *with the monster window (if it is enabled and if it is a DAC), and with the *four window base and window mask registers (the window base registers also *have an enable window bit and a scatter-gather enable bit). This process is *shown in the next figure: * * \code * 31 n n-1 20 19 13 12 0 * +----------+-----------+--------+----------+ * PCI Address | Peripheral Page Number | Offset | * +----------+-----------+--------+----------+ * |<-------->| * ^ * +-----------> COMPARE ----> Hit * v * |<-------->| * 31 n n-1 20 * Window Base +----------+-----------+ * Register | | xxxx | * +----------+-----------+ * 31 n n-1 20 * Window Mask +----------+-----------+ * Register | 0000 | 1111 | (Determines n) * +----------+-----------+ * \endcode * * If the address resides in one of the windows, and the window is enabled, then *if the scatter-gather enable bit is set, the translation is as described for * PCI_Phys_scatter_gather. Otherwise, the translation described for *PCI_Phys_direct_mapped is used. * * In addition, if the matching window has the PTP bit set, then the result of *the address translation is treated as if it had bit <43> set. That is, it is *treated like a PIO address from the CPU. Otherwise, the address is a system *memory address. * * Window Hole * * All window registers are simultaneously subject to a hole that inhibits *matching, under the control of the PCTL CSR bit described in *Section 10.2.5.4. If that bit is set, the hole is enabled in all windows and *has the following extent: * - From PCI address base 512K (address<31:0> = 0008.0000) * - To PCI address limit 1M-1 (address<31:0> = 000F.FFFF) * . * * If enabled, the hole applies whether or not the PTP bit is set for the *window. * * The documentation is not explicit on this, but the assumption was made that *if an address coming off the PCI-bus is not matched, the Pchip doesn't respond *to that address, and it is up to other PCI devices to respond to the address. *So, if no match is found, we treat the address as an address on the local PCI *bus. * * \todo The documentation mentions a PTP bit set for a window, but the register *descriptions don't show a PTP bit in one of the three registers (WSBA, WSM and *TBA). So, for now, we can only do peer-to-peer through a scatter-gather PTE. * * \todo Dual-Acces-Cycle (DAC) access from the PCI bus is not supported. If a *device is ever added that uses this, we should probably support it. **/ u64 CSystem::PCI_Phys(int pcibus, u32 address) { u64 a; int j; #if defined(DEBUG_PCI) printf("-------------- PCI MEMORY ACCESS FOR PCI HOSE %d --------------\n", pcibus); // Step through windows for (j = 0; j < 4; j++) { printf("WSBA%d: %016" PRIx64 " WSM: %016" PRIx64 " TBA: %016" PRIx64 "\n", j, state.pchip[pcibus].wsba[j], state.pchip[pcibus].wsm[j], state.pchip[pcibus].tba[j]); } printf("HOLE: %s\n", test_bit_64(state.pchip[pcibus].pctl, 5) ? "enabled" : "disabled"); printf("--------------------------------------------------------------\n"); #endif if (!(state.pchip[pcibus].pctl & PCI_PCTL_HOLE) // hole disabled || (address < PCI_PCTL_HOLE_START) || (address > PCI_PCTL_HOLE_END)) // or address outside hole { // Step through windows for (j = 0; j < 4; j++) { if ((state.pchip[pcibus].wsba[j] & 1) // window enabled... && !((address ^ state.pchip[pcibus].wsba[j]) & 0xfff00000 & ~state.pchip[pcibus].wsm[j])) // address in range... { if (state.pchip[pcibus].wsba[j] & 2) { try { a = PCI_Phys_scatter_gather(address, state.pchip[pcibus].wsm[j], state.pchip[pcibus].tba[j]); } catch (char) { // window disabled... // not matched; treat as local PCI bus address return U64(0x80000000000) | (pcibus * U64(0x200000000)) | (u64)address; } } else a = PCI_Phys_direct_mapped(address, state.pchip[pcibus].wsm[j], state.pchip[pcibus].tba[j]); #if defined(DEBUG_PCI) printf("PCI memory address %08x translated to %016" PRIx64 "\n", address, a); #endif return a; } } } // not matched; treat as local PCI bus address return U64(0x80000000000) | (pcibus * U64(0x200000000)) | (u64)address; } /** * Translate a 32-bit address coming off the PCI bus into a 64-bit * system address using direct-mapped DMA address translation. * * Source: HRM, 10.1.4.2: * * Direct-Mapped DMA Address Translation * * Direct-mapped addressing uses a base address register, a translated base *address (TBA) register, and a mask register. The block of PCI addresses at *base address, of a size as determined by the mask register, is translated to a *block of addresses at translated base address. Values in the WSMn field other *than those shown produce unspecified results. * * \code * +-------------+----------------+---------------------------+ * | Window Size | WSMn<31:20> | Translated Address <34:0> | * +-------------+----------------+---------------------------+ * | 1MB | 0000.0000.0000 | TBA<34:20>:ad<19:0> | * +-------------+----------------+---------------------------+ * | 2MB | 0000.0000.0001 | TBA<34:21>:ad<20:0> | * +-------------+----------------+---------------------------+ * | 4MB | 0000.0000.0011 | TBA<34:22>:ad<21:0> | * +-------------+----------------+---------------------------+ * | 8MB | 0000.0000.0111 | TBA<34:23>:ad<22:0> | * | ... | ... | ... | * | 2GB | 0111.1111.1111 | TBA<34:31>:ad<30:0> | * +-------------+----------------+---------------------------+ * | 4GB | N/A | 000:ad<31:0> (monster | * | | | window only) | * +-------------+----------------+---------------------------+ * \endcode **/ u64 CSystem::PCI_Phys_direct_mapped(u32 address, u64 wsm, u64 tba) { u64 a; wsm &= PCI_WSM_MASK; a = (address & (wsm | PCI_ADD_MASK)) | (tba & ~wsm & PCI_TBA_MASK); return a; } /** * Translate a 32-bit address coming off the PCI bus into a 64-bit * system address using scatter-gather DMA address translation. * * If address can't be matched (PTE is invalid), an exception of type char * is thrown. The calling function should catch the exception, and do * The Right Thing(tm): treat the address as a local PCI-bus address. * * Source: HRM, 10.1.4.3: * * Scatter-Gather DMA Address Translation * * Scatter-gather addressing uses a base address register, a mask register, a *translated base address register, and a page table entry (PTE) in system *memory. An 8KB page of PCI addresses at base address is translated to an 8KB *page of system addresses through one level of indirection. The PTE contains *the address of the 8KB page. * [...] * At TBA is a region (of size SG PTE AREA) of PTEs, each of which is eight *bytes. Bits <22:1> of the PTE become bits <34:13> (the 8KB page) of the system *address, and bits <12:0> of the PCI address become bits <12:0> (the page *offset) of the system address. * * The following table shows how the address of the page table entry (to be used *as part of the final system address) is generated. Values in the WSM field *other than those shown produce unspecified results. * * \code * +-------------+-------------+----------------+----------------------+ * | Window Size | SG PTE AREA | WSMn<31:20> | PTE Address <34:3> | * +-------------+-------------+----------------+----------------------+ * | 1MB | 1KB | 0000.0000.0000 | TBA<34:10>:ad<19:13> | * +-------------+-------------+----------------+----------------------+ * | 2MB | 2KB | 0000.0000.0001 | TBA<34:11>:ad<20:13> | * +-------------+-------------+----------------+----------------------+ * | 4MB | 4KB | 0000.0000.0011 | TBA<34:12>:ad<21:13> | * +-------------+-------------+----------------+----------------------+ * | 8MB | 8KB | 0000.0000.0111 | TBA<34:13>:ad<22:13> | * | ... | ... | ... | ... | * | 2GB | 2MB | 0111.1111.1111 | TBA<34:21>:ad<30:13> | * +-------------+-------------+----------------+----------------------+ * | 4GB | 4MB | N/A | TBA<34:22>:ad<31:13> | * | | | | (Window 3 in DAC | * | | | | mode only) | * +-------------+-------------+----------------+----------------------+ * \endcode * * The following figure shows the structure of a page table entry in memory. If *either bit <31> or bit <28> is set, the address is interpreted as being a *peer-to-peer address. * * \code * 63 32 31 30 29 28 27 23 22 1 0 * +-------------------+--+-----+--+----------+----------------------+-+ * | |PP| |PP| | Page Address <34:13> |V| * +-------------------+--+-----+--+----------+----------------------+-+ * +--> V = *valid bit \endcode * * The last figure shows how a page table entry is used in conjunction with an *incoming PCI address to generate a system address. * * \code * PTE <22:1> PCI address <12:0> * | | * 34 v 13 12 v 0 * +----------------------------+-------------------+ * | Page addres <34:13> | Offset <12:0> | * +----------------------------+-------------------+ * \endcode **/ u64 CSystem::PCI_Phys_scatter_gather(u32 address, u64 wsm, u64 tba) { u64 pte_a; u64 pte; u64 a; wsm &= PCI_WSM_MASK; pte_a = ((address & (wsm | PCI_PTE_ADD_MASK)) >> PCI_PTE_ADD_SHIFT) // ad part of pte address | (tba & PCI_PTE_TBA_MASK & ~(wsm >> PCI_PTE_ADD_SHIFT)); // tba part of pte address pte = ReadMem(pte_a, 64, 0); if (pte & 1) { a = ((pte << PCI_PTE_SHIFT) & PCI_PTE_MASK) | (address & PCI_PTE_ADD2_MASK); if (pte & PCI_PTE_PEER_BIT) // peer-to-peer a |= (PHYS_PIO_ACCESS); // PIO access. return a; } else { throw((char)'0'); } } /** * Initialize all devices. **/ void CSystem::init() { for (int i = 0; i < iNumComponents; i++) acComponents[i]->init(); } void CSystem::start_threads() { int i; printf("Start threads:"); for (i = 0; i < iNumComponents; i++) { #ifdef IDB // When running with IDB, the trace engine takes care of managing the CPU, // so its thread shouldn't be started. if (dynamic_cast(acComponents[i])) continue; #endif acComponents[i]->start_threads(); } printf("\n"); for (i = 0; i < iNumCPUs; i++) acCPUs[i]->release_threads(); } void CSystem::stop_threads() { printf("Stop threads:"); for (int i = 0; i < iNumComponents; i++) acComponents[i]->stop_threads(); printf("\n"); } /** * Save system state to a state file. **/ void CSystem::SaveState(const char *fn) { FILE *f; int i; unsigned int m; unsigned int j; int *mem = (int *)memory; int int0 = 0; unsigned int memints = (1 << iNumMemoryBits) / (unsigned int)sizeof(int); u32 temp_32; f = fopen(fn, "wb"); if (f) { temp_32 = 0xa1fae540; // MAGIC NUMBER (ALFAES40 ==> A1FAE540 ) fwrite(&temp_32, sizeof(u32), 1, f); temp_32 = 0x00020001; // File Format Version 2.1 fwrite(&temp_32, sizeof(u32), 1, f); // memory for (m = 0; m < memints; m++) { if (mem[m]) { fwrite(&(mem[m]), 1, sizeof(int), f); } else { j = 0; m++; while (!mem[m] && (m < memints)) { m++; j++; if ((int)j == -1) break; } if (mem[m]) m--; fwrite(&int0, 1, sizeof(int), f); fwrite(&j, 1, sizeof(int), f); } } fwrite(&state, sizeof(state), 1, f); // components // // Components should also save any non-initial memory-registrations and // re-register upon restore! // for (i = 0; i < iNumComponents; i++) acComponents[i]->SaveState(f); fclose(f); } } /** * Restore system state from a state file. **/ void CSystem::RestoreState(const char *fn) { FILE *f; int i; unsigned int m; unsigned int j; int *mem = (int *)memory; unsigned int memints = (1 << iNumMemoryBits) / (unsigned int)sizeof(int); u32 temp_32; f = fopen(fn, "rb"); if (!f) { printf("%%SYS-F-NOFILE: Can't open restore file %s\n", fn); return; } (void)!fread(&temp_32, sizeof(u32), 1, f); if (temp_32 != 0xa1fae540) // MAGIC NUMBER (ALFAES40 ==> A1FAE540 ) { printf("%%SYS-F-FORMAT: %s does not appear to be a state file.\n", fn); return; } (void)!fread(&temp_32, sizeof(u32), 1, f); if (temp_32 != 0x00020001) // File Format Version 2.1 { printf("%%SYS-I-VERSION: State file %s is a different version.\n", fn); return; } // memory for (m = 0; m < memints; m++) { (void)!fread(&(mem[m]), 1, sizeof(int), f); if (!mem[m]) { (void)!fread(&j, 1, sizeof(int), f); while (j--) { mem[++m] = 0; } } } (void)!fread(&state, sizeof(state), 1, f); // components // // Components should also save any non-initial memory-registrations and // re-register upon restore! // for (i = 0; i < iNumComponents; i++) { if (acComponents[i]->RestoreState(f)) FAILURE(Runtime, "Unable to restore system state"); } fclose(f); } /** * Dump memory contents to a file. **/ void CSystem::DumpMemory(unsigned int filenum) { char file[100]; int x; int *mem = (int *)memory; FILE *f; sprintf(file, "memory_%012d.dmp", filenum); f = fopen(file, "wb"); x = (1 << iNumMemoryBits) / (unsigned int)sizeof(int) / 2; while (!mem[x - 1]) x--; fwrite(mem, 1, x * sizeof(int), f); fclose(f); } /** * Dump system state to stdout for debugging purposes. **/ void CSystem::panic(char *message, int flags) { int cpunum; int i; CAlphaCPU *cpu; printf("\n******** SYSTEM PANIC *********\n"); printf("* %s\n", message); printf("*******************************\n"); for (cpunum = 0; cpunum < iNumCPUs; cpunum++) { cpu = acCPUs[cpunum]; printf("\n==================== STATE OF CPU %d ====================\n", cpunum); printf("PC: %016" PRIx64 "\n", cpu->get_pc()); #ifdef IDB printf("Physical PC: %016" PRIx64 "\n", cpu->get_current_pc_physical()); printf("Instruction Count: %" PRId64 "\n", cpu->get_instruction_count()); #endif printf("\n"); for (i = 0; i < 32; i++) { if (i < 10) printf("R"); printf("%d:%016" PRIx64, i, cpu->get_r(i, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } printf("\n"); for (i = 4; i < 8; i++) { if (i < 10) printf("S"); printf("%d:%016" PRIx64, i, cpu->get_r(i + 32, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } for (i = 20; i < 24; i++) { if (i < 10) printf("S"); printf("%d:%016" PRIx64, i, cpu->get_r(i + 32, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } printf("\n"); for (i = 0; i < 32; i++) { if (i < 10) printf("F"); printf("%d:%016" PRIx64, i, cpu->get_f(i)); if (i % 4 == 3) printf("\n"); else printf(" "); } } printf("\n"); #ifdef IDB if (flags & PANIC_LISTING) { u64 start; u64 end; start = cpu->get_pc() - 64; end = start + 128; cpu->listing(start, end, cpu->get_pc()); } #endif if (flags & PANIC_ASKSHUTDOWN) { printf("Stop Emulation? "); int c = getc(stdin); if (c == 'y' || c == 'Y') flags |= PANIC_SHUTDOWN; } if (flags & PANIC_SHUTDOWN) { FAILURE(Abort, "Panic shutdown"); } return; } /** * Clear the clock interrupt for a specific CPU. Used by the CPU to acknowledge *the interrupt. **/ void CSystem::clear_clock_int(int ProcNum) { state.cchip.misc &= ~(U64(0x10) << ProcNum); acCPUs[ProcNum]->irq_h(2, false, 0); } #if defined(PROFILE) u64 profile_buckets[PROFILE_BUCKETS]; u64 profiled_insts; bool profile_started = false; #endif CSystem *theSystem = 0; ================================================ FILE: src/System.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "SystemComponent.hpp" #include "TraceEngine.hpp" #if !defined(INCLUDED_SYSTEM_H) #define INCLUDED_SYSTEM_H #define MAX_COMPONENTS 100 #if defined(PROFILE) #define PROFILE_FROM U64(0x8000) #define PROFILE_TO U64(0x1a81c0) #define PROFILE_AFTER U64(0x200000) #define PROFILE_BUCKSIZE 16 #define PROFILE_LENGTH (PROFILE_TO - PROFILE_FROM) #define PROFILE_INSTS (PROFILE_LENGTH / 4) #define PROFILE_BUCKETS (PROFILE_INSTS / PROFILE_BUCKSIZE) #define PROFILE_YN(a) \ ((a >= PROFILE_FROM) && (a < PROFILE_TO) && profile_started) #define PROFILE_BUCKET(a) \ profile_buckets[(a - PROFILE_FROM) / 4 / PROFILE_BUCKSIZE] #define PROFILE_DO(a) \ if ((a & (~U64(0x3))) >= PROFILE_AFTER) \ profile_started = true; \ if (PROFILE_YN(a)) { \ PROFILE_BUCKET(a)++; \ profiled_insts++; \ } extern u64 profile_buckets[PROFILE_BUCKETS]; extern u64 profiled_insts; extern bool profile_started; #endif #if defined(LS_MASTER) || defined(LS_SLAVE) extern char *dbg_strptr; #endif /// Structure used for mapping memory ranges to devices. struct SMemoryUser { CSystemComponent *component; /**< Device that occupies this range. */ int index; /**< Index within the device. Used by devices that occupy more than one range. */ u64 base; /**< Address of first byte. */ u64 length; /**< Number of bytes in range. */ }; /// Structure used for configuration values. struct SConfig { char *key; /**< Name of the value. */ char *value; /**< Value of the value. */ }; /** * \brief Emulated Typhoon 21272 chipset. * * Documentation consulted: * - Tsunami/Typhoon 21272 Chipset Hardware Reference Manual [HRM] *(http://download.majix.org/dec/tsunami_typhoon_21272_hrm.pdf) * - AlphaServer ES40 and AlphaStation ES40 Service Guide [SG] *(http://www.dec-store.com/PD_00158.aspx) * - Tru64 include file dc104x.h [T64] *(http://samy.pl/packet/MISC/tru64/usr/include/alpha/dc104x.h) * . * * The ES40 emulator has the following chipset configuration: * - 1 x 21274-C1 Cchip (controller chip) - The Cchip controls the other chips *in the chipset, as well as the DRAM memory array in a system. The Cchip *interfaces with the CPU's command and address buses. * - 8 x 21274-D1 Dchip (data slice chip) - The Dchips interface with the *system data bus and provide the data path between the CPU, DRAM memory, and *the Pchip(s). * - 2 x 21272-P1 Pchip (peripheral interface chip) - The interface to the PCI *bus. * . **/ class CSystem { public: void DumpMemory(unsigned int filenum); char *PtrToMem(u64 address); unsigned int get_memory_bits(); void RestoreState(const char *fn); void SaveState(const char *fn); u64 PCI_Phys(int pcibus, u32 address); u64 PCI_Phys_direct_mapped(u32 address, u64 wsm, u64 tba); u64 PCI_Phys_scatter_gather(u32 address, u64 wsm, u64 tba); void interrupt(int number, bool assert); int LoadROM(); u64 ReadMem(u64 address, int dsize, CSystemComponent *source); void WriteMem(u64 address, int dsize, u64 data, CSystemComponent *source); void Run(); int SingleStep(); void init(); void start_threads(); void stop_threads(); int RegisterMemory(CSystemComponent *component, int index, u64 base, u64 length); void RegisterComponent(CSystemComponent *component); void UnregisterComponent(CSystemComponent *component); int RegisterCPU(class CAlphaCPU *cpu); CSystem(CConfigurator *cfg); void ResetMem(unsigned int membits); CAlphaCPU *get_cpu(int cpunum) { return acCPUs[cpunum]; }; int get_cpu_num() { return iNumCPUs; }; virtual ~CSystem(); unsigned int iNumMemoryBits; void panic(char *message, int flags); #define PANIC_NOSHUTDOWN 0 #define PANIC_SHUTDOWN 1 #define PANIC_ASKSHUTDOWN 2 #define PANIC_LISTING 4 void clear_clock_int(int ProcNum); u64 get_c_misc(); u64 get_c_dir(int ProcNum); u64 get_c_dim(int ProcNum); void set_c_dim(int ProcNum, u64 value); void cpu_lock(int cpuid, u64 address); bool cpu_unlock(int cpuid); void cpu_break_lock(int cpuid, CSystemComponent *source); private: u64 cchip_csr_read(u32 address, CSystemComponent *source); void cchip_csr_write(u32 address, u64 data, CSystemComponent *source); u64 pchip_csr_read(int num, u32 address); void pchip_csr_write(int num, u32 address, u64 data); u8 dchip_csr_read(u32 address); void dchip_csr_write(u32 address, u8 data); u8 tig_read(u32 address); void tig_write(u32 address, u8 data); int iNumCPUs; CFastMutex *cpu_lock_mutex; /// The state structure contains all elements that need to be saved to the /// statefile. struct SSys_state { int cpu_lock_flags; u64 cpu_lock_address[4]; /** * TIGbus state data * * More details in: HRM, 6.3; T64. Detailed information is hard to find... * * The TIGbus (TTL Integrated Glue Logic) is the interface between the *chipset and the interrupt controller, flash ROM, and possibly some other *system components. **/ struct SSys_tig { u8 FwWrite; u8 HaltA; u8 HaltB; } tig; /** * CCHIP state data * * More details in: HRM, 1.2.1. * * The 21274-C1 Cchip (controller chip) is the heart of the ES40's Typhoon *chipset. It interfaces directly with the CPU's through the System address *ports, it issues controls to the Dchips (data slice chips) and Pchips *(peripheral interface chips) using the Dchip control ports, and the CAPbus *(C-And-P-chip bus). It controls memory using the DRAM command and address *ports. It also controls the TIGbus. **/ struct SSys_cchip { /** * DIM: Device Interrupt Mask Registers. * * These mask registers control which interrupts are allowed to go through *to the CPUs. No interrupt in DRIR will get through to the masked *interrupt registers (and on to interrupt the CPUs) unless the *corresponding mask bit is set in DIMn. All bits are initialized to 0 at *reset. **/ u64 dim[4]; /** * DRIR: Device Raw Interrupt Request Register. * * DRIR indicates which of the 64 possible device interrupts is asserted. * * \code * +---------+---------+---------+------+-------------------------------------+ * | Field | Bits | Type | Init | Description | * +---------+---------+---------+------+-------------------------------------+ * | ERR | <63:58> | RO | 0 | IRQ0 error interrupts | | | | | *| <63> Chip detected MISC | | | | | *| <62> hookup to Pchip0 error | | | | | *| <61> hookup to Pchip1 errror | * +---------+---------+---------+------+-------------------------------------+ * | RES | <57:56> | RO | 0 | Reserved | * +---------+---------+---------+------+-------------------------------------+ * | DEV | <55:0> | RO | 0 | PCI interrupts pending to the *CPU | * +---------+---------+---------+------+-------------------------------------+ * \endcode * * Combined with DIM[n] to form DIR[n]: * * DIR: Device Interrupt Request Registers. * * These registers indicate which interrupts are pending to the CPUs. If a *raw request bit is set and the corresponding mask bit is set, then the *corresponding bit in this register will be set and the appropriate CPU *will be interrupted. **/ u64 drir; /** * Miscellaneous Register (MISC - RW). * * +---------+---------+---------+------+-------------------------------------+ * | Field | Bits | Type | Init | Description | * +---------+---------+---------+------+-------------------------------------+ * | RES | <63:44> | MBZ,RAZ | 0 | Reserved. | | DEVSUP | <43:40> *| WO | 0 | Suppress IRQ1 interrupts to the CPU | | | | *| | corresponding to a 1 in this field | | | | | *| until the interrupt polling machine | | | | | *| has completed a poll of all PCI | | | | | *| devices. | * +---------+---------+---------+------+-------------------------------------+ * | REV | <39:32> | RO | 8 | Latest revision of Cchip | * +---------+---------+---------+------+-------------------------------------+ * | NXS | <31:29> | RO | 0 | NXM source - Device that caused *NXM | | | | | | - UNPREDICTABLE if NXM is *not set. | | | | | | Value Source | | | *| | | 0..3 CPU 0..3 | | | | *| | 4..5 Pchip 0..1 | * +---------+---------+---------+------+-------------------------------------+ * | NXM | <28> | R,W1C | 0 | Nonexistent memory address *detected.| | | | | | Sets DRIR<63> and *locks the NXS | | | | | | field until *it is cleared. | * +---------+---------+---------+------+-------------------------------------+ * | RES | <27:25> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | ACL | <24> | WO | 0 | Arbitration clear - writing a 1 *to | | | | | | this bit clears ABT and ABW *fields. | * +---------+---------+---------+------+-------------------------------------+ * | ABT | <23:20> | R,W1S | 0 | Arbitration try - writing a 1 to *| | | | | | these bits sets them. | * +---------+---------+---------+------+-------------------------------------+ * | ABW | <19:16> | R,W1S | 0 | Arbitration won - writing a 1 to *| | | | | | these bits sets them unless one *is | | | | | | already set, in which case *the | | | | | | write is ignored. | * +---------+---------+---------+------+-------------------------------------+ * | IPREQ | <15:12> | WO | 0 | Interprocessor interrupt request *- | | | | | | write a 1 to the bit *corresponding | | | | | | to the CPU you *want to interrupt. | | | | | | Writing a *1 here sets the corres- | | | | | | *ponding bit in IPINTR. | * +---------+---------+---------+------+-------------------------------------+ * | IPINTR | <11:8> | R,W1C | 0 | Interprocessor interrupt pending *- | | | | | | one bit per CPU. Pin irq<3> *is | | | | | | asserted to the CPU *corresponding | | | | | | to a 1 in this *field. | * +---------+---------+---------+------+-------------------------------------+ * | ITINTR | <7:4> | R,W1C | 0 | Interval timer interrupt pending *- | | | | | | one bit per CPU. Pin irq<2> *is | | | | | | asserted to the CPU *corresponding | | | | | | to a 1 in this *field. | * +---------+---------+---------+------+-------------------------------------+ * | RES | <3:2> | MBZ,RAZ | 0 | Reserved. | * +---------+---------+---------+------+-------------------------------------+ * | CPUID | <1:0> | RO | | ID of the CPU performing the *read. | * +---------+---------+---------+------+-------------------------------------+ * \endcode **/ u64 misc; u64 csc; } cchip; /** * DCHIP state data * * More details in: HRM, 1.2.2. * * The ES40 contains eight 21274-D1 Dchips (data slice chips). Each Dchip is * responsible for handling 8 bits of the 64-bit data bus (in the ES40, *other configurations using less Dchips are possible). Each Dchip *interfaces with the Cchip for control, with each of the Pchips, with each *of the CPU's and with each of the DRAM arrays. **/ struct SSys_dchip { u8 drev; u8 dsc; u8 dsc2; u8 str; } dchip; /** * PCHIP state data * * More details in: HRM, 1.2.3. * * The ES40 contains two 21272-P1 Pchips (peripheral interface chips). Each *Pchip controls one 64-bit PCI bus, and interfaces it to the Cchip and the *Dchips. * * On PIO transfers from the CPU's (or PTP transfers from the other PCI *bus), the Pchip acts as bus master on the PCI bus. * * On DMA or PTP transfers from a PCI device, the Pchip acts as target on *the PCI bus. To determine on which addresses to respond, each Pchip *contains 4 DMA/PTP windows, that support both direct mapped and *scatter-gather DMA/PTP memory access. **/ struct SSys_pchip { u64 plat; u64 perr; u64 perrmask; u64 pctl; u64 wsba[4]; u64 wsm[4]; u64 tba[4]; } pchip[2]; u32 cf8_address[2]; } state; void *memory; // void * memmap; int iNumComponents; CSystemComponent *acComponents[MAX_COMPONENTS]; int iNumMemories; struct SMemoryUser *asMemories[MAX_COMPONENTS]; class CAlphaCPU *acCPUs[4]; CConfigurator *myCfg; int iSingleStep; #if defined(IDB) int iSSCycles; #endif }; inline u64 CSystem::get_c_misc() { return state.cchip.misc; } inline u64 CSystem::get_c_dir(int ProcNum) { return state.cchip.drir & state.cchip.dim[ProcNum]; } inline u64 CSystem::get_c_dim(int ProcNum) { return state.cchip.dim[ProcNum]; } inline void CSystem::set_c_dim(int ProcNum, u64 value) { state.cchip.dim[ProcNum] = value; } extern CSystem *theSystem; /* constants for P-Chip CSR's */ #define PCI_PCTL_HOLE U64(0x0000000000000020) /* <5> */ #define PCI_PCTL_HOLE_START 0x00080000 #define PCI_PCTL_HOLE_END 0x000fffff /* constants for pci-to-phys-address-mapping */ #define PCI_WSM_MASK U64(0x00000000fff00000) /* <31:20> */ #define PCI_ADD_MASK U64(0x00000000000fffff) /* <19:0> */ #define PCI_TBA_MASK U64(0x00000007fff00000) /* <34:20> */ #define PCI_PTE_ADD_MASK U64(0x00000000000fe000) /* <19:13> */ #define PCI_PTE_ADD_SHIFT 10 #define PCI_PTE_TBA_MASK U64(0x00000007fffffc00) /* <34:10> */ #define PCI_PTE_MASK U64(0x00000007ffffe000) /* <34:13> */ #define PCI_PTE_SHIFT 12 #define PCI_PTE_ADD2_MASK U64(0x0000000000001fff) /* <12:0> */ #define PCI_PTE_PEER_BIT U64(0x0000000090000000) /* <31,28> */ #define PHYS_PIO_ACCESS U64(0x0000080000000000) /* <43> */ #endif // !defined(INCLUDED_SYSTEM_H) ================================================ FILE: src/SystemComponent.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "SystemComponent.hpp" #include "StdAfx.hpp" #include "System.hpp" /** * Constructor. **/ CSystemComponent::CSystemComponent(CConfigurator *cfg, CSystem *system) { char *a; char *b; system->RegisterComponent(this); cSystem = system; myCfg = cfg; a = myCfg->get_myName(); b = myCfg->get_myValue(); CHECK_ALLOCATION(devid_string = (char *)malloc(strlen(a) + strlen(b) + 3)); sprintf(devid_string, "%s(%s)", a, b); } /** * destructor. **/ CSystemComponent::~CSystemComponent() { cSystem->UnregisterComponent(this); free(devid_string); devid_string = nullptr; } ================================================ FILE: src/SystemComponent.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_SYSTEMCOMPONENT_H) #define INCLUDED_SYSTEMCOMPONENT_H #include "Configurator.hpp" /** * \brief Abstract base class for devices that connect to the Typhoon chipset. **/ class CSystemComponent { public: virtual int RestoreState(FILE *f) = 0; virtual int SaveState(FILE *f) = 0; CSystemComponent(class CConfigurator *cfg, class CSystem *system); virtual ~CSystemComponent(); //=== abstract === virtual u64 ReadMem(int index, u64 address, int dsize) { return 0; }; virtual void WriteMem(int index, u64 address, int dsize, u64 data){}; virtual void check_state(){}; virtual void ResetPCI(){}; virtual void init(){}; virtual void start_threads(){}; virtual void stop_threads(){}; char *devid_string; protected: class CSystem *cSystem; class CConfigurator *myCfg; }; #endif // !defined(INCLUDED_SYSTEMCOMPONENT_H) ================================================ FILE: src/TraceEngine.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "StdAfx.hpp" #if defined(IDB) #include "AlphaCPU.hpp" #include "DPR.hpp" #include "Flash.hpp" #include "System.hpp" #include "TraceEngine.hpp" #include "lockstep.hpp" #include CTraceEngine *trc; /** * Convert a string to a new string that contains only printable characters. * * The maximum length of the resulting string is 100 characters. * * @param dest Destination string * @param org Source string **/ inline void write_printable_s(char *dest, const char *org) { int cnt = 100; while (*org && cnt--) { *(dest++) = printable(*(org++)); } *dest = '\0'; } /** * Get the physical address that matches a given virtual address. * * This never generates a CPU exception. **/ inline u64 real_address(u64 address, CAlphaCPU *c, bool bIBOX) { bool b; u64 a; if (bIBOX && (address & 1)) return address & U64(0xfffffffffffffffc); if (!(c->virt2phys(address, &a, ACCESS_READ | NO_CHECK | FAKE, &b, 0))) return a & (bIBOX ? U64(0xfffffffffffffffc) : U64(0xffffffffffffffff)); return ((address & U64(0xfffffffff0000000)) == U64(0x0000000020000000)) ? address - U64(0x000000001fe00000) : (((address & U64(0xfffffffff0000000)) == U64(0x0000000010000000)) ? address - U64(0x000000000fffe000) : address) & (bIBOX ? U64(0xfffffffffffffffc) : U64(0xffffffffffffffff)); } /** * Constructor. **/ CTraceEngine::CTraceEngine(CSystem *sys) { int i; iNumFunctions = 0; iNumPRBRs = 0; cSystem = sys; for (i = 0; i < 4; i++) { asCPUs[0].last_prbr = -1; } current_trace_file = stdout; bBreakPoint = false; } /** * Destructor. **/ CTraceEngine::~CTraceEngine(void) { int i; for (i = 0; i < iNumPRBRs; i++) fclose(asPRBRs[i].f); } /** * Trace function calling. * * @param cpu Pointer to the CPU that jumps * @param f Address of the current instruction * @param t Address being jumped to * @param down This could be a function call * @param up This could be a return from a call * @param x Optional printf argument that describes what happens (eg "GO PAL *%0x") * @param y Optional extra argument for the printf statement. **/ void CTraceEngine::trace(CAlphaCPU *cpu, u64 f, u64 t, bool down, bool up, const char *x, int y) { // Not a real jump. if ((t == f + 4) || (t == f)) return; // p = new process context int p = get_prbr(cpu->get_prbr(), cpu->get_hwpcb()); // o = old process context int o = asCPUs[cpu->get_cpuid()].last_prbr; // Check for process context switch if (p != o) { if (o != -1) { /* Process context switch has happened. Print this in the trace files for both the old and the new process context. */ fprintf(asPRBRs[o].f, "\n==> Switch to PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n", asPRBRs[p].prbr, asPRBRs[p].hwpcb, asPRBRs[p].procname); fprintf(asPRBRs[p].f, " This is PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n", asPRBRs[p].prbr, asPRBRs[p].hwpcb, asPRBRs[p].procname); fprintf(asPRBRs[p].f, "<== Switch from PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n\n", asPRBRs[o].prbr, asPRBRs[o].hwpcb, asPRBRs[o].procname); } // save process context in cpu record asCPUs[cpu->get_cpuid()].last_prbr = p; } // Has tracing been temporarily disabled for this process? if (asPRBRs[p].trc_waitfor) { // Have we reached the point where tracing should be re-enabled? if ((t & ~U64(0x3)) == asPRBRs[p].trc_waitfor) asPRBRs[p].trc_waitfor = 0; // Don't trace for now. return; } // Get the physical to/from addresses. u64 pc_f = real_address(f, cpu, true); u64 pc_t = real_address(f, cpu, true); /* This is where it gets tricky... * * If the up parameter is true, we check if the to address of this trace is * equal to the from address of a previous trace (that was a function call), * or equal to that from address + 4 (next instruction). If this is the case, * we consider this a function return. (decreasing the trace level). * * If this is not the case, we then check if tracing is hidden at this level. * If this is the case, we don't trace this call. * * Next, we check if the down parameter is false, if this is the case, we * handle the trace through trace_br. * * Otherwise, we treat the trace as a function call (increasing the trace * level). We will then check if the to address is a known function address. * In either case we will print the address or the function name and either * the formatted function list or the first few function argument registers. */ int oldlvl = asPRBRs[p].trclvl; int i; int j; if (up) { // Check if this is a function return for (i = oldlvl - 1; i >= 0; i--) { // Is this a return to an old "from" address? if ((asPRBRs[p].trcadd[i] == (pc_t - 4)) || (asPRBRs[p].trcadd[i] == (pc_t))) { // Yes, return to this address' tracelevel asPRBRs[p].trclvl = i; // Should we stop hiding the trace again? if (asPRBRs[p].trchide > i) asPRBRs[p].trchide = -1; // Is tracing hidden at this level? if (asPRBRs[p].trchide != -1) return; // Indent to the pevious (higher) trace level for (j = 0; j < oldlvl; j++) fprintf(asPRBRs[p].f, " "); // And print the from address, and the value of the r0 register (return // value) fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ") ($r0 = %" PRIx64 ")\n", f, pc_f, cpu->get_r(0, true)); // Indent to the new (lower) trace level for (j = 0; j < asPRBRs[p].trclvl; j++) fprintf(asPRBRs[p].f, " "); // Print the to address fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ") <--\n", t, pc_t); return; } } } // Are traces hidden at this level? if (asPRBRs[p].trchide != -1) return; if (!down) { // If we're not allowed to consider this a function call, handle the trace // through trace_br trace_br(cpu, f, t); return; } // Maximum trace level reached? if (oldlvl < 700) asPRBRs[p].trclvl = oldlvl + 1; // Set the from address for the old (lower) trace level asPRBRs[p].trcadd[oldlvl] = pc_f; // Is there a special message to print? if (x) { // Indent to the old (lower) trace level for (i = 0; i < oldlvl; i++) fprintf(asPRBRs[p].f, " "); // Print the special message fprintf(asPRBRs[p].f, x, y); fprintf(asPRBRs[p].f, "\n"); } // Indent to the old (lower) trace level for (i = 0; i < oldlvl; i++) fprintf(asPRBRs[p].f, " "); // Print the from address fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ") -->\n", f, pc_f); // Indent to the new (higher) trace level for (i = 0; i < asPRBRs[p].trclvl; i++) fprintf(asPRBRs[p].f, " "); // Check if this is a known function for (i = 0; i < iNumFunctions; i++) { if (asFunctions[i].address == pc_t) { // Function found // Print function name fprintf(asPRBRs[p].f, "%016" PRIx64 "(%s)", t, asFunctions[i].fn_name); // And print the argument list write_arglist(cpu, asPRBRs[p].f, asFunctions[i].fn_arglist); fprintf(asPRBRs[p].f, "\n"); // If this is a "step-over" function, we hide tracing for higher levels. if (asFunctions[i].step_over) asPRBRs[p].trchide = asPRBRs[p].trclvl; return; } } // No known function // Print the to address fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ")", t, pc_t); // Print a default argument list write_arglist(cpu, asPRBRs[p].f, "(%s|16%, %s|17%, %s|18%, %s|19%)"); fprintf(asPRBRs[p].f, "\n"); } /** * Trace a branch. * * This can't be a function call or return; stay at the same trace level. * * @param cpu Pointer to the CPU that jumps * @param f Address of the current instruction * @param t Address being jumped to **/ void CTraceEngine::trace_br(CAlphaCPU *cpu, u64 f, u64 t) { int p; int o; // Get the physical to/from addresses. u64 pc_f = real_address(f, cpu, true); u64 pc_t = real_address(t, cpu, true); // p = new process context p = get_prbr(cpu->get_prbr(), cpu->get_hwpcb()); // o = old process context o = asCPUs[cpu->get_cpuid()].last_prbr; // Has tracing been temporarily disabled for this process? if (asPRBRs[p].trc_waitfor) { // Have we reached the point where tracing should be re-enabled? if ((t & ~U64(0x3)) == asPRBRs[p].trc_waitfor) asPRBRs[p].trc_waitfor = 0; // Don't trace for now return; } // Is tracing hidden at this level? if (asPRBRs[p].trchide != -1) return; // Check for process context switch if (p != o) { if (o != -1) { /* Process context switch has happened. Print this in the trace files for both the old and the new process context. */ fprintf(asPRBRs[o].f, "\n==> Switch to PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n", asPRBRs[p].prbr, asPRBRs[p].hwpcb, asPRBRs[p].procname); fprintf(asPRBRs[p].f, " This is PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n", asPRBRs[p].prbr, asPRBRs[p].hwpcb, asPRBRs[p].procname); fprintf(asPRBRs[p].f, "<== Switch from PRBR %08" PRIx64 " %08" PRIx64 " (%s)\n\n", asPRBRs[o].prbr, asPRBRs[o].hwpcb, asPRBRs[o].procname); } // save process context in cpu record asCPUs[cpu->get_cpuid()].last_prbr = p; } int i; // Is this a real jump? if ((pc_t > pc_f + 4) || (pc_t < pc_f)) { // Indent to the trace level for (i = 0; i < asPRBRs[p].trclvl; i++) fprintf(asPRBRs[p].f, " "); // Print from address fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ") --+\n", f, pc_f); // Indent to the trace level for (i = 0; i < asPRBRs[p].trclvl; i++) fprintf(asPRBRs[p].f, " "); // Is this a known function? for (i = 0; i < iNumFunctions; i++) { if (asFunctions[i].address == pc_t) { // Yes, this is a known function // Print the function name fprintf(asPRBRs[p].f, "%016" PRIx64 "(%s)", t, asFunctions[i].fn_name); // Print the argument list write_arglist(cpu, asPRBRs[p].f, asFunctions[i].fn_arglist); fprintf(asPRBRs[p].f, " <-+\n"); // If this is a "step-over" function, we hide tracing for higher levels. if (asFunctions[i].step_over) asPRBRs[p].trchide = asPRBRs[p].trclvl; return; } } // Print the to address. (No function call, so, no argument list) fprintf(asPRBRs[p].f, "%016" PRIx64 "(%08" PRIx64 ") <-+\n", t, pc_t); } } /** * Add a function to the database of known functions. **/ void CTraceEngine::add_function(u64 address, const char *fn_name, const char *fn_arglist, bool step_over) { asFunctions[iNumFunctions].address = (u32)address & ~3; asFunctions[iNumFunctions].fn_name = strdup(fn_name); asFunctions[iNumFunctions].fn_arglist = strdup(fn_arglist); asFunctions[iNumFunctions].step_over = step_over; iNumFunctions++; } /** * Suspend tracing until the given address is reached. **/ void CTraceEngine::set_waitfor(CAlphaCPU *cpu, u64 address) { int p; p = get_prbr(cpu->get_prbr(), cpu->get_hwpcb()); if (asPRBRs[p].trc_waitfor == 0) asPRBRs[p].trc_waitfor = address & ~U64(0x3); } /** * See if a function name is known for a given address. **/ bool CTraceEngine::get_fnc_name(CAlphaCPU *c, u64 address, char **p_fn_name) { int i; u64 a = real_address(address, c, true); for (i = 0; i < iNumFunctions; i++) { if (asFunctions[i].address == a) { *p_fn_name = asFunctions[i].fn_name; return true; } } *p_fn_name = (char *)0; return false; } /** * Get process context record for a PRBR/HWPCB combination **/ int CTraceEngine::get_prbr(u64 prbr, u64 hwpcb) { int i; char filename[100]; for (i = 0; i < iNumPRBRs; i++) { if ((asPRBRs[i].prbr == prbr) && (asPRBRs[i].hwpcb == hwpcb)) { if (asPRBRs[i].f == current_trace_file) return i; if (!strncmp(asPRBRs[i].procname, cSystem->PtrToMem(prbr + 0x154), 20)) { current_trace_file = asPRBRs[i].f; return i; } fclose(asPRBRs[i].f); break; } } if (i == iNumPRBRs) { asPRBRs[i].generation = 0; iNumPRBRs++; } asPRBRs[i].generation++; asPRBRs[i].prbr = prbr; asPRBRs[i].hwpcb = hwpcb; if (prbr > 0 && prbr < (U64(0x1) << cSystem->get_memory_bits())) strncpy(asPRBRs[i].procname, cSystem->PtrToMem(prbr + 0x154), 20); else strcpy(asPRBRs[i].procname, ""); sprintf(filename, "trace_%08" PRIx64 "_%08" PRIx64 "_%02d_%s.trc", prbr, hwpcb, asPRBRs[i].generation, asPRBRs[i].procname); asPRBRs[i].f = fopen(filename, "w"); if (asPRBRs[i].f == 0) printf("Failed to open file!!\n"); asPRBRs[i].trclvl = 0; asPRBRs[i].trchide = -1; asPRBRs[i].trc_waitfor = 0; current_trace_file = asPRBRs[i].f; printf("Add PRBR: %08" PRIx64 "_%08" PRIx64 "\n", prbr, hwpcb); return i; } /** * Parse the argument list string a, get the necessary registers from CPU c, * and print the formatted argument list to file fl. **/ void CTraceEngine::write_arglist(CAlphaCPU *c, FILE *fl, const char *a) { char o[500]; char *op = o; const char *ap = a; char f[20]; char *fp; char *rp; int r; u64 value; while (*ap) { if (*ap == '%') { ap++; fp = f; *(fp++) = '%'; while (*ap != '%') *(fp++) = *(ap++); ap++; *fp = '\0'; // now we have a formatter in f. rp = strchr(f, '|'); *(rp++) = '\0'; // and the register in rp; r = atoi(rp); value = c->get_r(r, true); if (!strcmp(f, "%s")) { sprintf(op, "%" PRIx64 " (", value); while (*op) op++; value = real_address(value, c, false); if ((value > 0) && (value < (U64(0x1) << cSystem->get_memory_bits()))) write_printable_s(op, cSystem->PtrToMem(value)); else sprintf(op, "INVPTR"); while (*op) op++; *(op++) = ')'; *(op) = '\0'; } else if (!strcmp(f, "%c")) sprintf(op, "%02" PRIx64 " (%c)", value, printable((char)value)); else if (!strcmp(f, "%d")) sprintf(op, "%" PRId64 "", value); else if (!strcmp(f, "%x")) sprintf(op, "%" PRIx64 "", value); else if (!strcmp(f, "%0x")) sprintf(op, "%016" PRIx64 "", value); else if (!strcmp(f, "%016x")) sprintf(op, "%016" PRIx64 "", value); else if (!strcmp(f, "%08x")) sprintf(op, "%08" PRIx64 "", value); else if (!strcmp(f, "%04x")) sprintf(op, "%04" PRIx64 "", value); else if (!strcmp(f, "%02x")) sprintf(op, "%02" PRIx64 "", value); else sprintf(op, f, value); while (*op) op++; } else { *(op++) = *(ap++); } } *op = '\0'; fprintf(fl, "%s", o); } /** * Read a procfile. This file contains lines of the form: *
;;; **/ void CTraceEngine::read_procfile(const char *filename) { FILE *f; u64 address; char linebuffer[1000]; char *fn_name; char *fn_args; char *sov; int step_over; int result; int i = 0; f = fopen(filename, "r"); if (f) { while (fscanf(f, "%[^\n] ", linebuffer) != EOF) { address = U64(0x0); fn_name = strchr(linebuffer, ';'); if (fn_name) { *fn_name = '\0'; fn_name++; result = sscanf(linebuffer, "%" PRIx64 "", &address); if ((result == 1) && address) { fn_args = strchr(fn_name, ';'); if (fn_args) { *fn_args = '\0'; fn_args++; sov = strchr(fn_args, ';'); if (sov) { *sov = '\0'; sov++; result = sscanf(sov, "%d", &step_over); if (result == 1) { add_function(address, fn_name, fn_args, step_over ? true : false); i++; } } } } } } fclose(f); printf("%%IDB-I-RDTRC : Read %d entries from trace-file %s\n", i, filename); } } /** * Print a string to the current trace file. **/ void CTraceEngine::trace_dev(const char *text) { fprintf(current_trace_file, "%s", text); } /** * Get file handle for current trace file. **/ FILE *CTraceEngine::trace_file() { return current_trace_file; } /** * Run an IDB script, or prompt user for input. **/ void CTraceEngine::run_script(const char *filename) { char s[100][100]; int i; #if !defined(LS_SLAVE) char c = '\0'; int j; FILE *f = NULL; if (filename) { f = fopen(filename, "r"); if (!f) { printf("%%IDB-F-NOLOAD: File %s could not be opened.\n", filename); return; } } else { printf("This is the ES40 interactive debugger. To start non-interactively, " "run es40,\n"); printf("Or run this executable (es40_idb) with a last argument of " "@\n"); f = stdin; } #endif for (;;) { #if !defined(LS_SLAVE) if (filename) { if (feof(f)) break; } else { printf("IDB %016" PRIx64 " %c>", theSystem->get_cpu(0)->get_clean_pc(), (theSystem->get_cpu(0)->get_pc() & U64(0x1)) ? 'P' : '-'); } #endif for (i = 0; i < 100;) { #if defined(LS_SLAVE) lockstep_receive(s[i], 100); if (!strcmp(s[i], "TERM")) break; #else bool u = false; for (j = 0; j < 100;) { fscanf(f, "%c", &c); if (c != '\n' && c != '\r' && c != ' ' && c != '\t') { s[i][j++] = c; u = true; } if (c == ' ' || c == '\t' || c == '\n') break; } s[i][j] = '\0'; if (u) #endif { #if defined(LS_MASTER) lockstep_send(s[i]); #endif i++; } #if !defined(LS_SLAVE) if (c == '\n') { #if defined(LS_MASTER) lockstep_send("TERM"); #endif break; } #endif } s[i][0] = '\0'; if (parse(s)) break; } #if !defined(LS_SLAVE) if (filename) fclose(f); #endif } /** * Parse an IDB command **/ int CTraceEngine::parse(char command[100][100]) { int i = 0; int numargs; int result; int RunCycles; u64 iFrom; u64 iTo; u64 iJump; for (numargs = 0; command[numargs][0] != '\0'; numargs++) ; if ((numargs > 0) && (command[0][0] == '#' || command[0][0] == ';' || command[0][0] == '!' || (command[0][0] == '/' && command[0][1] == '/'))) // comment return 0; switch (numargs) { case 0: // empty command return 0; case 1: if (!strncasecmp(command[0], "EXIT", strlen(command[0])) || !strncasecmp(command[0], "QUIT", strlen(command[0]))) return 1; if (!strncasecmp(command[0], "HELP", strlen(command[0])) || !strncasecmp(command[0], "?", strlen(command[0]))) { printf(" " " \n"); printf("Available commands: " " \n"); printf(" HELP | ? " " \n"); printf(" EXIT | QUIT " " \n"); printf(" STEP " " \n"); printf(" TRACE [ ON | OFF ] " " \n"); printf(" HASHING [ ON | OFF ] " " \n"); printf(" BREAKPOINT [ OFF | [ > | < | = | INSTRUCTION " " \n"); printf(" | WRITE | READ | ACCESS ] ] " " \n"); printf(" DISASSEMBLE [ON | OFF ] " " \n"); #if defined(DEBUG_TB) printf(" TBDEBUG [ON | OFF ] " " \n"); #endif printf(" LIST [ ALL | - ] " " \n"); printf(" RUN [ ] " " \n"); printf(" LOAD [ STATE | DPR | FLASH | CSV ] " " \n"); printf(" SAVE [ STATE | DPR | FLASH ] " " \n"); printf(" JUMP " " \n"); printf(" PAL [ ON | OFF ] " " \n"); printf(" DUMPREGS " " \n"); printf(" LOADREG " " \n"); printf(" LOADFPREG " " \n"); printf(" @ " " \n"); printf(" # | // | ; | ! " " \n"); printf(" " " \n"); printf("The words of each command can be abbreviated, e.g. B or BRE for " " \n"); printf("BREAKPOINT; S F for save flash. " " \n"); printf(" " " \n"); return 0; } if (!strncasecmp(command[0], "STEP", strlen(command[0]))) { printf("%%IDB-I-SSTEP : Single step.\n"); theSystem->SingleStep(); return 0; } if (!strncasecmp(command[0], "DUMPREGS", strlen(command[0]))) { printf("\n==================== SYSTEM STATE =======================\n"); printf("PC: %" PRIx64 "\n", theSystem->get_cpu(0)->get_pc()); printf("Physical PC: %" PRIx64 "\n", theSystem->get_cpu(0)->get_current_pc_physical()); printf("Instruction count: %" PRId64 "\n", theSystem->get_cpu(0)->get_instruction_count()); printf("\n==================== REGISTER VALUES ====================\n"); for (i = 0; i < 32; i++) { if (i < 10) printf("R"); printf("%d:%016" PRIx64 "", i, theSystem->get_cpu(0)->get_r(i, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } printf("\n"); for (i = 4; i < 8; i++) { if (i < 10) printf("S"); printf("%d:%016" PRIx64 "", i, theSystem->get_cpu(0)->get_r(i + 32, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } for (i = 20; i < 24; i++) { if (i < 10) printf("S"); printf("%d:%016" PRIx64 "", i, theSystem->get_cpu(0)->get_r(i + 32, false)); if (i % 4 == 3) printf("\n"); else printf(" "); } printf("\n"); for (i = 0; i < 32; i++) { if (i < 10) printf("F"); printf("%d:%016" PRIx64 "", i, theSystem->get_cpu(0)->get_f(i)); if (i % 4 == 3) printf("\n"); else printf(" "); } printf("=========================================================\n"); return 0; } if (!strncasecmp(command[0], "RUN", strlen(command[0]))) { if (!bBreakPoint) { printf("%%IDB-F-NOBRKP: No breakpoint set, press Ctrl-C to end run.\n"); /* catch CTRL-C and shutdown gracefully */ extern int got_sigint; void sigint_handler(int); signal(SIGINT, &sigint_handler); while (!got_sigint) { theSystem->SingleStep(); } got_sigint = 0; return 0; } printf("%%IDB-I-RUNBPT: Running until breakpoint found.\n"); switch (iBreakPointMode) { case -1: for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() < iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 0: for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 1: for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() > iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 2: // break when the instruction matches. Great for stopping when // pal_halt is called! extern int got_sigint; void sigint_handler(int); signal(SIGINT, &sigint_handler); while (1) { theSystem->SingleStep(); if (theSystem->get_cpu(0)->get_last_instruction() == iBreakPointInstruction || got_sigint) { printf("%%IDB-I-BRKPT: Found trapped instruction or control-c\n"); break; } } got_sigint = 0; break; case 3: // access for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_last_read_loc() == iBreakPoint || theSystem->get_cpu(0)->get_last_write_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 4: // read for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_last_read_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 5: // write for (i = 0;; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (theSystem->get_cpu(0)->get_last_write_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; default: break; } printf("%%IDB-I-ENDRUN: End of run.\n"); return 0; } #if !defined(IDB) || !defined(LS_SLAVE) if (command[0][0] == '@') { run_script(command[0] + 1); return 0; } #endif // !defined(IDB) || !defined(LS_SLAVE) break; case 2: if (!strncasecmp(command[0], "LIST", strlen(command[0])) && !strncasecmp(command[1], "ALL", strlen(command[1]))) { list_all(); return 0; } if (!strncasecmp(command[0], "TRACE", strlen(command[0]))) { if (!strcasecmp(command[1], "ON")) { printf("%%IDB-I-TRCON : Tracing enabled.\n"); bTrace = true; return 0; } if (!strcasecmp(command[1], "OFF")) { printf("%%IDB-I-TRCOFF: Tracing disabled.\n"); bTrace = false; return 0; } } #if defined(DEBUG_TB) if (!strncasecmp(command[0], "TBDEBUG", strlen(command[0]))) { if (!strcasecmp(command[1], "ON")) { printf("%%IDB-I-TBDON : Translation Buffer Debugging enabled.\n"); bTB_Debug = true; return 0; } if (!strcasecmp(command[1], "OFF")) { printf("%%IDB-I-TBDOFF: Translation Buffer Debugging disabled.\n"); bTB_Debug = false; return 0; } } #endif if (!strncasecmp(command[0], "HASHING", strlen(command[0]))) { if (!strcasecmp(command[1], "ON")) { printf("%%IDB-I-HSHON : Hashing enabled.\n"); bHashing = true; return 0; } if (!strcasecmp(command[1], "OFF")) { printf("%%IDB-I-HSHOFF: Hashing disabled.\n"); bHashing = false; return 0; } } if (!strncasecmp(command[0], "PAL", strlen(command[0]))) { if (!strcasecmp(command[1], "ON")) { printf("%%IDB-I-PALON : PALmode enabled.\n"); theSystem->get_cpu(0)->set_pc(theSystem->get_cpu(0)->get_clean_pc() + 1); return 0; } if (!strcasecmp(command[1], "OFF")) { printf("%%IDB-I-PALOFF: PALmode disabled.\n"); theSystem->get_cpu(0)->set_pc(theSystem->get_cpu(0)->get_clean_pc()); return 0; } } if (!strncasecmp(command[0], "DISASSEMBLE", strlen(command[0]))) { if (!strcasecmp(command[1], "ON")) { printf("%%IDB-I-DISON : Disassembling enabled.\n"); bDisassemble = true; return 0; } if (!strcasecmp(command[1], "OFF")) { printf("%%IDB-I-DISOFF: Disassembling disabled.\n"); bDisassemble = false; return 0; } } if (!strncasecmp(command[0], "BREAKPOINT", strlen(command[0]))) { if (!strncasecmp(command[1], "OFF", strlen(command[1]))) { printf("%%IDB-I-BRKOFF: Breakpoint disabled.\n"); bBreakPoint = false; return 0; } } if (!strncasecmp(command[0], "RUN", strlen(command[0]))) { result = sscanf(command[1], "%d", &RunCycles); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid decimal value.\n"); return 0; } if (bBreakPoint) { printf("%%IDB-I-RUNCBP: Running until breakpoint found or max cycles " "reached.\n"); switch (iBreakPointMode) { case -1: for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() < iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 0: for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 1: for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_clean_pc() > iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 2: for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_last_instruction() == iBreakPointInstruction) { printf("%%IDB-I-BRKPT: Found trapped instruction\n"); break; } } break; case 3: // access for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_last_read_loc() == iBreakPoint || theSystem->get_cpu(0)->get_last_write_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 4: // read for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_last_read_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; case 5: // write for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from " "serial port)\n"); break; } if (theSystem->get_cpu(0)->get_last_write_loc() == iBreakPoint) { printf("%%IDB-I-BRKPT : Breakpoint encountered.\n"); break; } } break; default: break; } } else { printf("%%IDB-I-RUNCYC: Running until max cycles reached.\n"); extern int got_sigint; void sigint_handler(int); signal(SIGINT, &sigint_handler); for (i = 0; i < RunCycles; i++) { if (theSystem->SingleStep()) { printf("%%IDB-I-ABORT : Abort run requested (probably from serial " "port)\n"); break; } if (got_sigint) break; } got_sigint = 0; } printf("%%IDB-I-ENDRUN: End of run.\n"); return 0; } if (!strncasecmp(command[0], "JUMP", strlen(command[0]))) { result = sscanf(command[1], "%" PRIx64 "", &iJump); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); return 0; } if (iJump & U64(0x3)) { printf("%%IDB-F-ALGVAL: Value not aligned on a 4-byte bounday.\n"); return 0; } printf("%%IDB-I-JUMPTO: Jumping.\n"); theSystem->get_cpu(0)->set_pc( iJump + (theSystem->get_cpu(0)->get_pc() & U64(0x1))); return 0; } break; case 3: if (!strncasecmp(command[0], "BREAKPOINT", strlen(command[0]))) { if (!strcmp(command[1], "=") || !strcmp(command[1], ">") || !strcmp(command[1], "<")) { result = sscanf(command[2], "%" PRIx64 "", &iBreakPoint); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); bBreakPoint = false; return 0; } if (iBreakPoint & U64(0x3)) { printf("%%IDB-F-ALGVAL: Value not aligned on a 4-byte bounday.\n"); bBreakPoint = false; return 0; } switch (command[1][0]) { case '=': iBreakPointMode = 0; break; case '>': iBreakPointMode = 1; break; case '<': iBreakPointMode = -1; break; } printf("%%IDB-I-BRKSET: Breakpoint set when PC %c %016" PRIx64 ".\n", command[1][0], iBreakPoint); bBreakPoint = true; return 0; } else { if (!strncasecmp(command[1], "INSTRUCTION", strlen(command[1]))) { result = sscanf(command[2], "%" PRIx64 "", &iBreakPointInstruction); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); bBreakPoint = false; return 0; } printf("%%IDB-I-BRKSET: Breakpoint set when instruction %08x is " "executed.\n", iBreakPointInstruction); bBreakPoint = true; iBreakPointMode = 2; return 0; } else if (!strncasecmp(command[1], "ACCESS", strlen(command[1]))) { result = sscanf(command[2], "%" PRIx64 "", &iBreakPoint); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); bBreakPoint = false; return 0; } printf("%%IDB-I-BRKSET: Breakpoint set when data is read/written at " "%016" PRIx64 ".\n", iBreakPoint); bBreakPoint = true; iBreakPointMode = 3; return 0; } else if (!strncasecmp(command[1], "READ", strlen(command[1]))) { result = sscanf(command[2], "%" PRIx64 "", &iBreakPoint); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); bBreakPoint = false; return 0; } printf("%%IDB-I-BRKSET: Breakpoint set when data is read at %016" PRIx64 "x.\n", iBreakPoint); bBreakPoint = true; iBreakPointMode = 4; return 0; } else if (!strncasecmp(command[1], "WRITE", strlen(command[1]))) { result = sscanf(command[2], "%" PRIx64 "", &iBreakPoint); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); bBreakPoint = false; return 0; } printf( "%%IDB-I-BRKSET: Breakpoint set when data is written at %016" PRIx64 "x.\n", iBreakPoint); bBreakPoint = true; iBreakPointMode = 5; return 0; } } } if (!strncasecmp(command[0], "LOAD", strlen(command[0]))) { if (!strncasecmp(command[1], "CSV", strlen(command[1]))) { read_procfile(command[2]); return 0; } if (!strncasecmp(command[1], "STATE", strlen(command[1]))) { theSystem->RestoreState(command[2]); return 0; } if (!strncasecmp(command[1], "DPR", strlen(command[1]))) { theDPR->RestoreStateF(command[2]); return 0; } if (!strncasecmp(command[1], "FLASH", strlen(command[1]))) { theSROM->RestoreStateF(command[2]); return 0; } } if (!strncasecmp(command[0], "SAVE", strlen(command[0]))) { if (!strncasecmp(command[1], "STATE", strlen(command[1]))) { theSystem->SaveState(command[2]); return 0; } if (!strncasecmp(command[1], "DPR", strlen(command[1]))) { theDPR->SaveStateF(command[2]); return 0; } if (!strncasecmp(command[1], "FLASH", strlen(command[1]))) { theSROM->SaveStateF(command[2]); return 0; } } if (!strncasecmp(command[0], "LOADREG", strlen(command[0]))) { int reg; u64 value; result = sscanf(command[1], "%d", ®); if (result != 1 || reg > 31) { printf("%%IDB-F-INVREG: Invalid register number.\n"); return 0; } result = sscanf(command[2], "%" PRIx64 "", &value); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); return 0; } theSystem->get_cpu(0)->set_r(reg, value); return 0; } if (!strncasecmp(command[0], "LOADFPREG", strlen(command[0]))) { int reg; u64 value; result = sscanf(command[1], "%d", ®); if (result != 1 || reg > 31) { printf("%%IDB-F-INVREG: Invalid register number.\n"); return 0; } result = sscanf(command[2], "%" PRIx64 "", &value); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); return 0; } theSystem->get_cpu(0)->set_f(reg, value); return 0; } break; case 4: if (!strncasecmp(command[0], "LIST", strlen(command[0])) && !strcmp(command[2], "-")) { result = sscanf(command[1], "%" PRIx64 "", &iFrom); if (result == 1) result = sscanf(command[3], "%" PRIx64 "", &iTo); if (result != 1) { printf("%%IDB-F-INVVAL: Invalid hexadecimal value.\n"); return 0; } if (iFrom & U64(0x3) || iTo & U64(0x3)) { printf("%%IDB-F-ALGVAL: Value not aligned on a 4-byte bounday.\n"); return 0; } if (iFrom > iTo) { printf("%%IDB-F-FRLTTO: From value exceeds to value.\n"); return 0; } theSystem->get_cpu(0)->listing(iFrom, iTo); return 0; } break; default: break; } printf("%%IDB-F-SYNTAX: Syntax error. Type \"?\" or \"HELP\" for help.\n"); return 0; } struct sRegion { u64 from; u64 to; struct sRegion *pNext; }; /** * List all memory contents. Try to leave out memory that is only 00's, ef's or *ff's. **/ void CTraceEngine::list_all() { struct sRegion *pR = NULL; struct sRegion **ppN = &pR; struct sRegion *p = NULL; int f = 0; int t = 0; int ms = 1 << (theSystem->get_memory_bits() - 3); u64 *pM = (u64 *)theSystem->PtrToMem(0); for (;;) { while ((!pM[f] || pM[f] == U64(0xefefefefefefefef) || pM[f] == U64(0xffffffffffffffff)) && f < ms) { f++; if (!(f & 0x1ffff)) printf("."); } if (f >= ms) break; t = f; for (;;) { while (pM[t] && pM[t] != U64(0xefefefefefefefef) && pM[t] != U64(0xffffffffffffffff) && t < ms) { t++; if (!(t & 0x1ffff)) printf("x"); } if (t + 3 < ms) { if ((!pM[t + 1] || pM[t + 1] == U64(0xefefefefefefefef) || pM[t + 1] == U64(0xffffffffffffffff)) && (!pM[t + 2] || pM[t + 2] == U64(0xefefefefefefefef) || pM[t + 2] == U64(0xffffffffffffffff)) && (!pM[t + 3] || pM[t + 3] == U64(0xefefefefefefefef) || pM[t + 3] == U64(0xffffffffffffffff))) break; } if (t >= ms) break; t++; if (!(t & 0x1ffff)) printf("x"); } *ppN = new sRegion; (*ppN)->from = (u64)f * 8; (*ppN)->to = (u64)((t - 1) * 8) + 4; (*ppN)->pNext = NULL; ppN = &((*ppN)->pNext); f = t; } printf("\n"); p = pR; while (p) { printf("\n======== DISASSEMBLING %08" PRIx64 " TO %08" PRIx64 " ========\n\n", p->from, p->to); theSystem->get_cpu(0)->listing(p->from, p->to); p = p->pNext; } } bool bTrace = false; bool bDisassemble = false; bool bHashing = false; bool bListing = false; #if defined(DEBUG_TB) bool bTB_Debug = false; #endif #endif // IDB ================================================ FILE: src/TraceEngine.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_TRACEENGINE_H) #define INCLUDED_TRACEENGINE_H #if defined(IDB) #include "datatypes.hpp" /// Structure used to define named functions within memory. struct STraceFunction { u32 address; char *fn_name; char *fn_arglist; bool step_over; }; /// Structure used to keep track of PRBR values. struct STracePRBR { u64 prbr; u64 hwpcb; FILE *f; u64 trcadd[701]; int trclvl; int trchide; u64 trc_waitfor; char procname[30]; int generation; }; /// Structure used to keep track of CPU's struct STraceCPU { int last_prbr; }; /** * \brief CPU tracing engine. **/ class CTraceEngine { public: void read_procfile(const char *filename); CTraceEngine(class CSystem *sys); ~CTraceEngine(void); void trace(class CAlphaCPU *cpu, u64 f, u64 t, bool down, bool up, const char *x, int y); void trace_br(class CAlphaCPU *cpu, u64 f, u64 t); void add_function(u64 address, const char *fn_name, const char *fn_arglist, bool step_over); bool get_fnc_name(class CAlphaCPU *cpu, u64 address, char **p_fn_name); void set_waitfor(class CAlphaCPU *cpu, u64 address); FILE *trace_file(); void trace_dev(const char *text); int parse(char command[100][100]); void run_script(const char *filename); void list_all(); protected: class CSystem *cSystem; int trcfncs; int iNumFunctions; int iNumPRBRs; struct STraceFunction asFunctions[25000]; struct STraceCPU asCPUs[4]; struct STracePRBR asPRBRs[1000]; int get_prbr(u64 prbr, u64 hwpcb); void write_arglist(CAlphaCPU *c, FILE *f, const char *a); FILE *current_trace_file; u64 iBreakPoint; int iBreakPointMode; bool bBreakPoint; u32 iBreakPointInstruction; }; extern bool bTrace; extern bool bDisassemble; extern bool bHashing; extern bool bListing; #if defined(DEBUG_TB) extern bool bTB_Debug; #endif extern CTraceEngine *trc; #define TRC_DEV(a) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a); \ trc->trace_dev(t); \ } \ } #define TRC_DEV2(a, b) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a, b); \ trc->trace_dev(t); \ } \ } #define TRC_DEV3(a, b, c) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a, b, c); \ trc->trace_dev(t); \ } \ } #define TRC_DEV4(a, b, c, d) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a, b, c, d); \ trc->trace_dev(t); \ } \ } #define TRC_DEV5(a, b, c, d, e) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a, b, c, d, e); \ trc->trace_dev(t); \ } \ } #define TRC_DEV6(a, b, c, d, e, f) \ { \ if (bTrace) { \ char t[1000]; \ sprintf(t, a, b, c, d, e, f); \ trc->trace_dev(t); \ } \ } #define DO_ACTION !bListing #else // IDB #define TRC_DEV(a) ; #define TRC_DEV2(a, b) ; #define TRC_DEV3(a, b, c) ; #define TRC_DEV4(a, b, c, d) ; #define TRC_DEV5(a, b, c, d, e) ; #define TRC_DEV6(a, b, c, d, e, f) ; #define DO_ACTION 1 #endif #endif ================================================ FILE: src/VGA.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "VGA.hpp" #include "StdAfx.hpp" /** * Constructor. * * Checks if more than one VGA card is present. If so, throws a failure. **/ CVGA::CVGA(class CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev) : CPCIDevice(cfg, c, pcibus, pcidev) { if (theVGA != 0) FAILURE(Configuration, "More than one VGA"); theVGA = this; } /** * Destructor. **/ CVGA::~CVGA(void) {} /** * Variable pointer to the one and only VGA card. **/ CVGA *theVGA = 0; ================================================ FILE: src/VGA.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(__VGA_H__) #define __VGA_H__ #include "PCIDevice.hpp" /** * \brief Abstract base class for PCI VGA cards. **/ class CVGA : public CPCIDevice { public: CVGA(class CConfigurator *cfg, class CSystem *c, int pcibus, int pcidev); ~CVGA(void); virtual u8 get_actl_palette_idx(u8 index) = 0; virtual void redraw_area(unsigned x0, unsigned y0, unsigned width, unsigned height) = 0; }; extern CVGA *theVGA; #endif ================================================ FILE: src/base/Bugcheck.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Bugcheck.cpp // // $Id: Bugcheck.cpp,v 1.1 2008/05/31 15:47:19 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Bugcheck // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Bugcheck.hpp" #include "Exception.hpp" #include void CBugcheck::assertion(const char *cond, const char *file, int line) { throw CAssertionViolationException(what(cond, file, line)); } void CBugcheck::nullPointer(const char *ptr, const char *file, int line) { throw CNullPointerException(what(ptr, file, line)); } void CBugcheck::bugcheck(const char *file, int line) { throw CBugcheckException(what(0, file, line)); } void CBugcheck::bugcheck(const char *msg, const char *file, int line) { std::string m("Bugcheck"); if (msg) { m.append(": "); m.append(msg); } throw CBugcheckException(what(msg, file, line)); } void CBugcheck::debugger(const char *file, int line) { // Debugger::enter(file, line); } void CBugcheck::debugger(const char *msg, const char *file, int line) { // Debugger::enter(msg, file, line); } std::string CBugcheck::what(const char *msg, const char *file, int line) { std::ostringstream str; if (msg) str << msg << " "; str << "in file \"" << file << "\", line " << line; return str.str(); } ================================================ FILE: src/base/Bugcheck.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Bugcheck.h // // $Id: Bugcheck.h,v 1.1 2008/05/31 15:47:20 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Bugcheck // // Definition of the Bugcheck class and the self-testing macros. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Bugcheck_INCLUDED #define Foundation_Bugcheck_INCLUDED #include "Foundation.hpp" #include class CBugcheck /// This class provides some static methods that are /// used by the /// poco_assert_dbg(), poco_assert(), poco_check_ptr() /// and poco_bugcheck() macros. /// You should not invoke these methods /// directly. Use the macros instead, as they /// automatically provide useful context information. { public: static void assertion(const char *cond, const char *file, int line); /// An assertion failed. Break into the debugger, if /// possible, then throw an AssertionViolationException. static void nullPointer(const char *ptr, const char *file, int line); /// An null pointer was encountered. Break into the debugger, if /// possible, then throw an NullPointerException. static void bugcheck(const char *file, int line); /// An internal error was encountered. Break into the debugger, if /// possible, then throw an BugcheckException. static void bugcheck(const char *msg, const char *file, int line); /// An internal error was encountered. Break into the debugger, if /// possible, then throw an BugcheckException. static void debugger(const char *file, int line); /// An internal error was encountered. Break into the debugger, if /// possible. static void debugger(const char *msg, const char *file, int line); /// An internal error was encountered. Break into the debugger, if /// possible. protected: static std::string what(const char *msg, const char *file, int line); }; // // useful macros (these automatically supply line number and file name) // #define poco_assert(cond) \ if (!(cond)) \ CBugcheck::assertion(#cond, __FILE__, __LINE__); \ else \ (void)0 #define poco_check_ptr(ptr) \ if (!(ptr)) \ CBugcheck::nullPointer(#ptr, __FILE__, __LINE__); \ else \ (void)0 #define poco_bugcheck() CBugcheck::bugcheck(__FILE__, __LINE__) #define poco_bugcheck_msg(msg) CBugcheck::bugcheck(msg, __FILE__, __LINE__) #define poco_debugger() CBugcheck::debugger(__FILE__, __LINE__) #define poco_debugger_msg(msg) CBugcheck::debugger(msg, __FILE__, __LINE__) #endif // Foundation_Bugcheck_INCLUDED ================================================ FILE: src/base/ErrorHandler.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // ErrorHandler.cpp // // $Id: ErrorHandler.cpp,v 1.1 2008/05/31 15:47:20 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: ErrorHandler // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "ErrorHandler.hpp" #include "SingletonHolder.hpp" CErrorHandler *CErrorHandler::_pHandler = CErrorHandler::defaultHandler(); CFastMutex CErrorHandler::_mutex; CErrorHandler::CErrorHandler() {} CErrorHandler::~CErrorHandler() {} void CErrorHandler::exception(const CException &exc) { poco_debugger_msg(exc.what()); } void CErrorHandler::exception(const std::exception &exc) { poco_debugger_msg(exc.what()); } void CErrorHandler::exception() { poco_debugger_msg("unknown exception"); } void CErrorHandler::handle(const CException &exc) { CFastMutex::CScopedLock lock(&_mutex); try { _pHandler->exception(exc); } catch (...) { } } void CErrorHandler::handle(const std::exception &exc) { CFastMutex::CScopedLock lock(&_mutex); try { _pHandler->exception(exc); } catch (...) { } } void CErrorHandler::handle() { CFastMutex::CScopedLock lock(&_mutex); try { _pHandler->exception(); } catch (...) { } } CErrorHandler *CErrorHandler::set(CErrorHandler *pHandler) { poco_check_ptr(pHandler); CFastMutex::CScopedLock lock(&_mutex); CErrorHandler *pOld = _pHandler; _pHandler = pHandler; return pOld; } CErrorHandler *CErrorHandler::defaultHandler() { static CSingletonHolder sh; return sh.get(); } ================================================ FILE: src/base/ErrorHandler.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // ErrorHandler.h // // $Id: ErrorHandler.h,v 1.1 2008/05/31 15:47:20 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: ErrorHandler // // Definition of the ErrorHandler class. // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_ErrorHandler_INCLUDED #define Foundation_ErrorHandler_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include "Mutex.hpp" class CErrorHandler /// This is the base class for thread error handlers. /// /// An unhandled exception that causes a thread to terminate is usually /// silently ignored, since the class library cannot do anything meaningful /// about it. /// /// The Thread class provides the possibility to register a /// global ErrorHandler that is invoked whenever a thread has /// been terminated by an unhandled exception. /// The ErrorHandler must be derived from this class and can /// provide implementations of all three handleException() overloads. /// /// The ErrorHandler is always invoked within the context of /// the offending thread. { public: CErrorHandler(); /// Creates the ErrorHandler. virtual ~CErrorHandler(); /// Destroys the ErrorHandler. virtual void exception(const CException &exc); /// Called when a Poco::Exception (or a subclass) /// caused the thread to terminate. /// /// This method should not throw any exception - it would /// be silently ignored. /// /// The default implementation just breaks into the debugger. virtual void exception(const std::exception &exc); /// Called when a std::exception (or a subclass) /// caused the thread to terminate. /// /// This method should not throw any exception - it would /// be silently ignored. /// /// The default implementation just breaks into the debugger. virtual void exception(); /// Called when an exception that is neither a /// Poco::Exception nor a std::exception caused /// the thread to terminate. /// /// This method should not throw any exception - it would /// be silently ignored. /// /// The default implementation just breaks into the debugger. static void handle(const CException &exc); /// Invokes the currently registered ErrorHandler. static void handle(const std::exception &exc); /// Invokes the currently registered ErrorHandler. static void handle(); /// Invokes the currently registered ErrorHandler. static CErrorHandler *set(CErrorHandler *pHandler); /// Registers the given handler as the current error handler. /// /// Returns the previously registered handler. static CErrorHandler *get(); /// Returns a pointer to the currently registered /// ErrorHandler. protected: static CErrorHandler *defaultHandler(); /// Returns the default ErrorHandler. private: static CErrorHandler *_pHandler; static CFastMutex _mutex; }; // // inlines // inline CErrorHandler *CErrorHandler::get() { return _pHandler; } #endif // Foundation_ErrorHandler_INCLUDED ================================================ FILE: src/base/Event.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event.cpp // // $Id: Event.cpp,v 1.1 2008/05/31 15:47:21 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Event.hpp" #if defined(POCO_OS_FAMILY_WINDOWS) #include "Event_WIN32.cpp" #else #include "Event_POSIX.cpp" #endif CEvent::CEvent(bool autoReset) : CEventImpl(autoReset) {} CEvent::~CEvent() {} ================================================ FILE: src/base/Event.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event.h // // $Id: Event.h,v 1.1 2008/05/31 15:47:21 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Definition of the Event class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Event_INCLUDED #define Foundation_Event_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #if defined(POCO_OS_FAMILY_WINDOWS) #include "Event_WIN32.hpp" #else #include "Event_POSIX.hpp" #endif class CEvent : private CEventImpl /// An Event is a synchronization object that /// allows one thread to signal one or more /// other threads that a certain event /// has happened. /// Usually, one thread signals an event, /// while one or more other threads wait /// for an event to become signalled. { public: CEvent(bool autoReset = true); /// Creates the event. If autoReset is true, /// the event is automatically reset after /// a wait() successfully returns. ~CEvent(); /// Destroys the event. void set(); /// Signals the event. If autoReset is true, /// only one thread waiting for the event /// can resume execution. /// If autoReset is false, all waiting threads /// can resume execution. void wait(); /// Waits for the event to become signalled. void wait(long milliseconds); /// Waits for the event to become signalled. /// Throws a TimeoutException if the event /// does not become signalled within the specified /// time interval. bool tryWait(long milliseconds); /// Waits for the event to become signalled. /// Returns true if the event /// became signalled within the specified /// time interval, false otherwise. void reset(); /// Resets the event to unsignalled state. private: CEvent(const CEvent &); CEvent &operator=(const CEvent &); }; // // inlines // inline void CEvent::set() { setImpl(); } inline void CEvent::wait() { waitImpl(); } inline void CEvent::wait(long milliseconds) { if (!waitImpl(milliseconds)) throw CTimeoutException(); } inline bool CEvent::tryWait(long milliseconds) { return waitImpl(milliseconds); } inline void CEvent::reset() { resetImpl(); } #endif // Foundation_Event_INCLUDED ================================================ FILE: src/base/Event_POSIX.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event_POSIX.cpp // // $Id: Event_POSIX.cpp,v 1.1 2008/05/31 15:47:21 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "base/Event_POSIX.hpp" #include CEventImpl::CEventImpl(bool autoReset) : _auto(autoReset), _state(false) { if (pthread_mutex_init(&_mutex, NULL)) throw CSystemException("cannot create event (mutex)"); if (pthread_cond_init(&_cond, NULL)) throw CSystemException("cannot create event (condition)"); } CEventImpl::~CEventImpl() { pthread_cond_destroy(&_cond); pthread_mutex_destroy(&_mutex); } void CEventImpl::waitImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("wait for event failed (lock)"); while (!_state) { if (pthread_cond_wait(&_cond, &_mutex)) { pthread_mutex_unlock(&_mutex); throw CSystemException("wait for event failed"); } } if (_auto) _state = false; pthread_mutex_unlock(&_mutex); } bool CEventImpl::waitImpl(long milliseconds) { int rc = 0; struct timespec abstime; #if defined(__VMS) struct timespec delta; delta.tv_sec = milliseconds / 1000; delta.tv_nsec = (milliseconds % 1000) * 1000000; pthread_get_expiration_np(&delta, &abstime); #else struct timeval tv; gettimeofday(&tv, NULL); abstime.tv_sec = tv.tv_sec + milliseconds / 1000; abstime.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000; if (abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; } #endif if (pthread_mutex_lock(&_mutex) != 0) throw CSystemException("wait for event failed (lock)"); while (!_state) { if ((rc = pthread_cond_timedwait(&_cond, &_mutex, &abstime))) { if (rc == ETIMEDOUT) break; pthread_mutex_unlock(&_mutex); throw CSystemException("cannot wait for event"); } } if (rc == 0 && _auto) _state = false; pthread_mutex_unlock(&_mutex); return rc == 0; } ================================================ FILE: src/base/Event_POSIX.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event_POSIX.h // // $Id: Event_POSIX.h,v 1.1 2008/05/31 15:47:21 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Definition of the EventImpl class for POSIX Threads. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Event_POSIX_INCLUDED #define Foundation_Event_POSIX_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include #include class CEventImpl { protected: CEventImpl(bool autoReset); ~CEventImpl(); void setImpl(); void waitImpl(); bool waitImpl(long milliseconds); void resetImpl(); private: bool _auto; volatile bool _state; pthread_mutex_t _mutex; pthread_cond_t _cond; }; // // inlines // inline void CEventImpl::setImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("cannot signal event (lock)"); _state = true; if (pthread_cond_broadcast(&_cond)) { pthread_mutex_unlock(&_mutex); throw CSystemException("cannot signal event"); } pthread_mutex_unlock(&_mutex); } inline void CEventImpl::resetImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("cannot reset event"); _state = false; pthread_mutex_unlock(&_mutex); } #endif // Foundation_Event_POSIX_INCLUDED ================================================ FILE: src/base/Event_WIN32.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event_WIN32.cpp // // $Id: Event_WIN32.cpp,v 1.1 2008/05/31 15:47:21 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Event_WIN32.hpp" CEventImpl::CEventImpl(bool autoReset) { _event = CreateEventW(NULL, autoReset ? FALSE : TRUE, FALSE, NULL); if (!_event) throw CSystemException("cannot create event"); } CEventImpl::~CEventImpl() { CloseHandle(_event); } void CEventImpl::waitImpl() { switch (WaitForSingleObject(_event, INFINITE)) { case WAIT_OBJECT_0: return; default: throw CSystemException("wait for event failed"); } } bool CEventImpl::waitImpl(long milliseconds) { switch (WaitForSingleObject(_event, milliseconds + 1)) { case WAIT_TIMEOUT: return false; case WAIT_OBJECT_0: return true; default: throw CSystemException("wait for event failed"); } } ================================================ FILE: src/base/Event_WIN32.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Event_WIN32.h // // $Id: Event_WIN32.h,v 1.1 2008/05/31 15:47:22 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Event // // Definition of the EventImpl class for WIN32. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Event_WIN32_INCLUDED #define Foundation_Event_WIN32_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include "UnWindows.hpp" class CEventImpl { protected: CEventImpl(bool autoReset = false); ~CEventImpl(); void setImpl(); void waitImpl(); bool waitImpl(long milliseconds); void resetImpl(); private: HANDLE _event; }; // // inlines // inline void CEventImpl::setImpl() { if (!SetEvent(_event)) { throw CSystemException("cannot signal event"); } } inline void CEventImpl::resetImpl() { if (!ResetEvent(_event)) { throw CSystemException("cannot reset event"); } } #endif // Foundation_Event_WIN32_INCLUDED ================================================ FILE: src/base/Exception.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Exception.cpp // // $Id: Exception.cpp,v 1.1 2008/05/31 15:47:22 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Exception // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Exception.hpp" #include CException::CException(int code) : _pNested(0), _code(code) {} CException::CException(const std::string &msg, int code) : _msg(msg), _pNested(0), _code(code) {} CException::CException(const std::string &msg, const std::string &arg, int code) : _msg(msg), _pNested(0), _code(code) { if (!arg.empty()) { _msg.append(": "); _msg.append(arg); } } CException::CException(const std::string &msg, const CException &nested, int code) : _msg(msg), _pNested(nested.clone()), _code(code) {} CException::CException(const CException &exc) : std::exception(exc), _msg(exc._msg), _code(exc._code) { _pNested = exc._pNested ? exc._pNested->clone() : 0; } CException::~CException() throw() { delete _pNested; } CException &CException::operator=(const CException &exc) { if (&exc != this) { delete _pNested; _msg = exc._msg; _pNested = exc._pNested ? exc._pNested->clone() : 0; _code = exc._code; } return *this; } const char *CException::name() const throw() { return "Exception"; } const char *CException::className() const throw() { return typeid(*this).name(); } const char *CException::what() const throw() { return name(); } std::string CException::displayText() const { std::string txt = name(); if (!_msg.empty()) { txt.append(": "); txt.append(_msg); } return txt; } CException *CException::clone() const { return new CException(*this); } void CException::rethrow() const { throw *this; } POCO_IMPLEMENT_EXCEPTION(CLogicException, CException, "Logic exception") POCO_IMPLEMENT_EXCEPTION(CAssertionViolationException, CLogicException, "Assertion violation") POCO_IMPLEMENT_EXCEPTION(CNullPointerException, CLogicException, "Null pointer") POCO_IMPLEMENT_EXCEPTION(CBugcheckException, CLogicException, "Bugcheck") POCO_IMPLEMENT_EXCEPTION(CInvalidArgumentException, CLogicException, "Invalid argument") POCO_IMPLEMENT_EXCEPTION(CNotImplementedException, CLogicException, "Not implemented") POCO_IMPLEMENT_EXCEPTION(CRangeException, CLogicException, "Out of range") POCO_IMPLEMENT_EXCEPTION(CIllegalStateException, CLogicException, "Illegal state") POCO_IMPLEMENT_EXCEPTION(CInvalidAccessException, CLogicException, "Invalid access") POCO_IMPLEMENT_EXCEPTION(CSignalException, CLogicException, "Signal received") POCO_IMPLEMENT_EXCEPTION(CUnhandledException, CLogicException, "Signal received") POCO_IMPLEMENT_EXCEPTION(CRuntimeException, CException, "Runtime exception") POCO_IMPLEMENT_EXCEPTION(CNotFoundException, CRuntimeException, "Not found") POCO_IMPLEMENT_EXCEPTION(CExistsException, CRuntimeException, "Exists") POCO_IMPLEMENT_EXCEPTION(CTimeoutException, CRuntimeException, "Timeout") POCO_IMPLEMENT_EXCEPTION(CSystemException, CRuntimeException, "System exception") POCO_IMPLEMENT_EXCEPTION(CRegularExpressionException, CRuntimeException, "Error in regular expression") POCO_IMPLEMENT_EXCEPTION(CLibraryLoadException, CRuntimeException, "Cannot load library") POCO_IMPLEMENT_EXCEPTION(CLibraryAlreadyLoadedException, CRuntimeException, "Library already loaded") POCO_IMPLEMENT_EXCEPTION(CNoThreadAvailableException, CRuntimeException, "No thread available") POCO_IMPLEMENT_EXCEPTION(CPropertyNotSupportedException, CRuntimeException, "Property not supported") POCO_IMPLEMENT_EXCEPTION(CPoolOverflowException, CRuntimeException, "Pool overflow") POCO_IMPLEMENT_EXCEPTION(CNoPermissionException, CRuntimeException, "No permission") POCO_IMPLEMENT_EXCEPTION(COutOfMemoryException, CRuntimeException, "Out of memory") POCO_IMPLEMENT_EXCEPTION(CDataException, CRuntimeException, "Data error") POCO_IMPLEMENT_EXCEPTION(CDataFormatException, CDataException, "Bad data format") POCO_IMPLEMENT_EXCEPTION(CSyntaxException, CDataException, "Syntax error") POCO_IMPLEMENT_EXCEPTION(CCircularReferenceException, CDataException, "Circular reference") POCO_IMPLEMENT_EXCEPTION(CPathSyntaxException, CSyntaxException, "Bad path syntax") POCO_IMPLEMENT_EXCEPTION(CIOException, CRuntimeException, "I/O error") POCO_IMPLEMENT_EXCEPTION(CFileException, CIOException, "File access error") POCO_IMPLEMENT_EXCEPTION(CFileExistsException, CFileException, "File exists") POCO_IMPLEMENT_EXCEPTION(CFileNotFoundException, CFileException, "File not found") POCO_IMPLEMENT_EXCEPTION(CPathNotFoundException, CFileException, "Path not found") POCO_IMPLEMENT_EXCEPTION(CFileReadOnlyException, CFileException, "File is read-only") POCO_IMPLEMENT_EXCEPTION(CFileAccessDeniedException, CFileException, "Access to file denied") POCO_IMPLEMENT_EXCEPTION(CCreateFileException, CFileException, "Cannot create file") POCO_IMPLEMENT_EXCEPTION(COpenFileException, CFileException, "Cannot open file") POCO_IMPLEMENT_EXCEPTION(CWriteFileException, CFileException, "Cannot write file") POCO_IMPLEMENT_EXCEPTION(CReadFileException, CFileException, "Cannot read file") POCO_IMPLEMENT_EXCEPTION(CUnknownURISchemeException, CRuntimeException, "Unknown URI scheme") POCO_IMPLEMENT_EXCEPTION(CApplicationException, CException, "Application exception") POCO_IMPLEMENT_EXCEPTION(CBadCastException, CRuntimeException, "Bad cast exception") POCO_IMPLEMENT_EXCEPTION(CConfigurationException, CException, "Configuration error"); POCO_IMPLEMENT_EXCEPTION(CThreadException, CException, "Threading error"); POCO_IMPLEMENT_EXCEPTION(CWin32Exception, CException, "Win32 error"); POCO_IMPLEMENT_EXCEPTION(CSDLException, CException, "SDL error"); POCO_IMPLEMENT_EXCEPTION(CGracefulException, CException, "Graceful exit"); /* User request to exit */ POCO_IMPLEMENT_EXCEPTION(CAbortException, CException, "Abort requested"); /* User request to abort */ ================================================ FILE: src/base/Exception.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Exception.h // // $Id: Exception.h,v 1.1 2008/05/31 15:47:23 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Exception // // Definition of various Poco exception classes. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Exception_INCLUDED #define Foundation_Exception_INCLUDED #include "Foundation.hpp" #include class CException : public std::exception /// This is the base class for all exceptions defined /// in the Poco class library. { public: CException(const std::string &msg, int code = 0); /// Creates an exception. CException(const std::string &msg, const std::string &arg, int code = 0); /// Creates an exception. CException(const std::string &msg, const CException &nested, int code = 0); /// Creates an exception and stores a clone /// of the nested exception. CException(const CException &exc); /// Copy constructor. ~CException() throw(); /// Destroys the exception and deletes the nested exception. CException &operator=(const CException &exc); /// Assignment operator. virtual const char *name() const throw(); /// Returns a static string describing the exception. virtual const char *className() const throw(); /// Returns the name of the exception class. virtual const char *what() const throw(); /// Returns a static string describing the exception. /// /// Same as name(), but for compatibility with std::exception. const CException *nested() const; /// Returns a pointer to the nested exception, or /// null if no nested exception exists. const std::string &message() const; /// Returns the message text. int code() const; /// Returns the exception code if defined. std::string displayText() const; /// Returns a string consisting of the /// message name and the message text. virtual CException *clone() const; /// Creates an exact copy of the exception. /// /// The copy can later be thrown again by /// invoking rethrow() on it. virtual void rethrow() const; /// (Re)Throws the exception. /// /// This is useful for temporarily storing a /// copy of an exception (see clone()), then /// throwing it again. protected: CException(int code = 0); /// Standard constructor. private: std::string _msg; CException *_pNested; int _code; }; // // inlines // inline const CException *CException::nested() const { return _pNested; } inline const std::string &CException::message() const { return _msg; } inline int CException::code() const { return _code; } // // Macros for quickly declaring and implementing exception classes. // Unfortunately, we cannot use a template here because character // pointers (which we need for specifying the exception name) // are not allowed as template arguments. // #define POCO_DECLARE_EXCEPTION(CLS, BASE) \ class CLS : public BASE { \ public: \ CLS(int code = 0); \ CLS(const std::string &msg, int code = 0); \ CLS(const std::string &msg, const std::string &arg, int code = 0); \ CLS(const std::string &msg, const CException &exc, int code = 0); \ CLS(const CLS &exc); \ ~CLS() throw(); \ CLS &operator=(const CLS &exc); \ const char *name() const throw(); \ const char *className() const throw(); \ CException *clone() const; \ void rethrow() const; \ }; #define POCO_IMPLEMENT_EXCEPTION(CLS, BASE, NAME) \ CLS::CLS(int code) : BASE(code) {} \ CLS::CLS(const std::string &msg, int code) : BASE(msg, code) {} \ CLS::CLS(const std::string &msg, const std::string &arg, int code) \ : BASE(msg, arg, code) {} \ CLS::CLS(const std::string &msg, const CException &exc, int code) \ : BASE(msg, exc, code) {} \ CLS::CLS(const CLS &exc) : BASE(exc) {} \ CLS::~CLS() throw() {} \ CLS &CLS::operator=(const CLS &exc) { \ BASE::operator=(exc); \ return *this; \ } \ const char *CLS::name() const throw() { return NAME; } \ const char *CLS::className() const throw() { return typeid(*this).name(); } \ CException *CLS::clone() const { return new CLS(*this); } \ void CLS::rethrow() const { throw *this; } // // Standard exception classes // POCO_DECLARE_EXCEPTION(CLogicException, CException) POCO_DECLARE_EXCEPTION(CAssertionViolationException, CLogicException) POCO_DECLARE_EXCEPTION(CNullPointerException, CLogicException) POCO_DECLARE_EXCEPTION(CBugcheckException, CLogicException) POCO_DECLARE_EXCEPTION(CInvalidArgumentException, CLogicException) POCO_DECLARE_EXCEPTION(CNotImplementedException, CLogicException) POCO_DECLARE_EXCEPTION(CRangeException, CLogicException) POCO_DECLARE_EXCEPTION(CIllegalStateException, CLogicException) POCO_DECLARE_EXCEPTION(CInvalidAccessException, CLogicException) POCO_DECLARE_EXCEPTION(CSignalException, CLogicException) POCO_DECLARE_EXCEPTION(CUnhandledException, CLogicException) POCO_DECLARE_EXCEPTION(CRuntimeException, CException) POCO_DECLARE_EXCEPTION(CNotFoundException, CRuntimeException) POCO_DECLARE_EXCEPTION(CExistsException, CRuntimeException) POCO_DECLARE_EXCEPTION(CTimeoutException, CRuntimeException) POCO_DECLARE_EXCEPTION(CSystemException, CRuntimeException) POCO_DECLARE_EXCEPTION(CRegularExpressionException, CRuntimeException) POCO_DECLARE_EXCEPTION(CLibraryLoadException, CRuntimeException) POCO_DECLARE_EXCEPTION(CLibraryAlreadyLoadedException, CRuntimeException) POCO_DECLARE_EXCEPTION(CNoThreadAvailableException, CRuntimeException) POCO_DECLARE_EXCEPTION(CPropertyNotSupportedException, CRuntimeException) POCO_DECLARE_EXCEPTION(CPoolOverflowException, CRuntimeException) POCO_DECLARE_EXCEPTION(CNoPermissionException, CRuntimeException) POCO_DECLARE_EXCEPTION(COutOfMemoryException, CRuntimeException) POCO_DECLARE_EXCEPTION(CDataException, CRuntimeException) POCO_DECLARE_EXCEPTION(CDataFormatException, CDataException) POCO_DECLARE_EXCEPTION(CSyntaxException, CDataException) POCO_DECLARE_EXCEPTION(CCircularReferenceException, CDataException) POCO_DECLARE_EXCEPTION(CPathSyntaxException, CSyntaxException) POCO_DECLARE_EXCEPTION(CIOException, CRuntimeException) POCO_DECLARE_EXCEPTION(CFileException, CIOException) POCO_DECLARE_EXCEPTION(CFileExistsException, CFileException) POCO_DECLARE_EXCEPTION(CFileNotFoundException, CFileException) POCO_DECLARE_EXCEPTION(CPathNotFoundException, CFileException) POCO_DECLARE_EXCEPTION(CFileReadOnlyException, CFileException) POCO_DECLARE_EXCEPTION(CFileAccessDeniedException, CFileException) POCO_DECLARE_EXCEPTION(CCreateFileException, CFileException) POCO_DECLARE_EXCEPTION(COpenFileException, CFileException) POCO_DECLARE_EXCEPTION(CWriteFileException, CFileException) POCO_DECLARE_EXCEPTION(CReadFileException, CFileException) POCO_DECLARE_EXCEPTION(CUnknownURISchemeException, CRuntimeException) POCO_DECLARE_EXCEPTION(CApplicationException, CException) POCO_DECLARE_EXCEPTION(CBadCastException, CRuntimeException) POCO_DECLARE_EXCEPTION(CConfigurationException, CException); POCO_DECLARE_EXCEPTION(CThreadException, CException); POCO_DECLARE_EXCEPTION(CWin32Exception, CException); POCO_DECLARE_EXCEPTION(CSDLException, CException); POCO_DECLARE_EXCEPTION(CGracefulException, CException); /* User request to exit */ POCO_DECLARE_EXCEPTION(CAbortException, CException); /* User request to abort */ #endif // Foundation_Exception_INCLUDED ================================================ FILE: src/base/Foundation.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Foundation.h // // $Id: Foundation.h,v 1.2 2010/03/11 22:44:16 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Foundation // // Basic definitions for the POCO Foundation library. // This file must be the first file included by every other Foundation // header file. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Foundation_INCLUDED #define Foundation_Foundation_INCLUDED // // Include library configuration // // CAVA #include "Config.h" // // Include platform-specific definitions // #include "Platform.hpp" #if defined(_WIN32) #include "Platform_WIN32.hpp" #elif defined(__VMS) #include "Platform_VMS.hpp" #elif defined(POCO_OS_FAMILY_UNIX) #include "Platform_POSIX.hpp" #endif // // POCO_JOIN // // The following piece of macro magic joins the two // arguments together, even when one of the arguments is // itself a macro (see 16.3.1 in C++ standard). The key // is that macro expansion of macro arguments does not // occur in POCO_DO_JOIN2 but does in POCO_DO_JOIN. // #define POCO_JOIN(X, Y) POCO_DO_JOIN(X, Y) #define POCO_DO_JOIN(X, Y) POCO_DO_JOIN2(X, Y) #define POCO_DO_JOIN2(X, Y) X##Y // // Pull in basic definitions // #include "Bugcheck.hpp" #include "Types.hpp" #include #include #endif // Foundation_Foundation_INCLUDED ================================================ FILE: src/base/Mutex.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex.cpp // // $Id: Mutex.cpp,v 1.2 2008/06/12 06:52:33 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Mutex.hpp" #include #if defined(POCO_OS_FAMILY_WINDOWS) #include "Mutex_WIN32.cpp" #else #include "Mutex_POSIX.cpp" #endif CMutex::CMutex() { lockName = strdup("anon"); } CMutex::CMutex(const char *lName) { lockName = strdup(lName); } CMutex::~CMutex() { free(lockName); } CFastMutex::CFastMutex() { lockName = strdup("anon"); } CFastMutex::CFastMutex(const char *lName) { lockName = strdup(lName); } CFastMutex::~CFastMutex() { free(lockName); } void CMutex::lock() { #if defined(DEBUG_LOCKS) printf(" LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { lockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } void CMutex::lock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { if (!tryLockImpl(milliseconds)) FAILURE(Timeout, "Timeout"); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } bool CMutex::tryLock() { bool res; #if defined(DEBUG_LOCKS) printf(" TRY LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryLockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } bool CMutex::tryLock(long milliseconds) { bool res; #if defined(DEBUG_LOCKS) printf(" TRY LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryLockImpl(milliseconds); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } void CMutex::unlock() { #if defined(DEBUG_LOCKS) printf(" UNLOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { unlockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to unlock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" UNLOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } void CFastMutex::lock() { #if defined(DEBUG_LOCKS) printf(" LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { lockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } void CFastMutex::lock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { if (!tryLockImpl(milliseconds)) FAILURE(Timeout, "Timeout"); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } bool CFastMutex::tryLock() { bool res; #if defined(DEBUG_LOCKS) printf(" TRY LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryLockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } bool CFastMutex::tryLock(long milliseconds) { bool res; #if defined(DEBUG_LOCKS) printf(" TRY LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryLockImpl(milliseconds); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } void CFastMutex::unlock() { #if defined(DEBUG_LOCKS) printf(" UNLOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { unlockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to unlock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" UNLOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } ================================================ FILE: src/base/Mutex.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex.h // // $Id: Mutex.h,v 1.2 2008/06/12 06:52:33 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Definition of the Mutex and FastMutex classes. // // Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Mutex_INCLUDED #define Foundation_Mutex_INCLUDED #if defined(NO_LOCK_TIMEOUTS) #define LOCK_TIMEOUT_MS #else #if !defined(LOCK_TIMEOUT_MS) #define LOCK_TIMEOUT_MS 5000 #endif #endif // Placeholder #define CURRENT_THREAD_NAME "" #include "../es40_debug.hpp" #include "Exception.hpp" #include "Foundation.hpp" #include "ScopedLock.hpp" #if defined(POCO_OS_FAMILY_WINDOWS) #include "Mutex_WIN32.hpp" #else #include "Mutex_POSIX.hpp" #endif class CMutex : private CMutexImpl /// A Mutex (mutual exclusion) is a synchronization /// mechanism used to control access to a shared resource /// in a concurrent (multithreaded) scenario. /// Mutexes are recursive, that is, the same mutex can be /// locked multiple times by the same thread (but, of course, /// not by other threads). /// Using the ScopedLock class is the preferred way to automatically /// lock and unlock a mutex. { public: typedef ::CScopedLock CScopedLock; CMutex(const char *lName); char *lockName; CMutex(); /// creates the Mutex. ~CMutex(); /// destroys the Mutex. void lock(); /// Locks the mutex. Blocks if the mutex /// is held by another thread. void lock(long milliseconds); /// Locks the mutex. Blocks up to the given number of milliseconds /// if the mutex is held by another thread. Throws a TimeoutException /// if the mutex can not be locked within the given timeout. /// /// Performance Note: On most platforms (including Windows), this member /// function is implemented using a loop calling (the equivalent of) tryLock() /// and Thread::sleep(). On POSIX platforms that support /// pthread_mutex_timedlock(), this is used. bool tryLock(); /// Tries to lock the mutex. Returns false immediately /// if the mutex is already held by another thread. /// Returns true if the mutex was successfully locked. bool tryLock(long milliseconds); /// Locks the mutex. Blocks up to the given number of milliseconds /// if the mutex is held by another thread. /// Returns true if the mutex was successfully locked. /// /// Performance Note: On most platforms (including Windows), this member /// function is implemented using a loop calling (the equivalent of) tryLock() /// and Thread::sleep(). On POSIX platforms that support /// pthread_mutex_timedlock(), this is used. void unlock(); /// Unlocks the mutex so that it can be acquired by /// other threads. private: CMutex(const CMutex &); CMutex &operator=(const CMutex &); }; class CFastMutex : private CFastMutexImpl /// A FastMutex (mutual exclusion) is similar to a Mutex. /// Unlike a Mutex, however, a FastMutex is not recursive, /// which means that a deadlock will occur if the same /// thread tries to lock a mutex it has already locked again. /// Locking a FastMutex is faster than locking a recursive Mutex. /// Using the ScopedLock class is the preferred way to automatically /// lock and unlock a mutex. { public: typedef ::CScopedLock CScopedLock; CFastMutex(const char *lName); char *lockName; CFastMutex(); /// creates the Mutex. ~CFastMutex(); /// destroys the Mutex. void lock(); /// Locks the mutex. Blocks if the mutex /// is held by another thread. void lock(long milliseconds); /// Locks the mutex. Blocks up to the given number of milliseconds /// if the mutex is held by another thread. Throws a TimeoutException /// if the mutex can not be locked within the given timeout. /// /// Performance Note: On most platforms (including Windows), this member /// function is implemented using a loop calling (the equivalent of) tryLock() /// and Thread::sleep(). On POSIX platforms that support /// pthread_mutex_timedlock(), this is used. bool tryLock(); /// Tries to lock the mutex. Returns false immediately /// if the mutex is already held by another thread. /// Returns true if the mutex was successfully locked. bool tryLock(long milliseconds); /// Locks the mutex. Blocks up to the given number of milliseconds /// if the mutex is held by another thread. /// Returns true if the mutex was successfully locked. /// /// Performance Note: On most platforms (including Windows), this member /// function is implemented using a loop calling (the equivalent of) tryLock() /// and Thread::sleep(). On POSIX platforms that support /// pthread_mutex_timedlock(), this is used. void unlock(); /// Unlocks the mutex so that it can be acquired by /// other threads. private: CFastMutex(const CFastMutex &); CFastMutex &operator=(const CFastMutex &); }; #define MUTEX_LOCK(mutex) mutex->lock(LOCK_TIMEOUT_MS) #define MUTEX_READ_LOCK(mutex) mutex->readLock(LOCK_TIMEOUT_MS) #define MUTEX_WRITE_LOCK(mutex) mutex->writeLock(LOCK_TIMEOUT_MS) #define MUTEX_UNLOCK(mutex) mutex->unlock() #define SCOPED_M_LOCK(mutex) CMutex::CScopedLock L_##__LINE__(mutex) #define SCOPED_FM_LOCK(mutex) CFastMutex::CScopedLock L_##__LINE__(mutex) #endif // Foundation_Mutex_INCLUDED ================================================ FILE: src/base/Mutex_POSIX.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex_POSIX.cpp // // $Id: Mutex_POSIX.cpp,v 1.1 2008/05/31 15:47:24 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Mutex_POSIX.hpp" #include "Timestamp.hpp" #if !defined(POCO_NO_SYS_SELECT_H) #include #endif #include #include #if defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS - 200112L) >= 0L #if defined(_POSIX_THREADS) && (_POSIX_THREADS - 200112L) >= 0L #define POCO_HAVE_MUTEX_TIMEOUT #endif #endif CMutexImpl::CMutexImpl() { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); #if defined(PTHREAD_MUTEX_RECURSIVE_NP) pthread_mutexattr_settype_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP); #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #endif if (pthread_mutex_init(&_mutex, &attr)) { pthread_mutexattr_destroy(&attr); throw CSystemException("cannot create mutex"); } pthread_mutexattr_destroy(&attr); } CMutexImpl::CMutexImpl(bool fast) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); #if defined(PTHREAD_MUTEX_RECURSIVE_NP) pthread_mutexattr_settype_np(&attr, fast ? PTHREAD_MUTEX_NORMAL_NP : PTHREAD_MUTEX_RECURSIVE_NP); #else pthread_mutexattr_settype(&attr, fast ? PTHREAD_MUTEX_NORMAL : PTHREAD_MUTEX_RECURSIVE); #endif if (pthread_mutex_init(&_mutex, &attr)) { pthread_mutexattr_destroy(&attr); throw CSystemException("cannot create mutex"); } pthread_mutexattr_destroy(&attr); } CMutexImpl::~CMutexImpl() { pthread_mutex_destroy(&_mutex); } bool CMutexImpl::tryLockImpl(long milliseconds) { #if defined(POCO_HAVE_MUTEX_TIMEOUT) struct timespec abstime; struct timeval tv; gettimeofday(&tv, NULL); abstime.tv_sec = tv.tv_sec + milliseconds / 1000; abstime.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000; if (abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; } int rc = pthread_mutex_timedlock(&_mutex, &abstime); if (rc == 0) return true; else if (rc == ETIMEDOUT) return false; else throw CSystemException("cannot lock mutex"); #else const int sleepMillis = 5; CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { int rc = pthread_mutex_trylock(&_mutex); if (rc == 0) return true; else if (rc != EBUSY) throw CSystemException("cannot lock mutex"); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = sleepMillis * 1000; select(0, NULL, NULL, NULL, &tv); } while (!now.isElapsed(diff)); return false; #endif } CFastMutexImpl::CFastMutexImpl() : CMutexImpl(true) {} CFastMutexImpl::~CFastMutexImpl() {} ================================================ FILE: src/base/Mutex_POSIX.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex_POSIX.h // // $Id: Mutex_POSIX.h,v 1.1 2008/05/31 15:47:24 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Definition of the MutexImpl and FastMutexImpl classes for POSIX Threads. // // Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Mutex_POSIX_INCLUDED #define Foundation_Mutex_POSIX_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include #include class CMutexImpl { protected: CMutexImpl(); CMutexImpl(bool fast); ~CMutexImpl(); void lockImpl(); bool tryLockImpl(); bool tryLockImpl(long milliseconds); void unlockImpl(); private: pthread_mutex_t _mutex; }; class CFastMutexImpl : public CMutexImpl { protected: CFastMutexImpl(); ~CFastMutexImpl(); }; // // inlines // inline void CMutexImpl::lockImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("cannot lock mutex"); } inline bool CMutexImpl::tryLockImpl() { int rc = pthread_mutex_trylock(&_mutex); if (rc == 0) return true; else if (rc == EBUSY) return false; else throw CSystemException("cannot lock mutex"); } inline void CMutexImpl::unlockImpl() { if (pthread_mutex_unlock(&_mutex)) throw CSystemException("cannot unlock mutex"); } #endif // Foundation_Mutex_POSIX_INCLUDED ================================================ FILE: src/base/Mutex_WIN32.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex_WIN32.cpp // // $Id: Mutex_WIN32.cpp,v 1.1 2008/05/31 15:47:24 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Mutex_WIN32.hpp" #include "Timestamp.hpp" CMutexImpl::CMutexImpl() { // the fct has a boolean return value under WInnNt/2000/XP but not on Win98 // the return only checks if the input address of &_cs was valid, so it is // safe to omit it InitializeCriticalSectionAndSpinCount(&_cs, 4000); } CMutexImpl::~CMutexImpl() { DeleteCriticalSection(&_cs); } bool CMutexImpl::tryLockImpl(long milliseconds) { const int sleepMillis = 5; CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { try { if (TryEnterCriticalSection(&_cs) == TRUE) return true; } catch (...) { throw CSystemException("cannot lock mutex"); } Sleep(sleepMillis); } while (!now.isElapsed(diff)); return false; } ================================================ FILE: src/base/Mutex_WIN32.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Mutex_WIN32.h // // $Id: Mutex_WIN32.h,v 1.1 2008/05/31 15:47:24 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Definition of the MutexImpl and FastMutexImpl classes for WIN32. // // Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Mutex_WIN32_INCLUDED #define Foundation_Mutex_WIN32_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include "UnWindows.hpp" class CMutexImpl { protected: CMutexImpl(); ~CMutexImpl(); void lockImpl(); bool tryLockImpl(); bool tryLockImpl(long milliseconds); void unlockImpl(); private: CRITICAL_SECTION _cs; }; typedef CMutexImpl CFastMutexImpl; // // inlines // inline void CMutexImpl::lockImpl() { try { EnterCriticalSection(&_cs); } catch (...) { throw CSystemException("cannot lock mutex"); } } inline bool CMutexImpl::tryLockImpl() { try { return TryEnterCriticalSection(&_cs) == TRUE; } catch (...) { } throw CSystemException("cannot lock mutex"); } inline void CMutexImpl::unlockImpl() { LeaveCriticalSection(&_cs); } #endif // Foundation_Mutex_WIN32_INCLUDED ================================================ FILE: src/base/NumberFormatter.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // NumberFormatter.cpp // // $Id: NumberFormatter.cpp,v 1.1 2008/05/31 15:47:24 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: NumberFormatter // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "NumberFormatter.hpp" #include #include #include #include std::string CNumberFormatter::format(int value) { char buffer[64]; std::sprintf(buffer, "%d", value); return std::string(buffer); } std::string CNumberFormatter::format(int value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*d", width, value); return std::string(buffer); } std::string CNumberFormatter::format0(int value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*d", width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(int value) { char buffer[64]; std::sprintf(buffer, "%X", value); return std::string(buffer); } std::string CNumberFormatter::formatHex(int value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*X", width, value); return std::string(buffer); } std::string CNumberFormatter::format(unsigned value) { char buffer[64]; std::sprintf(buffer, "%u", value); return std::string(buffer); } std::string CNumberFormatter::format(unsigned value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*u", width, value); return std::string(buffer); } std::string CNumberFormatter::format0(unsigned int value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*u", width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(unsigned value) { char buffer[64]; std::sprintf(buffer, "%X", value); return std::string(buffer); } std::string CNumberFormatter::formatHex(unsigned value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*X", width, value); return std::string(buffer); } std::string CNumberFormatter::format(long value) { char buffer[64]; std::sprintf(buffer, "%ld", value); return std::string(buffer); } std::string CNumberFormatter::format(long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*ld", width, value); return std::string(buffer); } std::string CNumberFormatter::format0(long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*ld", width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(long value) { char buffer[64]; std::sprintf(buffer, "%lX", value); return std::string(buffer); } std::string CNumberFormatter::formatHex(long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*lX", width, value); return std::string(buffer); } std::string CNumberFormatter::format(unsigned long value) { char buffer[64]; std::sprintf(buffer, "%lu", value); return std::string(buffer); } std::string CNumberFormatter::format(unsigned long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*lu", width, value); return std::string(buffer); } std::string CNumberFormatter::format0(unsigned long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*lu", width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(unsigned long value) { char buffer[64]; std::sprintf(buffer, "%lX", value); return std::string(buffer); } std::string CNumberFormatter::formatHex(unsigned long value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*lX", width, value); return std::string(buffer); } #if defined(POCO_HAVE_INT64) && !defined(POCO_LONG_IS_64_BIT) std::string CNumberFormatter::format(Int64 value) { char buffer[64]; std::sprintf(buffer, "%" PRId64, value); return std::string(buffer); } std::string CNumberFormatter::format(Int64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*" PRId64, width, value); return std::string(buffer); } std::string CNumberFormatter::format0(Int64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*" PRId64, width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(Int64 value) { char buffer[64]; std::sprintf(buffer, "%" PRIX64, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(Int64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*" PRIX64, width, value); return std::string(buffer); } std::string CNumberFormatter::format(UInt64 value) { char buffer[64]; std::sprintf(buffer, "%" PRIu64, value); return std::string(buffer); } std::string CNumberFormatter::format(UInt64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%*" PRIu64, width, value); return std::string(buffer); } std::string CNumberFormatter::format0(UInt64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*" PRIu64, width, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(UInt64 value) { char buffer[64]; std::sprintf(buffer, "%" PRIX64, value); return std::string(buffer); } std::string CNumberFormatter::formatHex(UInt64 value, int width) { poco_assert(width > 0 && width < 64); char buffer[64]; std::sprintf(buffer, "%0*" PRIX64, width, value); return std::string(buffer); } #endif // defined(POCO_HAVE_INT64) && !defined(POCO_LONG_IS_64_BIT) std::string CNumberFormatter::format(float value) { char buffer[64]; std::sprintf(buffer, "%.*g", 10, (double)value); return std::string(buffer); } std::string CNumberFormatter::format(double value) { char buffer[64]; std::sprintf(buffer, "%.*g", 20, value); return std::string(buffer); } std::string CNumberFormatter::format(double value, int precision) { poco_assert(precision >= 0 && precision < 32); char buffer[64]; std::sprintf(buffer, "%.*f", precision, value); return std::string(buffer); } std::string CNumberFormatter::format(double value, int width, int precision) { poco_assert(width > 0 && width < 64 && precision >= 0 && precision < width); char buffer[64]; std::sprintf(buffer, "%*.*f", width, precision, value); return std::string(buffer); } std::string CNumberFormatter::format(const void *ptr) { char buffer[24]; std::sprintf(buffer, "%p", ptr); return std::string(buffer); } ================================================ FILE: src/base/NumberFormatter.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // NumberFormatter.h // // $Id: NumberFormatter.h,v 1.1 2008/05/31 15:47:25 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: NumberFormatter // // Definition of the NumberFormatter class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_NumberFormatter_INCLUDED #define Foundation_NumberFormatter_INCLUDED #include "Foundation.hpp" class CNumberFormatter /// The NumberFormatter class provides static methods /// for formatting numeric values into strings. { public: static std::string format(int value); /// Formats an integer value in decimal notation. static std::string format(int value, int width); /// Formats an integer value in decimal notation, /// right justified in a field having at least /// the specified width. static std::string format0(int value, int width); /// Formats an integer value in decimal notation, /// right justified and zero-padded in a field /// having at least the specified width. static std::string formatHex(int value); /// Formats an int value in hexadecimal notation. /// The value is treated as unsigned. static std::string formatHex(int value, int width); /// Formats a int value in hexadecimal notation, /// right justified and zero-padded in /// a field having at least the specified width. /// The value is treated as unsigned. static std::string format(unsigned value); /// Formats an unsigned int value in decimal notation. static std::string format(unsigned value, int width); /// Formats an unsigned long int in decimal notation, /// right justified in a field having at least the /// specified width. static std::string format0(unsigned int value, int width); /// Formats an unsigned int value in decimal notation, /// right justified and zero-padded in a field having at /// least the specified width. static std::string formatHex(unsigned value); /// Formats an unsigned int value in hexadecimal notation. static std::string formatHex(unsigned value, int width); /// Formats a int value in hexadecimal notation, /// right justified and zero-padded in /// a field having at least the specified width. static std::string format(long value); /// Formats a long value in decimal notation. static std::string format(long value, int width); /// Formats a long value in decimal notation, /// right justified in a field having at least the /// specified width. static std::string format0(long value, int width); /// Formats a long value in decimal notation, /// right justified and zero-padded in a field /// having at least the specified width. static std::string formatHex(long value); /// Formats an unsigned long value in hexadecimal notation. /// The value is treated as unsigned. static std::string formatHex(long value, int width); /// Formats an unsigned long value in hexadecimal notation, /// right justified and zero-padded in a field having at least the /// specified width. /// The value is treated as unsigned. static std::string format(unsigned long value); /// Formats an unsigned long value in decimal notation. static std::string format(unsigned long value, int width); /// Formats an unsigned long value in decimal notation, /// right justified in a field having at least the specified /// width. static std::string format0(unsigned long value, int width); /// Formats an unsigned long value in decimal notation, /// right justified and zero-padded /// in a field having at least the specified width. static std::string formatHex(unsigned long value); /// Formats an unsigned long value in hexadecimal notation. static std::string formatHex(unsigned long value, int width); /// Formats an unsigned long value in hexadecimal notation, /// right justified and zero-padded in a field having at least the /// specified width. #if defined(POCO_HAVE_INT64) && !defined(POCO_LONG_IS_64_BIT) static std::string format(Int64 value); /// Formats a 64-bit integer value in decimal notation. static std::string format(Int64 value, int width); /// Formats a 64-bit integer value in decimal notation, /// right justified in a field having at least the specified width. static std::string format0(Int64 value, int width); /// Formats a 64-bit integer value in decimal notation, /// right justified and zero-padded in a field having at least /// the specified width. static std::string formatHex(Int64 value); /// Formats a 64-bit integer value in hexadecimal notation. /// The value is treated as unsigned. static std::string formatHex(Int64 value, int width); /// Formats a 64-bit integer value in hexadecimal notation, /// right justified and zero-padded in a field having at least /// the specified width. /// The value is treated as unsigned. static std::string format(UInt64 value); /// Formats an unsigned 64-bit integer value in decimal notation. static std::string format(UInt64 value, int width); /// Formats an unsigned 64-bit integer value in decimal notation, /// right justified in a field having at least the specified width. static std::string format0(UInt64 value, int width); /// Formats an unsigned 64-bit integer value in decimal notation, /// right justified and zero-padded in a field having at least the /// specified width. static std::string formatHex(UInt64 value); /// Formats a 64-bit integer value in hexadecimal notation. static std::string formatHex(UInt64 value, int width); /// Formats a 64-bit integer value in hexadecimal notation, /// right justified and zero-padded in a field having at least /// the specified width. #endif // defined(POCO_HAVE_INT64) && !defined(POCO_LONG_IS_64_BIT) static std::string format(float value); /// Formats a float value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 8 fractional /// digits. static std::string format(double value); /// Formats a double value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 16 fractional /// digits. static std::string format(double value, int precision); /// Formats a double value in decimal floating-point notation, /// according to std::printf's %f format with the given precision. static std::string format(double value, int width, int precision); /// Formats a double value in decimal floating-point notation, /// right justified in a field of the specified width, /// with the number of fractional digits given in precision. static std::string format(const void *ptr); /// Formats a pointer in an eight (32-bit architectures) or /// sixteen (64-bit architectures) characters wide /// field in hexadecimal notation. }; #endif // Foundation_NumberFormatter_INCLUDED ================================================ FILE: src/base/Platform.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Platform.h // // $Id: Platform.h,v 1.1 2008/05/31 15:47:25 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Platform // // Platform and architecture identification macros. // // NOTE: This file may be included from both C++ and C code, so it // must not contain any C++ specific things. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Platform_INCLUDED #define Foundation_Platform_INCLUDED // // Platform Identification // #define POCO_OS_FREE_BSD 0x0001 #define POCO_OS_AIX 0x0002 #define POCO_OS_HPUX 0x0003 #define POCO_OS_TRU64 0x0004 #define POCO_OS_LINUX 0x0005 #define POCO_OS_MAC_OS_X 0x0006 #define POCO_OS_NET_BSD 0x0007 #define POCO_OS_OPEN_BSD 0x0008 #define POCO_OS_IRIX 0x0009 #define POCO_OS_SOLARIS 0x000a #define POCO_OS_QNX 0x000b #define POCO_OS_VXWORKS 0x000c #define POCO_OS_CYGWIN 0x000d #define POCO_OS_UNKNOWN_UNIX 0x00ff #define POCO_OS_WINDOWS_NT 0x1001 #define POCO_OS_WINDOWS_CE 0x1011 #define POCO_OS_VMS 0x2001 #if defined(__FreeBSD__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS_FAMILY_BSD 1 #define POCO_OS POCO_OS_FREE_BSD #elif defined(_AIX) || defined(__TOS_AIX__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_AIX #elif defined(hpux) || defined(_hpux) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_HPUX #elif defined(__digital__) || defined(__osf__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_TRU64 #elif defined(linux) || defined(__linux) || defined(__linux__) || \ defined(__TOS_LINUX__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_LINUX #elif defined(__APPLE__) || defined(__TOS_MACOS__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS_FAMILY_BSD 1 #define POCO_OS POCO_OS_MAC_OS_X #elif defined(__NetBSD__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS_FAMILY_BSD 1 #define POCO_OS POCO_OS_NET_BSD #elif defined(__OpenBSD__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS_FAMILY_BSD 1 #define POCO_OS POCO_OS_OPEN_BSD #elif defined(sgi) || defined(__sgi) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_IRIX #elif defined(sun) || defined(__sun) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_SOLARIS #elif defined(__QNX__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_QNX #elif defined(unix) || defined(__unix) || defined(__unix__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_UNKNOWN_UNIX #elif defined(_WIN32_WCE) #define POCO_OS_FAMILY_WINDOWS 1 #define POCO_OS POCO_OS_WINDOWS_CE #elif defined(_WIN32) || defined(_WIN64) #define POCO_OS_FAMILY_WINDOWS 1 #define POCO_OS POCO_OS_WINDOWS_NT #elif defined(__CYGWIN__) #define POCO_OS_FAMILY_UNIX 1 #define POCO_OS POCO_OS_CYGWIN #elif defined(__VMS) #define POCO_OS_FAMILY_VMS 1 #define POCO_OS POCO_OS_VMS #endif // // Hardware Architecture and Byte Order // #define POCO_ARCH_ALPHA 0x01 #define POCO_ARCH_IA32 0x02 #define POCO_ARCH_IA64 0x03 #define POCO_ARCH_MIPS 0x04 #define POCO_ARCH_HPPA 0x05 #define POCO_ARCH_PPC 0x06 #define POCO_ARCH_POWER 0x07 #define POCO_ARCH_SPARC 0x08 #define POCO_ARCH_AMD64 0x09 #define POCO_ARCH_ARM 0x0a #if defined(__ALPHA) || defined(__alpha) || defined(__alpha__) || \ defined(_M_ALPHA) #define POCO_ARCH POCO_ARCH_ALPHA #define POCO_ARCH_LITTLE_ENDIAN 1 #elif defined(i386) || defined(__i386) || defined(__i386__) || defined(_M_IX86) #define POCO_ARCH POCO_ARCH_IA32 #define POCO_ARCH_LITTLE_ENDIAN 1 #elif defined(_IA64) || defined(__IA64__) || defined(__ia64__) || \ defined(__ia64) || defined(_M_IA64) #define POCO_ARCH POCO_ARCH_IA64 #if defined(hpux) || defined(_hpux) #define POCO_ARCH_BIG_ENDIAN 1 #else #define POCO_ARCH_LITTLE_ENDIAN 1 #endif #elif defined(__x86_64__) || defined(_M_X64) #define POCO_ARCH POCO_ARCH_AMD64 #define POCO_ARCH_LITTLE_ENDIAN 1 #elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || \ defined(_M_MRX000) #define POCO_ARCH POCO_ARCH_MIPS #define POCO_ARCH_BIG_ENDIAN 1 #elif defined(__hppa) || defined(__hppa__) #define POCO_ARCH POCO_ARCH_HPPA #define POCO_ARCH_BIG_ENDIAN 1 #elif defined(__PPC) || defined(__POWERPC__) || defined(__powerpc) || \ defined(__PPC__) || defined(__powerpc__) || defined(__ppc__) || \ defined(_ARCH_PPC) || defined(_M_PPC) #define POCO_ARCH POCO_ARCH_PPC #define POCO_ARCH_BIG_ENDIAN 1 #elif defined(_POWER) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || \ defined(_ARCH_PWR3) || defined(_ARCH_PWR4) || defined(__THW_RS6000) #define POCO_ARCH POCO_ARCH_POWER #define POCO_ARCH_BIG_ENDIAN 1 #elif defined(__sparc__) || defined(__sparc) || defined(sparc) #define POCO_ARCH POCO_ARCH_SPARC #define POCO_ARCH_BIG_ENDIAN 1 #elif defined(__arm__) || defined(__arm) || defined(ARM) || defined(_ARM_) || \ defined(__ARM__) || defined(_M_ARM) #define POCO_ARCH POCO_ARCH_ARM #if defined(__ARMEB__) #define POCO_ARCH_BIG_ENDIAN 1 #else #define POCO_ARCH_LITTLE_ENDIAN 1 #endif #endif #endif // Foundation_Platform_INCLUDED ================================================ FILE: src/base/Platform_POSIX.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Platform_POSIX.h // // $Id: Platform_POSIX.h,v 1.1 2008/05/31 15:47:25 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Platform // // Platform and architecture identification macros // and platform-specific definitions for various POSIX platforms // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Platform_POSIX_INCLUDED #define Foundation_Platform_POSIX_INCLUDED // // PA-RISC based HP-UX platforms have some issues... // #if defined(hpux) || defined(_hpux) #if defined(__hppa) || defined(__hppa__) #define POCO_NO_SYS_SELECT_H 1 #if defined(__HP_aCC) #define POCO_NO_TEMPLATE_ICOMPARE 1 #endif #endif #endif #endif // Foundation_Platform_POSIX_INCLUDED ================================================ FILE: src/base/Platform_VMS.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Platform_VMS.h // // $Id: Platform_VMS.h,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Platform // // Platform and architecture identification macros // and platform-specific definitions for OpenVMS. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Platform_VMS_INCLUDED #define Foundation_Platform_VMS_INCLUDED // Define the POCO_DESCRIPTOR_STRING and POCO_DESCRIPTOR_LITERAL // macros which we use instead of $DESCRIPTOR and $DESCRIPTOR64. // Our macros work with both 32bit and 64bit pointer sizes. #if __INITIAL_POINTER_SIZE != 64 #define POCO_DESCRIPTOR_STRING(name, string) \ struct dsc$descriptor_s name = {string.size(), DSC$K_DTYPE_T, DSC$K_CLASS_S, \ (char *)string.data()} #define POCO_DESCRIPTOR_LITERAL(name, string) \ struct dsc$descriptor_s name = {sizeof(string) - 1, DSC$K_DTYPE_T, \ DSC$K_CLASS_S, (char *)string} #else #define POCO_DESCRIPTOR_STRING(name, string) \ struct dsc64$descriptor_s name = { \ 1, DSC64$K_DTYPE_T, DSC64$K_CLASS_S, \ -1, string.size(), (char *)string.data()} #define POCO_DESCRIPTOR_LITERAL(name, string) \ struct dsc64$descriptor_s name = {1, DSC64$K_DTYPE_T, DSC64$K_CLASS_S, \ -1, sizeof(string) - 1, (char *)string} #endif // No header file #define POCO_NO_SYS_SELECT_H #endif // Foundation_Platform_VMS_INCLUDED ================================================ FILE: src/base/Platform_WIN32.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Platform_WIN32.h // // $Id: Platform_WIN32.h,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Platform // // Platform and architecture identification macros // and platform-specific definitions for Windows. // // Copyright (c) 2004-2007, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Platform_WIN32_INCLUDED #define Foundation_Platform_WIN32_INCLUDED // Verify that we're built with the multithreaded // versions of the runtime libraries #if defined(_MSC_VER) && !defined(_MT) #error Must compile with /MD, /MDd, /MT or /MTd #endif // Check debug/release settings consistency #if defined(NDEBUG) && defined(_DEBUG) #error Inconsistent build settings (check for /MD[d]) #endif // Reduce bloat imported by "UnWindows.h" #if defined(_WIN32) #if !defined(_WIN32_WINNT) #define _WIN32_WINNT 0x0500 #endif #if !defined(WIN32_LEAN_AND_MEAN) && !defined(POCO_BLOATED_WIN32) #define WIN32_LEAN_AND_MEAN #endif #endif // Unicode Support #if defined(UNICODE) && !defined(POCO_WIN32_UTF8) #define POCO_WIN32_UTF8 #endif // Turn off some annoying warnings #if defined(_MSC_VER) #define _CRT_SECURE_NO_DEPRECATE 1 // #pragma warning(disable:4018) // signed/unsigned comparison // #pragma warning(disable:4251) // ... needs to have dll-interface warning // #pragma warning(disable:4355) // 'this' : used in base member // initializer list #pragma warning(disable : 4996) // VC++ 8.0 deprecation warnings // #pragma warning(disable:4351) // new behavior: elements of array '...' // will be default initialized #pragma warning(disable:4675) // resolved // overload was found by argument-dependent lookup #endif //#if defined(__INTEL_COMPILER) // #pragma warning(disable:1738) // base class dllexport/dllimport // specification differs from that of the derived class #pragma // warning(disable:1478) // function ... was declared "deprecated" #pragma // warning(disable:1744) // field of class type without a DLL interface used in // a class with a DLL interface #endif #endif // Foundation_Platform_WIN32_INCLUDED ================================================ FILE: src/base/Poco.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Poco.h // // $Id: Poco.h,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Foundation // // Basic definitions for the POCO libraries. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Poco_INCLUDED #define Foundation_Poco_INCLUDED #include "Foundation.hpp" #endif // Foundation_Poco_INCLUDED ================================================ FILE: src/base/RWLock.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock.cpp // // $Id: RWLock.cpp,v 1.3 2008/06/12 06:58:24 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "RWLock.hpp" #include #if defined(POCO_OS_FAMILY_WINDOWS) #include "RWLock_WIN32.cpp" #else #include "RWLock_POSIX.cpp" #endif CRWLock::CRWLock() { lockName = strdup("anon"); } CRWLock::CRWLock(const char *lName) { lockName = strdup(lName); } CRWLock::~CRWLock() { free(lockName); } ================================================ FILE: src/base/RWLock.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock.h // // $Id: RWLock.h,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Definition of the RWLock class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_RWLock_INCLUDED #define Foundation_RWLock_INCLUDED #include "../es40_debug.hpp" #include "Exception.hpp" #include "Foundation.hpp" #include "Mutex.hpp" #include "Timestamp.hpp" #include #if defined(POCO_OS_FAMILY_WINDOWS) #include "RWLock_WIN32.hpp" #else #include "RWLock_POSIX.hpp" #endif class CScopedRWLock; class CRWLock : private CRWLockImpl /// A reader writer lock allows multiple concurrent /// readers or one exclusive writer. { public: typedef CScopedRWLock CScopedLock; CRWLock(const char *lName); void readLock(long milliseconds); bool tryReadLock(long milliseconds); void writeLock(long milliseconds); bool tryWriteLock(long milliseconds); char *lockName; CRWLock(); /// Creates the Reader/Writer lock. ~CRWLock(); /// Destroys the Reader/Writer lock. void readLock(); /// Acquires a read lock. If another thread currently holds a write lock, /// waits until the write lock is released. bool tryReadLock(); /// Tries to acquire a read lock. Immediately returns true if successful, or /// false if another thread currently holds a write lock. void writeLock(); /// Acquires a write lock. If one or more other threads currently hold /// locks, waits until all locks are released. The results are undefined /// if the same thread already holds a read or write lock bool tryWriteLock(); /// Tries to acquire a write lock. Immediately returns true if successful, /// or false if one or more other threads currently hold /// locks. The result is undefined if the same thread already /// holds a read or write lock. void unlock(); /// Releases the read or write lock. private: CRWLock(const CRWLock &); CRWLock &operator=(const CRWLock &); }; class CScopedRWLock /// A variant of ScopedLock for reader/writer locks. { public: CScopedRWLock(CRWLock *rwl, bool write = false); ~CScopedRWLock(); private: CRWLock *_rwl; CScopedRWLock(); CScopedRWLock(const CScopedRWLock &); CScopedRWLock &operator=(const CScopedRWLock &); }; // // inlines // inline void CRWLock::readLock() { #if defined(DEBUG_LOCKS) printf(" READ LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { readLockImpl(); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to read-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" READ LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } inline bool CRWLock::tryReadLock() { bool res; #if defined(DEBUG_LOCKS) printf(" TRY RD LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryReadLockImpl(); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to read-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } inline bool CRWLock::tryWriteLock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" TRY WR LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { if (tryWriteLock()) { #if defined(DEBUG_LOCKS) printf(" WR LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return true; } std::this_thread::sleep_for(std::chrono::milliseconds(5)); } while (!now.isElapsed(diff)); #if defined(DEBUG_LOCKS) printf("CAN'T W LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return false; } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to write-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } } inline bool CRWLock::tryReadLock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" TRY RD LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { if (tryReadLock()) { #if defined(DEBUG_LOCKS) printf(" RD LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return true; } std::this_thread::sleep_for(std::chrono::milliseconds(5)); } while (!now.isElapsed(diff)); #if defined(DEBUG_LOCKS) printf("CAN'T R LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return false; } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to read-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } } inline void CRWLock::writeLock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" WRITE LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { if (tryWriteLock()) { #if defined(DEBUG_LOCKS) printf(" WR LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return; } std::this_thread::sleep_for(std::chrono::milliseconds(20)); } while (!now.isElapsed(diff)); FAILURE(Timeout, "Timeout"); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to write-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } } inline void CRWLock::readLock(long milliseconds) { #if defined(DEBUG_LOCKS) printf(" READ LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { CTimestamp now; CTimestamp::TimeDiff diff(CTimestamp::TimeDiff(milliseconds) * 1000); do { if (tryReadLock()) { #if defined(DEBUG_LOCKS) printf(" RD LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif return; } std::this_thread::sleep_for(std::chrono::milliseconds(5)); } while (!now.isElapsed(diff)); FAILURE(Timeout, "Timeout"); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to read-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } } inline void CRWLock::writeLock() { #if defined(DEBUG_LOCKS) printf(" WRITE LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { writeLockImpl(); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to write-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf("WRITE LOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } inline bool CRWLock::tryWriteLock() { bool res; #if defined(DEBUG_LOCKS) printf(" TRY WR LOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { res = tryWriteLockImpl(); } catch (CException &e) { FAILURE_3( Thread, "Locking error (%s) trying to write-lock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" %s mutex %s from thread %s. \n", res ? " LOCKED" : "CAN'T LOCK", lockName, CURRENT_THREAD_NAME); #endif return res; } inline void CRWLock::unlock() { #if defined(DEBUG_LOCKS) printf(" UNLOCK mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif try { unlockImpl(); } catch (CException &e) { FAILURE_3(Thread, "Locking error (%s) trying to unlock mutex %s from thread %s.\n", e.message().c_str(), lockName, CURRENT_THREAD_NAME); } #if defined(DEBUG_LOCKS) printf(" UNLOCKED mutex %s from thread %s. \n", lockName, CURRENT_THREAD_NAME); #endif } inline CScopedRWLock::CScopedRWLock(CRWLock *rwl, bool write) : _rwl(rwl) { _rwl = rwl; if (write) _rwl->writeLock(LOCK_TIMEOUT_MS); else _rwl->readLock(LOCK_TIMEOUT_MS); } inline CScopedRWLock::~CScopedRWLock() { _rwl->unlock(); } #define SCOPED_READ_LOCK(mutex) CRWLock::CScopedLock L_##__LINE__(mutex, false) #define SCOPED_WRITE_LOCK(mutex) CRWLock::CScopedLock L_##__LINE__(mutex, true) #endif // Foundation_RWLock_INCLUDED ================================================ FILE: src/base/RWLock_POSIX.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock_POSIX.cpp // // $Id: RWLock_POSIX.cpp,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "RWLock_POSIX.hpp" CRWLockImpl::CRWLockImpl() { if (pthread_rwlock_init(&_rwl, NULL)) throw CSystemException("cannot create reader/writer lock"); } CRWLockImpl::~CRWLockImpl() { pthread_rwlock_destroy(&_rwl); } ================================================ FILE: src/base/RWLock_POSIX.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock_POSIX.h // // $Id: RWLock_POSIX.h,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Definition of the RWLockImpl class for POSIX Threads. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_RWLock_POSIX_INCLUDED #define Foundation_RWLock_POSIX_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include #include class CRWLockImpl { protected: CRWLockImpl(); ~CRWLockImpl(); void readLockImpl(); bool tryReadLockImpl(); void writeLockImpl(); bool tryWriteLockImpl(); void unlockImpl(); private: pthread_rwlock_t _rwl; }; // // inlines // inline void CRWLockImpl::readLockImpl() { if (pthread_rwlock_rdlock(&_rwl)) throw CSystemException("cannot lock reader/writer lock"); } inline bool CRWLockImpl::tryReadLockImpl() { int rc = pthread_rwlock_tryrdlock(&_rwl); if (rc == 0) return true; else if (rc == EBUSY) return false; else throw CSystemException("cannot lock reader/writer lock"); } inline void CRWLockImpl::writeLockImpl() { if (pthread_rwlock_wrlock(&_rwl)) throw CSystemException("cannot lock reader/writer lock"); } inline bool CRWLockImpl::tryWriteLockImpl() { int rc = pthread_rwlock_trywrlock(&_rwl); if (rc == 0) return true; else if (rc == EBUSY) return false; else throw CSystemException("cannot lock reader/writer lock"); } inline void CRWLockImpl::unlockImpl() { if (pthread_rwlock_unlock(&_rwl)) throw CSystemException("cannot unlock mutex"); } #endif // Foundation_RWLock_POSIX_INCLUDED ================================================ FILE: src/base/RWLock_WIN32.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock_WIN32.cpp // // $Id: RWLock_WIN32.cpp,v 1.1 2008/05/31 15:47:26 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "RWLock_WIN32.hpp" CRWLockImpl::CRWLockImpl() : _readers(0), _writers(0) { _mutex = CreateMutexW(NULL, FALSE, NULL); if (_mutex == NULL) throw CSystemException("cannot create reader/writer lock"); _readEvent = CreateEventW(NULL, TRUE, TRUE, NULL); if (_readEvent == NULL) throw CSystemException("cannot create reader/writer lock"); _writeEvent = CreateEventW(NULL, TRUE, TRUE, NULL); if (_writeEvent == NULL) throw CSystemException("cannot create reader/writer lock"); } CRWLockImpl::~CRWLockImpl() { CloseHandle(_mutex); CloseHandle(_readEvent); CloseHandle(_writeEvent); } inline void CRWLockImpl::addWriter() { switch (WaitForSingleObject(_mutex, INFINITE)) { case WAIT_OBJECT_0: if (++_writers == 1) ResetEvent(_readEvent); ReleaseMutex(_mutex); break; default: throw CSystemException("cannot lock reader/writer lock"); } } inline void CRWLockImpl::removeWriter() { switch (WaitForSingleObject(_mutex, INFINITE)) { case WAIT_OBJECT_0: if (--_writers == 0) SetEvent(_readEvent); ReleaseMutex(_mutex); break; default: throw CSystemException("cannot lock reader/writer lock"); } } void CRWLockImpl::readLockImpl() { HANDLE h[2]; h[0] = _mutex; h[1] = _readEvent; switch (WaitForMultipleObjects(2, h, TRUE, INFINITE)) { case WAIT_OBJECT_0: case WAIT_OBJECT_0 + 1: ++_readers; ResetEvent(_writeEvent); ReleaseMutex(_mutex); break; default: throw CSystemException("cannot lock reader/writer lock"); } } bool CRWLockImpl::tryReadLockImpl() { HANDLE h[2]; h[0] = _mutex; h[1] = _readEvent; switch (WaitForMultipleObjects(2, h, TRUE, 1)) { case WAIT_OBJECT_0: case WAIT_OBJECT_0 + 1: ++_readers; ResetEvent(_writeEvent); ReleaseMutex(_mutex); return true; case WAIT_TIMEOUT: return false; default: throw CSystemException("cannot lock reader/writer lock"); } } void CRWLockImpl::writeLockImpl() { addWriter(); HANDLE h[2]; h[0] = _mutex; h[1] = _writeEvent; switch (WaitForMultipleObjects(2, h, TRUE, INFINITE)) { case WAIT_OBJECT_0: case WAIT_OBJECT_0 + 1: --_writers; ++_readers; ResetEvent(_readEvent); ResetEvent(_writeEvent); ReleaseMutex(_mutex); break; default: removeWriter(); throw CSystemException("cannot lock reader/writer lock"); } } bool CRWLockImpl::tryWriteLockImpl() { addWriter(); HANDLE h[2]; h[0] = _mutex; h[1] = _writeEvent; switch (WaitForMultipleObjects(2, h, TRUE, 1)) { case WAIT_OBJECT_0: case WAIT_OBJECT_0 + 1: --_writers; ++_readers; ResetEvent(_readEvent); ResetEvent(_writeEvent); ReleaseMutex(_mutex); return true; case WAIT_TIMEOUT: removeWriter(); return false; default: removeWriter(); throw CSystemException("cannot lock reader/writer lock"); } } void CRWLockImpl::unlockImpl() { switch (WaitForSingleObject(_mutex, INFINITE)) { case WAIT_OBJECT_0: if (_writers == 0) SetEvent(_readEvent); if (--_readers == 0) SetEvent(_writeEvent); ReleaseMutex(_mutex); break; default: throw CSystemException("cannot unlock reader/writer lock"); } } ================================================ FILE: src/base/RWLock_WIN32.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RWLock_WIN32.h // // $Id: RWLock_WIN32.h,v 1.1 2008/05/31 15:47:27 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: RWLock // // Definition of the RWLockImpl class for WIN32. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_RWLock_WIN32_INCLUDED #define Foundation_RWLock_WIN32_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include "UnWindows.hpp" class CRWLockImpl { protected: CRWLockImpl(); ~CRWLockImpl(); void readLockImpl(); bool tryReadLockImpl(); void writeLockImpl(); bool tryWriteLockImpl(); void unlockImpl(); private: void addWriter(); void removeWriter(); HANDLE _mutex; HANDLE _readEvent; HANDLE _writeEvent; unsigned _readers; unsigned _writers; }; #endif // Foundation_RWLock_WIN32_INCLUDED ================================================ FILE: src/base/RefCountedObject.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RefCountedObject.cpp // // $Id: RefCountedObject.cpp,v 1.1 2008/05/31 15:47:27 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: RefCountedObject // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "RefCountedObject.hpp" #include "Mutex.hpp" CRefCountedObject::CRefCountedObject() : _rc(1), _rcMutex("rc") {} CRefCountedObject::~CRefCountedObject() {} void CRefCountedObject::duplicate() const { _rcMutex.lock(); ++_rc; _rcMutex.unlock(); } void CRefCountedObject::release() const { _rcMutex.lock(); int rc = --_rc; _rcMutex.unlock(); if (rc == 0) delete this; } ================================================ FILE: src/base/RefCountedObject.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // RefCountedObject.h // // $Id: RefCountedObject.h,v 1.1 2008/05/31 15:47:27 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: RefCountedObject // // Definition of the RefCountedObject class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_RefCountedObject_INCLUDED #define Foundation_RefCountedObject_INCLUDED #include "Foundation.hpp" #include "Mutex.hpp" class CRefCountedObject /// A base class for objects that employ /// reference counting based garbage collection. /// /// Reference-counted objects inhibit construction /// by copying and assignment. { public: CRefCountedObject(); /// Creates the RefCountedObject. /// The initial reference count is one. void duplicate() const; /// Increments the object's reference count. void release() const; /// Decrements the object's reference count /// and deletes the object if the count /// reaches zero. int referenceCount() const; /// Returns the reference count. protected: virtual ~CRefCountedObject(); /// Destroys the RefCountedObject. private: CRefCountedObject(const CRefCountedObject &); CRefCountedObject &operator=(const CRefCountedObject &); mutable int _rc; mutable CFastMutex _rcMutex; }; // // inlines // inline int CRefCountedObject::referenceCount() const { return _rc; } #endif // Foundation_RefCountedObject_INCLUDED ================================================ FILE: src/base/ScopedLock.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // ScopedLock.h // // $Id: ScopedLock.h,v 1.1 2008/05/31 15:47:27 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Mutex // // Definition of the ScopedLock template class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_ScopedLock_INCLUDED #define Foundation_ScopedLock_INCLUDED #include "Foundation.hpp" /** * \brief A class that simplifies thread synchronization with a mutex or *fastmutex. * * The constructor accepts a Mutex and locks it. * The destructor unlocks the mutex. **/ template class CScopedLock { public: inline CScopedLock(M *mutex) : _mutex(mutex) { _mutex->lock(LOCK_TIMEOUT_MS); } inline ~CScopedLock() { _mutex->unlock(); } private: M *_mutex; CScopedLock(); CScopedLock(const CScopedLock &); CScopedLock &operator=(const CScopedLock &); }; #endif // Foundation_ScopedLock_INCLUDED ================================================ FILE: src/base/Semaphore.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore.cpp // // $Id: Semaphore.cpp,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Semaphore // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Semaphore.hpp" #if defined(POCO_OS_FAMILY_WINDOWS) #include "Semaphore_WIN32.cpp" #else #include "Semaphore_POSIX.cpp" #endif CSemaphore::CSemaphore(int n) : CSemaphoreImpl(n, n) {} CSemaphore::CSemaphore(int n, int max) : CSemaphoreImpl(n, max) {} CSemaphore::~CSemaphore() {} ================================================ FILE: src/base/Semaphore.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore.h // // $Id: Semaphore.h,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Semaphore // // Definition of the Semaphore class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Semaphore_INCLUDED #define Foundation_Semaphore_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #if defined(POCO_OS_FAMILY_WINDOWS) #include "Semaphore_WIN32.hpp" #else #include "Semaphore_POSIX.hpp" #endif class CSemaphore : private CSemaphoreImpl /// A Semaphore is a synchronization object with the following /// characteristics: /// A semaphore has a value that is constrained to be a non-negative /// integer and two atomic operations. The allowable operations are V /// (here called set()) and P (here called wait()). A V (set()) operation /// increases the value of the semaphore by one. /// A P (wait()) operation decreases the value of the semaphore by one, /// provided that can be done without violating the constraint that the /// value be non-negative. A P (wait()) operation that is initiated when /// the value of the semaphore is 0 suspends the calling thread. /// The calling thread may continue when the value becomes positive again. { public: CSemaphore(int n); CSemaphore(int n, int max); /// Creates the semaphore. The current value /// of the semaphore is given in n. The /// maximum value of the semaphore is given /// in max. /// If only n is given, it must be greater than /// zero. /// If both n and max are given, max must be /// greater than zero, n must be greater than /// or equal to zero and less than or equal /// to max. ~CSemaphore(); /// Destroys the semaphore. void set(); /// Increments the semaphore's value by one and /// thus signals the semaphore. Another thread /// waiting for the semaphore will be able /// to continue. void wait(); /// Waits for the semaphore to become signalled. /// To become signalled, a semaphore's value must /// be greater than zero. /// Decrements the semaphore's value by one. void wait(long milliseconds); /// Waits for the semaphore to become signalled. /// To become signalled, a semaphore's value must /// be greater than zero. /// Throws a TimeoutException if the semaphore /// does not become signalled within the specified /// time interval. /// Decrements the semaphore's value by one /// if successful. bool tryWait(long milliseconds); /// Waits for the semaphore to become signalled. /// To become signalled, a semaphore's value must /// be greater than zero. /// Returns true if the semaphore /// became signalled within the specified /// time interval, false otherwise. /// Decrements the semaphore's value by one /// if successful. private: CSemaphore(); CSemaphore(const CSemaphore &); CSemaphore &operator=(const CSemaphore &); }; // // inlines // inline void CSemaphore::set() { setImpl(); } inline void CSemaphore::wait() { waitImpl(); } inline void CSemaphore::wait(long milliseconds) { if (!waitImpl(milliseconds)) throw CTimeoutException(); } inline bool CSemaphore::tryWait(long milliseconds) { return waitImpl(milliseconds); } #endif // Foundation_Semaphore_INCLUDED ================================================ FILE: src/base/Semaphore_POSIX.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore_POSIX.cpp // // Library: Foundation // Package: Threading // Module: Semaphore // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "Semaphore_POSIX.hpp" #include // // Note: pthread_cond_timedwait() with CLOCK_MONOTONIC is supported // on Linux and QNX, as well as on Android >= 5.0 (API level 21). // On Android < 5.0, HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC is defined // to indicate availability of non-standard pthread_cond_timedwait_monotonic(). // #ifndef POCO_HAVE_MONOTONIC_PTHREAD_COND_TIMEDWAIT #if (defined(__linux__) || defined(__QNX__)) && \ !(defined(__ANDROID__) && \ (defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) || \ __ANDROID_API__ <= 21)) #define POCO_HAVE_MONOTONIC_PTHREAD_COND_TIMEDWAIT 1 #endif #endif #ifndef POCO_HAVE_CLOCK_GETTIME #if (defined(_POSIX_TIMERS) && defined(CLOCK_REALTIME)) || \ defined(POCO_VXWORKS) || defined(__QNX__) #ifndef __APPLE__ // See GitHub issue #1453 - not available before Mac // OS 10.12/iOS 10 #define POCO_HAVE_CLOCK_GETTIME #endif #endif #endif CSemaphoreImpl::CSemaphoreImpl(int n, int max) : _n(n), _max(max) { poco_assert(n >= 0 && max > 0 && n <= max); #if defined(POCO_VXWORKS) // This workaround is for VxWorks 5.x where // pthread_mutex_init() won't properly initialize the mutex // resulting in a subsequent freeze in pthread_mutex_destroy() // if the mutex has never been used. std::memset(&_mutex, 0, sizeof(_mutex)); #endif if (pthread_mutex_init(&_mutex, NULL)) throw CSystemException("cannot create semaphore (mutex)"); #if defined(POCO_HAVE_MONOTONIC_PTHREAD_COND_TIMEDWAIT) pthread_condattr_t attr; if (pthread_condattr_init(&attr)) { pthread_mutex_destroy(&_mutex); throw CSystemException("cannot create semaphore (condition attribute)"); } if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { pthread_condattr_destroy(&attr); pthread_mutex_destroy(&_mutex); throw CSystemException( "cannot create semaphore (condition attribute clock)"); } if (pthread_cond_init(&_cond, &attr)) { pthread_condattr_destroy(&attr); pthread_mutex_destroy(&_mutex); throw CSystemException("cannot create semaphore (condition)"); } pthread_condattr_destroy(&attr); #else if (pthread_cond_init(&_cond, NULL)) { pthread_mutex_destroy(&_mutex); throw CSystemException("cannot create semaphore (condition)"); } #endif } CSemaphoreImpl::~CSemaphoreImpl() { pthread_cond_destroy(&_cond); pthread_mutex_destroy(&_mutex); } void CSemaphoreImpl::waitImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("wait for semaphore failed (lock)"); while (_n < 1) { if (pthread_cond_wait(&_cond, &_mutex)) { pthread_mutex_unlock(&_mutex); throw CSystemException("wait for semaphore failed"); } } --_n; pthread_mutex_unlock(&_mutex); } bool CSemaphoreImpl::waitImpl(long milliseconds) { int rc = 0; struct timespec abstime; #if defined(POCO_HAVE_MONOTONIC_PTHREAD_COND_TIMEDWAIT) clock_gettime(CLOCK_MONOTONIC, &abstime); abstime.tv_sec += milliseconds / 1000; abstime.tv_nsec += (milliseconds % 1000) * 1000000; if (abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; } #elif defined(POCO_HAVE_CLOCK_GETTIME) clock_gettime(CLOCK_REALTIME, &abstime); abstime.tv_sec += milliseconds / 1000; abstime.tv_nsec += (milliseconds % 1000) * 1000000; if (abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; } #else struct timeval tv; gettimeofday(&tv, NULL); abstime.tv_sec = tv.tv_sec + milliseconds / 1000; abstime.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000; if (abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; } #endif if (pthread_mutex_lock(&_mutex) != 0) throw CSystemException("wait for semaphore failed (lock)"); while (_n < 1) { if ((rc = pthread_cond_timedwait(&_cond, &_mutex, &abstime))) { if (rc == ETIMEDOUT) break; pthread_mutex_unlock(&_mutex); throw CSystemException("cannot wait for semaphore"); } } if (rc == 0) --_n; pthread_mutex_unlock(&_mutex); return rc == 0; } ================================================ FILE: src/base/Semaphore_POSIX.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore_POSIX.h // // $Id: Semaphore_POSIX.h,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Semaphore // // Definition of the SemaphoreImpl class for POSIX Threads. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Semaphore_POSIX_INCLUDED #define Foundation_Semaphore_POSIX_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include #include class CSemaphoreImpl { protected: CSemaphoreImpl(int n, int max); ~CSemaphoreImpl(); void setImpl(); void waitImpl(); bool waitImpl(long milliseconds); private: volatile int _n; int _max; pthread_mutex_t _mutex; pthread_cond_t _cond; }; // // inlines // inline void CSemaphoreImpl::setImpl() { if (pthread_mutex_lock(&_mutex)) throw CSystemException("cannot signal semaphore (lock)"); if (_n < _max) { ++_n; } else { pthread_mutex_unlock(&_mutex); throw CSystemException( "cannot signal semaphore: count would exceed maximum"); } if (pthread_cond_signal(&_cond)) { pthread_mutex_unlock(&_mutex); throw CSystemException("cannot signal semaphore"); } pthread_mutex_unlock(&_mutex); } #endif // Foundation_Semaphore_POSIX_INCLUDED ================================================ FILE: src/base/Semaphore_WIN32.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore_WIN32.cpp // // $Id: Semaphore_WIN32.cpp,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Semaphore // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Semaphore_WIN32.hpp" CSemaphoreImpl::CSemaphoreImpl(int n, int max) { poco_assert(n >= 0 && max > 0 && n <= max); _sema = CreateSemaphoreW(NULL, n, max, NULL); if (!_sema) { throw CSystemException("cannot create semaphore"); } } CSemaphoreImpl::~CSemaphoreImpl() { CloseHandle(_sema); } void CSemaphoreImpl::waitImpl() { switch (WaitForSingleObject(_sema, INFINITE)) { case WAIT_OBJECT_0: return; default: throw CSystemException("wait for semaphore failed"); } } bool CSemaphoreImpl::waitImpl(long milliseconds) { switch (WaitForSingleObject(_sema, milliseconds + 1)) { case WAIT_TIMEOUT: return false; case WAIT_OBJECT_0: return true; default: throw CSystemException("wait for semaphore failed"); } } ================================================ FILE: src/base/Semaphore_WIN32.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Semaphore_WIN32.h // // $Id: Semaphore_WIN32.h,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Threading // Module: Semaphore // // Definition of the SemaphoreImpl class for WIN32. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Semaphore_WIN32_INCLUDED #define Foundation_Semaphore_WIN32_INCLUDED #include "Exception.hpp" #include "Foundation.hpp" #include "UnWindows.hpp" class CSemaphoreImpl { protected: CSemaphoreImpl(int n, int max); ~CSemaphoreImpl(); void setImpl(); void waitImpl(); bool waitImpl(long milliseconds); private: HANDLE _sema; }; // // inlines // inline void CSemaphoreImpl::setImpl() { if (!ReleaseSemaphore(_sema, 1, NULL)) { throw CSystemException("cannot signal semaphore"); } } #endif // Foundation_Semaphore_WIN32_INCLUDED ================================================ FILE: src/base/SingletonHolder.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // SingletonHolder.h // // $Id: SingletonHolder.h,v 1.1 2008/05/31 15:47:28 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: SingletonHolder // // Definition of the SingletonHolder template. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_SingletonHolder_INCLUDED #define Foundation_SingletonHolder_INCLUDED #include "Foundation.hpp" #include "Mutex.hpp" template class CSingletonHolder /// This is a helper template class for managing /// singleton objects allocated on the heap. /// The class ensures proper deletion (including /// calling of the destructor) of singleton objects /// when the application that created them terminates. { public: CSingletonHolder() /// Creates the SingletonHolder. { _pS = 0; } ~CSingletonHolder() /// Destroys the SingletonHolder and the singleton /// object that it holds. { delete _pS; } S *get() /// Returns a pointer to the singleton object /// hold by the SingletonHolder. The first call /// to get will create the singleton. { CFastMutex::CScopedLock lock(&_m); if (!_pS) _pS = new S; return _pS; } private: S *_pS; CFastMutex _m; }; #endif // Foundation_SingletonHolder_INCLUDED ================================================ FILE: src/base/Timestamp.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Timestamp.cpp // // $Id: Timestamp.cpp,v 1.1 2008/05/31 15:47:29 iamcamiel Exp $ // // Library: Foundation // Package: DateTime // Module: Timestamp // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Timestamp.hpp" #include "Exception.hpp" #include #if defined(POCO_OS_FAMILY_UNIX) #include #include #include #include #elif defined(POCO_OS_FAMILY_WINDOWS) #include "UnWindows.hpp" #endif CTimestamp::CTimestamp() { update(); } CTimestamp::CTimestamp(TimeVal tv) { _ts = tv; } CTimestamp::CTimestamp(const CTimestamp &other) { _ts = other._ts; } CTimestamp::~CTimestamp() {} CTimestamp &CTimestamp::operator=(const CTimestamp &other) { _ts = other._ts; return *this; } CTimestamp &CTimestamp::operator=(TimeVal tv) { _ts = tv; return *this; } void CTimestamp::swap(CTimestamp ×tamp) { std::swap(_ts, timestamp._ts); } CTimestamp CTimestamp::fromEpochTime(std::time_t t) { return CTimestamp(TimeVal(t) * resolution()); } CTimestamp CTimestamp::fromUtcTime(UtcTimeVal val) { val -= (TimeDiff(0x01b21dd2) << 32) + 0x13814000; val /= 10; return CTimestamp(val); } void CTimestamp::update() { #if defined(POCO_OS_FAMILY_WINDOWS) FILETIME ft; GetSystemTimeAsFileTime(&ft); ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows // NT FILETIME epoch.LowPart = 0xD53E8000; epoch.HighPart = 0x019DB1DE; ULARGE_INTEGER ts; ts.LowPart = ft.dwLowDateTime; ts.HighPart = ft.dwHighDateTime; ts.QuadPart -= epoch.QuadPart; _ts = ts.QuadPart / 10; #else struct timeval tv; if (gettimeofday(&tv, NULL)) throw CSystemException("cannot get time of day"); _ts = TimeVal(tv.tv_sec) * resolution() + tv.tv_usec; #endif } #if defined(_WIN32) CTimestamp CTimestamp::fromFileTimeNP(UInt32 fileTimeLow, UInt32 fileTimeHigh) { ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows // NT FILETIME epoch.LowPart = 0xD53E8000; epoch.HighPart = 0x019DB1DE; ULARGE_INTEGER ts; ts.LowPart = fileTimeLow; ts.HighPart = fileTimeHigh; ts.QuadPart -= epoch.QuadPart; return CTimestamp(ts.QuadPart / 10); } void CTimestamp::toFileTimeNP(UInt32 &fileTimeLow, UInt32 &fileTimeHigh) const { ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows // NT FILETIME epoch.LowPart = 0xD53E8000; epoch.HighPart = 0x019DB1DE; ULARGE_INTEGER ts; ts.QuadPart = _ts * 10; ts.QuadPart += epoch.QuadPart; fileTimeLow = ts.LowPart; fileTimeHigh = ts.HighPart; } #endif ================================================ FILE: src/base/Timestamp.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Timestamp.h // // $Id: Timestamp.h,v 1.1 2008/05/31 15:47:29 iamcamiel Exp $ // // Library: Foundation // Package: DateTime // Module: Timestamp // // Definition of the Timestamp class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Timestamp_INCLUDED #define Foundation_Timestamp_INCLUDED #include "Foundation.hpp" #include class CTimestamp /// A Timestamp stores a monotonic time value /// with (theoretical) microseconds resolution. /// Timestamps can be compared with each other /// and simple arithmetics are supported. /// Timestamps are UTC (Coordinated Universal Time) /// based and thus independent of the timezone /// in effect on the system. { public: typedef Int64 TimeVal; /// monotonic UTC time value in microsecond resolution typedef Int64 UtcTimeVal; /// monotonic UTC time value in 100 nanosecond resolution typedef Int64 TimeDiff; /// difference between two timestamps in microseconds CTimestamp(); /// Creates a timestamp with the current time. CTimestamp(TimeVal tv); /// Creates a timestamp from the given time value. CTimestamp(const CTimestamp &other); /// Copy constructor. ~CTimestamp(); /// Destroys the timestamp CTimestamp &operator=(const CTimestamp &other); CTimestamp &operator=(TimeVal tv); void swap(CTimestamp ×tamp); /// Swaps the Timestamp with another one. void update(); /// Updates the Timestamp with the current time. bool operator==(const CTimestamp &ts) const; bool operator!=(const CTimestamp &ts) const; bool operator>(const CTimestamp &ts) const; bool operator>=(const CTimestamp &ts) const; bool operator<(const CTimestamp &ts) const; bool operator<=(const CTimestamp &ts) const; CTimestamp operator+(TimeDiff d) const; CTimestamp operator-(TimeDiff d) const; TimeDiff operator-(const CTimestamp &ts) const; CTimestamp &operator+=(TimeDiff d); CTimestamp &operator-=(TimeDiff d); std::time_t epochTime() const; /// Returns the timestamp expressed in time_t. /// time_t base time is midnight, January 1, 1970. /// Resolution is one second. UtcTimeVal utcTime() const; /// Returns the timestamp expressed in UTC-based /// time. UTC base time is midnight, October 15, 1582. /// Resolution is 100 nanoseconds. TimeVal epochMicroseconds() const; /// Returns the timestamp expressed in microseconds /// since the Unix epoch, midnight, January 1, 1970. TimeDiff elapsed() const; /// Returns the time elapsed since the time denoted by /// the timestamp. Equivalent to Timestamp() - *this. bool isElapsed(TimeDiff interval) const; /// Returns true iff the given interval has passed /// since the time denoted by the timestamp. static CTimestamp fromEpochTime(std::time_t t); /// Creates a timestamp from a std::time_t. static CTimestamp fromUtcTime(UtcTimeVal val); /// Creates a timestamp from a UTC time value. static TimeVal resolution(); /// Returns the resolution in units per second. /// Since the timestamp has microsecond resolution, /// the returned value is always 1000000. #if defined(_WIN32) static CTimestamp fromFileTimeNP(UInt32 fileTimeLow, UInt32 fileTimeHigh); void toFileTimeNP(UInt32 &fileTimeLow, UInt32 &fileTimeHigh) const; #endif private: TimeVal _ts; }; // // inlines // inline bool CTimestamp::operator==(const CTimestamp &ts) const { return _ts == ts._ts; } inline bool CTimestamp::operator!=(const CTimestamp &ts) const { return _ts != ts._ts; } inline bool CTimestamp::operator>(const CTimestamp &ts) const { return _ts > ts._ts; } inline bool CTimestamp::operator>=(const CTimestamp &ts) const { return _ts >= ts._ts; } inline bool CTimestamp::operator<(const CTimestamp &ts) const { return _ts < ts._ts; } inline bool CTimestamp::operator<=(const CTimestamp &ts) const { return _ts <= ts._ts; } inline CTimestamp CTimestamp::operator+(CTimestamp::TimeDiff d) const { return CTimestamp(_ts + d); } inline CTimestamp CTimestamp::operator-(CTimestamp::TimeDiff d) const { return CTimestamp(_ts - d); } inline CTimestamp::TimeDiff CTimestamp::operator-(const CTimestamp &ts) const { return _ts - ts._ts; } inline CTimestamp &CTimestamp::operator+=(CTimestamp::TimeDiff d) { _ts += d; return *this; } inline CTimestamp &CTimestamp::operator-=(CTimestamp::TimeDiff d) { _ts -= d; return *this; } inline std::time_t CTimestamp::epochTime() const { return std::time_t(_ts / resolution()); } inline CTimestamp::UtcTimeVal CTimestamp::utcTime() const { return _ts * 10 + (TimeDiff(0x01b21dd2) << 32) + 0x13814000; } inline CTimestamp::TimeVal CTimestamp::epochMicroseconds() const { return _ts; } inline CTimestamp::TimeDiff CTimestamp::elapsed() const { CTimestamp now; return now - *this; } inline bool CTimestamp::isElapsed(CTimestamp::TimeDiff interval) const { CTimestamp now; CTimestamp::TimeDiff diff = now - *this; return diff >= interval; } inline CTimestamp::TimeVal CTimestamp::resolution() { return 1000000; } inline void swap(CTimestamp &s1, CTimestamp &s2) { s1.swap(s2); } #endif // Foundation_Timestamp_INCLUDED ================================================ FILE: src/base/Types.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // Types.h // // $Id: Types.h,v 1.1 2008/05/31 15:47:29 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: Types // // Definitions of fixed-size integer types for various platforms // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_Types_INCLUDED #define Foundation_Types_INCLUDED #include "Foundation.hpp" #if defined(_MSC_VER) // // Windows/Visual C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed __int64 Int64; typedef unsigned __int64 UInt64; #if defined(_WIN64) #define POCO_PTR_IS_64_BIT 1 typedef signed __int64 IntPtr; typedef unsigned __int64 UIntPtr; #else typedef signed long IntPtr; typedef unsigned long UIntPtr; #endif #define POCO_HAVE_INT64 1 #elif defined(__GNUC__) // // Unix/GCC // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed long IntPtr; typedef unsigned long UIntPtr; #if defined(__LP64__) #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 typedef signed long Int64; typedef unsigned long UInt64; #else typedef signed long long Int64; typedef unsigned long long UInt64; #endif #define POCO_HAVE_INT64 1 #elif defined(__DECCXX) // // Compaq C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed __int64 Int64; typedef unsigned __int64 UInt64; #if defined(__VMS) #if defined(__32BITS) typedef signed long IntPtr; typedef unsigned long UIntPtr; #else typedef Int64 IntPtr; typedef UInt64 UIntPtr; #define POCO_PTR_IS_64_BIT 1 #endif #else typedef signed long IntPtr; typedef unsigned long UIntPtr; #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 #endif #define POCO_HAVE_INT64 1 #elif defined(__HP_aCC) // // HP Ansi C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed long IntPtr; typedef unsigned long UIntPtr; #if defined(__LP64__) #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 typedef signed long Int64; typedef unsigned long UInt64; #else typedef signed long long Int64; typedef unsigned long long UInt64; #endif #define POCO_HAVE_INT64 1 #elif defined(__SUNPRO_CC) // // SUN Forte C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed long IntPtr; typedef unsigned long UIntPtr; #if defined(__sparcv9) #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 typedef signed long Int64; typedef unsigned long UInt64; #else typedef signed long long Int64; typedef unsigned long long UInt64; #endif #define POCO_HAVE_INT64 1 #elif defined(__IBMCPP__) // // IBM XL C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed long IntPtr; typedef unsigned long UIntPtr; #if defined(__64BIT__) #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 typedef signed long Int64; typedef unsigned long UInt64; #else typedef signed long long Int64; typedef unsigned long long UInt64; #endif #define POCO_HAVE_INT64 1 #elif defined(__sgi) // // MIPSpro C++ // typedef signed char Int8; typedef unsigned char UInt8; typedef signed short Int16; typedef unsigned short UInt16; typedef signed int Int32; typedef unsigned int UInt32; typedef signed long IntPtr; typedef unsigned long UIntPtr; #if _MIPS_SZLONG == 64 #define POCO_PTR_IS_64_BIT 1 #define POCO_LONG_IS_64_BIT 1 typedef signed long Int64; typedef unsigned long UInt64; #else typedef signed long long Int64; typedef unsigned long long UInt64; #endif #define POCO_HAVE_INT64 1 #endif #endif // Foundation_Types_INCLUDED ================================================ FILE: src/base/UnWindows.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon the Poco C++ Libraries, which is Copyright (C) * 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. */ // // UnWindows.h // // $Id: UnWindows.h,v 1.1 2008/05/31 15:47:30 iamcamiel Exp $ // // Library: Foundation // Package: Core // Module: UnWindows // // A wrapper around the "Poco/UnWindows.h" header file that #undef's some // of the macros for function names defined by "Poco/UnWindows.h" that // are a frequent source of conflicts (e.g., GetUserName). // // Remember, that most of the WIN32 API functions come in two variants, // an Unicode variant (e.g., GetUserNameA) and an ASCII variant (GetUserNameW). // There is also a macro (GetUserName) that's either defined to be the Unicode // name or the ASCII name, depending on whether the UNICODE macro is #define'd // or not. POCO always calls the Unicode or ASCII functions directly (depending // on whether POCO_WIN32_UTF8 is #define'd or not), so the macros are not // ignored. // // These macro definitions are a frequent case of problems and naming conflicts, // especially for C++ programmers. Say, you define a class with a member // function named GetUserName. Depending on whether "Poco/UnWindows.h" has been // included by a particular translation unit or not, this might be changed to // GetUserNameA/GetUserNameW, or not. While, due to naming conventions used, // this is less of a problem in POCO, some of the users of POCO might use a // different naming convention where this can become a problem. // // To disable the #undef's, compile POCO with the POCO_NO_UNWINDOWS macro // #define'd. // // Copyright (c) 2007, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include #if !defined(POCO_NO_UNWINDOWS) // A list of annoying macros to #undef. // Feel free to extend as required. #undef GetBinaryType #undef GetShortPathName #undef GetLongPathName #undef GetEnvironmentStrings #undef SetEnvironmentStrings #undef FreeEnvironmentStrings #undef FormatMessage #undef EncryptFile #undef DecryptFile #undef CreateMutex #undef OpenMutex #undef CreateEvent #undef OpenEvent #undef CreateSemaphore #undef OpenSemaphore #undef LoadLibrary #undef GetModuleFileName #undef CreateProcess #undef GetCommandLine #undef GetEnvironmentVariable #undef SetEnvironmentVariable #undef ExpandEnvironmentStrings #undef OutputDebugString #undef FindResource #undef UpdateResource #undef FindAtom #undef AddAtom #undef GetSystemDirector #undef GetTempPath #undef GetTempFileName #undef SetCurrentDirectory #undef GetCurrentDirectory #undef CreateDirectory #undef RemoveDirectory #undef CreateFile #undef DeleteFile #undef SearchPath #undef CopyFile #undef MoveFile #undef ReplaceFile #undef GetComputerName #undef SetComputerName #undef GetUserName #undef LogonUser #undef GetVersion #undef GetObject #endif // POCO_NO_UNWINDOWS ================================================ FILE: src/config.hpp.in ================================================ /* Define to 1 if you have the `alarm' function. */ #cmakedefine HAVE_ALARM /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_INET_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_TELNET_H /* Define to 1 if you have the `atexit' function. */ #cmakedefine HAVE_ATEXIT /* Define to 1 if you have the header file. */ #cmakedefine HAVE_CTYPE_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H /* Define to 1 if you have large file support */ #cmakedefine HAVE_LARGE_FILES /* Define to 1 if you have large file support */ #cmakedefine _LARGEFILE_SOURCE /* Define to 1 if you have large file support */ #cmakedefine _LARGE_FILES /* Define to 64 if you have large file support */ #cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ /* Define to 1 if you have large file support */ #cmakedefine AXPBOX_HAVE_FSEEKO @AXPBOX_HAVE_FSEEKO@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H /* Define to 1 if you have the `fopen' function. */ #cmakedefine HAVE_FOPEN /* Define to 1 if you have the `fopen64' function. */ #cmakedefine HAVE_FOPEN64 /* Define to 1 if you have the `fork' function. */ #cmakedefine HAVE_FORK /* Define to 1 if you have the `fseek' function. */ #cmakedefine HAVE_FSEEK /* Define to 1 if you have the `fseeko' function. */ #cmakedefine HAVE_FSEEKO /* Define to 1 if you have the `fseeko64' function. */ #cmakedefine HAVE_FSEEKO64 /* Define to 1 if you have the `ftell' function. */ #cmakedefine HAVE_FTELL /* Define to 1 if you have the `ftello' function. */ #cmakedefine HAVE_FTELLO /* Define to 1 if you have the `ftello64' function. */ #cmakedefine HAVE_FTELLO64 /* Define to 1 if you have the `gmtime_s' function. */ #cmakedefine HAVE_GMTIME_S /* Define to 1 if you have the `localtime_s' function. */ #cmakedefine HAVE_LOCALTIME_S /* Define to 1 if you have the `inet_aton' function. */ #cmakedefine HAVE_INET_ATON /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INET_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IN_H /* Define to 1 if you have the `isblank' function. */ #cmakedefine HAVE_ISBLANK /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #cmakedefine HAVE_MALLOC /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MALLOC_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #cmakedefine HAVE_MEMSET /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IN_H /* Define to 1 if you have the `pow' function. */ #cmakedefine HAVE_POW /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PROCESS_H /* Define if you have POSIX threads libraries and header files. */ #cmakedefine HAVE_PTHREAD /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTHREAD_H /* Define to 1 if your system has a GNU libc compatible `realloc' function, and to 0 otherwise. */ #cmakedefine HAVE_REALLOC /* Define to 1 if you have the `select' function. */ #cmakedefine HAVE_SELECT /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SIGNAL_H /* Define to 1 if you have the `socket' function. */ #cmakedefine HAVE_SOCKET /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SOCKET_H /* Define to 1 if you have the `sqrt' function. */ #cmakedefine HAVE_SQRT /* Define to 1 if stdbool.h conforms to C99. */ #cmakedefine HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H /* Define to 1 if you have the `strcasecmp' function. */ #cmakedefine HAVE_STRCASECMP /* Define to 1 if you have the `strchr' function. */ #cmakedefine HAVE_STRCHR /* Define to 1 if you have the `strdup' function. */ #cmakedefine HAVE_STRDUP /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H /* Define to 1 if you have the `strncasecmp' function. */ #cmakedefine HAVE_STRNCASECMP /* Define to 1 if you have the `strspn' function. */ #cmakedefine HAVE_STRSPN /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H /* Define to 1 if you have that is POSIX.1 compatible. */ #cmakedefine HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H /* Define to 1 if you have the `vfork' function. */ #cmakedefine HAVE_VFORK /* Define to 1 if you have the header file. */ #cmakedefine HAVE_VFORK_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINSOCK2_H /* Define to 1 if `fork' works. */ #cmakedefine HAVE_WORKING_FORK /* Define to 1 if `vfork' works. */ #cmakedefine HAVE_WORKING_VFORK /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WS2TCPIP_H /* Define to 1 if the system has the type `_Bool'. */ #cmakedefine HAVE__BOOL /* Define to 1 if you have the `_fseeki64' function. */ #cmakedefine HAVE__FSEEKI64 /* Define to 1 if you have the `_ftelli64' function. */ #cmakedefine HAVE__FTELLI64 /* Define to 1 if you have the `_strdup' function. */ #cmakedefine HAVE__STRDUP /* Define to 1 if you have the `_stricasecmp' function. */ #cmakedefine HAVE__STRICASECMP /* Define to 1 if you have the `_stricmp' function. */ #cmakedefine HAVE__STRICMP /* Name of package */ #cmakedefine PACKAGE @PACKAGE@ /* Define to the address where bug reports for this package should be sent. */ #cmakedefine PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@ /* Define to the full name of this package. */ #cmakedefine PACKAGE_NAME @PACKAGE_NAME@ /* Define to the full name and version of this package. */ #cmakedefine PACKAGE_STRING @PACKAGE_STRING@ /* Define to the one symbol short name of this package. */ #cmakedefine PACKAGE_TARNAME @PACKAGE_TARNAME@ /* Define to the home page for this package. */ #cmakedefine PACKAGE_URL @PACKAGE_URL@ /* Define to the version of this package. */ #cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* Define to the git revision of this package. */ #cmakedefine PACKAGE_GITSHA @PACKAGE_GITSHA@ /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #cmakedefine PTHREAD_CREATE_JOINABLE @PTHREAD_CREATE_JOINABLE@ /* Define as the return type of signal handlers (`int' or `void'). */ #cmakedefine RETSIGTYPE @RETSIGTYPE@ /* Define to the type of arg 1 for `select'. */ #cmakedefine SELECT_TYPE_ARG1 @SELECT_TYPE_ARG1@ /* Define to the type of args 2, 3 and 4 for `select'. */ #cmakedefine SELECT_TYPE_ARG234 @SELECT_TYPE_ARG234@ /* Define to the type of arg 5 for `select'. */ #cmakedefine SELECT_TYPE_ARG5 @SELECT_TYPE_ARG5@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS /* Define to 1 if you can safely include both and . */ #cmakedefine TIME_WITH_SYS_TIME /* Define to 1 if your declares `struct tm'. */ #cmakedefine TM_IN_SYS_TIME /* Optional features */ #cmakedefine HAVE_PCAP #cmakedefine HAVE_SDL #cmakedefine HAVE_X11 /* Version number of package */ #cmakedefine VERSION @PACKAGE_VERSION@ /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #cmakedefine _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #cmakedefine _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #cmakedefine _UINT8_T /* Define to empty if `const' does not conform to ANSI C. */ #cmakedefine const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #cmakedefine inline #endif /* Define to the type of a signed integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #cmakedefine int16_t /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #cmakedefine int32_t /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #cmakedefine int64_t /* Define to the type of a signed integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #cmakedefine int8_t /* Define to rpl_malloc if the replacement function should be used. */ #cmakedefine malloc /* Define to `int' if does not define. */ #cmakedefine pid_t /* Define to rpl_realloc if the replacement function should be used. */ #cmakedefine realloc /* Define to `unsigned int' if does not define. */ #cmakedefine size_t /* Define to `int' if does not define. */ #cmakedefine ssize_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #cmakedefine uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #cmakedefine uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #cmakedefine uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #cmakedefine uint8_t /* Define as `fork' if `vfork' does not work. */ #cmakedefine vfork /* Define if system is little endian */ #cmakedefine ES40_LITTLE_ENDIAN /* Define if system is big endian */ #cmakedefine ES40_BIG_ENDIAN ================================================ FILE: src/config_debug.hpp ================================================ // This is config_debug.h // // This file contains the debug configuration options. // This file was generated by configure_1.sh // // $Id: config_debug.h,v 1.5 2010/03/11 22:44:13 iamcamiel Exp $ // Define to 1 if you want to show the cycle counter #define HIDE_COUNTER 1 // Define to 1 if you want to show estimate speed #undef MIPS_ESTIMATE // Define to 1 if you want to show memory map #undef DUMP_MEMMAP // Define to 1 if you want to check for overlapping of memory ranges #define CHECK_MEM_RANGES 1 // Define to 1 if you want to use the new floating-point implementation #undef HAVE_NEW_FP // Define to 1 if you want to enable VGA debugging #undef DEBUG_VGA // Define to 1 if you want to enable Serial Port debugging #undef DEBUG_SERIAL // Define to 1 if you want to enable IDE General debugging #undef DEBUG_IDE // Define to 1 if you want to enable IDE Busmaster debugging #undef DEBUG_IDE_BUSMASTER // Define to 1 if you want to enable IDE Command debugging #undef DEBUG_IDE_COMMAND // Define to 1 if you want to enable IDE CMD debugging #undef DEBUG_IDE_CMD // Define to 1 if you want to enable IDE DMA debugging #undef DEBUG_IDE_DMA // Define to 1 if you want to enable IDE Interrupt debugging #undef DEBUG_IDE_INTERRUPT // Define to 1 if you want to enable IDE Command Register debugging #undef DEBUG_IDE_REG_COMMAND // Define to 1 if you want to enable IDE Control Register debugging #undef DEBUG_IDE_REG_CONTROL // Define to 1 if you want to enable IDE ATAPI Packet debugging #undef DEBUG_IDE_PACKET // Define to 1 if you want to enable IDE Thread debugging #undef DEBUG_IDE_THREADS // Define to 1 if you want to enable IDE Mutexes debugging #undef DEBUG_IDE_LOCKS // Define to 1 if you want to enable IDE Multiple debugging #undef DEBUG_IDE_MULTIPLE // Define to 1 if you want to enable Floating Point conversions debugging #undef DEBUG_FP_CONVERSION // Define to 1 if you want to enable Floating Point load/store debugging #undef DEBUG_FP_LOADSTORE // Define to 1 if you want to enable General NIC debugging #undef DEBUG_NIC // Define to 1 if you want to enable NIC Filter debugging #undef DEBUG_NIC_FILTER // Define to 1 if you want to enable NIC Serial ROM debugging #undef DEBUG_NIC_SROM // Define to 1 if you want to enable unknown memory access debugging #undef DEBUG_UNKMEM // Define to 1 if you want to enable PCI debugging #undef DEBUG_PCI // Define to 1 if you want to enable Translationbuffer debugging #undef DEBUG_TB // Define to 1 if you want to enable I/O Port Access debugging #undef DEBUG_PORTACCESS // Define to 1 if you want to enable Keyboard debugging #undef DEBUG_KBD // Define to 1 if you want to enable Programmable Interrupt Controller (PIC) // debugging #undef DEBUG_PIC // Define to 1 if you want to enable Printer port debugging #undef DEBUG_LPT // Define to 1 if you want to enable USB Controller debugging #undef DEBUG_USB // Define to 1 if you want to enable SCSI Device debugging #undef DEBUG_SCSI // Define to 1 if you want to enable Symbios SCSI Controller debugging #undef DEBUG_SYM // Define to 1 if you want to enable Symbios Registers debugging #undef DEBUG_SYM_REGS // Define to 1 if you want to enable Symbios SCRIPTS Execution debugging #undef DEBUG_SYM_SCRIPTS // Define to 1 if you want to enable DMA Controller debugging #undef DEBUG_DMA // Define to 1 if you want to enable backtrace on SIGSEGV debugging #undef DEBUG_BACKTRACE // Define to 1 if you want to enable mutex debugging #undef DEBUG_LOCKS // Define to 1 if you want to enable SDL Key translation debugging #undef DEBUG_SDL_KEY ================================================ FILE: src/cpu_arith.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /* comparison */ #define DO_CMPEQ RCV = (RAV == RBV) ? 1 : 0; #define DO_CMPLT RCV = ((s64)RAV < (s64)RBV) ? 1 : 0; #define DO_CMPLE RCV = ((s64)RAV <= (s64)RBV) ? 1 : 0; /* addition */ #define DO_ADDQ RCV = RAV + RBV; #define DO_S4ADDQ RCV = (RAV * 4) + RBV; #define DO_S8ADDQ RCV = (RAV * 8) + RBV; #define DO_ADDQ_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ RCV = rav + rbv; \ \ /* test for integer overflow */ \ if (((~rav ^ rbv) & (rav ^ RCV)) & Q_SIGN) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("ADDQ_V %016" PRIu64 "x + %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } #define DO_ADDL RCV = sext_u64_32(RAV + RBV); #define DO_S4ADDL RCV = sext_u64_32((RAV * 4) + RBV); #define DO_S8ADDL RCV = sext_u64_32((RAV * 8) + RBV); #define DO_ADDL_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ RCV = sext_u64_32(rav + rbv); \ \ /* test for integer overflow */ \ if (((~rav ^ rbv) & (rav ^ RCV)) & L_SIGN) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("ADDL_V %016" PRIu64 "x + %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } #define DO_CTLZ \ temp_64 = 0; \ temp_64_2 = RBV; \ for (i = 63; i >= 0; i--) \ if ((temp_64_2 >> i) & 1) \ break; \ else \ temp_64++; \ RCV = temp_64; #define DO_CTPOP \ temp_64 = 0; \ temp_64_2 = RBV; \ for (i = 0; i < 64; i++) \ if ((temp_64_2 >> i) & 1) \ temp_64++; \ RCV = temp_64; #define DO_CTTZ \ temp_64 = 0; \ temp_64_2 = RBV; \ for (i = 0; i < 64; i++) \ if ((temp_64_2 >> i) & 1) \ break; \ else \ temp_64++; \ RCV = temp_64; #define DO_CMPULT RCV = ((u64)RAV < (u64)RBV) ? 1 : 0; #define DO_CMPULE RCV = ((u64)RAV <= (u64)RBV) ? 1 : 0; /* multiplication */ #define DO_MULL RCV = sext_u64_32(sext_u64_32(RAV) * sext_u64_32(RBV)); #define DO_MULL_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ u64 sr = sext_u64_32(rav) * sext_u64_32(rbv); \ RCV = sext_u64_32(sr); \ if ((RCV ^ sr) & U64(0xffffffff00000000)) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("MULL_V %016" PRIu64 "x * %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } #define DO_MULQ RCV = RAV * RBV; #define DO_MULQ_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ u64 t64; \ RCV = uemul64(rav, rbv, &t64); \ if (Q_GETSIGN(rav)) \ t64 -= rbv; \ if (Q_GETSIGN(rbv)) \ t64 -= rav; \ if (Q_GETSIGN(RCV) ? (t64 != X64_QUAD) : (t64 != 0)) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("MULQ_V %016" PRIu64 "x * %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } #define DO_UMULH uemul64(RAV, RBV, &RCV); /* subtraction */ #define DO_SUBQ RCV = RAV - RBV; #define DO_S4SUBQ RCV = (RAV * 4) - RBV; #define DO_S8SUBQ RCV = (RAV * 8) - RBV; #define DO_SUBQ_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ RCV = rav - rbv; \ \ /* test for integer overflow */ \ if (((rav ^ rbv) & (rav ^ RCV)) & Q_SIGN) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("SUBQ_V %016" PRIu64 "x - %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } #define DO_SUBL RCV = sext_u64_32(RAV - RBV); #define DO_S4SUBL RCV = sext_u64_32((RAV * 4) - RBV); #define DO_S8SUBL RCV = sext_u64_32((RAV * 8) - RBV); #define DO_SUBL_V \ { \ u64 rav = RAV; \ u64 rbv = RBV; \ RCV = sext_u64_32(rav - rbv); \ \ /* test for integer overflow */ \ if (((rav ^ rbv) & (rav ^ RCV)) & L_SIGN) { \ ARITH_TRAP_I(TRAP_IOV, RC); \ printf("SUBL_V %016" PRIu64 "x - %016" PRIu64 "x = %016" PRIu64 \ "x + TRAP.\n", \ rav, rbv, RCV); \ } \ } ================================================ FILE: src/cpu_bwx.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_CMPBGE \ state.r[REG_3] = \ (((u8)(state.r[REG_1] & 0xff) >= (u8)(V_2 & 0xff)) ? 1 : 0) | \ (((u8)((state.r[REG_1] >> 8) & 0xff) >= (u8)((V_2 >> 8) & 0xff)) ? 2 \ : 0) | \ (((u8)((state.r[REG_1] >> 16) & 0xff) >= (u8)((V_2 >> 16) & 0xff)) \ ? 4 \ : 0) | \ (((u8)((state.r[REG_1] >> 24) & 0xff) >= (u8)((V_2 >> 24) & 0xff)) \ ? 8 \ : 0) | \ (((u8)((state.r[REG_1] >> 32) & 0xff) >= (u8)((V_2 >> 32) & 0xff)) \ ? 16 \ : 0) | \ (((u8)((state.r[REG_1] >> 40) & 0xff) >= (u8)((V_2 >> 40) & 0xff)) \ ? 32 \ : 0) | \ (((u8)((state.r[REG_1] >> 48) & 0xff) >= (u8)((V_2 >> 48) & 0xff)) \ ? 64 \ : 0) | \ (((u8)((state.r[REG_1] >> 56) & 0xff) >= (u8)((V_2 >> 56) & 0xff)) ? 128 \ : 0); #define DO_EXTBL \ state.r[REG_3] = (state.r[REG_1] >> ((V_2 & 7) * 8)) & X64_BYTE; #define DO_EXTWL \ state.r[REG_3] = (state.r[REG_1] >> ((V_2 & 7) * 8)) & X64_WORD; #define DO_EXTLL \ state.r[REG_3] = (state.r[REG_1] >> ((V_2 & 7) * 8)) & X64_LONG; #define DO_EXTQL state.r[REG_3] = (state.r[REG_1] >> ((V_2 & 7) * 8)); #define DO_EXTWH \ state.r[REG_3] = (state.r[REG_1] << ((64 - ((V_2 & 7) * 8)) & 63)) & X64_WORD; #define DO_EXTLH \ state.r[REG_3] = (state.r[REG_1] << ((64 - ((V_2 & 7) * 8)) & 63)) & X64_LONG; #define DO_EXTQH \ state.r[REG_3] = (state.r[REG_1] << ((64 - ((V_2 & 7) * 8)) & 63)) & X64_QUAD; #define DO_INSBL \ state.r[REG_3] = (state.r[REG_1] & X64_BYTE) << ((V_2 & 7) * 8); #define DO_INSWL \ state.r[REG_3] = (state.r[REG_1] & X64_WORD) << ((V_2 & 7) * 8); #define DO_INSLL \ state.r[REG_3] = (state.r[REG_1] & X64_LONG) << ((V_2 & 7) * 8); #define DO_INSQL state.r[REG_3] = (state.r[REG_1]) << ((V_2 & 7) * 8); #define DO_INSWH \ state.r[REG_3] = \ (V_2 & 7) \ ? ((state.r[REG_1] & X64_WORD) >> ((64 - ((V_2 & 7) * 8)) & 63)) \ : 0; #define DO_INSLH \ state.r[REG_3] = \ (V_2 & 7) \ ? ((state.r[REG_1] & X64_LONG) >> ((64 - ((V_2 & 7) * 8)) & 63)) \ : 0; #define DO_INSQH \ state.r[REG_3] = \ (V_2 & 7) \ ? ((state.r[REG_1] & X64_QUAD) >> ((64 - ((V_2 & 7) * 8)) & 63)) \ : 0; #define DO_MSKBL \ state.r[REG_3] = state.r[REG_1] & ~(X64_BYTE << ((V_2 & 7) * 8)); #define DO_MSKWL \ state.r[REG_3] = state.r[REG_1] & ~(X64_WORD << ((V_2 & 7) * 8)); #define DO_MSKLL \ state.r[REG_3] = state.r[REG_1] & ~(X64_LONG << ((V_2 & 7) * 8)); #define DO_MSKQL \ state.r[REG_3] = state.r[REG_1] & ~(X64_QUAD << ((V_2 & 7) * 8)); #define DO_MSKWH \ state.r[REG_3] = \ (V_2 & 7) \ ? (state.r[REG_1] & ~(X64_WORD >> ((64 - ((V_2 & 7) * 8)) & 63))) \ : state.r[REG_1]; #define DO_MSKLH \ state.r[REG_3] = \ (V_2 & 7) \ ? (state.r[REG_1] & ~(X64_LONG >> ((64 - ((V_2 & 7) * 8)) & 63))) \ : state.r[REG_1]; #define DO_MSKQH \ state.r[REG_3] = \ (V_2 & 7) \ ? (state.r[REG_1] & ~(X64_QUAD >> ((64 - ((V_2 & 7) * 8)) & 63))) \ : state.r[REG_1]; #define DO_SEXTB state.r[REG_3] = sext_u64_8(V_2); #define DO_SEXTW state.r[REG_3] = sext_u64_16(V_2); #define DO_ZAP \ state.r[REG_3] = \ state.r[REG_1] & \ (((V_2 & 1) ? 0 : U64(0xff)) | ((V_2 & 2) ? 0 : U64(0xff00)) | \ ((V_2 & 4) ? 0 : U64(0xff0000)) | ((V_2 & 8) ? 0 : U64(0xff000000)) | \ ((V_2 & 16) ? 0 : U64(0xff00000000)) | \ ((V_2 & 32) ? 0 : U64(0xff0000000000)) | \ ((V_2 & 64) ? 0 : U64(0xff000000000000)) | \ ((V_2 & 128) ? 0 : U64(0xff00000000000000))); #define DO_ZAPNOT \ state.r[REG_3] = \ state.r[REG_1] & \ (((V_2 & 1) ? U64(0xff) : 0) | ((V_2 & 2) ? U64(0xff00) : 0) | \ ((V_2 & 4) ? U64(0xff0000) : 0) | ((V_2 & 8) ? U64(0xff000000) : 0) | \ ((V_2 & 16) ? U64(0xff00000000) : 0) | \ ((V_2 & 32) ? U64(0xff0000000000) : 0) | \ ((V_2 & 64) ? U64(0xff000000000000) : 0) | \ ((V_2 & 128) ? U64(0xff00000000000000) : 0)); ================================================ FILE: src/cpu_control.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_BEQ \ if (!state.r[REG_1]) \ add_pc(DISP_21 * 4); #define DO_BGE \ if ((s64)state.r[REG_1] >= 0) \ add_pc(DISP_21 * 4); #define DO_BGT \ if ((s64)state.r[REG_1] > 0) \ add_pc(DISP_21 * 4); #define DO_BLBC \ if (!(state.r[REG_1] & 1)) \ add_pc(DISP_21 * 4); #define DO_BLBS \ if (state.r[REG_1] & 1) \ add_pc(DISP_21 * 4); #define DO_BLE \ if ((s64)state.r[REG_1] <= 0) \ add_pc(DISP_21 * 4); #define DO_BLT \ if ((s64)state.r[REG_1] < 0) \ add_pc(DISP_21 * 4); #define DO_BNE \ if (state.r[REG_1]) \ add_pc(DISP_21 * 4); #define DO_BR \ { \ state.r[REG_1] = state.pc & ~U64(0x3); \ add_pc(DISP_21 * 4); \ } #define DO_BSR DO_BR #define DO_JMP \ { \ temp_64 = state.r[REG_2] & ~U64(0x3); \ state.r[REG_1] = state.pc & ~U64(0x3); \ set_pc(temp_64 | (state.pc & 3)); \ } // JSR, RET and JSR_COROUTINE is really JMP, just with different prediction // bits. ================================================ FILE: src/cpu_debug.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(IDB) extern const char *PAL_NAME[]; extern const char *IPR_NAME[]; extern char dbg_string[1000]; #if !defined(LS_MASTER) && !defined(LS_SLAVE) extern char *dbg_strptr; #endif void handle_debug_string(char *s); #define TRC_(down, up, x, y) \ if (bTrace) \ trc->trace(this, state.current_pc, state.pc, down, up, x, y); #define TRC(down, up) \ if (bTrace) \ trc->trace(this, state.current_pc, state.pc, down, up, (char *)0, 0); #define TRC_BR \ if (bTrace) \ trc->trace_br(this, state.current_pc, state.pc); #define GO_PAL(offset) \ { \ if (bDisassemble) { \ sprintf(dbg_strptr, " ==> PAL %x!\n", offset); \ dbg_strptr += strlen(dbg_strptr); \ } \ handle_debug_string(dbg_string); \ state.exc_addr = state.current_pc; \ set_pc(state.pal_base | offset | 1); \ if ((offset == DTBM_SINGLE || offset == ITB_MISS) && bTrace) \ trc->set_waitfor(this, state.exc_addr & ~U64(0x3)); \ else \ TRC_(true, false, "GO_PAL %04x", offset); \ } #else #define TRC_(down, up, x, y) ; #define TRC(down, up) ; #define TRC_BR ; #define GO_PAL(offset) \ { \ state.exc_addr = state.current_pc; \ set_pc(state.pal_base | offset | 1); \ } #endif #if defined(IDB) #define DEBUG_XX \ if (trc->get_fnc_name(this, state.current_pc & ~U64(0x3), &funcname)) { \ if (bListing && !strcmp(funcname, "")) { \ printf("%08" PRIx64 ": \"%s\"\n", state.current_pc, \ cSystem->PtrToMem(state.current_pc)); \ state.pc = (state.current_pc + \ strlen(cSystem->PtrToMem(state.current_pc)) + 4) & \ ~U64(0x3); \ while (state.pc < 0x600000 && cSystem->ReadMem(state.pc, 32, this) == 0) \ state.pc += 4; \ return; \ } else if (bListing && !strcmp(funcname, "!SKIP")) { \ while (state.pc < 0x600000 && cSystem->ReadMem(state.pc, 32, this) == 0) \ state.pc += 4; \ return; \ } else if (bListing && !strncmp(funcname, "!CHAR-", 6)) { \ u64 xx_upto; \ int xx_result; \ xx_result = sscanf(&(funcname[6]), "%" PRIx64 "", &xx_upto); \ if (xx_result == 1) { \ state.pc = state.current_pc; \ while (state.pc < xx_upto) { \ printf("%08" PRIx64 ": \"%s\"\n", state.pc, \ cSystem->PtrToMem(state.pc)); \ state.pc += strlen(cSystem->PtrToMem(state.pc)); \ while (state.pc < xx_upto && \ cSystem->ReadMem(state.pc, 8, this) == 0) \ state.pc++; \ } \ return; \ } \ } else if (bListing && !strncmp(funcname, "!LCHAR-", 7)) { \ char stringval[300]; \ int stringlen; \ u64 xx_upto; \ int xx_result; \ xx_result = sscanf(&(funcname[7]), "%" PRIx64 "", &xx_upto); \ if (xx_result == 1) { \ state.pc = state.current_pc; \ while (state.pc < xx_upto) { \ stringlen = (int)cSystem->ReadMem(state.pc++, 8, this); \ memset(stringval, 0, 300); \ strncpy(stringval, cSystem->PtrToMem(state.pc), stringlen); \ printf("%08" PRIx64 ": \"%s\"\n", state.pc - 1, stringval); \ state.pc += stringlen; \ while (state.pc < xx_upto && \ cSystem->ReadMem(state.pc, 8, this) == 0) \ state.pc++; \ } \ return; \ } \ } else if (bListing && !strncmp(funcname, "!X64-", 5)) { \ printf("\n%s:\n", &(funcname[5])); \ state.pc = state.current_pc; \ while ((state.pc == state.current_pc) || \ !trc->get_fnc_name(this, state.pc, &funcname)) { \ printf("%08" PRIx64 ": %016" PRIx64 "\n", state.pc, \ cSystem->ReadMem(state.pc, 64, this)); \ state.pc += 8; \ } \ return; \ } else if (bListing && !strncmp(funcname, "!X32-", 5)) { \ printf("\n%s:\n", &(funcname[5])); \ state.pc = state.current_pc; \ while ((state.pc == state.current_pc) || \ !trc->get_fnc_name(this, state.pc, &funcname)) { \ printf("%08" PRIx64 ": %08" PRIx64 "\n", state.pc, \ cSystem->ReadMem(state.pc, 32, this)); \ state.pc += 4; \ } \ return; \ } else if (!strncmp(funcname, ":", 1)) { \ sprintf(dbg_strptr, "%s:\n", funcname); \ dbg_strptr += strlen(dbg_strptr); \ } else { \ sprintf(dbg_strptr, "\n%s:\n", funcname); \ dbg_strptr += strlen(dbg_strptr); \ } \ } \ sprintf(dbg_strptr, bListing ? "%08" PRIx64 ": " : "%016" PRIx64 "", \ state.current_pc); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) \ sprintf(dbg_strptr, "(%08" PRIx64 "): ", current_pc_physical); \ else { \ sprintf(dbg_strptr, "%08x %c%c%c%c: ", ins, printable((char)(ins)), \ printable((char)(ins >> 8)), printable((char)(ins >> 16)), \ printable((char)(ins >> 24))); \ } \ dbg_strptr += strlen(dbg_strptr); #define UNKNOWN1 \ if (bDisassemble) { \ DEBUG_XX \ } \ sprintf(dbg_strptr, "Unknown opcode: %02x ", opcode); \ dbg_strptr += strlen(dbg_strptr); \ handle_debug_string(dbg_string); \ return; #define UNKNOWN2 \ if (bDisassemble) { \ DEBUG_XX \ } \ sprintf(dbg_strptr, "Unknown opcode: %02x.%02x ", opcode, function); \ dbg_strptr += strlen(dbg_strptr); \ handle_debug_string(dbg_string); \ return; #define POST_X64(a) \ if (bDisassemble) { \ if (!bListing) { \ sprintf(dbg_strptr, " ==> %" PRIx64 "", a); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define PRE_PAL(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ if (function < 0x40 || (function > 0x7f && function < 0xc0)) \ sprintf(dbg_strptr, #mnemonic " %s", PAL_NAME[function]); \ else \ sprintf(dbg_strptr, #mnemonic " ?%x?", function); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_PAL TRC(1, 0); #define PRE_BR(mnemonic) \ if (bDisassemble) { \ u64 dbg_x = (state.current_pc + 4 + (DISP_21 * 4)) & ~U64(0x3); \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " r%d, ", REG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (trc->get_fnc_name(this, dbg_x, &funcname)) \ sprintf(dbg_strptr, "%s", funcname); \ else \ sprintf(dbg_strptr, "%" PRIx64 "", dbg_x); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_BR TRC_BR; #define PRE_COND(mnemonic) \ if (bDisassemble) { \ u64 dbg_x = (state.current_pc + 4 + (DISP_21 * 4)) & ~U64(0x3); \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " r%d, ", REG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (trc->get_fnc_name(this, dbg_x, &funcname)) \ sprintf(dbg_strptr, "%s", funcname); \ else \ sprintf(dbg_strptr, "%" PRIx64 "", dbg_x); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_1]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_COND TRC_BR; #define PRE_FCOND(mnemonic) \ if (bDisassemble) { \ u64 dbg_x = (state.current_pc + 4 + (DISP_21 * 4)) & ~U64(0x3); \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " f%d, ", FREG_1); \ dbg_strptr += strlen(dbg_strptr); \ if (trc->get_fnc_name(this, dbg_x, &funcname)) \ sprintf(dbg_strptr, "%s", funcname); \ else \ sprintf(dbg_strptr, "%" PRIx64 "", dbg_x); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.f[FREG_1]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_FCOND TRC_BR; #define PRE_BSR(mnemonic) \ if (bDisassemble) { \ u64 dbg_x = (state.current_pc + 4 + (DISP_21 * 4)) & ~U64(0x3); \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " r%d, ", REG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (trc->get_fnc_name(this, dbg_x, &funcname)) \ sprintf(dbg_strptr, "%s", funcname); \ else \ sprintf(dbg_strptr, "%" PRIx64 "", dbg_x); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_BSR \ if (REG_1 == 31) { \ TRC(0, 1); \ } else { \ TRC(1, 1); \ } #define PRE_JMP(mnemonic) \ if (bDisassemble) { \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " r%d, r%d", REG_1 & 31, \ REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_JMP \ if (REG_1 == 31) { \ TRC(0, 1); \ } else { \ TRC(1, 1); \ } #define PRE_RET(mnemonic) \ if (bDisassemble) { \ DEBUG_XX sprintf(dbg_strptr, #mnemonic " r%d", REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_RET TRC(0, 1); #define PRE_MFPR(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d, %s", REG_1 & 31, IPR_NAME[function]); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_MFPR POST_X64(state.r[REG_1]); #define PRE_MTPR(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d, %s", REG_2 & 31, IPR_NAME[function]); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_MTPR POST_X64(state.r[REG_2]); #define PRE_NOP(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic ""); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_NOP ; #define PRE_MEM(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d, %04xH(r%d)", REG_1 & 31, \ (u32)DISP_16, REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_MEM POST_X64(state.r[REG_1]); #define PRE_R12_R3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d, ", REG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (ins & 0x1000) \ sprintf(dbg_strptr, "%02" PRIx64 "H", V_2); \ else \ sprintf(dbg_strptr, "r%d", REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ sprintf(dbg_strptr, ", r%d", REG_3 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ",%" PRIx64 ")", state.r[REG_1], V_2); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_R12_R3 POST_X64(state.r[REG_3]); // Pre-debugging macro for floating-point instructions that have Fa and Fb // as input and Fc as output. #define PRE_F12_F3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " f%d, f%d, f%d", FREG_1, FREG_2, FREG_3); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ",%" PRIx64 ")", state.f[FREG_1], \ state.f[FREG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } // Post-debugging macro for floating-point instructions that have Fa and Fb // as input and Fc as output. #define POST_F12_F3 POST_X64(state.f[FREG_3]); #define PRE_R1_F3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d, f%d ", REG_1 & 31, FREG_3); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_1]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_R1_F3 POST_X64(state.f[FREG_3]); #define PRE_F1_R3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " f%d, r%d ", FREG_1, REG_3 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.f[FREG_1]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_F1_R3 POST_X64(state.r[REG_3]); #define PRE_X_F1(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " f%d ", FREG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_X_F1 POST_X64(state.f[FREG_1]); #define PRE_R2_R3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " "); \ dbg_strptr += strlen(dbg_strptr); \ if (ins & 0x1000) \ sprintf(dbg_strptr, "%02" PRIx64 "H", V_2); \ else \ sprintf(dbg_strptr, "r%d", REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ sprintf(dbg_strptr, ", r%d", REG_3 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", V_2); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_R2_R3 POST_X64(state.r[REG_3]); #define PRE_X_R1(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d", REG_1 & 31); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_X_R1 POST_X64(state.r[REG_1]); #define PRE_X_R3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " r%d", REG_3 & 31); \ dbg_strptr += strlen(dbg_strptr); \ } #define POST_X_R3 POST_X64(state.r[REG_3]); #define PRE_HW_LD(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic); \ dbg_strptr += strlen(dbg_strptr); \ switch (function & ~1) { \ case 0: \ sprintf(dbg_strptr, "/Phys"); \ break; \ case 2: \ sprintf(dbg_strptr, "/Phys/Lock"); \ break; \ case 4: \ sprintf(dbg_strptr, "/Vpte"); \ break; \ case 10: \ sprintf(dbg_strptr, "/Chk"); \ break; \ case 12: \ sprintf(dbg_strptr, "/Alt"); \ break; \ case 14: \ sprintf(dbg_strptr, "/Alt/Chk"); \ break; \ } \ dbg_strptr += strlen(dbg_strptr); \ sprintf(dbg_strptr, " r%d, %04xH(r%d)", REG_1 & 31, (u32)DISP_12, \ REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_HW_LD POST_X64(state.r[REG_1]); #define PRE_HW_ST(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic); \ dbg_strptr += strlen(dbg_strptr); \ switch (function & ~1) { \ case 0: \ sprintf(dbg_strptr, "/Phys"); \ break; \ case 2: \ sprintf(dbg_strptr, "/Phys/Cond"); \ break; \ case 12: \ sprintf(dbg_strptr, "/Alt"); \ break; \ } \ dbg_strptr += strlen(dbg_strptr); \ sprintf(dbg_strptr, " r%d, %04xH(r%d)", REG_1 & 31, (u32)DISP_12, \ REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } #define POST_HW_ST POST_X64(state.r[REG_1]); // Pre-debugging macro for floating-point load/store instructions. #define PRE_FMEM(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " f%d, %04xH(r%d)", FREG_1, (u32)DISP_16, \ REG_2 & 31); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.r[REG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } // Post-debugging macro for floating-point load/store instructions. #define POST_FMEM POST_X64(state.f[FREG_1]); // Pre-debugging macro for floating-point instructions that have Fb as input // And Fc as output. #define PRE_F2_F3(mnemonic) \ if (bDisassemble) { \ DEBUG_XX; \ sprintf(dbg_strptr, #mnemonic " f%d, f%d", FREG_2, FREG_3); \ dbg_strptr += strlen(dbg_strptr); \ if (!bListing) { \ sprintf(dbg_strptr, ": (%" PRIx64 ")", state.f[FREG_2]); \ dbg_strptr += strlen(dbg_strptr); \ } \ } // Post-debugging macro for floating-point instructions that have Fb as input // And Fc as output. #define POST_F2_F3 POST_X64(state.f[FREG_3]); #else #ifndef NDEBUG #define UNKNOWN1 \ printf("Unknown opcode: %02x \n", opcode); \ return; #define UNKNOWN2 \ printf("Unknown opcode: %02x.%02x \n", opcode, function); \ return; #else #define UNKNOWN1 return; #define UNKNOWN2 return; #endif #endif #if defined(IDB) // Debugging version of the OP macro: // Execute the DO_ macro for an instruction, along with disassembling // the instruction if needed. #define OP(mnemonic, format) \ PRE_##format(mnemonic); \ if (!bListing) { \ DO_##mnemonic; \ } \ POST_##format; \ handle_debug_string(dbg_string); \ return; // Execute a function rather than a DO_ macro for an instruction #define OP_FNC(mnemonic, format) \ PRE_##format(mnemonic); \ if (!bListing) { \ mnemonic(); \ } \ POST_##format; \ handle_debug_string(dbg_string); \ return; #else // defined(IDB) // Non-debugging version of the OP macro: // Execute the DO_ macro for an instruction. #define OP(mnemonic, format) \ DO_##mnemonic; \ return; // Execute a function rather than a DO_ macro for an instruction #define OP_FNC(mnemonic, format) \ mnemonic(); \ return; #endif // defined(IDB) ================================================ FILE: src/cpu_defs.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ /* Copyright notice from Simh/alpha/alpha_fpi.c and alpha_fpv.c: Copyright (c) 2003-2006, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #if !defined(__CPU_DEFS__) #define __CPU_DEFS__ /* Instruction formats */ #define I_V_OP 26 /* opcode */ #define I_M_OP 0x3F #define I_OP (I_M_OP << I_V_OP) #define I_V_RA 21 /* Ra */ #define I_M_RA 0x1F #define I_V_RB 16 /* Rb */ #define I_M_RB 0x1F #define I_V_FTRP 13 /* floating trap mode */ #define I_M_FTRP 0x7 #define I_FTRP (I_M_FTRP << I_V_FTRP) #define I_F_VAXRSV 0x4800 /* VAX reserved */ #define I_FTRP_V 0x2000 /* /V trap */ #define I_FTRP_U 0x2000 /* /U trap */ #define I_FTRP_S 0x8000 /* /S trap */ #define I_FTRP_SUI 0xE000 /* /SUI trap */ #define I_FTRP_SVI 0xE000 /* /SVI trap */ #define I_V_FRND 11 /* floating round mode */ #define I_M_FRND 0x3 #define I_FRND (I_M_FRND << I_V_FRND) #define I_FRND_C 0 /* chopped */ #define I_FRND_M 1 /* to minus inf */ #define I_FRND_N 2 /* normal */ #define I_FRND_D 3 /* dynamic */ #define I_FRND_P 3 /* in FPCR: plus inf */ #define I_V_FSRC 9 /* floating source */ #define I_M_FSRC 0x3 #define I_FSRC (I_M_FSRC << I_V_FSRC) #define I_FSRC_X 0x0200 /* data type X */ #define I_V_FFNC 5 /* floating function */ #define I_M_FFNC 0x3F #define I_V_LIT8 13 /* integer 8b literal */ #define I_M_LIT8 0xFF #define I_V_ILIT 12 /* literal flag */ #define I_ILIT (1u << I_V_ILIT) #define I_V_IFNC 5 /* integer function */ #define I_M_IFNC 0x3F #define I_V_RC 0 /* Rc */ #define I_M_RC 0x1F #define I_V_MDSP 0 /* memory displacement */ #define I_M_MDSP 0xFFFF #define I_V_BDSP 0 #define I_M_BDSP 0x1FFFFF /* branch displacement */ #define I_V_PALOP 0 #define I_M_PALOP 0x3FFFFFF /* PAL subopcode */ #define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) #define I_GETRA(x) (((x) >> I_V_RA) & I_M_RA) #define I_GETRB(x) (((x) >> I_V_RB) & I_M_RB) #define I_GETLIT8(x) (((x) >> I_V_LIT8) & I_M_LIT8) #define I_GETIFNC(x) (((x) >> I_V_IFNC) & I_M_IFNC) #define I_GETFRND(x) (((x) >> I_V_FRND) & I_M_FRND) #define I_GETFFNC(x) (((x) >> I_V_FFNC) & I_M_FFNC) #define I_GETRC(x) (((x) >> I_V_RC) & I_M_RC) #define I_GETMDSP(x) (((x) >> I_V_MDSP) & I_M_MDSP) #define I_GETBDSP(x) (((x) >> I_V_BDSP) & I_M_BDSP) #define I_GETPAL(x) (((x) >> I_V_PALOP) & I_M_PALOP) /* Floating point types */ #define DT_F 0 /* type F */ #define DT_G 1 /* type G */ #define DT_S 0 /* type S */ #define DT_T 1 /* type T */ /* Floating point memory format (VAX F) */ #define F_V_SIGN 15 #define F_SIGN (1u << F_V_SIGN) #define F_V_EXP 7 #define F_M_EXP 0xFF #define F_BIAS 0x80 #define F_EXP (F_M_EXP << F_V_EXP) #define F_V_FRAC 29 #define F_GETEXP(x) (((x) >> F_V_EXP) & F_M_EXP) #define SWAP_VAXF(x) ((((x) >> 16) & 0xFFFF) | (((x)&0xFFFF) << 16)) /* Floating point memory format (VAX G) */ #define G_V_SIGN 15 #define G_SIGN (1u << F_V_SIGN) #define G_V_EXP 4 #define G_M_EXP 0x7FF #define G_BIAS 0x400 #define G_EXP (G_M_EXP << G_V_EXP) #define G_GETEXP(x) (((x) >> G_V_EXP) & G_M_EXP) #define SWAP_VAXG(x) \ ((((x)&U64(0x000000000000FFFF)) << 48) | \ (((x)&U64(0x00000000FFFF0000)) << 16) | \ (((x) >> 16) & U64(0x00000000FFFF0000)) | \ (((x) >> 48) & U64(0x000000000000FFFF))) /* Floating memory format (IEEE S) */ #define S_V_SIGN 31 #define S_SIGN (1u << S_V_SIGN) #define S_V_EXP 23 #define S_M_EXP 0xFF #define S_BIAS 0x7F #define S_NAN 0xFF #define S_EXP (S_M_EXP << S_V_EXP) #define S_V_FRAC 29 #define S_GETEXP(x) (((x) >> S_V_EXP) & S_M_EXP) /* Floating point memory format (IEEE T) */ #define T_V_SIGN 63 #define T_SIGN U64(0x8000000000000000) #define T_V_EXP 52 #define T_M_EXP 0x7FF #define T_BIAS 0x3FF #define T_NAN 0x7FF #define T_EXP U64(0x7FF0000000000000) #define T_FRAC U64(0x000FFFFFFFFFFFFF) #define T_GETEXP(x) (((u32)((x) >> T_V_EXP)) & T_M_EXP) /* Floating point register format (all except VAX D) */ #define FPR_V_SIGN 63 #define FPR_SIGN U64(0x8000000000000000) #define FPR_V_EXP 52 #define FPR_M_EXP 0x7FF #define FPR_NAN 0x7FF #define FPR_EXP U64(0x7FF0000000000000) #define FPR_HB U64(0x0010000000000000) #define FPR_FRAC U64(0x000FFFFFFFFFFFFF) #define FPR_GUARD (UF_V_NM - FPR_V_EXP) #define FPR_GETSIGN(x) (((u32)((x) >> FPR_V_SIGN)) & 1) #define FPR_GETEXP(x) (((u32)((x) >> FPR_V_EXP)) & FPR_M_EXP) #define FPR_GETFRAC(x) ((x)&FPR_FRAC) #define FP_TRUE U64(0x4000000000000000) /* 0.5/2.0 in reg */ /* Floating point register format (VAX D) */ #define FDR_V_SIGN 63 #define FDR_SIGN U64(0x8000000000000000) #define FDR_V_EXP 55 #define FDR_M_EXP 0xFF #define FDR_EXP U64(0x7F80000000000000) #define FDR_HB U64(0x0080000000000000) #define FDR_FRAC U64(0x007FFFFFFFFFFFFF) #define FDR_GUARD (UF_V_NM - FDR_V_EXP) #define FDR_GETSIGN(x) (((u32)((x) >> FDR_V_SIGN)) & 1) #define FDR_GETEXP(x) (((u32)((x) >> FDR_V_EXP)) & FDR_M_EXP) #define FDR_GETFRAC(x) ((x)&FDR_FRAC) #define D_BIAS 0x80 /* Unpacked floating point number */ struct ufp { u32 sign; s32 exp; u64 frac; }; typedef struct ufp UFP; #define UF_V_NM 63 #define UF_NM U64(0x8000000000000000) /* normalized */ /* Bit patterns */ #define X64_BYTE U64(0xff) #define X64_WORD U64(0xffff) #define X64_LONG U64(0xffffffff) #define X64_QUAD U64(0xffffffffffffffff) #define B_SIGN U64(0x80) #define W_SIGN U64(0x8000) #define L_SIGN U64(0x80000000) #define Q_SIGN U64(0x8000000000000000) #define Q_GETSIGN(x) (((x) >> 63) & 1) /* IEEE control register (left 32b only) */ #define FPCR_SUM U64(0x8000000000000000) /* summary */ #define FPCR_INED U64(0x4000000000000000) /* inexact disable */ #define FPCR_UNFD U64(0x2000000000000000) /* underflow disable */ #define FPCR_UNDZ U64(0x1000000000000000) /* underflow to 0 */ #define FPCR_V_RMOD 58 /* rounding mode */ #define FPCR_M_RMOD 0x3 #define FPCR_IOV U64(0x0200000000000000) /* integer overflow */ #define FPCR_INE U64(0x0100000000000000) /* inexact */ #define FPCR_UNF U64(0x0080000000000000) /* underflow */ #define FPCR_OVF U64(0x0040000000000000) /* overflow */ #define FPCR_DZE U64(0x0020000000000000) /* div by zero */ #define FPCR_INV U64(0x0010000000000000) /* invalid operation */ #define FPCR_OVFD U64(0x0008000000000000) /* overflow disable */ #define FPCR_DZED U64(0x0004000000000000) /* div by zero disable */ #define FPCR_INVD U64(0x0002000000000000) /* invalid op disable */ #define FPCR_DNZ U64(0x0001000000000000) /* denormal to zero */ #define FPCR_DNOD U64(0x0000800000000000) /* denormal disable */ #define FPCR_RAZ U64(0x00007FFF00000000) /* zero */ #define FPCR_ERR \ (FPCR_IOV | FPCR_INE | FPCR_UNF | FPCR_OVF | FPCR_DZE | FPCR_INV) #define FPCR_GETFRND(x) (((x) >> FPCR_V_RMOD) & FPCR_M_RMOD) #define NEG_Q(x) ((~(x) + 1) & X64_QUAD) #define ABS_Q(x) (((x)&Q_SIGN) ? NEG_Q(x) : (x)) /* IEEE */ #define UFT_ZERO 0 /* unpacked: zero */ #define UFT_FIN 1 /* finite */ #define UFT_DENORM 2 /* denormal */ #define UFT_INF 3 /* infinity */ #define UFT_NAN 4 /* not a number */ #define Q_FINITE(x) ((x) <= UFT_FIN) /* finite */ #define Q_SUI(x) (((x)&I_FTRP) == I_FTRP_SVI) /* 64b * 64b unsigned multiply */ inline u64 uemul64(u64 a, u64 b, u64 *hi) { u64 ahi; u64 alo; u64 bhi; u64 blo; u64 rhi; u64 rmid1; u64 rmid2; u64 rlo; ahi = (a >> 32) & X64_LONG; alo = a & X64_LONG; bhi = (b >> 32) & X64_LONG; blo = b & X64_LONG; rhi = ahi * bhi; rmid1 = ahi * blo; rmid2 = alo * bhi; rlo = alo * blo; rhi = rhi + ((rmid1 >> 32) & X64_LONG) + ((rmid2 >> 32) & X64_LONG); rmid1 = (rmid1 << 32) & X64_QUAD; rmid2 = (rmid2 << 32) & X64_QUAD; rlo = (rlo + rmid1) & X64_QUAD; if (rlo < rmid1) rhi = rhi + 1; rlo = (rlo + rmid2) & X64_QUAD; if (rlo < rmid2) rhi = rhi + 1; if (hi) *hi = rhi & X64_QUAD; return rlo; } /* 64b / 64b unsigned fraction divide */ inline u64 ufdiv64(u64 dvd, u64 dvr, u32 prec, u32 *sticky) { u64 quo; u32 i; quo = 0; /* clear quotient */ for (i = 0; (i < prec) && dvd; i++) { /* divide loop */ quo = quo << 1; /* shift quo */ if (dvd >= dvr) { /* div step ok? */ dvd = dvd - dvr; /* subtract */ quo = quo + 1; } /* quo bit = 1 */ dvd = dvd << 1; } /* shift divd */ quo = quo << (UF_V_NM - i + 1); /* shift quo */ if (sticky) *sticky = (dvd ? 1 : 0); /* set sticky bit */ return quo; /* return quotient */ } /* Fraction square root routine - code from SoftFloat */ inline u64 fsqrt64(u64 asig, s32 exp) { static const u32 sqrtOdd[] = {0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67}; static const u32 sqrtEven[] = {0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002}; u64 zsig; u64 remh; u64 reml; u64 t; u32 index; u32 z; u32 a; u32 sticky = 0; /* Calculate an approximation to the square root of the 32-bit significand given by 'a'. Considered as an integer, 'a' must be at least 2^31. If bit 0 of 'exp' (the least significant bit) is 1, the integer returned approximates 2^31*sqrt('a'/2^31), where 'a' is considered an integer. If bit 0 of 'exp' is 0, the integer returned approximates 2^31*sqrt('a'/2^30). In either case, the approximation returned lies strictly within +/-2 of the exact value. */ a = (u32)(asig >> 32); /* high order frac */ index = (a >> 27) & 0xF; /* bits<30:27> */ if (exp & 1) { /* odd exp? */ z = 0x4000 + (a >> 17) - sqrtOdd[index]; /* initial guess */ z = ((a / z) << 14) + (z << 15); /* Newton iteration */ a = a >> 1; } else { z = 0x8000 + (a >> 17) - sqrtEven[index]; /* initial guess */ z = (a / z) + z; /* Newton iteration */ z = (z >= 0x20000) ? 0xFFFF8000 : (z << 15); if (z <= a) z = (a >> 1) | 0x80000000; } zsig = (((((u64)a) << 31) / ((u64)z)) + (z >> 1)) & X64_LONG; /* Calculate the final answer in two steps. First, do one iteration of Newton's approximation. The divide-by-2 is accomplished by clever positioning of the operands. Then, check the bits just below the (double precision) rounding bit to see if they are close to zero (that is, the rounding bits are close to midpoint). If so, make sure that the result^2 is the input operand */ asig = asig >> ((exp & 1) ? 3 : 2); /* leave 2b guard */ zsig = ufdiv64(asig, zsig << 32, 64, NULL) + (zsig << 30); /* Newton iteration */ if ((zsig & 0x1FF) <= 5) { /* close to even? */ remh = uemul64(zsig, zsig, &reml); /* result^2 */ remh = (asig - remh - (reml ? 1 : 0)) & X64_QUAD; /* arg - result^2 */ reml = NEG_Q(reml); while (Q_GETSIGN(remh) != 0) { /* if arg < result^2 */ zsig = (zsig - 1) & X64_QUAD; /* decr result */ t = ((zsig << 1) & X64_QUAD) | 1; /* incr result^2 */ reml = (reml + t) & X64_QUAD; /* and retest */ remh = (remh + (zsig >> 63) + ((reml < t) ? 1 : 0)) & X64_QUAD; } if ((remh | reml) != 0) sticky = 1; } /* not exact? */ zsig = (zsig << 1) | sticky; /* left justify result */ return zsig; } // INTERRUPT VECTORS #define DTBM_DOUBLE_3 U64(0x100) #define DTBM_DOUBLE_4 U64(0x180) #define FEN U64(0x200) #define UNALIGN U64(0x280) #define DTBM_SINGLE U64(0x300) #define DFAULT U64(0x380) #define OPCDEC U64(0x400) #define IACV U64(0x480) #define MCHK U64(0x500) #define ITB_MISS U64(0x580) #define ARITH U64(0x600) #define INTERRUPT U64(0x680) #define MT_FPCR U64(0x700) #define RESET U64(0x780) /** Chip ID (EV68CB pass 4) [HRM p 5-16]; actual value derived from SRM-code */ #define CPU_CHIP_ID 0x21 /** Major CPU type (EV68CB) [ARM pp D-1..3] */ #define CPU_TYPE_MAJOR 12 /** Minor CPU type (pass 4) [ARM pp D-1..3] */ #define CPU_TYPE_MINOR 6 /** Implementation version [HRM p 2-38; ARM p D-5] */ #define CPU_IMPLVER 2 /** Architecture mask [HRM p 2-38; ARM p D-4]; FIX not implemented */ #define CPU_AMASK U64(0x1305) #define DISP_12 (sext_u64_12(ins)) #define DISP_13 (sext_u64_13(ins)) #define DISP_16 (sext_u64_16(ins)) #define DISP_21 (sext_u64_21(ins)) #define DATA_PHYS_NT(addr, flags) \ if (virt2phys(addr, &phys_address, flags, NULL, ins)) \ return; #define ALIGN_PHYS(a) (phys_address & ~((u64)((a)-1))) /* Increase memory access page alignment checking to 8KB instead of 4KB, 8KB minimum on AXP. Potentially should be dynamic in future, resolves OpenVMS installation problems. Source: https://www.openvmshobbyist.com/forum/viewthread.php?forum_id=161&thread_id=2801 https://github.com/gdwnldsKSC/es40/commit/dad191fb2f279164122654aba6da53acc1040d97 */ #define DATA_PHYS(addr, flags, align) \ if ((addr) & (align)) { \ u64 a1 = (addr); \ u64 a2 = (addr) + (align); \ if ((a1 ^ a2) & ~U64(0x1fff)) /* 8K page boundary crossed*/ \ { \ state.fault_va = addr; \ state.exc_sum = ((REG_1 &0x1f) << 8); \ state.mm_stat = (I_GETOP(ins) << 4) | ((flags & ACCESS_WRITE) ? 1 : 0); \ printf("unaligned addess %d, %d -> trap! ",flags,align); \ printf("exc_sum = 0x%04" PRId64 "x, fault_va = 0x%016" PRId64 \ "x, mm_stat = 0x%03" PRId64 "x.\n",state.exc_sum, state.fault_va, \ state.mm_stat); \ GO_PAL(UNALIGN); \ return; \ } \ } \ DATA_PHYS_NT(addr, flags) // use the define above instead of duplicating /** * Normal variant of read action * In reality, these would generate an alignment trap, and the exception * handler would put things straight. Instead, to speed things up, we'll * just perform the read as requested using the unaligned address. **/ #if defined(IDB) #define LLR last_read_loc = phys_address #define LWR last_write_loc = phys_address #else #define LLR #define LWR #endif #define READ_PHYS(size) \ cSystem->ReadMem(phys_address, size, this); \ LLR #define READ_VIRT(va, size, dest) \ pbc = false; \ DATA_PHYS(va, ACCESS_READ, (size / 8) - 1); \ LLR; \ if (pbc) { \ dest = 0; \ for (int ii = 0; ii < (size / 8); ii++) { \ DATA_PHYS(va + ii, ACCESS_READ, 0); \ dest |= (cSystem->ReadMem(phys_address, 8, this) << (ii * 8)); \ } \ } else { \ dest = cSystem->ReadMem(phys_address, size, this); \ } #define READ_VIRT_LOCK(va, size, dest) \ pbc = false; \ DATA_PHYS(va, ACCESS_READ, (size / 8) - 1); \ LLR; \ cSystem->cpu_lock(state.iProcNum, phys_address); \ if (pbc) { \ dest = 0; \ for (int ii = 0; ii < (size / 8); ii++) { \ DATA_PHYS(va + ii, ACCESS_READ, 0); \ dest |= (cSystem->ReadMem(phys_address, 8, this) << (ii * 8)); \ } \ } else { \ dest = cSystem->ReadMem(phys_address, size, this); \ } #define READ_VIRT_F(va, size, dest, f) \ pbc = false; \ DATA_PHYS(va, ACCESS_READ, (size / 8) - 1); \ LLR; \ if (pbc) { \ u64 aa = 0; \ for (int ii = 0; ii < (size / 8); ii++) { \ DATA_PHYS(va + ii, ACCESS_READ, 0); \ aa |= (cSystem->ReadMem(phys_address, 8, this) << (ii * 8)); \ } \ dest = f(aa); \ } else { \ dest = f(cSystem->ReadMem(phys_address, size, this)); \ } #define READ_VIRT_LOCK_F(va, size, dest, f) \ pbc = false; \ DATA_PHYS(va, ACCESS_READ, (size / 8) - 1); \ LLR; \ cSystem->cpu_lock(state.iProcNum, phys_address); \ if (pbc) { \ u64 aa = 0; \ for (int ii = 0; ii < (size / 8); ii++) { \ DATA_PHYS(va + ii, ACCESS_READ, 0); \ aa |= (cSystem->ReadMem(phys_address, 8, this) << (ii * 8)); \ } \ dest = f(aa); \ } else { \ dest = f(cSystem->ReadMem(phys_address, size, this)); \ } /** * Normal variant of write action * In reality, these would generate an alignment trap, and the exception * handler would put things straight. Instead, to speed things up, we'll * just perform the write as requested using the unaligned address. **/ #define WRITE_PHYS(data, size) \ cSystem->WriteMem(phys_address, size, data, this); \ LWR #define WRITE_VIRT(va, size, src) \ pbc = false; \ DATA_PHYS(va, ACCESS_WRITE, (size / 8) - 1); \ LWR; \ if (pbc) { \ u64 aa = src; \ for (int ii = 0; ii < (size / 8); ii++) { \ DATA_PHYS(va + ii, ACCESS_WRITE, 0); \ cSystem->WriteMem(phys_address, 8, aa, this); \ aa >>= 8; \ } \ } else { \ cSystem->WriteMem(phys_address, size, src, this); \ } /** * NO-TRAP (NT) variants of read action. * This is used for HW_LD, where alignment traps are * inhibited. We'll align the adress and read using the aligned * address. **/ #define READ_PHYS_NT(size) \ cSystem->ReadMem(ALIGN_PHYS((size) / 8), size, this); \ LLR; /** * NO-TRAP (NT) variants of write action. * This is used for HW_ST, where alignment traps are * inhibited. We'll align the adress and write using the aligned * address. **/ #if defined(IDB) #define WRITE_PHYS_NT(data, size) \ cSystem->WriteMem(ALIGN_PHYS((size) / 8), size, data, this); \ LWR #else #define WRITE_PHYS_NT(data, size) \ cSystem->WriteMem(ALIGN_PHYS((size) / 8), size, data, this) #endif #define REG_1 RREG(I_GETRA(ins)) #define REG_2 RREG(I_GETRB(ins)) #define REG_3 RREG(I_GETRC(ins)) #define FREG_1 (I_GETRA(ins)) #define FREG_2 (I_GETRB(ins)) #define FREG_3 (I_GETRC(ins)) #define RA REG_1 #define RAV state.r[RA] #define RB REG_2 #define RBV ((ins & 0x1000) ? ((ins >> 13) & 0xff) : state.r[RB]) #define V_2 RBV #define RC REG_3 #define RCV state.r[RC] #define ACCESS_READ 0 #define ACCESS_WRITE 1 #define ACCESS_EXEC 2 #define ACCESS_MODE 3 #define NO_CHECK 4 #define VPTE 8 #define FAKE 16 #define ALT 32 #define RECUR 128 #define PROBE 256 #define PROBEW 512 #define FPSTART \ if (state.fpen == 0) /* flt point disabled? */ \ { \ GO_PAL(FEN); /* set trap */ \ break; /* and stop current instruction */ \ } \ state.exc_sum = 0; /* Traps - corresponds to arithmetic trap summary register */ #define TRAP_SWC U64(0x01) /* software completion */ #define TRAP_INV U64(0x02) /* invalid operand */ #define TRAP_DZE U64(0x04) /* divide by zero */ #define TRAP_OVF U64(0x08) /* overflow */ #define TRAP_UNF U64(0x10) /* underflow */ #define TRAP_INE U64(0x20) /* inexact */ #define TRAP_IOV U64(0x40) /* integer overflow */ #define TRAP_INT U64(0x80) /* exception register is integer reg */ #define ARITH_TRAP(flags, reg) \ { \ state.exc_sum |= flags; /* cause of trap */ \ state.exc_sum |= (reg & 0x1f) << 8; /* destination register */ \ GO_PAL(ARITH); /* trap */ \ } #define ARITH_TRAP_I(flags, reg) \ { \ state.exc_sum = 0; \ ARITH_TRAP(TRAP_INT | flags, reg) \ } #define SPE_0_MASK U64(0x0000ffffc0000000) /* <47:30> */ #define SPE_0_MATCH U64(0x0000ffff80000000) /* <47:31> */ #define SPE_0_MAP U64(0x000000003fffffff) /* <29:0> */ #define SPE_1_MASK U64(0x0000fe0000000000) /* <47:41> */ #define SPE_1_MATCH U64(0x0000fc0000000000) /* <47:42> */ #define SPE_1_MAP U64(0x000001ffffffffff) /* <40:0> */ #define SPE_1_TEST U64(0x0000010000000000) /* <40> */ #define SPE_1_ADD U64(0x00000e0000000000) /* <43:41> */ #define SPE_2_MASK U64(0x0000c00000000000) /* <47:46> */ #define SPE_2_MATCH U64(0x0000800000000000) /* <47> */ #define SPE_2_MAP U64(0x00000fffffffffff) /* <43:0> */ #endif ================================================ FILE: src/cpu_fp_branch.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(HAVE_NEW_FP) #define DO_FBEQ \ FPSTART; \ if ((state.f[FREG_1] & ~FPR_SIGN) == 0) /* +0 or - 0? */ \ add_pc(DISP_21 * 4); #define DO_FBGE \ FPSTART; \ if (state.f[FREG_1] <= FPR_SIGN) /* +0 to + n? */ \ add_pc(DISP_21 * 4); #define DO_FBGT \ FPSTART; \ if (!(state.f[FREG_1] & FPR_SIGN) && (state.f[FREG_1] != 0)) \ \ /* not - and not 0? */ \ add_pc(DISP_21 * 4); #define DO_FBLE \ FPSTART; \ if ((state.f[FREG_1] & FPR_SIGN) || (state.f[FREG_1] == 0)) \ \ /* - or 0? */ \ add_pc(DISP_21 * 4); #define DO_FBLT \ FPSTART; \ if (state.f[FREG_1] > FPR_SIGN) /* -0 to -n? */ \ add_pc(DISP_21 * 4); #define DO_FBNE \ FPSTART; \ if ((state.f[FREG_1] & ~FPR_SIGN) != 0) /* not +0 or -0? */ \ add_pc(DISP_21 * 4); #else #define DO_FBEQ \ FPSTART; \ if (state.f[FREG_1] == U64(0x0000000000000000) || \ state.f[FREG_1] == U64(0x8000000000000000)) \ add_pc(DISP_21 * 4); #define DO_FBGE \ FPSTART; \ if (!(state.f[FREG_1] & U64(0x8000000000000000)) || \ state.f[FREG_1] == U64(0x8000000000000000)) \ add_pc(DISP_21 * 4); #define DO_FBGT \ FPSTART; \ if (!(state.f[FREG_1] & U64(0x8000000000000000)) && \ state.f[FREG_1] != U64(0x0000000000000000)) \ add_pc(DISP_21 * 4); #define DO_FBLE \ FPSTART; \ if ((state.f[FREG_1] & U64(0x8000000000000000)) || \ state.f[FREG_1] == U64(0x0000000000000000)) \ add_pc(DISP_21 * 4); #define DO_FBLT \ FPSTART; \ if ((state.f[FREG_1] & U64(0x8000000000000000)) && \ state.f[FREG_1] != U64(0x8000000000000000)) \ add_pc(DISP_21 * 4); #define DO_FBNE \ FPSTART; \ if (state.f[FREG_1] != U64(0x0000000000000000) && \ state.f[FREG_1] != U64(0x8000000000000000)) \ add_pc(DISP_21 * 4); #endif ================================================ FILE: src/cpu_fp_memory.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(HAVE_NEW_FP) #define DO_LDF \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 32, state.f[FREG_1], vax_ldf); \ } #define DO_LDG \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 64, state.f[FREG_1], vax_ldg); \ } #define DO_LDS \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 32, state.f[FREG_1], ieee_lds); \ } #define DO_LDT \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT(state.r[REG_2] + DISP_16, 64, state.f[FREG_1]); \ } #define DO_STF \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 32, vax_stf(state.f[FREG_1])); #define DO_STG \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, vax_stg(state.f[FREG_1])); #define DO_STS \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 32, ieee_sts(state.f[FREG_1])); #define DO_STT \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, state.f[FREG_1]); #else #define DO_LDF \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 32, state.f[FREG_1], load_f); \ } #define DO_LDG \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 64, state.f[FREG_1], load_g); \ } #define DO_LDS \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT_F(state.r[REG_2] + DISP_16, 32, state.f[FREG_1], load_s); \ } #define DO_LDT \ FPSTART; \ if (FREG_1 != 31) { \ READ_VIRT(state.r[REG_2] + DISP_16, 64, state.f[FREG_1]); \ } #define DO_STF \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 32, store_f(state.f[FREG_1])); #define DO_STG \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, store_g(state.f[FREG_1])); #define DO_STS \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 32, store_s(state.f[FREG_1])); #define DO_STT \ FPSTART; \ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, state.f[FREG_1]); #endif ================================================ FILE: src/cpu_fp_operate.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if defined(HAVE_NEW_FP) /* copy sign */ #define DO_CPYS \ FPSTART; \ state.f[FREG_3] = \ (state.f[FREG_1] & FPR_SIGN) | (state.f[FREG_2] & ~FPR_SIGN); #define DO_CPYSN \ FPSTART; \ state.f[FREG_3] = ((state.f[FREG_1] & FPR_SIGN) ^ FPR_SIGN) | \ (state.f[FREG_2] & ~FPR_SIGN); #define DO_CPYSE \ FPSTART; \ state.f[FREG_3] = (state.f[FREG_1] & (FPR_SIGN | FPR_EXP)) | \ (state.f[FREG_2] & ~(FPR_SIGN | FPR_EXP)); /* conditional move */ #define DO_FCMOVEQ \ FPSTART; \ if ((state.f[FREG_1] & ~FPR_SIGN) == 0) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVGE \ FPSTART; \ if (state.f[FREG_1] <= FPR_SIGN) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVGT \ FPSTART; \ if (!FPR_GETSIGN(state.f[FREG_1]) && (state.f[FREG_1] != 0)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVLE \ FPSTART; \ if (FPR_GETSIGN(state.f[FREG_1]) || (state.f[FREG_1] == 0)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVLT \ FPSTART; \ if (state.f[FREG_1] > FPR_SIGN) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVNE \ FPSTART; \ if ((state.f[FREG_1] & ~FPR_SIGN) != 0) \ state.f[FREG_3] = state.f[FREG_2]; /* floating-point control register */ #define DO_MF_FPCR \ FPSTART; \ state.f[FREG_1] = state.fpcr; #define DO_MT_FPCR \ FPSTART; \ state.fpcr = state.f[FREG_1] & U64(0x7fff800000000000); \ if (state.fpcr & U64(0x03f0000000000000)) \ state.fpcr |= U64(0x8000000000000000); /* SUM */ /* add */ #define DO_ADDG \ FPSTART; \ state.f[FREG_3] = vax_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_G, 0); #define DO_ADDF \ FPSTART; \ state.f[FREG_3] = vax_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_F, 0); #define DO_ADDT \ FPSTART; \ state.f[FREG_3] = ieee_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_T, 0); #define DO_ADDS \ FPSTART; \ state.f[FREG_3] = ieee_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_S, 0); /* subtract */ #define DO_SUBG \ FPSTART; \ state.f[FREG_3] = vax_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_G, 1); #define DO_SUBF \ FPSTART; \ state.f[FREG_3] = vax_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_F, 1); #define DO_SUBT \ FPSTART; \ state.f[FREG_3] = ieee_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_T, 1); #define DO_SUBS \ FPSTART; \ state.f[FREG_3] = ieee_fadd(state.f[FREG_1], state.f[FREG_2], ins, DT_S, 1); /* comparison */ #define DO_CMPGEQ \ FPSTART; \ state.f[FREG_3] = \ (vax_fcmp(state.f[FREG_1], state.f[FREG_2], ins) == 0) ? FP_TRUE : 0; #define DO_CMPGLE \ FPSTART; \ state.f[FREG_3] = \ (vax_fcmp(state.f[FREG_1], state.f[FREG_2], ins) <= 0) ? FP_TRUE : 0; #define DO_CMPGLT \ FPSTART; \ state.f[FREG_3] = \ (vax_fcmp(state.f[FREG_1], state.f[FREG_2], ins) < 0) ? FP_TRUE : 0; #define DO_CMPTEQ \ FPSTART; \ state.f[FREG_3] = (ieee_fcmp(state.f[FREG_1], state.f[FREG_2], ins, 0) == 0) \ ? FP_TRUE \ : 0; #define DO_CMPTLE \ FPSTART; \ state.f[FREG_3] = (ieee_fcmp(state.f[FREG_1], state.f[FREG_2], ins, 1) <= 0) \ ? FP_TRUE \ : 0; #define DO_CMPTLT \ FPSTART; \ state.f[FREG_3] = \ (ieee_fcmp(state.f[FREG_1], state.f[FREG_2], ins, 1) < 0) ? FP_TRUE : 0; #define DO_CMPTUN \ FPSTART; \ state.f[FREG_3] = ((ieee_unpack(state.f[FREG_1], &ufp1, ins) == UFT_NAN) || \ (ieee_unpack(state.f[FREG_2], &ufp2, ins) == UFT_NAN)) \ ? FP_TRUE \ : 0; /* format conversions */ #define DO_CVTQL \ FPSTART; \ state.f[FREG_3] = ((state.f[FREG_2] & 0xC0000000) << 32) | \ ((state.f[FREG_2] & 0x3FFFFFFF) << 29); \ if (FPR_GETSIGN(state.f[FREG_2]) \ ? (state.f[FREG_2] < U64(0xFFFFFFFF80000000)) \ : (state.f[FREG_2] > U64(0x000000007FFFFFFF))) { \ if (ins & I_FTRP_V) \ vax_trap(TRAP_IOV, ins); \ } #define DO_CVTLQ \ FPSTART; \ state.f[FREG_3] = sext_u64_32(((state.f[FREG_2] >> 32) & 0xC0000000) | \ ((state.f[FREG_2] >> 29) & 0x3FFFFFFF)); #define DO_CVTGQ \ FPSTART; \ state.f[FREG_3] = vax_cvtfi(state.f[FREG_2], ins); #define DO_CVTQG \ FPSTART; \ state.f[FREG_3] = vax_cvtif(state.f[FREG_2], ins, DT_G); #define DO_CVTQF \ FPSTART; \ state.f[FREG_3] = vax_cvtif(state.f[FREG_2], ins, DT_F); #define DO_CVTTQ \ FPSTART; \ state.f[FREG_3] = ieee_cvtfi(state.f[FREG_2], ins); #define DO_CVTQT \ FPSTART; \ state.f[FREG_3] = ieee_cvtif(state.f[FREG_2], ins, DT_T); #define DO_CVTQS \ FPSTART; \ state.f[FREG_3] = ieee_cvtif(state.f[FREG_2], ins, DT_S); #define DO_CVTGD \ FPSTART; \ vax_unpack(state.f[FREG_2], &ufp2, ins); \ state.f[FREG_3] = vax_rpack_d(&ufp2, ins); #define DO_CVTDG \ FPSTART; \ vax_unpack_d(state.f[FREG_2], &ufp2, ins); \ state.f[FREG_3] = vax_rpack(&ufp2, ins, DT_G); #define DO_CVTGF \ FPSTART; \ vax_unpack(state.f[FREG_2], &ufp2, ins); \ state.f[FREG_3] = vax_rpack(&ufp2, ins, DT_F); #define DO_CVTST \ FPSTART; \ state.f[FREG_3] = ieee_cvtst(state.f[FREG_2], ins); #define DO_CVTTS \ FPSTART; \ state.f[FREG_3] = ieee_cvtts(state.f[FREG_2], ins); /* float <-> integer register moves */ #define DO_FTOIS \ FPSTART; \ state.r[REG_3] = ieee_sts(state.f[FREG_1]); #define DO_FTOIT \ FPSTART; \ state.r[REG_3] = state.f[FREG_1]; #define DO_ITOFT \ FPSTART; \ state.f[FREG_3] = state.r[REG_1]; #define DO_ITOFS \ FPSTART; \ state.f[FREG_3] = ieee_lds((u32)state.r[REG_1]); #define DO_ITOFF \ FPSTART; \ state.f[FREG_3] = vax_ldf(SWAP_VAXF((u32)state.r[REG_1])); /* Multiply */ #define DO_MULG \ FPSTART; \ state.f[FREG_3] = vax_fmul(state.f[FREG_1], state.f[FREG_2], ins, DT_G); #define DO_MULF \ FPSTART; \ state.f[FREG_3] = vax_fmul(state.f[FREG_1], state.f[FREG_2], ins, DT_F); #define DO_MULT \ FPSTART; \ state.f[FREG_3] = ieee_fmul(state.f[FREG_1], state.f[FREG_2], ins, DT_T); #define DO_MULS \ FPSTART; \ state.f[FREG_3] = ieee_fmul(state.f[FREG_1], state.f[FREG_2], ins, DT_S); /* Divide */ #define DO_DIVG \ FPSTART; \ state.f[FREG_3] = vax_fdiv(state.f[FREG_1], state.f[FREG_2], ins, DT_G); #define DO_DIVF \ FPSTART; \ state.f[FREG_3] = vax_fdiv(state.f[FREG_1], state.f[FREG_2], ins, DT_F); #define DO_DIVT \ FPSTART; \ state.f[FREG_3] = ieee_fdiv(state.f[FREG_1], state.f[FREG_2], ins, DT_T); #define DO_DIVS \ FPSTART; \ state.f[FREG_3] = ieee_fdiv(state.f[FREG_1], state.f[FREG_2], ins, DT_S); /* Square-root */ #define DO_SQRTG \ FPSTART; \ state.f[FREG_3] = vax_sqrt(state.f[FREG_2], ins, DT_G); #define DO_SQRTF \ FPSTART; \ state.f[FREG_3] = vax_sqrt(state.f[FREG_2], ins, DT_F); #define DO_SQRTT \ FPSTART; \ state.f[FREG_3] = ieee_sqrt(state.f[FREG_2], ins, DT_T); #define DO_SQRTS \ FPSTART; \ state.f[FREG_3] = ieee_sqrt(state.f[FREG_2], ins, DT_S); #else #define DO_CPYS \ FPSTART; \ state.f[FREG_3] = (state.f[FREG_1] & U64(0x8000000000000000)) | \ (state.f[FREG_2] & U64(0x7fffffffffffffff)); #define DO_CPYSN \ FPSTART; \ state.f[FREG_3] = \ (state.f[FREG_1] & U64(0x8000000000000000) ^ U64(0x8000000000000000)) | \ (state.f[FREG_2] & U64(0x7fffffffffffffff)); #define DO_CPYSE \ FPSTART; \ state.f[FREG_3] = (state.f[FREG_1] & U64(0xfff0000000000000)) | \ (state.f[FREG_2] & U64(0x000fffffffffffff)); #define DO_CVTQL \ FPSTART; \ state.f[FREG_3] = ((state.f[FREG_2] & U64(0x00000000c0000000)) << 32) | \ ((state.f[FREG_2] & U64(0x000000003fffffff)) << 29); #define DO_CVTLQ \ FPSTART; \ state.f[FREG_3] = \ sext_u64_32(((state.f[FREG_2] >> 32) & U64(0x00000000c0000000)) | \ ((state.f[FREG_2] >> 29) & U64(0x000000003fffffff))); #define DO_FCMOVEQ \ FPSTART; \ if (state.f[FREG_1] == U64(0x0000000000000000) || \ state.f[FREG_1] == U64(0x8000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVGE \ FPSTART; \ if (!(state.f[FREG_1] & U64(0x8000000000000000)) || \ state.f[FREG_1] == U64(0x8000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVGT \ FPSTART; \ if (!(state.f[FREG_1] & U64(0x8000000000000000)) && \ state.f[FREG_1] != U64(0x0000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVLE \ FPSTART; \ if ((state.f[FREG_1] & U64(0x8000000000000000)) || \ state.f[FREG_1] == U64(0x0000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVLT \ FPSTART; \ if ((state.f[FREG_1] & U64(0x8000000000000000)) && \ state.f[FREG_1] != U64(0x8000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_FCMOVNE \ FPSTART; \ if (state.f[FREG_1] != U64(0x0000000000000000) && \ state.f[FREG_1] != U64(0x8000000000000000)) \ state.f[FREG_3] = state.f[FREG_2]; #define DO_MF_FPCR \ FPSTART; \ state.f[FREG_1] = state.fpcr; #define DO_MT_FPCR \ FPSTART; \ state.fpcr = state.f[FREG_1]; #define DO_ADDG \ FPSTART; \ state.f[FREG_3] = host2g(g2host(state.f[FREG_1]) + g2host(state.f[FREG_2])); #define DO_ADDF \ FPSTART; \ state.f[FREG_3] = host2f(f2host(state.f[FREG_1]) + f2host(state.f[FREG_2])); #define DO_ADDT \ FPSTART; \ state.f[FREG_3] = host2t(t2host(state.f[FREG_1]) + t2host(state.f[FREG_2])); #define DO_ADDS \ FPSTART; \ state.f[FREG_3] = host2s(s2host(state.f[FREG_1]) + s2host(state.f[FREG_2])); #define DO_SUBG \ FPSTART; \ state.f[FREG_3] = host2g(g2host(state.f[FREG_1]) - g2host(state.f[FREG_2])); #define DO_SUBF \ FPSTART; \ state.f[FREG_3] = host2f(f2host(state.f[FREG_1]) - f2host(state.f[FREG_2])); #define DO_SUBT \ FPSTART; \ state.f[FREG_3] = host2t(t2host(state.f[FREG_1]) - t2host(state.f[FREG_2])); #define DO_SUBS \ FPSTART; \ state.f[FREG_3] = host2s(s2host(state.f[FREG_1]) - s2host(state.f[FREG_2])); #define DO_CMPGEQ \ FPSTART; \ state.f[FREG_3] = (g2host(state.f[FREG_1]) == g2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPGLE \ FPSTART; \ state.f[FREG_3] = (g2host(state.f[FREG_1]) <= g2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPGLT \ FPSTART; \ state.f[FREG_3] = (g2host(state.f[FREG_1]) < g2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPTEQ \ FPSTART; \ state.f[FREG_3] = (t2host(state.f[FREG_1]) == t2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPTLE \ FPSTART; \ state.f[FREG_3] = (t2host(state.f[FREG_1]) <= t2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPTLT \ FPSTART; \ state.f[FREG_3] = (t2host(state.f[FREG_1]) < t2host(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CMPTUN \ FPSTART; \ state.f[FREG_3] = (i_isnan(state.f[FREG_1]) || i_isnan(state.f[FREG_2])) \ ? U64(0x4000000000000000) \ : 0; #define DO_CVTGQ \ FPSTART; \ state.f[FREG_3] = (u64)((s64)g2host(state.f[FREG_2])); #define DO_CVTQG \ FPSTART; \ state.f[FREG_3] = host2g((double)((s64)state.f[FREG_2])); #define DO_CVTQF \ FPSTART; \ state.f[FREG_3] = host2f((double)((s64)state.f[FREG_2])); #define DO_CVTTQ \ FPSTART; \ state.f[FREG_3] = (u64)((s64)t2host(state.f[FREG_2])); #define DO_CVTQT \ FPSTART; \ state.f[FREG_3] = host2t((double)((s64)state.f[FREG_2])); #define DO_CVTQS \ FPSTART; \ state.f[FREG_3] = host2s((double)((s64)state.f[FREG_2])); #define DO_CVTGD \ FPSTART; \ state.f[FREG_3] = host2d(g2host(state.f[FREG_2])); #define DO_CVTDG \ FPSTART; \ state.f[FREG_3] = host2g(d2host(state.f[FREG_2])); #define DO_CVTGF \ FPSTART; \ state.f[FREG_3] = host2f(g2host(state.f[FREG_2])); #define DO_FTOIS \ FPSTART; \ temp_64 = state.f[FREG_1]; \ state.r[REG_3] = \ (temp_64 & U64(0x000000003fffffff)) | \ ((temp_64 & U64(0xc000000000000000)) >> 32) | \ (((temp_64 & U64(0x8000000000000000)) >> 31) * U64(0xffffffff)); #define DO_FTOIT \ FPSTART; \ state.r[REG_3] = state.f[FREG_1]; #define DO_ITOFT \ FPSTART; \ state.f[FREG_3] = state.r[REG_1]; #define DO_ITOFS \ FPSTART; \ state.f[FREG_3] = load_s((u32)state.r[REG_1]); #define DO_ITOFF \ FPSTART; \ state.f[FREG_3] = itof_f(state.r[REG_1]); #define DO_MULG \ FPSTART; \ state.f[FREG_3] = host2g(g2host(state.f[FREG_1]) * g2host(state.f[FREG_2])); #define DO_MULF \ FPSTART; \ state.f[FREG_3] = host2f(f2host(state.f[FREG_1]) * f2host(state.f[FREG_2])); #define DO_MULT \ FPSTART; \ state.f[FREG_3] = host2t(t2host(state.f[FREG_1]) * t2host(state.f[FREG_2])); #define DO_MULS \ FPSTART; \ state.f[FREG_3] = host2s(s2host(state.f[FREG_1]) * s2host(state.f[FREG_2])); #define DO_DIVG \ FPSTART; \ state.f[FREG_3] = host2g(g2host(state.f[FREG_1]) / g2host(state.f[FREG_2])); #define DO_DIVF \ FPSTART; \ state.f[FREG_3] = host2f(f2host(state.f[FREG_1]) / f2host(state.f[FREG_2])); #define DO_DIVT \ FPSTART; \ state.f[FREG_3] = host2t(t2host(state.f[FREG_1]) / t2host(state.f[FREG_2])); #define DO_DIVS \ FPSTART; \ state.f[FREG_3] = host2s(s2host(state.f[FREG_1]) / s2host(state.f[FREG_2])); #define DO_SQRTG \ FPSTART; \ state.f[FREG_3] = host2g(sqrt(g2host(state.f[FREG_2]))); #define DO_SQRTF \ FPSTART; \ state.f[FREG_3] = host2f(sqrt(f2host(state.f[FREG_2]))); #define DO_SQRTT \ FPSTART; \ state.f[FREG_3] = host2t(sqrt(t2host(state.f[FREG_2]))); #define DO_SQRTS \ FPSTART; \ state.f[FREG_3] = host2s(sqrt(s2host(state.f[FREG_2]))); #define DO_CVTST \ FPSTART; \ state.f[FREG_3] = host2t(s2host(state.f[FREG_2])); #define DO_CVTTS \ FPSTART; \ state.f[FREG_3] = host2s(t2host(state.f[FREG_2])); #endif ================================================ FILE: src/cpu_logical.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_AND state.r[REG_3] = state.r[REG_1] & V_2; #define DO_BIC state.r[REG_3] = state.r[REG_1] & ~V_2; #define DO_BIS state.r[REG_3] = state.r[REG_1] | V_2; #define DO_EQV state.r[REG_3] = state.r[REG_1] ^ ~V_2; #define DO_ORNOT state.r[REG_3] = state.r[REG_1] | ~V_2; #define DO_XOR state.r[REG_3] = state.r[REG_1] ^ V_2; #define DO_CMOVEQ \ if (!state.r[REG_1]) \ state.r[REG_3] = V_2; #define DO_CMOVGE \ if ((s64)state.r[REG_1] >= 0) \ state.r[REG_3] = V_2; #define DO_CMOVGT \ if ((s64)state.r[REG_1] > 0) \ state.r[REG_3] = V_2; #define DO_CMOVLBC \ if (!(state.r[REG_1] & U64(0x1))) \ state.r[REG_3] = V_2; #define DO_CMOVLBS \ if (state.r[REG_1] & U64(0x1)) \ state.r[REG_3] = V_2; #define DO_CMOVLE \ if ((s64)state.r[REG_1] <= 0) \ state.r[REG_3] = V_2; #define DO_CMOVLT \ if ((s64)state.r[REG_1] < 0) \ state.r[REG_3] = V_2; #define DO_CMOVNE \ if (state.r[REG_1]) \ state.r[REG_3] = V_2; #define DO_SLL state.r[REG_3] = state.r[REG_1] << (V_2 & 63); #define DO_SRA \ state.r[REG_3] = \ (V_2 & 63) \ ? ((state.r[REG_1] >> (V_2 & 63)) | \ ((state.r[REG_1] >> 63) ? (X64_QUAD << (64 - (V_2 & 63))) : 0)) \ : state.r[REG_1]; #define DO_SRL state.r[REG_3] = state.r[REG_1] >> (V_2 & 63); ================================================ FILE: src/cpu_memory.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_LDA state.r[REG_1] = state.r[REG_2] + DISP_16; #define DO_LDAH state.r[REG_1] = state.r[REG_2] + (DISP_16 << 16); #define DO_LDBU READ_VIRT(state.r[REG_2] + DISP_16, 8, state.r[REG_1]); #define DO_LDL \ READ_VIRT_F(state.r[REG_2] + DISP_16, 32, state.r[REG_1], sext_u64_32); #define DO_LDL_L \ READ_VIRT_LOCK_F(state.r[REG_2] + DISP_16, 32, state.r[REG_1], sext_u64_32); #define DO_LDQ READ_VIRT(state.r[REG_2] + DISP_16, 64, state.r[REG_1]); #define DO_LDQ_L READ_VIRT_LOCK(state.r[REG_2] + DISP_16, 64, state.r[REG_1]); #define DO_LDQ_U \ READ_VIRT((state.r[REG_2] + DISP_16) & ~U64(0x7), 64, state.r[REG_1]); #define DO_LDWU READ_VIRT(state.r[REG_2] + DISP_16, 16, state.r[REG_1]); #define DO_STB WRITE_VIRT(state.r[REG_2] + DISP_16, 8, state.r[REG_1]); #define DO_STL WRITE_VIRT(state.r[REG_2] + DISP_16, 32, state.r[REG_1]); #define DO_STL_C \ if (cSystem->cpu_unlock(state.iProcNum)) { \ WRITE_VIRT(state.r[REG_2] + DISP_16, 32, state.r[REG_1]); \ state.r[REG_1] = 1; \ } else \ state.r[REG_1] = 0; #define DO_STQ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, state.r[REG_1]); #define DO_STQ_C \ if (cSystem->cpu_unlock(state.iProcNum)) { \ WRITE_VIRT(state.r[REG_2] + DISP_16, 64, state.r[REG_1]); \ state.r[REG_1] = 1; \ } else \ state.r[REG_1] = 0; #define DO_STQ_U \ WRITE_VIRT((state.r[REG_2] + DISP_16) & ~U64(0x07), 64, state.r[REG_1]); #define DO_STW WRITE_VIRT(state.r[REG_2] + DISP_16, 16, state.r[REG_1]); ================================================ FILE: src/cpu_misc.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_AMASK state.r[REG_3] = V_2 & ~CPU_AMASK; #define DO_CALL_PAL \ if (((function < 0x40) && ((state.cm != 0))) || \ ((function > 0x3f) && (function < 0x80)) || (function > 0xbf)) { \ UNKNOWN2 \ } else { \ if (state.pal_vms) { \ switch (function) { \ case 0x01: /* CFLUSH */ \ vmspal_call_cflush(); \ break; \ \ case 0x02: /* DRAINA */ \ vmspal_call_draina(); \ break; \ \ case 0x03: /* LDQP */ \ vmspal_call_ldqp(); \ break; \ \ case 0x04: /* STQP */ \ vmspal_call_stqp(); \ break; \ \ case 0x05: /* SWPCTX */ \ vmspal_call_swpctx(); \ break; \ \ case 0x06: /* MFPR_ASN */ \ vmspal_call_mfpr_asn(); \ break; \ \ case 0x07: /* MTPR_ASTEN */ \ vmspal_call_mtpr_asten(); \ break; \ \ case 0x08: /* MTPR_ASTSR */ \ vmspal_call_mtpr_astsr(); \ break; \ \ case 0x09: /* CSERVE */ \ vmspal_call_cserve(); \ break; \ \ case 0x0b: /* MFPR_FEN */ \ vmspal_call_mfpr_fen(); \ break; \ \ case 0x0c: /* MTPR_FEN */ \ vmspal_call_mtpr_fen(); \ break; \ \ case 0x0e: /* MFPR_IPL */ \ vmspal_call_mfpr_ipl(); \ break; \ \ case 0x0f: /* MTPR_IPL */ \ vmspal_call_mtpr_ipl(); \ break; \ \ case 0x10: /* MFPR_MCES */ \ vmspal_call_mfpr_mces(); \ break; \ \ case 0x11: /* MTPR_MCES */ \ vmspal_call_mtpr_mces(); \ break; \ \ case 0x12: /* MFPR_PCBB */ \ vmspal_call_mfpr_pcbb(); \ break; \ \ case 0x13: /* MFPR_PRBR */ \ vmspal_call_mfpr_prbr(); \ break; \ \ case 0x14: /* MTPR_PRBR */ \ vmspal_call_mtpr_prbr(); \ break; \ \ case 0x15: /* MFPR_PTBR */ \ vmspal_call_mfpr_ptbr(); \ break; \ \ case 0x16: /* MFPR_SCBB */ \ vmspal_call_mfpr_scbb(); \ break; \ \ case 0x17: /* MTPR_SCBB */ \ vmspal_call_mtpr_scbb(); \ break; \ \ case 0x18: /* MTPR_SIRR */ \ vmspal_call_mtpr_sirr(); \ break; \ \ case 0x19: /* MFPR_SISR */ \ vmspal_call_mfpr_sisr(); \ break; \ \ case 0x1a: /* MFPR_TBCHK */ \ vmspal_call_mfpr_tbchk(); \ break; \ \ case 0x1b: /* MTPR_TBIA */ \ vmspal_call_mtpr_tbia(); \ break; \ \ case 0x1c: /* MTPR_TBIAP */ \ vmspal_call_mtpr_tbiap(); \ break; \ \ case 0x1d: /* MTPR_TBIS */ \ vmspal_call_mtpr_tbis(); \ break; \ \ case 0x1e: /* MFPR_ESP */ \ vmspal_call_mfpr_esp(); \ break; \ \ case 0x1f: /* MTPR_ESP */ \ vmspal_call_mtpr_esp(); \ break; \ \ case 0x20: /* MFPR_SSP */ \ vmspal_call_mfpr_ssp(); \ break; \ \ case 0x21: /* MTPR_SSP */ \ vmspal_call_mtpr_ssp(); \ break; \ \ case 0x22: /* MFPR_USP */ \ vmspal_call_mfpr_usp(); \ break; \ \ case 0x23: /* MTPR_USP */ \ vmspal_call_mtpr_usp(); \ break; \ \ case 0x24: /* MTPR_TBISD */ \ vmspal_call_mtpr_tbisd(); \ break; \ \ case 0x25: /* MTPR_TBISI */ \ vmspal_call_mtpr_tbisi(); \ break; \ \ case 0x26: /* MFPR_ASTEN */ \ vmspal_call_mfpr_asten(); \ break; \ \ case 0x27: /* MFPR_ASTSR */ \ vmspal_call_mfpr_astsr(); \ break; \ \ case 0x29: /* MFPR_VPTB */ \ vmspal_call_mfpr_vptb(); \ break; \ \ case 0x2e: /* MTPR_DATFX */ \ vmspal_call_mtpr_datfx(); \ break; \ \ case 0x3f: /* MFPR_WHAMI */ \ vmspal_call_mfpr_whami(); \ break; \ \ case 0x86: /* IMB */ \ vmspal_call_imb(); \ break; \ \ case 0x8f: /* PROBER */ \ vmspal_call_prober(); \ break; \ \ case 0x90: /* PROBEW */ \ vmspal_call_probew(); \ break; \ \ case 0x91: /* RD_PS */ \ vmspal_call_rd_ps(); \ break; \ \ case 0x92: /* REI */ \ vmspal_call_rei(); \ break; \ \ case 0x9b: /* SWASTEN */ \ vmspal_call_swasten(); \ break; \ \ case 0x9c: /* WR_PS_SW */ \ vmspal_call_wr_ps_sw(); \ break; \ \ case 0x9d: /* RSCC */ \ vmspal_call_rscc(); \ break; \ \ case 0x9e: /* READ_UNQ */ \ vmspal_call_read_unq(); \ break; \ \ case 0x9f: /* WRITE_UNQ */ \ vmspal_call_write_unq(); \ break; \ \ default: \ state.r[32 + 23] = state.pc; \ set_pc(state.pal_base | (1 << 13) | ((function & 0x80) << 5) | \ ((function & 0x3f) << 6) | 1); \ TRC(true, false) \ } \ } else { \ state.r[32 + 23] = state.pc; \ set_pc(state.pal_base | (1 << 13) | ((function & 0x80) << 5) | \ ((function & 0x3f) << 6) | 1); \ TRC(true, false) \ } \ } #define DO_IMPLVER state.r[REG_3] = CPU_IMPLVER; #define DO_RPCC \ state.r[REG_1] = ((u64)state.cc_offset) << 32 | (state.cc & U64(0xffffffff)); // The following ops have no function right now (at least, not until multiple // CPU's are supported). #define DO_TRAPB ; #define DO_EXCB ; #define DO_MB ; #define DO_WMB ; #define DO_FETCH ; #define DO_FETCH_M ; #define DO_ECB ; #define DO_WH64 ; #define DO_WH64EN ; ================================================ FILE: src/cpu_mvi.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_MINUB8 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 8) { \ if ((u8)((temp_64_1 >> i) & X64_BYTE) > (u8)((temp_64_2 >> i) & X64_BYTE)) \ temp_64 |= (((temp_64_2 >> i) & X64_BYTE) << i); \ else \ temp_64 |= (((temp_64_1 >> i) & X64_BYTE) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MINSB8 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 8) { \ if ((s8)((temp_64_1 >> i) & X64_BYTE) > (s8)((temp_64_2 >> i) & X64_BYTE)) \ temp_64 |= (((temp_64_2 >> i) & X64_BYTE) << i); \ else \ temp_64 |= (((temp_64_1 >> i) & X64_BYTE) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MINUW4 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 16) { \ if ((u16)((temp_64_1 >> i) & X64_WORD) > \ (u16)((temp_64_2 >> i) & X64_WORD)) \ temp_64 |= (((temp_64_2 >> i) & X64_WORD) << i); \ else \ temp_64 |= (((temp_64_1 >> i) & X64_WORD) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MINSW4 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 16) { \ if ((s16)((temp_64_1 >> i) & X64_WORD) > \ (s16)((temp_64_2 >> i) & X64_WORD)) \ temp_64 |= (((temp_64_2 >> i) & X64_WORD) << i); \ else \ temp_64 |= (((temp_64_1 >> i) & X64_WORD) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MAXUB8 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 8) { \ if ((u8)((temp_64_1 >> i) & X64_BYTE) > (u8)((temp_64_2 >> i) & X64_BYTE)) \ temp_64 |= (((temp_64_1 >> i) & X64_BYTE) << i); \ else \ temp_64 |= (((temp_64_2 >> i) & X64_BYTE) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MAXSB8 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 8) { \ if ((s8)((temp_64_1 >> i) & X64_BYTE) > (s8)((temp_64_2 >> i) & X64_BYTE)) \ temp_64 |= (((temp_64_1 >> i) & X64_BYTE) << i); \ else \ temp_64 |= (((temp_64_2 >> i) & X64_BYTE) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MAXUW4 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 16) { \ if ((u16)((temp_64_1 >> i) & X64_WORD) > \ (u16)((temp_64_2 >> i) & X64_WORD)) \ temp_64 |= (((temp_64_1 >> i) & X64_WORD) << i); \ else \ temp_64 |= (((temp_64_2 >> i) & X64_WORD) << i); \ } \ state.r[REG_3] = temp_64; #define DO_MAXSW4 \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 16) { \ if ((s16)((temp_64_1 >> i) & X64_WORD) > \ (s16)((temp_64_2 >> i) & X64_WORD)) \ temp_64 |= (((temp_64_1 >> i) & X64_WORD) << i); \ else \ temp_64 |= (((temp_64_2 >> i) & X64_WORD) << i); \ } \ state.r[REG_3] = temp_64; #define DO_PERR \ temp_64 = 0; \ temp_64_1 = state.r[REG_1]; \ temp_64_2 = V_2; \ for (i = 0; i < 64; i += 8) { \ if ((s8)((temp_64_1 >> i) & X64_BYTE) > (s8)((temp_64_2 >> i) & X64_BYTE)) \ temp_64 |= ((u64)((s8)((temp_64_1 >> i) & X64_BYTE) - \ (s8)((temp_64_2 >> i) & X64_BYTE)) \ << i); \ else \ temp_64 |= ((u64)((s8)((temp_64_2 >> i) & X64_BYTE) - \ (s8)((temp_64_1 >> i) & X64_BYTE)) \ << i); \ } \ state.r[REG_3] = temp_64; #define DO_PKLB \ temp_64_2 = V_2; \ state.r[REG_3] = (temp_64_2 & U64(0x00000000000000ff)) | \ ((temp_64_2 & U64(0x000000ff00000000)) >> 24); #define DO_PKWB \ temp_64_2 = V_2; \ state.r[REG_3] = (temp_64_2 & U64(0x00000000000000ff)) | \ ((temp_64_2 & U64(0x0000000000ff0000)) >> 8) | \ ((temp_64_2 & U64(0x000000ff00000000)) >> 16) | \ ((temp_64_2 & U64(0x00ff000000000000)) >> 24); #define DO_UNPKBL \ temp_64_2 = V_2; \ state.r[REG_3] = \ (temp_64_2 & U64(0x000000ff)) | ((temp_64_2 & U64(0x0000ff00)) << 24); #define DO_UNPKBW \ temp_64_2 = V_2; \ state.r[REG_3] = (temp_64_2 & U64(0x000000ff)) | \ ((temp_64_2 & U64(0x0000ff00)) << 8) | \ ((temp_64_2 & U64(0x00ff0000)) << 16) | \ ((temp_64_2 & U64(0xff000000)) << 24); ================================================ FILE: src/cpu_pal.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_HW_MFPR \ if ((function & 0xc0) == 0x40) { /* PCTX */ \ state.r[REG_1] = ((u64)state.asn << 39) | ((u64)state.astrr << 9) | \ ((u64)state.aster << 5) | \ (state.fpen ? U64(0x1) << 2 : 0) | \ (state.ppcen ? U64(0x1) << 1 : 0); \ } else { \ switch (function) { \ case 0x05: /* PMPC */ \ state.r[REG_1] = state.pmpc; \ break; \ \ case 0x06: /* EXC_ADDR */ \ state.r[REG_1] = state.exc_addr; \ break; \ \ case 0x07: /* IVA_FORM */ \ state.r[REG_1] = va_form(state.exc_addr, true); \ break; \ \ case 0x08: /* IER_CM */ \ case 0x09: /* CM */ \ case 0x0a: /* IER */ \ case 0x0b: /* IER_CM */ \ state.r[REG_1] = (((u64)state.eien) << 33) | (((u64)state.slen) << 32) | \ (((u64)state.cren) << 31) | (((u64)state.pcen) << 29) | \ (((u64)state.sien) << 13) | \ (((u64)state.asten) << 13) | (((u64)state.cm) << 3); \ break; \ \ case 0x0c: /* SIRR */ \ state.r[REG_1] = ((u64)state.sir) << 13; \ break; \ \ case 0x0d: /* ISUM */ \ state.r[REG_1] = \ (((u64)(state.eir & state.eien)) << 33) | \ (((u64)(state.slr & state.slen)) << 32) | \ (((u64)(state.crr & state.cren)) << 31) | \ (((u64)(state.pcr & state.pcen)) << 29) | \ (((u64)(state.sir & state.sien)) << 13) | \ (((u64)(((U64(0x1) << (state.cm + 1)) - 1) & state.aster & \ state.astrr & (state.asten * 0x3))) \ << 3) | \ (((u64)(((U64(0x1) << (state.cm + 1)) - 1) & state.aster & \ state.astrr & (state.asten * 0xc))) \ << 7); \ break; \ \ case 0x0f: /* EXC_SUM */ \ state.r[REG_1] = state.exc_sum; \ break; \ \ case 0x10: /* PAL_BASE */ \ state.r[REG_1] = state.pal_base; \ break; \ \ case 0x11: /* i_ctl */ \ state.r[REG_1] = \ state.i_ctl_other | (((u64)CPU_CHIP_ID) << 24) | \ (u64)state.i_ctl_vptb | (((u64)state.i_ctl_va_mode) << 15) | \ (state.hwe ? U64(0x1) << 12 : 0) | (state.sde ? U64(0x1) << 7 : 0) | \ (((u64)state.i_ctl_spe) << 3); \ break; \ \ case 0x14: /* PCTR_CTL */ \ state.r[REG_1] = state.pctr_ctl; \ break; \ \ case 0x16: /* I_STAT */ \ state.r[REG_1] = state.i_stat; \ break; \ \ case 0x27: /* MM_STAT */ \ state.r[REG_1] = state.mm_stat; \ break; \ \ case 0x2a: /* DC_STAT */ \ state.r[REG_1] = state.dc_stat; \ break; \ \ case 0x2b: /* C_DATA */ \ state.r[REG_1] = 0; \ break; \ \ case 0xc0: /* CC */ \ state.r[REG_1] = \ (((u64)state.cc_offset) << 32) | (state.cc & U64(0xffffffff)); \ break; \ \ case 0xc2: /* VA */ \ state.r[REG_1] = state.fault_va; \ break; \ \ case 0xc3: /* VA_FORM */ \ state.r[REG_1] = va_form(state.fault_va, false); \ break; \ \ default: \ UNKNOWN2; \ } \ } #define DO_HW_MTPR \ if ((function & 0xc0) == 0x40) { \ if (function & 1) \ state.asn = (int)(state.r[REG_2] >> 39) & 0xff; \ if (function & 2) { \ state.aster = (int)(state.r[REG_2] >> 5) & 0xf; \ state.check_int = true; \ } \ if (function & 4) { \ state.astrr = (int)(state.r[REG_2] >> 9) & 0xf; \ state.check_int = true; \ } \ if (function & 8) \ state.ppcen = (int)(state.r[REG_2] >> 1) & 1; \ if (function & 16) \ state.fpen = (int)(state.r[REG_2] >> 2) & 1; \ } else { \ switch (function) { \ case 0x00: /* ITB_TAG */ \ state.last_tb_virt = state.r[REG_2]; \ break; \ \ case 0x01: /* ITB_PTE */ \ add_tb_i(state.last_tb_virt, state.r[REG_2]); \ break; \ \ case 0x02: /* ITB_IAP */ \ tbiap(ACCESS_EXEC); \ break; \ \ case 0x03: /* ITB_IA */ \ tbia(ACCESS_EXEC); \ break; \ \ case 0x04: /* ITB_IS */ \ tbis(state.r[REG_2], ACCESS_EXEC); \ break; \ \ case 0x09: /* CM */ \ state.cm = (int)(state.r[REG_2] >> 3) & 3; \ state.check_int = true; \ break; \ \ case 0x0b: /* IER_CM */ \ state.cm = (int)(state.r[REG_2] >> 3) & 3; \ state.check_int = true; \ \ case 0x0a: /* IER */ \ state.asten = (int)(state.r[REG_2] >> 13) & 1; \ state.sien = (int)(state.r[REG_2] >> 13) & 0xfffe; \ state.pcen = (int)(state.r[REG_2] >> 29) & 3; \ state.cren = (int)(state.r[REG_2] >> 31) & 1; \ state.slen = (int)(state.r[REG_2] >> 32) & 1; \ state.eien = (int)(state.r[REG_2] >> 33) & 0x3f; \ state.check_int = true; \ break; \ \ case 0x0c: /* SIRR */ \ state.sir = (int)(state.r[REG_2] >> 13) & 0xfffe; \ state.check_int = true; \ break; \ \ case 0x0e: /* HW_INT_CLR */ \ state.pcr &= ~((state.r[REG_2] >> 29) & U64(0x3)); \ state.crr &= ~((state.r[REG_2] >> 31) & U64(0x1)); \ state.slr &= ~((state.r[REG_2] >> 32) & U64(0x1)); \ break; \ \ case 0x10: /* PAL_BASE */ \ set_PAL_BASE(state.r[REG_2] & U64(0x00000fffffff8000)); \ break; \ \ case 0x11: /* i_ctl */ \ state.i_ctl_other = state.r[REG_2] & U64(0x00000000007e2f67); \ state.i_ctl_vptb = \ sext_u64_48(state.r[REG_2] & U64(0x0000ffffc0000000)); \ state.i_ctl_spe = (int)(state.r[REG_2] >> 3) & 3; \ state.sde = (state.r[REG_2] >> 7) & 1; \ state.hwe = (state.r[REG_2] >> 12) & 1; \ state.i_ctl_va_mode = (int)(state.r[REG_2] >> 15) & 3; \ break; \ \ case 0x12: /* ic_flush_asm */ \ flush_icache_asm(); \ break; \ \ case 0x13: /* IC_FLUSH */ \ flush_icache(); \ break; \ \ case 0x14: /* PCTR_CTL */ \ state.pctr_ctl = state.r[REG_2] & U64(0xffffffffffffffdf); \ break; \ \ case 0x15: /* CLR_MAP */ \ case 0x17: /* SLEEP */ \ case 0x27: /* MM_STAT */ \ case 0x2b: /* C_DATA */ \ case 0x2c: /* C_SHIFT */ \ case 0x2d: /* M_FIX */ \ break; \ \ case 0x16: /* I_STAT */ \ state.i_stat &= ~state.r[REG_2]; /* W1C */ \ break; \ \ case 0x20: /* DTB_TAG0 */ \ state.last_tb_virt = state.r[REG_2]; \ break; \ \ case 0x21: /* DTB_PTE0 */ \ add_tb_d(state.last_tb_virt, state.r[REG_2]); \ break; \ \ case 0x24: /* DTB_IS0 */ \ tbis(state.r[REG_2], ACCESS_READ); \ break; \ \ case 0x25: /* DTB_ASN0 */ \ state.asn0 = (int)(state.r[REG_2] >> 56); \ break; \ \ case 0x26: /* DTB_ALTMODE */ \ state.alt_cm = (int)(state.r[REG_2] & 3); \ break; \ \ case 0x28: /* M_CTL */ \ state.smc = (int)(state.r[REG_2] >> 4) & 3; \ state.m_ctl_spe = (int)(state.r[REG_2] >> 1) & 7; \ break; \ \ case 0x29: /* DC_CTL */ \ state.dc_ctl = state.r[REG_2]; \ break; \ \ case 0x2a: /* DC_STAT */ \ state.dc_stat &= ~state.r[REG_2]; \ break; \ \ case 0xa0: /* DTB_TAG1 */ \ state.last_tb_virt = state.r[REG_2]; \ break; \ \ case 0xa1: /* DTB_PTE1 */ \ add_tb_d(state.last_tb_virt, state.r[REG_2]); \ break; \ \ case 0xa2: /* DTB_IAP */ \ tbiap(ACCESS_READ); \ break; \ \ case 0xa3: /* DTB_IA */ \ tbia(ACCESS_READ); \ break; \ \ case 0xa4: /* DTB_IS1 */ \ tbis(state.r[REG_2], ACCESS_READ); \ break; \ \ case 0xa5: /* DTB_ASN1 */ \ state.asn1 = (int)(state.r[REG_2] >> 56); \ break; \ \ case 0xc0: /* CC */ \ state.cc_offset = (u32)(state.r[REG_2] >> 32); \ break; \ \ case 0xc1: /* CC_CTL */ \ state.cc_ena = (state.r[REG_2] >> 32) & 1; \ state.cc = (u32)(state.r[REG_2] & U64(0xfffffff0)); \ break; \ \ case 0xc4: /* VA_CTL */ \ state.va_ctl_vptb = \ sext_u64_48(state.r[REG_2] & U64(0x0000ffffc0000000)); \ state.va_ctl_va_mode = (int)(state.r[REG_2] >> 1) & 3; \ break; \ \ default: \ UNKNOWN2; \ } \ } #define DO_HW_RET set_pc(state.r[REG_2]) #define DO_HW_LDL \ switch (function) { \ case 0: /* longword physical */ \ phys_address = state.r[REG_2] + DISP_12; \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 2: /* longword physical locked */ \ phys_address = state.r[REG_2] + DISP_12; \ cSystem->cpu_lock(state.iProcNum, phys_address); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 4: /* longword virtual vpte chk alt vpte */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | VPTE); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 8: /* longword virtual */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 10: /* longword virtual check */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 12: /* longword virtual alt */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | ALT); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ case 14: /* longword virtual alt check */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | ALT); \ state.r[REG_1] = READ_PHYS_NT(32); \ break; \ \ default: \ UNKNOWN2; \ } #define DO_HW_LDQ \ switch (function) { \ case 1: /* quadword physical */ \ phys_address = state.r[REG_2] + DISP_12; \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 3: /* quadword physical locked */ \ phys_address = state.r[REG_2] + DISP_12; \ cSystem->cpu_lock(state.iProcNum, phys_address); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 5: /* quadword virtual vpte chk alt vpte */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | VPTE); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 9: /* quadword virtual */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 11: /* quadword virtual check */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 13: /* quadword virtual alt */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | ALT); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ case 15: /* quadword virtual alt check */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | ALT); \ state.r[REG_1] = READ_PHYS_NT(64); \ break; \ \ default: \ UNKNOWN2; \ } #define DO_HW_STL \ switch (function) { \ case 0: /* longword physical */ \ phys_address = state.r[REG_2] + DISP_12; \ WRITE_PHYS_NT(state.r[REG_1], 32); \ break; \ \ case 2: /* longword physical conditional */ \ if (cSystem->cpu_unlock(state.iProcNum)) { \ phys_address = state.r[REG_2] + DISP_12; \ WRITE_PHYS_NT(state.r[REG_1], 32); \ state.r[REG_1] = 1; \ } else \ state.r[REG_1] = 0; \ break; \ \ case 4: /* longword virtual chk alt vpte */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK); \ WRITE_PHYS_NT(state.r[REG_1], 32); \ break; \ \ case 12: /* longword virtual alt */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | ALT); \ WRITE_PHYS_NT(state.r[REG_1], 32); \ break; \ \ default: \ UNKNOWN2; \ } #define DO_HW_STQ \ switch (function) { \ case 1: /* quadword physical */ \ phys_address = state.r[REG_2] + DISP_12; \ WRITE_PHYS_NT(state.r[REG_1], 64); \ break; \ \ case 3: /* quadword physical conditional */ \ if (cSystem->cpu_unlock(state.iProcNum)) { \ phys_address = state.r[REG_2] + DISP_12; \ WRITE_PHYS_NT(state.r[REG_1], 64); \ state.r[REG_1] = 1; \ } else \ state.r[REG_1] = 0; \ break; \ \ case 5: /* quadword virtual chk alt vpte */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK); \ WRITE_PHYS_NT(state.r[REG_1], 64); \ break; \ \ case 13: /* quadword virtual alt */ \ DATA_PHYS_NT(state.r[REG_2] + DISP_12, ACCESS_READ | NO_CHECK | ALT); \ WRITE_PHYS_NT(state.r[REG_1], 64); \ break; \ \ default: \ UNKNOWN2; \ } ================================================ FILE: src/cpu_vax.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #define DO_RC \ state.r[REG_1] = state.bIntrFlag ? 1 : 0; \ state.bIntrFlag = false; #define DO_RS \ state.r[REG_1] = state.bIntrFlag ? 1 : 0; \ state.bIntrFlag = true; ================================================ FILE: src/datatypes.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_DATATYPES_H) #define INCLUDED_DATATYPES_H #if defined(HAVE_STDINT_H) #include #endif #if defined(HAVE_INTTYPES_H) #include #endif #if defined(_WIN32) && !defined(__GNUWIN32__) #define U64(a) a##ui64 #else // defined(_WIN32) #define U64(a) UINT64_C(a) #endif // defined(_WIN32) typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; typedef u8 u_int8_t; typedef u16 u_int16_t; typedef u32 u_int32_t; typedef u64 u_int64_t; #define HAVE_U_INT8_T 1 #define HAVE_INT8_T 1 #define HAVE_U_INT16_T 1 #define HAVE_INT16_T 1 #define HAVE_U_INT32_T 1 #define HAVE_INT32_T 1 #define HAVE_U_INT64_T 1 #define HAVE_INT64_T 1 /** * Sign-extend an 8-bit value to 64 bits. **/ inline u64 sext_u64_8(u64 a) { #if defined(ES40_BIG_ENDIAN) return (((a)&U64(0x0000000000000080)) ? ((a) | U64(0xffffffffffffff00)) : ((a) & U64(0x00000000000000ff))); #else // Optimised implementation for LE machines of this hotpath inline // function. The implementation above compiles to three opcodes but // the following compiles to just one movsx? opcode. auto aa = static_cast(*reinterpret_cast(&a)); return *reinterpret_cast(&aa); #endif } /** * Sign-extend a 12-bit value to 64 bits. **/ inline u64 sext_u64_12(u64 a) { return (((a)&U64(0x0000000000000800)) ? ((a) | U64(0xfffffffffffff000)) : ((a) & U64(0x0000000000000fff))); } /** * Sign-extend a 13-bit value to 64 bits. **/ inline u64 sext_u64_13(u64 a) { return (((a)&U64(0x0000000000001000)) ? ((a) | U64(0xffffffffffffe000)) : ((a) & U64(0x0000000000001fff))); } /** * Sign-extend an 16-bit value to 64 bits. **/ inline u64 sext_u64_16(u64 a) { #if defined(ES40_BIG_ENDIAN) return (((a)&U64(0x0000000000008000)) ? ((a) | U64(0xffffffffffff0000)) : ((a) & U64(0x000000000000ffff))); #else // Optimised implementation for LE machines of this hotpath inline // function. The implementation above compiles to three opcodes but // the following compiles to just one movsx? opcode. auto aa = static_cast(*reinterpret_cast(&a)); return *reinterpret_cast(&aa); #endif } /** * Sign-extend a 21-bit value to 64 bits. **/ inline u64 sext_u64_21(u64 a) { return (((a)&U64(0x0000000000100000)) ? ((a) | U64(0xffffffffffe00000)) : ((a) & U64(0x00000000001fffff))); } /** * Sign-extend a 32-bit value to 64 bits. **/ inline u64 sext_u64_32(u64 a) { #if defined(ES40_BIG_ENDIAN) return (((a)&U64(0x0000000080000000)) ? ((a) | U64(0xffffffff00000000)) : ((a) & U64(0x00000000ffffffff))); #else // Optimised implementation for LE machines of this hotpath inline // function. The implementation above compiles to three opcodes but // the following compiles to just one movsx? opcode. auto aa = static_cast(*reinterpret_cast(&a)); return *reinterpret_cast(&aa); #endif } /** * Sign-extend a 48-bit value to 64 bits. **/ inline u64 sext_u64_48(u64 a) { return (((a)&U64(0x0000800000000000)) ? ((a) | U64(0xffff000000000000)) : ((a) & U64(0x0000ffffffffffff))); } inline bool test_bit_64(u64 x, int bit) { return (x & (U64(0x1) << bit)) ? true : false; } /** * Sign-extend a 24-bit value to 32 bits. **/ inline u32 sext_u32_24(u32 a) { return (((a)&0x00800000) ? ((a) | 0xff000000) : ((a)&0x00ffffff)); } #endif // INCLUDED_DATATYPES_H ================================================ FILE: src/es40-cfg.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "StdAfx.hpp" // C++ includes #include #include #include #include #include #include #if defined(HAVE_PCAP) #include #endif using namespace std; // Question classes #include "FreeTextQuestion.hpp" #include "MultipleChoiceQuestion.hpp" #include "NumberQuestion.hpp" #include "Question.hpp" #include "ShrinkingChoiceQuestion.hpp" /** * Add disks for a controller to the configuration file. * * \param disk_q: A ShrinkingChoiceQuestion that contains * all allowed disk names for this controller, * and a special answer "none" with a value * of "". * \param os: Output stream for the configuration file. **/ void add_disks(ShrinkingChoiceQuestion *disk_q, ostream *os) { /* Loop until there are no more disks to be added. */ for (;;) { /* If disk_q has only one answer left, this is the * "none" answer. This controller can contain no * more disks. */ if (disk_q->countAnswers() == 1) break; /* If the answer value is "", the answer "none" * was given. Stop adding disks to this controller. */ if (disk_q->ask() == "") break; /* Find out if this should be using: * - a disk image file * - a raw device * - a RAM DISK */ MultipleChoiceQuestion type_q; type_q.setQuestion("How should " + disk_q->getAnswer() + " be emulated?"); type_q.setDefault("file"); type_q.setExplanation("Disks can be emulated in several ways."); type_q.addAnswer("file", "file", "The disk uses a disk image on the host system's disk."); type_q.addAnswer("device", "device", "The disk uses one of the host system's raw disks."); type_q.addAnswer("ramdisk", "ramdisk", "The disk stores it's data in RAM. Volatile."); *os << " " << disk_q->getAnswer() << " = " << type_q.ask() << "\n"; *os << " {\n"; if (type_q.getAnswer() == "file" || type_q.getAnswer() == "device") { /* For a file or device, we need to know what * file or device to use. */ FreeTextQuestion img_q; img_q.setQuestion("What " + type_q.getAnswer() + " should " + disk_q->getAnswer() + " use?"); img_q.setExplanation("Enter the path to the " + type_q.getAnswer() + " to use for this disk."); *os << " " << type_q.getAnswer() << " = \"" << img_q.ask() << "\";\n"; } if (type_q.getAnswer() == "file") { /* For a file, we need to know whether to create * it when it doesn't exist or not. */ MultipleChoiceQuestion create_q; create_q.setQuestion( "If the file doesn't exist, do you want us to create it?"); create_q.setExplanation( "The file will be created the first time the emulator runs."); create_q.addAnswer("no", "no", "Don't create this file"); create_q.addAnswer("yes", "yes", "Create this file"); if (create_q.getAnswer() == "yes") { /* If we should create the file, we need to * know it's size. */ MultipleChoiceQuestion unit_q; unit_q.setQuestion( "What unit do you want to use to specify the disk size?"); unit_q.setExplanation( "This is needed to create the file if it doesn't exist."); unit_q.addAnswer("KB", "K", "Kilobytes"); unit_q.addAnswer("MB", "M", "Megabytes"); unit_q.addAnswer("GB", "G", "Gigabytes"); unit_q.setDefault("MB"); unit_q.ask(); NumberQuestion size_q; size_q.setQuestion("How many " + unit_q.getAnswer() + "Bytes should the disk be?"); size_q.setExplanation( "This is needed to create the file if it doesn't exist."); size_q.setRange(1, 1024); size_q.setDefault("10"); size_q.ask(); *os << " autocreate_size = \"" << size_q.getAnswer() << unit_q.getAnswer() << "\";\n"; } } if (type_q.getAnswer() == "ramdisk") { /* For a RAM DISK, we need to know what * size it should be. */ MultipleChoiceQuestion unit_q; unit_q.setQuestion( "What unit do you want to use to specify the disk size?"); unit_q.setExplanation("This is needed to create the RAMDISK."); unit_q.addAnswer("KB", "K", "Kilobytes"); unit_q.addAnswer("MB", "M", "Megabytes"); unit_q.addAnswer("GB", "G", "Gigabytes"); unit_q.setDefault("MB"); unit_q.ask(); NumberQuestion size_q; size_q.setQuestion("How many " + unit_q.getAnswer() + "Bytes should the disk be?"); size_q.setExplanation("This is needed to create the RAMDISK."); size_q.setRange(1, 1024); size_q.setDefault("10"); size_q.ask(); *os << " size = \"" << size_q.getAnswer() << unit_q.getAnswer() << "\";\n"; } /* We need to know whether to emulate this as * a cd-rom, or as a hard-disk. */ MultipleChoiceQuestion cdrom_q; cdrom_q.setQuestion("Should " + disk_q->getAnswer() + " be a disk or a cd-rom device?"); cdrom_q.setExplanation("Do you want the OS to see this " + type_q.getAnswer() + " as a hard-disk, or as a cd-rom?"); cdrom_q.addAnswer("disk", "false", "Hard-disk"); cdrom_q.addAnswer("cd-rom", "true", "CD-ROM drive"); cdrom_q.setDefault("disk"); /* We also need to know whether this is a * writeable or a read-only device. */ MultipleChoiceQuestion ro_q; ro_q.setQuestion("Should " + disk_q->getAnswer() + " be a read-only disk?"); ro_q.setExplanation("You might want to write-protect this disk."); ro_q.addAnswer("no", "false", "writeable"); ro_q.addAnswer("yes", "true", "read-only"); ro_q.setDefault("no"); if (cdrom_q.ask() == "true") { /* CD-ROMs are always read-only. */ ro_q.setAnswer("true"); } else if (type_q.getAnswer() == "ramdisk") { /* Read-only RAM DISKs don't make any sense. */ ro_q.setAnswer("false"); } else { /* Otherwise, ask. */ ro_q.ask(); } *os << " cdrom = " << cdrom_q.getAnswer() << ";\n"; *os << " read_only = " << ro_q.getAnswer() << ";\n"; /* The user can define a custom model * number for the device. */ FreeTextQuestion ft_q; ft_q.setQuestion("Would you like to set a disk model number?"); ft_q.setExplanation("Leave blank to choose the default."); if (ft_q.ask() != "") *os << " model_number = \"" << ft_q.getAnswer() << "\";\n"; /* The user can define a custom revision * number for the device. */ ft_q.setQuestion("Would you like to set a revision number?"); if (ft_q.ask() != "") *os << " rev_number = \"" << ft_q.getAnswer() << "\";\n"; /* The user can define a custom serial * number for the device. */ ft_q.setQuestion("Would you like to set a serial number?"); if (ft_q.ask() != "") *os << " serial_number = \"" << ft_q.getAnswer() << "\";\n"; *os << " }\n\n"; } } /** * Entry point for configuration. **/ int main_cfg(int argc, char *argv[]) { /* Explanation */ printf("We are now going to set up an initial configuration file for the " "Emulator.\n"); printf("This file will be saved as es40.cfg in the current directory.\n\n"); printf( "For more detailed information to the current question, answer '?'.\n"); /* Open es40.cfg for writing. */ filebuf fb; fb.open("es40.cfg", ios::out); ostream os(&fb); /* **************************** * * GUI Choice * * **************************** */ MultipleChoiceQuestion gui_q; gui_q.setQuestion("Do you want to have a GUI?"); gui_q.setExplanation( "You need a GUI if you want to use an emulated graphics card. You don't " "need this for most OS'es. If you don't need this, we recomment that you " "answer 'none' to this question."); gui_q.setDefault("none"); gui_q.addAnswer("none", "", "No GUI. Graphics cards are not supported."); #if defined(HAVE_SDL) gui_q.addAnswer("SDL", "sdl", "Simple Directmedia Layer. Preferred GUI mechanism."); #endif #if defined(HAVE_X11) gui_q.addAnswer("X11", "X11", "Unix X-Windows GUI support."); #endif #if defined(_WIN32) gui_q.addAnswer("win32", "win32", "Windows 32 GUI support."); #endif if (gui_q.countAnswers() == 1) { /* The only valid answer is "none". */ cout << "\nSorry, the GUI is not available! (no SDL, win32 or X11 support " "found).\n"; gui_q.setAnswer(""); } else { /* Ask what GUI to use? */ gui_q.ask(); } if (gui_q.getAnswer() != "") { /* Yes, we have a GUI. */ os << "gui = " << gui_q.getAnswer() << "\n"; os << "{\n"; os << "}\n\n"; } /* **************************** * * Memory Size * * **************************** */ MultipleChoiceQuestion mem_q; mem_q.setQuestion("How much RAM memory do you want to emulate?"); mem_q.setExplanation("Your system should have enough free memory to emulate " "the amount you choose here."); mem_q.setDefault("256M"); /* Add memory sizes from 32 MB to 8 GB * (25 to 34 bits). */ for (int i = 25; i < 34; i++) { string a; int j = i; if (i < 30) { /* Megabyte-range. */ j -= 20; a = "M"; } else { /* Gigabyte range. */ j -= 30; a = "G"; } mem_q.addAnswer(i2s(1 << j) + a, i2s(i), i2s(1 << j) + a + "Bytes of memory."); } os << "sys0 = tsunami\n"; os << "{\n"; os << " memory.bits = " << mem_q.ask() << ";\n"; /* **************************** * * ROM Files * * **************************** */ FreeTextQuestion rom_q; rom_q.setQuestion("Where can the SRM ROM image be found?"); rom_q.setExplanation("This file is required."); #if defined(_WIN32) rom_q.setDefault("rom\\cl67srmrom.exe"); #elif defined(__VMS) rom_q.setDefault("[.ROM]CL67SRMROM.EXE"); #else rom_q.setDefault("rom/cl67srmrom.exe"); #endif os << " rom.srm = \"" << rom_q.ask() << "\";\n"; rom_q.setQuestion("Where should the decompressed ROM image be saved?"); rom_q.setExplanation( "This file will be created the first time the emulator runs."); #if defined(_WIN32) rom_q.setDefault("rom\\decompressed.rom"); #elif defined(__VMS) rom_q.setDefault("[.ROM]DECOMPRESSED.ROM"); #else rom_q.setDefault("rom/decompressed.rom"); #endif os << " rom.decompressed = \"" << rom_q.ask() << "\";\n"; rom_q.setQuestion("Where should the Flash ROM image be saved?"); #if defined(_WIN32) rom_q.setDefault("rom\\flash.rom"); #elif defined(__VMS) rom_q.setDefault("[.ROM]FLASH.ROM"); #else rom_q.setDefault("rom/flash.rom"); #endif os << " rom.flash = \"" << rom_q.ask() << "\";\n"; rom_q.setQuestion("Where should the DPR EEPROM image be saved?"); #if defined(_WIN32) rom_q.setDefault("rom\\dpr.rom"); #elif defined(__VMS) rom_q.setDefault("[.ROM]DPR.ROM"); #else rom_q.setDefault("rom/dpr.rom"); #endif os << " rom.dpr = \"" << rom_q.ask() << "\";\n\n"; /* **************************** * * CPU's * * **************************** */ NumberQuestion cpu_q; cpu_q.setQuestion("How many CPU's do you want in the system?"); cpu_q.setRange(1, 4); cpu_q.setDefault("1"); cpu_q.setExplanation( "The normal value for the number of CPU's is 1. More CPU's are very " "experimental, and currently doesn't work."); cpu_q.ask(); MultipleChoiceQuestion icache_q; icache_q.setQuestion("Do you want the ICACHE on the CPU's enabled?"); icache_q.setExplanation( "The ICACHE makes the CPU emulation more accurate, but also slows down " "the emulator. Decent operating systems shouldn't depend on this."); icache_q.setDefault("no"); icache_q.addAnswer("yes", "true", "ICACHE enabled. Performance hit, but may be necessary " "for some software."); icache_q.addAnswer( "no", "false", "ICACHE disabled. Better performance, but may not always work."); icache_q.ask(); MultipleChoiceQuestion skip_memtest_hack_q; skip_memtest_hack_q.setQuestion("Do you want to skip memtest on SRM start?"); skip_memtest_hack_q.setExplanation( "This makes startup significantly faster, but may not work with some " "versions of the firmware."); skip_memtest_hack_q.setDefault("no"); skip_memtest_hack_q.addAnswer( "yes", "true", "Skip memtest hack enabled. CPU detects the instruction pointer where " "the memtest starts and skips it."); skip_memtest_hack_q.addAnswer("no", "false", "Skip memtest hack disabled. Firmware checks " "all available memory on startup."); skip_memtest_hack_q.ask(); NumberQuestion mhz_q; mhz_q.setQuestion("What should the reported speed of the CPU's be in MHz?"); mhz_q.setExplanation("This only changes the CPU speed reported to the OS; " "not the speed of the emulation."); mhz_q.setRange(10, 1250); mhz_q.setDefault("800"); mhz_q.ask(); for (int i = 0; i < cpu_q.getNum(); i++) { /* Repeat the CPU configuration for each * CPU. Differing CPU specs are not supported. */ os << " cpu" << i << " = ev68cb\n"; os << " {\n"; os << " speed = " << mhz_q.getAnswer() << "M;\n"; os << " icache = " << icache_q.getAnswer() << ";\n"; os << " skip_memtest_hack = " << skip_memtest_hack_q.getAnswer() << ";\n"; os << " }\n\n"; } /* **************************** * * Serial Ports * * **************************** */ /* There are two serial ports (0 and 1). */ for (int i = 0; i < 2; i++) { NumberQuestion port_q; port_q.setQuestion("What telnet port should serial " + i2s(i) + " listen?"); port_q.setRange(1, 65535); /* The default ports are 21264 and 21265. */ port_q.setDefault(i2s(21264 + i)); port_q.setExplanation("You will telnet to this port to establish a " "connection with emulated serial port " + i2s(i) + "."); port_q.ask(); FreeTextQuestion exec_q; exec_q.setQuestion( "What program should be started automatically for serial " + i2s(i) + "?"); #if defined(_WIN32) /* On windows, default to * C:\Program Files\Putty\Putty.exe */ exec_q.setDefault("C:\\Program Files\\Putty\\Putty.exe"); #else /* On other OS'es, default to * putty */ exec_q.setDefault("putty"); #endif exec_q.setExplanation("Enter the path to a program to start this to create " "an automatic connection with the serial port. Set " "to 'none' to establish the connection manually."); exec_q.ask(); FreeTextQuestion arg_q; /* If none was answered, we don't need to * ask for arguments. */ if (exec_q.getAnswer() != "none") { arg_q.setQuestion("What arguments should the program use to connect to " "the serial port?"); arg_q.setExplanation("Enter the arguments the program needs."); /* This is the argument format for PuTTy. */ arg_q.setDefault("telnet://localhost:" + port_q.getAnswer()); arg_q.ask(); } os << " serial" << i << " = serial\n"; os << " {\n"; os << " port = " << port_q.getAnswer() << ";\n"; if (exec_q.getAnswer() != "none") { #if defined(_WIN32) /* Quote the program path/name in "", * as it may contain spaces. */ os << " action = \"\"\"" << exec_q.getAnswer() << "\"\" " << arg_q.getAnswer() << "\";\n"; #else os << " action = \"" << exec_q.getAnswer() << " " << arg_q.getAnswer() << "\";\n"; #endif } os << " }\n\n"; } /* **************************** * * Floppy Disks * * **************************** */ MultipleChoiceQuestion fdc_q; fdc_q.setQuestion("Do you want a floppy controller in your system?"); fdc_q.setExplanation( "You need a floppy controller if you want to add floppy drives."); fdc_q.setDefault("no"); fdc_q.addAnswer("yes", "fdc", "FDC present."); fdc_q.addAnswer("no", "", "FDC not present."); if (fdc_q.ask() != "") { /* Use a ShrinkingChoiceQuestion; once * a disk position has been used, it * can't be used again. */ ShrinkingChoiceQuestion fd_q; fd_q.setQuestion("Do you want to add any disks to the Floppy controller?"); fd_q.setDefault("none"); fd_q.setExplanation("Here, you can add floppy drives to your system."); fd_q.addAnswer("none", "", "stop adding disks"); fd_q.addAnswer("0", "disk0.0", "A:"); fd_q.addAnswer("1", "disk0.1", "B:"); os << " fdc0 = floppy\n"; os << " {\n"; /* Ask what disks to add. */ add_disks(&fd_q, &os); os << " }\n\n"; } /* **************************** * * ALi IDE Disks * * **************************** */ /* Use a ShrinkingChoiceQuestion; once * a disk position has been used, it * can't be used again. */ ShrinkingChoiceQuestion ide_q; ide_q.setQuestion("Do you want to add any disks to the IDE controller?"); ide_q.setDefault("none"); ide_q.setExplanation("The IDE controller is mandatory. You can skip this, " "and set up a SCSI controller, too."); ide_q.addAnswer("none", "", "stop adding disks"); ide_q.addAnswer("0.0", "disk0.0", "primary master"); ide_q.addAnswer("0.1", "disk0.1", "primary slave"); ide_q.addAnswer("1.0", "disk1.0", "secondary master"); ide_q.addAnswer("1.1", "disk1.1", "secondary slave"); os << " pci0.15 = ali_ide\n"; os << " {\n"; /* Ask what disks to add. */ add_disks(&ide_q, &os); os << " }\n\n"; /* **************************** * * VGA Card * * **************************** */ /* Use a ShrinkingChoiceQuestion. Once a * card is using a specific PCI slot, it * can't be used by another card. */ ShrinkingChoiceQuestion pci_q; /* Only add the PCI bus 0 slots, as the * VGA card is only supported on hose 0. */ for (int i = 1; i < 5; i++) { pci_q.addAnswer("0." + i2s(i), "pci0." + i2s(i), "Bus 0, Slot " + i2s(i)); } pci_q.setExplanation("Only free PCI slots are listed."); MultipleChoiceQuestion vga_q; if (gui_q.getAnswer() != "") { vga_q.setQuestion( "What (if any) VGA card do you wish to add to the system?"); vga_q.setExplanation( "Functionality of the different cards is pretty much the same; some " "OS'es seem to have a preference, though."); vga_q.setDefault("Cirrus"); vga_q.addAnswer("none", "", "No graphics card"); vga_q.addAnswer("Cirrus", "cirrus", "Cirrus CL-GD something"); vga_q.addAnswer("S3", "s3", "S3 Trio 64"); #if defined(HAVE_RADEON) /* Radeon support is optional, and currently * unreleased, because the specs are only * available under an NDA with AMD. Once AMD * has publicly released the Radeon 7500 (RV200) * specs, the emulated Radeon card will be * released. */ vga_q.addAnswer("Radeon", "radeon", "Radeon 7500"); #endif vga_q.ask(); } if (vga_q.getAnswer() != "") { pci_q.setQuestion("What PCI slot should the " + vga_q.getAnswer() + " card be on?"); pci_q.setDefault("0.1"); rom_q.setQuestion("Where can the VGA BIOS ROM image be found?"); rom_q.setExplanation("This file is required."); #if defined(_WIN32) rom_q.setDefault("rom\\vgabios-0.6a.bin"); #elif defined(__VMS) rom_q.setDefault("[.ROM]VGABIOS_0_6A.BIN"); #else rom_q.setDefault("rom/vgabios-0.6a.bin"); #endif os << " " << pci_q.ask() << " = " << vga_q.getAnswer() << "\n"; os << " {\n"; os << " rom = \"" << rom_q.ask() << "\";\n"; os << " }\n\n"; } /* **************************** * * Free Form PCI Cards * * **************************** */ /* Add the PCI bus 1 slots. All non-VGA * PCI cards can be on either hose 0 or * hose 1. */ for (int i = 1; i < 7; i++) { pci_q.addAnswer("1." + i2s(i), "pci1." + i2s(i), "Bus 1, Slot " + i2s(i)); } MultipleChoiceQuestion card_q; card_q.setQuestion("Would you like to add another PCI card to the system?"); card_q.setDefault("none"); card_q.setExplanation("Choose what PCI card you'd like to add. Choose none " "if you have no more cards to add."); card_q.addAnswer("none", "", "No more cards to add"); #if defined(HAVE_PCAP) card_q.addAnswer("nic", "dec21143", "DEC 21143 Network Interface (1 max)"); #endif card_q.addAnswer("scsi", "sym53c810", "Symbios 53C810 narrow SCSI controller"); card_q.addAnswer( "wide scsi", "sym53c895", "Symbios 53C895 wide SCSI controller (doesn't work with OpenVMS)"); /* Loop until there are no more PCI * cards to add. */ for (;;) { /* If there are no more free PCI slots, * stop adding PCI cards. */ if (pci_q.countAnswers() == 0) break; /* Default to the first available free * PCI slot. */ pci_q.setDefault(pci_q.getFirstChoice()); /* If this answer has been answered with * "none", stop adding PCI cards. */ if (card_q.ask() == "") break; /* Determine where to put this card. */ pci_q.setQuestion("In what PCI slot would you like to put the " + card_q.getAnswer() + " card?"); os << " " << pci_q.ask() << " = " << card_q.getAnswer() << "\n"; os << " {\n"; if (card_q.getAnswer() == "dec21143") { /* Due to limitations in our network * emulation, only one NIC is allowed. * Remove it from the list of choices. */ card_q.dropChoice("nic"); #if defined(HAVE_PCAP) MultipleChoiceQuestion if_q; if_q.setQuestion("What host network interface should we connect to " "(answer ? for a list)?"); if_q.setExplanation("Choose 'list' to get a list at run-time."); if_q.addAnswer("list", "", "Get a list at run-time"); /* Get a list of network interfaces and * add them to the list. */ pcap_if_t *alldevs; pcap_if_t *d; char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&alldevs, errbuf) == -1) { /* No devices to add. */ printf("Error in pcap_findalldevs_ex: %s", errbuf); } else { int i = 1; for (d = alldevs; d; d = d->next) { // if_q.addAnswer(i2s(i),d->name, string(d->name) + "(" + // string(d->description) + ")"); if_q.addAnswer(i2s(i), d->name, d->name); i++; } } if (if_q.ask() != "") os << " adapter = \"" << if_q.getAnswer() << "\";\n"; FreeTextQuestion mac_q; mac_q.setQuestion("What should the NIC's MAC address be?"); mac_q.setExplanation("This should be unique on your network."); mac_q.setDefault("08-00-2B-E5-40-00"); os << " mac = \"" << mac_q.ask() << "\";\n"; #endif } else if (card_q.getAnswer() == "sym53c810") { /* Use a ShrinkingChoiceQuestion; once * a disk position has been used, it * can't be used again. */ ShrinkingChoiceQuestion disk_q; disk_q.setQuestion( "Do you want to add any disks to the Sym53C810 controller?"); disk_q.setDefault("none"); disk_q.setExplanation( "Add disks. Select 'none' if you have no more disks to add."); disk_q.addAnswer("none", "", "stop adding disks"); /* The narrow SCSI controller supports * devices at targets 0..6. */ for (int i = 0; i < 7; i++) { disk_q.addAnswer(i2s(i), "disk0." + i2s(i), "Target " + i2s(i)); } /* Ask what disks to add. */ add_disks(&disk_q, &os); } else if (card_q.getAnswer() == "sym53c895") { /* Use a ShrinkingChoiceQuestion; once * a disk position has been used, it * can't be used again. */ ShrinkingChoiceQuestion disk_q; disk_q.setQuestion( "Do you want to add any disks to the Sym53C895 controller?"); disk_q.setDefault("none"); disk_q.setExplanation( "Add disks. Select 'none' if you have no more disks to add."); disk_q.addAnswer("none", "", "stop adding disks"); /* The wide SCSI controller supports * devices at targets 0..6 and 8..15. */ for (int i = 0; i < 16; i++) { if (i != 7) disk_q.addAnswer(i2s(i), "disk0." + i2s(i), "Target " + i2s(i)); } /* Ask what disks to add. */ add_disks(&disk_q, &os); } os << " }\n\n"; } MultipleChoiceQuestion mouse_q; mouse_q.setQuestion("Would you like to emulate the mouse?"); mouse_q.setExplanation("The mouse is not really working yet... :-("); mouse_q.addAnswer("no", "false", "Disable the mouse"); mouse_q.addAnswer("yes", "true", "Enable the mouse"); mouse_q.setDefault("no"); MultipleChoiceQuestion vgacons_q; vgacons_q.setQuestion("Where would you like console output to go?"); vgacons_q.setExplanation("This is the SRM console option"); vgacons_q.addAnswer("serial", "false", "Console on serial port 0"); vgacons_q.addAnswer("graphics", "true", "Console on graphics controller"); vgacons_q.setDefault("graphics"); if (vga_q.getAnswer() != "") { /* If a VGA card is present, ask about * the mouse and the console. */ mouse_q.ask(); vgacons_q.ask(); } else { /* No VGA card present, mouse support * is disabled, and the console goes * to serial port 0. */ mouse_q.setAnswer("false"); vgacons_q.setAnswer("false"); } FreeTextQuestion lpt_q; lpt_q.setQuestion("Where would you like printer output to go?"); lpt_q.setExplanation("Output from the printer port will be saved to this " "file. Leave blank if not wanted."); lpt_q.ask(); os << " pci0.7 = ali\n"; os << " {\n"; os << " mouse.enabled = " << mouse_q.getAnswer() << ";\n"; os << " vga_console = " << vgacons_q.getAnswer() << ";\n"; if (lpt_q.getAnswer() != "") os << " lpt.outfile = \"" << lpt_q.getAnswer() << "\"\n"; os << " }\n\n"; /* The USB device is a fixed part, and * currently not configurable. */ os << " pci0.19 = ali_usb\n"; os << " {\n"; os << " }\n"; os << "}\n"; /* Close es40.cfg. */ fb.close(); /* All is well. */ return 0; } ================================================ FILE: src/es40_debug.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven This file is based upon GXemul. * * Copyright (C) 2004-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /***************************************************************************** * * NOTE: debug(), fatal(), and debug_indentation() are not re-entrant. * The global variable quiet_mode can be used to suppress the output * of debug(), but not the output of fatal(). * *****************************************************************************/ #include "StdAfx.hpp" // int verbose = 0; int quiet_mode = 0; static int debug_indent = 0; static int debug_currently_at_start_of_line = 1; /* * va_debug(): * * Used internally by debug() and fatal(). */ static void va_debug(va_list argp, char *fmt) { char buf[DEBUG_BUFSIZE + 1]; char *s; int i; buf[0] = buf[DEBUG_BUFSIZE] = 0; // vsnprintf(buf, DEBUG_BUFSIZE, fmt, argp); sprintf(buf, fmt, argp); s = buf; while (*s) { if (debug_currently_at_start_of_line) { for (i = 0; i < debug_indent; i++) printf(" "); } printf("%c", *s); debug_currently_at_start_of_line = 0; if (*s == '\n' || *s == '\r') debug_currently_at_start_of_line = 1; s++; } } /* * debug_indentation(): * * Modify the debug indentation. */ void debug_indentation(int diff) { debug_indent += diff; if (debug_indent < 0) fprintf(stderr, "WARNING: debug_indent less than 0!\n"); } /* * debug(): * * Debug output (ignored if quiet_mode is set). */ void debug(char *fmt, ...) { va_list argp; if (quiet_mode) return; va_start(argp, fmt); va_debug(argp, fmt); va_end(argp); } /* * fatal(): * * Fatal works like debug(), but doesn't care about the quiet_mode * setting. */ void fatal(char *fmt, ...) { va_list argp; va_start(argp, fmt); va_debug(argp, fmt); va_end(argp); } ================================================ FILE: src/es40_debug.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. * * Parts of this file based upon GXemul, which is Copyright (C) 2004-2007 * Anders Gavare. All rights reserved. */ #include #if !defined(INCLUDED_DEBUG_H) #define INCLUDED_DEBUG_H #define DEBUG_BUFSIZE 1024 #define FAILMSG_BUFSIZE 8000 #define DEBUG_INDENTATION 4 #ifdef HAVE___FUNCTION__ #define FAILURE(cls, error_msg) \ { \ char where_msg[FAILMSG_BUFSIZE];; \ sprintf(where_msg, "%s, line %i, function '%s'", __FILE__, __LINE__, \ __FUNCTION__); \ throw C##cls##Exception(error_msg, where_msg); \ } #else #define FAILURE(cls, error_msg) \ { \ char where_msg[FAILMSG_BUFSIZE]; \ sprintf(where_msg, "%s, line %i", __FILE__, __LINE__); \ throw C##cls##Exception(error_msg, where_msg); \ } #endif /* !HAVE___FUNCTION__ */ #define FAILURE_1(cls, error_msg, a) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a); \ FAILURE(cls, what_msg); \ } #define FAILURE_2(cls, error_msg, a, b) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a, b); \ FAILURE(cls, what_msg); \ } #define FAILURE_3(cls, error_msg, a, b, c) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a, b, c); \ FAILURE(cls, what_msg); \ } #define FAILURE_4(cls, error_msg, a, b, c, d) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a, b, c, d); \ FAILURE(cls, what_msg); \ } #define FAILURE_5(cls, error_msg, a, b, c, d, e) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a, b, c, d, e); \ FAILURE(cls, what_msg); \ } #define FAILURE_6(cls, error_msg, a, b, c, d, e, f) \ { \ char what_msg[FAILMSG_BUFSIZE]; \ sprintf(what_msg, error_msg, a, b, c, d, e, f); \ FAILURE(cls, what_msg); \ } #define CHECK_ALLOCATION(ptr) \ { \ if ((ptr) == NULL) \ FAILURE(OutOfMemory, "Out of memory"); \ } #define CHECK_REALLOCATION(dst, src, type) \ { \ type *rea_x; \ rea_x = (type *)src; \ if ((rea_x) == NULL) { \ FAILURE(OutOfMemory, "Out of memory"); \ } else { \ dst = rea_x; \ } \ } void debug_indentation(int diff); void debug(char *fmt, ...); void fatal(char *fmt, ...); #endif ================================================ FILE: src/es40_endian.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_ENDIAN_H) #define INCLUDED_ENDIAN_H #if !defined(ES40_LITTLE_ENDIAN) && !defined(ES40_BIG_ENDIAN) #if defined(_WIN32) || defined(__VMS) #define ES40_LITTLE_ENDIAN #else // defined (_WIN32) || defined(__VMS) #include #if !defined(__BYTE_ORDER) && defined(BYTE_ORDER) #define __BYTE_ORDER BYTE_ORDER #if !defined(__BIG_ENDIAN) && defined(BIG_ENDIAN) #define __BIG_ENDIAN BIG_ENDIAN #endif #if !defined(__LITTLE_ENDIAN) && defined(LITTLE_ENDIAN) #define __LITTLE_ENDIAN LITTLE_ENDIAN #endif #endif #if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(sparc) #define ES40_BIG_ENDIAN #else // assume little endian #define ES40_LITTLE_ENDIAN #endif #endif // defined (_WIN32) || defined(__VMS) #endif // !defined(ES40_LITTLE_ENDIAN) && !defined(ES40_BIG_ENDIAN) #define swap_64(x) \ ((((x)&U64(0x00000000000000ff)) << 56) | \ (((x)&U64(0x000000000000ff00)) << 40) | \ (((x)&U64(0x0000000000ff0000)) << 24) | \ (((x)&U64(0x00000000ff000000)) << 8) | \ (((x)&U64(0x000000ff00000000)) >> 8) | \ (((x)&U64(0x0000ff0000000000)) >> 24) | \ (((x)&U64(0x00ff000000000000)) >> 40) | \ (((x)&U64(0xff00000000000000)) >> 56)) #define swap_32(x) \ ((((x)&0x000000ff) << 24) | (((x)&0x0000ff00) << 8) | \ (((x)&0x00ff0000) >> 8) | (((x)&0xff000000) >> 24)) #define swap_16(x) ((((x)&0x00ff) << 8) | (((x)&0xff00) >> 8)) #define swap_8(x) ((x)&0xff) #if defined(ES40_BIG_ENDIAN) #define endian_64(x) swap_64(x) #define endian_32(x) swap_32(x) #define endian_16(x) swap_16(x) #define endian_8(x) swap_8(x) #else // defined(ES40_BIG_ENDIAN) #define endian_64(x) (x) #define endian_32(x) ((x)&0xffffffff) #define endian_16(x) ((x)&0xffff) #define endian_8(x) ((x)&0xff) #endif // defined(ES40_BIG_ENDIAN) inline u64 endian_bits(u64 x, int numbits) { switch (numbits) { case 64: return endian_64(x); case 32: return endian_32(x); case 16: return endian_16(x); case 8: return endian_8(x); default: FAILURE(InvalidArgument, "Weird numbits in endian_bits"); } } #endif // INCLUDED_ENDIAN_H ================================================ FILE: src/es40_float.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include //#define DEBUG_FP_CONVERSION 1 //#define DEBUG_FP_LOADSTORE 1 #define FLOAT_IS_IEEE 1 /** * VAX (G or F) floating point to host conversion. * Converts the register-form of F and G foating point values to a double. **/ inline double f2host(u64 val) { int s = (val & U64(0x8000000000000000)) ? 1 : 0; int e = (int)((val & U64(0x7ff0000000000000)) >> 52); s64 f = (val & U64(0x000fffffffffffff)); f |= U64(0x0010000000000000); double res; if (e == 0) res = 0.0; else res = (s ? -1.0 : 1.0) * pow((double)2.0, e - 1024) * ((double)f / (double)(s64)U64(0x0020000000000000)); #if defined(DEBUG_FP_CONVERSION) printf("f/g->host: %016" PRIx64 " -> %f \n", val, res); #endif return res; } #define g2host f2host /** * VAX (D) floating point to host conversion. * Converts the register-form of D foating point values to a double. **/ inline double d2host(u64 val) { int s = (val & U64(0x8000000000000000)) ? 1 : 0; int e = (int)((val & U64(0x7f80000000000000)) >> 55); s64 f = (val & U64(0x007fffffffffffff)); f |= U64(0x0080000000000000); double res; if (e == 0) res = 0.0; else res = (s ? -1.0 : 1.0) * pow((double)2.0, e - 128) * ((double)f / (double)(s64)U64(0x0100000000000000)); #if defined(DEBUG_FP_CONVERSION) printf("d->host: %016" PRIx64 " -> %f \n", val, res); #endif return res; } /** * IEEE (S or T) floating point to host conversion. * Converts the register-form of S and T foating point values to a double. **/ inline double s2host(u64 val) { double res; #if defined(FLOAT_IS_IEEE) if (sizeof(double) == 8) { union s2h_conv { u64 a; double b; } f_ieee; f_ieee.a = val; res = f_ieee.b; } else #endif { int s = (val & U64(0x8000000000000000)) ? 1 : 0; int e = (int)((val & U64(0x7ff0000000000000)) >> 52); s64 f = (val & U64(0x000fffffffffffff)); if (e == 2047) { if (f) res = (s ? -0.0 : 0.0) / 0.0; // NaN else res = (s ? -1.0 : 1.0) / 0.0; // +/- Inf } else if (e == 0) { if (f) res = (s ? -1.0 : 1.0) * ldexp((double)f / (double)((s64)U64(0x10000000000000)), -1022); else res = (s ? -1.0 : 1.0) * 0.0; } else { res = (s ? -1.0 : 1.0) * ldexp(1.0 + ((double)f / (double)((s64)U64(0x0010000000000000))), e - 1023); } } #if defined(DEBUG_FP_CONVERSION) printf("s/t->host: %016" PRIx64 " -> %f \n", val, res); #endif return res; } #define t2host s2host /** * Check IEEE floating point value for NaN. **/ inline bool i_isnan(u64 val) { int e = (int)((val & U64(0x7ff0000000000000)) >> 52); s64 f = (val & U64(0x000fffffffffffff)); return (e == 2047) && f; } /** * Host to VAX F floating point conversion. * Converts a double to the register-form of F foating point values. **/ inline u64 host2f(double val) { double fr; double v = val; int s = (v < 0.0) ? 1 : 0; if (s) v *= -1.0; int e = (int)(log((double)v) / log((double)2.0)); bool exp_down = true; if (val == 0.0) return 0; fr = v / pow((double)2.0, e); while ((fr >= 1.0 && e < 127) || e < -127) { e++; exp_down = false; fr = v / pow((double)2.0, e); } while (((fr < 0.5 && e > -127) || e > 127) && exp_down) { e--; fr = v / pow((double)2.0, e); } e += 1024; u64 f = (u64)(fr * (double)U64(0x0020000000000000) + 0.5); f = (s ? U64(0x8000000000000000) : 0) | (((u64)e << 52) & U64(0x7ff0000000000000)) | (f & U64(0x000fffffe0000000)); #if defined(DEBUG_FP_CONVERSION) printf("host->f: %f -> %016" PRIx64 " \n", val, f); #endif return f; } /** * Host to VAX G floating point conversion. * Converts a double to the register-form of G foating point values. **/ inline u64 host2g(double val) { double fr; double v = val; int s = (v < 0.0) ? 1 : 0; if (s) v *= -1.0; int e = (int)(log((double)v) / log((double)2.0)); bool exp_down = true; if (val == 0.0) return 0; fr = v / pow((double)2.0, e); while ((fr >= 1.0 && e < 1023) || e < -1023) { e++; exp_down = false; fr = v / pow((double)2.0, e); } while (((fr < 0.5 && e > -1023) || e > 1023) && exp_down) { e--; fr = v / pow((double)2.0, e); } e += 1024; u64 f = (u64)(fr * (double)U64(0x0020000000000000) + 0.5); f = (s ? U64(0x8000000000000000) : 0) | (((u64)e << 52) & U64(0x7ff0000000000000)) | (f & U64(0x000fffffffffffff)); #if defined(DEBUG_FP_CONVERSION) printf("host->g: %f -> %016" PRIx64 " \n", val, f); #endif return f; } /** * Host to VAX D floating point conversion. * Converts a double to the register-form of D foating point values. **/ inline u64 host2d(double val) { double fr; double v = val; int s = (v < 0.0) ? 1 : 0; if (s) v *= -1.0; int e = (int)(log((double)v) / log((double)2.0)); bool exp_down = true; if (val == 0.0) return 0; fr = v / pow((double)2.0, e); while ((fr >= 1.0 && e < 127) || e < -127) { e++; exp_down = false; fr = v / pow((double)2.0, e); } while (((fr < 0.5 && e > -127) || e > 127) && exp_down) { e--; fr = v / pow((double)2.0, e); } e += 128; u64 f = (u64)(fr * (double)U64(0x0100000000000000) + 0.5); f = (s ? U64(0x8000000000000000) : 0) | (((u64)e << 55) & U64(0x7f80000000000000)) | (f & U64(0x007fffffffffffff)); #if defined(DEBUG_FP_CONVERSION) printf("host->d: %f -> %016" PRIx64 " \n", val, f); #endif return f; } /** * Map an 8-bit IEEE (S) exponent to an 11-bit IEEE (T) exponent. **/ inline u32 map_s(u32 val) { if (val == 0) return 0; else if (val == 0xff) return 0x7ff; else if (val & 0x80) return (val & 0x7f) | 0x400; else return (val & 0x7f) | 0x380; } /** * Host to 32-bit IEEE (S) floating point conversion. * Converts a double to the register-form of S foating point values. **/ inline u64 host2s(double val) { u64 f; int s; int e; #if defined(FLOAT_IS_IEEE) if (sizeof(float) == 4) { union h2s_conv { u32 a; float b; } f_ieee; f_ieee.b = (float)val; s = (f_ieee.a >> 31) & 1; e = (f_ieee.a >> 23) & 0xff; f = (u64)(f_ieee.a >> 0 & 0x7fffff) << 29; } else #endif { double v = val; s = (v < 0.0) ? 1 : 0; if (s) v *= -1.0; e = (int)(log((double)v) / log((double)2.0)); double fr; bool exp_down = true; if (val == 0.0) return 0; fr = v / pow((double)2.0, e); while ((fr >= 2.0 && e < 127) || e < -127) { e++; exp_down = false; fr = v / pow((double)2.0, e); } while (((fr < 1.0 && e > -127) || e > 127) && exp_down) { e--; fr = v / pow((double)2.0, e); } e += 255; if (e == 0) fr = v / pow((double)2.0, -126); f = (u64)(fr * (double)U64(0x0010000000000000) + 0.5); } e = map_s(e); f = (s ? U64(0x800000000000000) : 0) | (((u64)e << 52) & U64(0x7ff0000000000000)) | (f & U64(0x000fffffe0000000)); #if defined(DEBUG_FP_CONVERSION) printf("host->s: %f -> %016" PRIx64 " \n", val, f); #endif return f; } /** * Host to 64-bit IEEE (T) floating point conversion. * Converts a double to the register-form of T foating point values. **/ inline u64 host2t(double val) { u64 f; #if defined(FLOAT_IS_IEEE) if (sizeof(double) == 8) { union h2t_conv { u64 a; double b; } f_ieee; f_ieee.b = val; f = f_ieee.a; } else #endif { double v = val; int s = (v < 0.0) ? 1 : 0; if (s) v *= -1.0; int e = (int)(log((double)v) / log((double)2.0)); double fr; bool exp_down = true; if (val == 0.0) return 0; fr = v / pow((double)2.0, e); while ((fr >= 2.0 && e < 1023) || e < -1023) { e++; exp_down = false; fr = v / pow((double)2.0, e); } while (((fr < 1.0 && e > -1023) || e > 1023) && exp_down) { e--; fr = v / pow((double)2.0, e); } e += 1023; if (e == 0) fr = v / pow((double)2.0, -1022); f = (u64)(fr * (double)U64(0x0010000000000000) + 0.5); f = (s ? U64(0x800000000000000) : 0) | (((u64)e << 52) & U64(0x7ff0000000000000)) | (f & U64(0x000fffffffffffff)); } #if defined(DEBUG_FP_CONVERSION) printf("host->t: %f -> %016" PRIx64 " \n", val, f); #endif return f; } /** * Perform the VAX-byte ordering swap + the SEF mapping necessary to store * 32-bit VAX (F) floating point values to memory. **/ inline u32 store_f(u64 val) { u64 retval = (val & U64(0x00001fffe0000000)) >> 13; /* frac.lo : 29..44 --> 16..31 */ retval |= (val & U64(0xc000000000000000)) >> 48; /* exp.hi + sign : 62..63 --> 14..15 */ retval |= (val & U64(0x07ffe00000000000)) >> 45; /* frac.hi + exp.lo : 45..58 --> 0..13 */ #if defined(DEBUG_FP_LOADSTORE) printf("f->mem: %016" PRIx64 " -> %08x \n", val, retval); #endif return (u32)retval; } /** * Perform the VAX-byte ordering swap necessary to store 64-bit VAX (G) * floating point values to memory. **/ inline u64 store_g(u64 val) { u64 retval = (val >> 48) & U64(0x000000000000ffff); retval |= (val >> 16) & U64(0x00000000ffff0000); retval |= (val << 48) & U64(0xffff000000000000); retval |= (val << 16) & U64(0x0000ffff00000000); #if defined(DEBUG_FP_LOADSTORE) printf("g->mem: %016" PRIx64 " -> %016" PRIx64 " \n", val, retval); #endif return retval; } /** * Perform the VAX-byte ordering swap + the SEF mapping necessary to load * 32-bit VAX (F) floating point values from memory. **/ inline u64 load_f(u32 val) { u64 retval = (u64)(val & 0xffff0000) << 13; /* frac.lo : 16..31 --> 29..44 */ retval |= (u64)(val & 0x0000c000) << 48; /* exp.hi + sign : 14..15 --> 62..63 */ retval |= (u64)(val & 0x00003fff) << 45; /* frac.hi + exp.lo : 0..13 --> 45..58 */ if (((val & 0x00004000) == 0) && ((val & 0x00003f80) != 0)) retval |= U64(0x3800000000000000); /* exp.mid */ #if defined(DEBUG_FP_LOADSTORE) printf("mem->f: %08x -> %016" PRIx64 " \n", val, retval); #endif return retval; } /** * Perform the SEF mapping necessary to load * 32-bit VAX (F) floating point values from an integer register. **/ inline u64 itof_f(u64 val) { u64 retval = (val & U64(0x3fffffff)) << 29; /* frac + exp.lo : 0..29 --> 29..58 */ retval |= (val & U64(0xc0000000)) << 32; /* exp.hi + sign : 30..31 --> 62..63 */ if (((val & U64(0x40000000)) == 0) && ((val & U64(0x3f800000)) != 0)) retval |= U64(0x3800000000000000); /* exp.mid */ #if defined(DEBUG_FP_LOADSTORE) printf("reg->f: %08x -> %016" PRIx64 " \n", val, retval); #endif return retval; } /** * Perform the VAX-byte ordering swap necessary to load 64-bit VAX (G) * floating point values from memory. **/ inline u64 load_g(u64 val) { u64 retval = (val & U64(0x000000000000ffff)) << 48; retval |= (val & U64(0x00000000ffff0000)) << 16; retval |= (val & U64(0x0000ffff00000000)) >> 16; retval |= (val & U64(0xffff000000000000)) >> 48; #if defined(DEBUG_FP_LOADSTORE) printf("mem->g: %016" PRIx64 " -> %016" PRIx64 " \n", val, retval); #endif return retval; } /** * Perform the the SEF mapping necessary to load 32-bit IEEE (S) * floating point values from memory. **/ inline u64 load_s(u32 val) { return ((val & U64(0x80000000)) << 32) // sign | ((u64)map_s((val >> 23) & 0xff) << 52) // exp | ((val & U64(0x7fffff)) << 29); } /** * Perform the the SEF mapping necessary to store 32-bit IEEE (S) * floating point values to memory. **/ inline u32 store_s(u64 val) { return ((u32)(val >> 32) & 0xc0000000) | ((u32)(val >> 29) & 0x3fffffff); } ================================================ FILE: src/gui/gui.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#define DEBUG_LOCKS //#define NO_LOCK_TIMEOUTS #include "../StdAfx.hpp" #include #include "gui.hpp" bx_gui_c *bx_gui = NULL; #define BX_KEY_UNKNOWN 0x7fffffff #define N_USER_KEYS 36 typedef struct { const char *key; u32 symbol; } user_key_t; static user_key_t user_keys[N_USER_KEYS] = { {"f1", BX_KEY_F1}, {"f2", BX_KEY_F2}, {"f3", BX_KEY_F3}, {"f4", BX_KEY_F4}, {"f5", BX_KEY_F5}, {"f6", BX_KEY_F6}, {"f7", BX_KEY_F7}, {"f8", BX_KEY_F8}, {"f9", BX_KEY_F9}, {"f10", BX_KEY_F10}, {"f11", BX_KEY_F11}, {"f12", BX_KEY_F12}, {"alt", BX_KEY_ALT_L}, {"bksl", BX_KEY_BACKSLASH}, {"bksp", BX_KEY_BACKSPACE}, {"ctrl", BX_KEY_CTRL_L}, {"del", BX_KEY_DELETE}, {"down", BX_KEY_DOWN}, {"end", BX_KEY_END}, {"enter", BX_KEY_ENTER}, {"esc", BX_KEY_ESC}, {"home", BX_KEY_HOME}, {"ins", BX_KEY_INSERT}, {"left", BX_KEY_LEFT}, {"menu", BX_KEY_MENU}, {"minus", BX_KEY_MINUS}, {"pgdwn", BX_KEY_PAGE_DOWN}, {"pgup", BX_KEY_PAGE_UP}, {"plus", BX_KEY_KP_ADD}, {"right", BX_KEY_RIGHT}, {"shift", BX_KEY_SHIFT_L}, {"space", BX_KEY_SPACE}, {"tab", BX_KEY_TAB}, {"up", BX_KEY_UP}, {"win", BX_KEY_WIN_L}, {"print", BX_KEY_PRINT}}; bx_gui_c::bx_gui_c(void) { framebuffer = NULL; guiMutex = new CMutex("gui-lock"); } bx_gui_c::~bx_gui_c() { if (framebuffer != NULL) { delete[] framebuffer; } } void bx_gui_c::init(unsigned tilewidth, unsigned tileheight) { new_gfx_api = 0; host_xres = 640; host_yres = 480; host_bpp = 8; specific_init(tilewidth, tileheight); charmap_updated = 0; if (!new_gfx_api && (framebuffer == NULL)) { framebuffer = new u8[BX_MAX_XRES * BX_MAX_YRES * 4]; } } void bx_gui_c::cleanup(void) {} u32 get_user_key(char *key) { int i = 0; while (i < N_USER_KEYS) { if (!strcmp(key, user_keys[i].key)) return user_keys[i].symbol; i++; } return BX_KEY_UNKNOWN; } void bx_gui_c::mouse_enabled_changed(bool val) { // This is only called when SIM->get_init_done is 1. Note that VAL // is the new value of mouse_enabled, which may not match the old // value which is still in SIM->get_param_bool(BXPN_MOUSE_ENABLED)->get(). bx_gui->mouse_enabled_changed_specific(val); } void bx_gui_c::init_signal_handlers() { #if BX_GUI_SIGHANDLER if (bx_gui_sighandler) { u32 mask = bx_gui->get_sighandler_mask(); for (u32 sig = 0; sig < 32; sig++) { if (mask & (1 << sig)) signal(sig, bx_signal_handler); } } #endif } void bx_gui_c::set_text_charmap(u8 *fbuffer) { memcpy(&bx_gui->vga_charmap, fbuffer, 0x2000); for (unsigned i = 0; i < 256; i++) bx_gui->char_changed[i] = 1; bx_gui->charmap_updated = 1; } void bx_gui_c::set_text_charbyte(u16 address, u8 data) { bx_gui->vga_charmap[address] = data; bx_gui->char_changed[address >> 5] = 1; bx_gui->charmap_updated = 1; } void bx_gui_c::beep_on(float frequency) { BX_INFO(("GUI Beep ON (frequency=%.2f)", frequency)); } void bx_gui_c::beep_off() { BX_INFO(("GUI Beep OFF")); } void bx_gui_c::get_capabilities(u16 *xres, u16 *yres, u16 *bpp) { *xres = 1024; *yres = 768; *bpp = 32; } bx_svga_tileinfo_t *bx_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info) { if (!info) { info = (bx_svga_tileinfo_t *)malloc(sizeof(bx_svga_tileinfo_t)); if (!info) { return NULL; } } host_pitch = host_xres * ((host_bpp + 1) >> 3); info->bpp = host_bpp; info->pitch = host_pitch; switch (info->bpp) { case 15: info->red_shift = 15; info->green_shift = 10; info->blue_shift = 5; info->red_mask = 0x7c00; info->green_mask = 0x03e0; info->blue_mask = 0x001f; break; case 16: info->red_shift = 16; info->green_shift = 11; info->blue_shift = 5; info->red_mask = 0xf800; info->green_mask = 0x07e0; info->blue_mask = 0x001f; break; case 24: case 32: info->red_shift = 24; info->green_shift = 16; info->blue_shift = 8; info->red_mask = 0xff0000; info->green_mask = 0x00ff00; info->blue_mask = 0x0000ff; break; } info->is_indexed = (host_bpp == 8); #ifdef BX_LITTLE_ENDIAN info->is_little_endian = 1; #else info->is_little_endian = 0; #endif return info; } u8 *bx_gui_c::graphics_tile_get(unsigned x0, unsigned y0, unsigned *w, unsigned *h) { if (x0 + X_TILESIZE > host_xres) { *w = host_xres - x0; } else { *w = X_TILESIZE; } if (y0 + Y_TILESIZE > host_yres) { *h = host_yres - y0; } else { *h = Y_TILESIZE; } return (u8 *)framebuffer + y0 * host_pitch + x0 * ((host_bpp + 1) >> 3); } void bx_gui_c::graphics_tile_update_in_place(unsigned x0, unsigned y0, unsigned w, unsigned h) { u8 tile[X_TILESIZE * Y_TILESIZE * 4]; u8 *tile_ptr; u8 *fb_ptr; u16 xc; u16 yc; u16 fb_pitch; u16 tile_pitch; u8 r; u8 diffx; u8 diffy; diffx = (x0 % X_TILESIZE); diffy = (y0 % Y_TILESIZE); if (diffx > 0) { x0 -= diffx; w += diffx; } if (diffy > 0) { y0 -= diffy; h += diffy; } fb_pitch = host_pitch; tile_pitch = X_TILESIZE * ((host_bpp + 1) >> 3); for (yc = y0; yc < (y0 + h); yc += Y_TILESIZE) { for (xc = x0; xc < (x0 + w); xc += X_TILESIZE) { fb_ptr = framebuffer + (yc * fb_pitch + xc * ((host_bpp + 1) >> 3)); tile_ptr = &tile[0]; for (r = 0; r < h; r++) { memcpy(tile_ptr, fb_ptr, tile_pitch); fb_ptr += fb_pitch; tile_ptr += tile_pitch; } graphics_tile_update(tile, xc, yc); } } } void bx_gui_c::lock() { MUTEX_LOCK(guiMutex); } void bx_gui_c::unlock() { MUTEX_UNLOCK(guiMutex); } ================================================ FILE: src/gui/gui.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __GUI_H__ #define __GUI_H__ #define BX_DEBUG(a) \ { \ printf a; \ printf(" \n"); \ } #define BX_INFO(a) BX_DEBUG(a) #define BX_PANIC(a) BX_DEBUG(a) #define BX_ERROR(a) BX_DEBUG(a) #include "vga.hpp" /// VGA mode information for GUI typedef struct { u16 start_address; u8 cs_start; u8 cs_end; u16 line_offset; u16 line_compare; u8 h_panning; u8 v_panning; bool line_graphics; bool split_hpanning; } bx_vga_tminfo_t; /// VGA tile information for GUI typedef struct { u16 bpp, pitch; u8 red_shift, green_shift, blue_shift; u8 is_indexed, is_little_endian; unsigned long red_mask, green_mask, blue_mask; } bx_svga_tileinfo_t; extern class bx_gui_c *bx_gui; /** * \brief Abstract base class for GUI implementations. **/ class bx_gui_c { public: bx_gui_c(void); virtual ~bx_gui_c(); virtual void specific_init(unsigned x_tilesize, unsigned y_tilesize) = 0; virtual void text_update(u8 *old_text, u8 *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned rows) = 0; virtual void graphics_tile_update(u8 *snapshot, unsigned x, unsigned y) = 0; virtual bx_svga_tileinfo_t *graphics_tile_info(bx_svga_tileinfo_t *info); virtual u8 *graphics_tile_get(unsigned x, unsigned y, unsigned *w, unsigned *h); virtual void graphics_tile_update_in_place(unsigned x, unsigned y, unsigned w, unsigned h); virtual void handle_events(void) = 0; virtual void flush(void) = 0; virtual void clear_screen(void) = 0; virtual bool palette_change(unsigned index, unsigned red, unsigned green, unsigned blue) = 0; virtual void dimension_update(unsigned x, unsigned y, unsigned fheight = 0, unsigned fwidth = 0, unsigned bpp = 8) = 0; virtual void mouse_enabled_changed_specific(bool val) = 0; virtual void exit(void) = 0; virtual u32 get_sighandler_mask() { return 0; } virtual void sighandler(int sig) {} virtual void beep_on(float frequency); virtual void beep_off(); virtual void get_capabilities(u16 *xres, u16 *yres, u16 *bpp); static void key_event(u32 key); static void set_text_charmap(u8 *fbuffer); static void set_text_charbyte(u16 address, u8 data); void init(unsigned x_tilesize, unsigned y_tilesize); void cleanup(void); static void mouse_enabled_changed(bool val); static void init_signal_handlers(); void lock(); void unlock(); protected: CMutex *guiMutex; static s32 make_text_snapshot(char **snapshot, u32 *length); // static void toggle_mouse_enable(void); unsigned char vga_charmap[0x2000]; bool charmap_updated; bool char_changed[256]; bool new_gfx_api; u16 host_xres; u16 host_yres; u16 host_pitch; u8 host_bpp; u8 *framebuffer; }; #define BX_KEY_PRESSED 0x00000000 #define BX_KEY_RELEASED 0x80000000 #define BX_KEY_UNHANDLED 0x10000000 #define BX_KEY_CTRL_L 0 #define BX_KEY_SHIFT_L 1 #define BX_KEY_F1 2 #define BX_KEY_F2 3 #define BX_KEY_F3 4 #define BX_KEY_F4 5 #define BX_KEY_F5 6 #define BX_KEY_F6 7 #define BX_KEY_F7 8 #define BX_KEY_F8 9 #define BX_KEY_F9 10 #define BX_KEY_F10 11 #define BX_KEY_F11 12 #define BX_KEY_F12 13 #define BX_KEY_CTRL_R 14 #define BX_KEY_SHIFT_R 15 #define BX_KEY_CAPS_LOCK 16 #define BX_KEY_NUM_LOCK 17 #define BX_KEY_ALT_L 18 #define BX_KEY_ALT_R 19 #define BX_KEY_A 20 #define BX_KEY_B 21 #define BX_KEY_C 22 #define BX_KEY_D 23 #define BX_KEY_E 24 #define BX_KEY_F 25 #define BX_KEY_G 26 #define BX_KEY_H 27 #define BX_KEY_I 28 #define BX_KEY_J 29 #define BX_KEY_K 30 #define BX_KEY_L 31 #define BX_KEY_M 32 #define BX_KEY_N 33 #define BX_KEY_O 34 #define BX_KEY_P 35 #define BX_KEY_Q 36 #define BX_KEY_R 37 #define BX_KEY_S 38 #define BX_KEY_T 39 #define BX_KEY_U 40 #define BX_KEY_V 41 #define BX_KEY_W 42 #define BX_KEY_X 43 #define BX_KEY_Y 44 #define BX_KEY_Z 45 #define BX_KEY_0 46 #define BX_KEY_1 47 #define BX_KEY_2 48 #define BX_KEY_3 49 #define BX_KEY_4 50 #define BX_KEY_5 51 #define BX_KEY_6 52 #define BX_KEY_7 53 #define BX_KEY_8 54 #define BX_KEY_9 55 #define BX_KEY_ESC 56 #define BX_KEY_SPACE 57 #define BX_KEY_SINGLE_QUOTE 58 #define BX_KEY_COMMA 59 #define BX_KEY_PERIOD 60 #define BX_KEY_SLASH 61 #define BX_KEY_SEMICOLON 62 #define BX_KEY_EQUALS 63 #define BX_KEY_LEFT_BRACKET 64 #define BX_KEY_BACKSLASH 65 #define BX_KEY_RIGHT_BRACKET 66 #define BX_KEY_MINUS 67 #define BX_KEY_GRAVE 68 #define BX_KEY_BACKSPACE 69 #define BX_KEY_ENTER 70 #define BX_KEY_TAB 71 #define BX_KEY_LEFT_BACKSLASH 72 #define BX_KEY_PRINT 73 #define BX_KEY_SCRL_LOCK 74 #define BX_KEY_PAUSE 75 #define BX_KEY_INSERT 76 #define BX_KEY_DELETE 77 #define BX_KEY_HOME 78 #define BX_KEY_END 79 #define BX_KEY_PAGE_UP 80 #define BX_KEY_PAGE_DOWN 81 #define BX_KEY_KP_ADD 82 #define BX_KEY_KP_SUBTRACT 83 #define BX_KEY_KP_END 84 #define BX_KEY_KP_DOWN 85 #define BX_KEY_KP_PAGE_DOWN 86 #define BX_KEY_KP_LEFT 87 #define BX_KEY_KP_RIGHT 88 #define BX_KEY_KP_HOME 89 #define BX_KEY_KP_UP 90 #define BX_KEY_KP_PAGE_UP 91 #define BX_KEY_KP_INSERT 92 #define BX_KEY_KP_DELETE 93 #define BX_KEY_KP_5 94 #define BX_KEY_UP 95 #define BX_KEY_DOWN 96 #define BX_KEY_LEFT 97 #define BX_KEY_RIGHT 98 #define BX_KEY_KP_ENTER 99 #define BX_KEY_KP_MULTIPLY 100 #define BX_KEY_KP_DIVIDE 101 #define BX_KEY_WIN_L 102 #define BX_KEY_WIN_R 103 #define BX_KEY_MENU 104 #define BX_KEY_ALT_SYSREQ 105 #define BX_KEY_CTRL_BREAK 106 #define BX_KEY_INT_BACK 107 #define BX_KEY_INT_FORWARD 108 #define BX_KEY_INT_STOP 109 #define BX_KEY_INT_MAIL 110 #define BX_KEY_INT_SEARCH 111 #define BX_KEY_INT_FAV 112 #define BX_KEY_INT_HOME 113 #define BX_KEY_POWER_MYCOMP 114 #define BX_KEY_POWER_CALC 115 #define BX_KEY_POWER_SLEEP 116 #define BX_KEY_POWER_POWER 117 #define BX_KEY_POWER_WAKE 118 #define BX_KEY_NBKEYS 119 // If you add BX_KEYs Please update // - BX_KEY_NBKEYS // - the scancodes table in the file iodev/scancodes.cc // - the bx_key_symbol table in the file gui/keymap.cc /////////////// GUI plugin support // Define macro to supply gui plugin code. This macro is called once in GUI to // supply the plugin initialization methods. Since it is nearly identical for // each gui module, the macro is easier to maintain than pasting the same code // in each one. // // Each gui should declare a class pointer called "theGui" which is derived // from bx_gui_c, before calling this macro. For example, the SDL port // says: // static bx_sdl_gui_c *theGui; #define IMPLEMENT_GUI_PLUGIN_CODE(gui_name) \ int lib##gui_name##_LTX_plugin_init(CConfigurator *cfg) { \ printf("%%GUI-I-INS: Installing %s module as the ES40 GUI\n", #gui_name); \ theGui = new bx_##gui_name##_gui_c(cfg); \ bx_gui = theGui; \ return (0); /* Success */ \ } \ \ void lib##gui_name##_LTX_plugin_fini(void) {} #endif ================================================ FILE: src/gui/gui_win32_font.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ typedef struct { unsigned char data[16]; } bx_fontcharbitmap_t; static const bx_fontcharbitmap_t bx_vgafont[256] = { {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xa5, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x36, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0x00, 0x00, 0x78, 0x60, 0x70, 0x58, 0x1e, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0xfc, 0xcc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0xfe, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xe7, 0x67, 0x03, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7f, 0x7c, 0x78, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0xfe, 0xdb, 0xdb, 0xdb, 0xde, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x3e, 0x63, 0x06, 0x1c, 0x36, 0x63, 0x63, 0x36, 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x7f, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x7f, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x36, 0x7f, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x3e, 0x3e, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x3e, 0x3e, 0x1c, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x18, 0x3e, 0x63, 0x43, 0x03, 0x3e, 0x60, 0x60, 0x61, 0x63, 0x3e, 0x18, 0x18, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, 0x18, 0x0c, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x6e, 0x3b, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x0c, 0x0c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x6b, 0x6b, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x1c, 0x1e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x30, 0x38, 0x3c, 0x36, 0x33, 0x7f, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x03, 0x03, 0x03, 0x3f, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x06, 0x03, 0x03, 0x3f, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x63, 0x60, 0x60, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x60, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x30, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x7b, 0x7b, 0x7b, 0x3b, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1f, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x7b, 0x63, 0x63, 0x66, 0x5c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x67, 0x66, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x77, 0x7f, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7b, 0x3e, 0x30, 0x70, 0x00, 0x00}}, {{0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x36, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3e, 0x63, 0x63, 0x06, 0x1c, 0x30, 0x60, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x6b, 0x6b, 0x7f, 0x77, 0x36, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x63, 0x36, 0x3e, 0x1c, 0x1c, 0x3e, 0x36, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x63, 0x61, 0x30, 0x18, 0x0c, 0x06, 0x43, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x08, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00}}, {{0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x07, 0x06, 0x06, 0x1e, 0x36, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x03, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x38, 0x30, 0x30, 0x3c, 0x36, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x33, 0x1e, 0x00}}, {{0x00, 0x00, 0x07, 0x06, 0x06, 0x36, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00}}, {{0x00, 0x00, 0x07, 0x06, 0x06, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x7f, 0x6b, 0x6b, 0x6b, 0x6b, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x30, 0x78, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x6e, 0x66, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x06, 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x08, 0x0c, 0x0c, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6b, 0x6b, 0x6b, 0x7f, 0x36, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1c, 0x1c, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1f, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x33, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x30, 0x60, 0x3e, 0x00, 0x00}}, {{0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x30, 0x18, 0x0c, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x08, 0x1c, 0x36, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x33, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x06, 0x0c, 0x18, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1c, 0x36, 0x1c, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x30, 0x60, 0x3c, 0x00, 0x00, 0x00}}, {{0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x18, 0x3c, 0x66, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x06, 0x0c, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x63, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x1c, 0x36, 0x1c, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x0c, 0x06, 0x00, 0x7f, 0x66, 0x06, 0x3e, 0x06, 0x06, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x6e, 0x6c, 0x7e, 0x1b, 0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7c, 0x36, 0x33, 0x33, 0x7f, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x0c, 0x1e, 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x06, 0x0c, 0x18, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1e, 0x00}}, {{0x00, 0x63, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x18, 0x18, 0x3c, 0x66, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x67, 0x3f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1f, 0x33, 0x33, 0x1f, 0x23, 0x33, 0x7b, 0x33, 0x33, 0x33, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x70, 0xd8, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x0e, 0x00, 0x00}}, {{0x00, 0x18, 0x0c, 0x06, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x30, 0x18, 0x0c, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x18, 0x0c, 0x06, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x18, 0x0c, 0x06, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x6e, 0x3b, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00}}, {{0x6e, 0x3b, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x3c, 0x36, 0x36, 0x7c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x06, 0x03, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x06, 0x3b, 0x61, 0x30, 0x18, 0x7c, 0x00, 0x00}}, {{0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x66, 0x73, 0x79, 0x7c, 0x60, 0x60, 0x00, 0x00}}, {{0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x36, 0x1b, 0x36, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x36, 0x6c, 0x36, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22}}, {{0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}}, {{0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}}, {{0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x1b, 0x1b, 0x3b, 0x6e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1e, 0x33, 0x33, 0x33, 0x1b, 0x33, 0x63, 0x63, 0x63, 0x33, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x7f, 0x63, 0x63, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x7f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x7f, 0x63, 0x06, 0x0c, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x36, 0x36, 0x36, 0x36, 0x77, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0xc0, 0x60, 0x7e, 0xdb, 0xdb, 0xcf, 0x7e, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x38, 0x0c, 0x06, 0x06, 0x3e, 0x06, 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x70, 0xd8, 0xd8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}}, {{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37, 0x36, 0x36, 0x3c, 0x38, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x1b, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x0e, 0x1b, 0x0c, 0x06, 0x13, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00}}, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, }; ================================================ FILE: src/gui/gui_x11.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define XK_PUBLISHING #define XK_TECHNICAL #include "../StdAfx.hpp" #if defined(HAVE_X11) #include "../Configurator.hpp" #include "../Keyboard.hpp" #include "../VGA.hpp" #include "gui.hpp" #include "keymap.hpp" extern "C" { #include #include #include #include #include } #include "gui_win32_font.hpp" class bx_x11_gui_c : public bx_gui_c { public: bx_x11_gui_c(CConfigurator *cfg) { myCfg = cfg; bx_keymap = new bx_keymap_c(cfg); }; virtual void specific_init(unsigned x_tilesize, unsigned y_tilesize); virtual void text_update(u8 *old_text, u8 *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned rows); virtual void graphics_tile_update(u8 *snapshot, unsigned x, unsigned y); virtual void handle_events(void); virtual void flush(void); virtual void clear_screen(void); virtual bool palette_change(unsigned index, unsigned red, unsigned green, unsigned blue); virtual void dimension_update(unsigned x, unsigned y, unsigned fheight = 0, unsigned fwidth = 0, unsigned bpp = 8); virtual void mouse_enabled_changed_specific(bool val); virtual void exit(void); virtual bx_svga_tileinfo_t *graphics_tile_info(bx_svga_tileinfo_t *info); virtual u8 *graphics_tile_get(unsigned x, unsigned y, unsigned *w, unsigned *h); virtual void graphics_tile_update_in_place(unsigned x, unsigned y, unsigned w, unsigned h); virtual void get_capabilities(u16 *xres, u16 *yres, u16 *bpp); private: CConfigurator *myCfg; }; // declare one instance of the gui object and call macro to insert the // plugin code static bx_x11_gui_c *theGui = NULL; IMPLEMENT_GUI_PLUGIN_CODE(x11) #define MAX_MAPPED_STRING_LENGTH 10 /* These are used as arguments to nearly every Xlib routine, so it saves * routine arguments to declare them global. If there were * additional source files, they would be declared extern there. */ Display *bx_x_display; int bx_x_screen_num; static Visual *default_visual; static Colormap default_cmap; static unsigned long white_pixel = 0, black_pixel = 0; static char *progname; /* name this program was invoked by */ static unsigned int text_rows = 25, text_cols = 80; static u8 h_panning = 0, v_panning = 0; static u16 line_compare = 1023; static Window win; static GC gc, gc_inv; static unsigned font_width, font_height; static unsigned dimension_x = 0, dimension_y = 0; static unsigned vga_bpp = 8; static XImage *ximage = NULL; static unsigned imDepth, imWide, imBPP; // current cursor coordinates static int prev_x = -1, prev_y = -1; static int current_x = -1, current_y = -1, current_z = 0; static unsigned mouse_button_state = 0; static bool CTRL_pressed = 0; static unsigned prev_cursor_x = 0; static unsigned prev_cursor_y = 0; static int warp_home_x = 200; static int warp_home_y = 200; static int mouse_enable_x = 0; static int mouse_enable_y = 0; static int warp_dx = 0; static int warp_dy = 0; static void warp_cursor(int dx, int dy); static void disable_cursor(); static void enable_cursor(); static u32 convertStringToXKeysym(const char *string); static bool x_init_done = false; static Pixmap vgafont[256]; static void send_keyboard_mouse_status(void); static bool x_keymapping; static bool x_private_colormap; u32 ascii_to_key_event[0x5f] = { // !"#$%&' BX_KEY_SPACE, BX_KEY_1, BX_KEY_SINGLE_QUOTE, BX_KEY_3, BX_KEY_4, BX_KEY_5, BX_KEY_7, BX_KEY_SINGLE_QUOTE, // ()*+,-./ BX_KEY_9, BX_KEY_0, BX_KEY_8, BX_KEY_EQUALS, BX_KEY_COMMA, BX_KEY_MINUS, BX_KEY_PERIOD, BX_KEY_SLASH, // 01234567 BX_KEY_0, BX_KEY_1, BX_KEY_2, BX_KEY_3, BX_KEY_4, BX_KEY_5, BX_KEY_6, BX_KEY_7, // 89:;<=>? BX_KEY_8, BX_KEY_9, BX_KEY_SEMICOLON, BX_KEY_SEMICOLON, BX_KEY_COMMA, BX_KEY_EQUALS, BX_KEY_PERIOD, BX_KEY_SLASH, // @ABCDEFG BX_KEY_2, BX_KEY_A, BX_KEY_B, BX_KEY_C, BX_KEY_D, BX_KEY_E, BX_KEY_F, BX_KEY_G, // HIJKLMNO BX_KEY_H, BX_KEY_I, BX_KEY_J, BX_KEY_K, BX_KEY_L, BX_KEY_M, BX_KEY_N, BX_KEY_O, // PQRSTUVW BX_KEY_P, BX_KEY_Q, BX_KEY_R, BX_KEY_S, BX_KEY_T, BX_KEY_U, BX_KEY_V, BX_KEY_W, // XYZ[\]^_ BX_KEY_X, BX_KEY_Y, BX_KEY_Z, BX_KEY_LEFT_BRACKET, BX_KEY_BACKSLASH, BX_KEY_RIGHT_BRACKET, BX_KEY_6, BX_KEY_MINUS, // `abcdefg BX_KEY_GRAVE, BX_KEY_A, BX_KEY_B, BX_KEY_C, BX_KEY_D, BX_KEY_E, BX_KEY_F, BX_KEY_G, // hijklmno BX_KEY_H, BX_KEY_I, BX_KEY_J, BX_KEY_K, BX_KEY_L, BX_KEY_M, BX_KEY_N, BX_KEY_O, // pqrstuvw BX_KEY_P, BX_KEY_Q, BX_KEY_R, BX_KEY_S, BX_KEY_T, BX_KEY_U, BX_KEY_V, BX_KEY_W, // xyz{|}~ BX_KEY_X, BX_KEY_Y, BX_KEY_Z, BX_KEY_LEFT_BRACKET, BX_KEY_BACKSLASH, BX_KEY_RIGHT_BRACKET, BX_KEY_GRAVE}; extern u8 graphics_snapshot[32 * 1024]; static void create_internal_vga_font(void); static void xkeypress(KeySym keysym, int press_release); // extern "C" void select_visual(void); #define ROUNDUP(nbytes, pad) ((((nbytes) + ((pad)-1)) / (pad)) * ((pad) >> 3)) #define MAX_VGA_COLORS 256 unsigned long col_vals[MAX_VGA_COLORS]; // 256 VGA colors unsigned curr_foreground, curr_background; static unsigned x_tilesize, y_tilesize; // BxEvent *x11_notify_callback (void *unused, BxEvent *event); // bxevent_handler old_callback = NULL; // void *old_callback_arg = NULL; // Try to allocate NCOLORS at once in the colormap provided. If it can // be done, return true. If not, return false. (In either case, free // up the color cells so that we don't add to the problem!) This is used // to determine whether Bochs should use a private colormap even when the // user did not specify it. static bool test_alloc_colors(Colormap cmap, u32 n_tries) { XColor color; unsigned long pixel[MAX_VGA_COLORS]; bool pixel_valid[MAX_VGA_COLORS]; u32 n_allocated = 0; u32 i; color.flags = DoRed | DoGreen | DoBlue; for (i = 0; i < n_tries; i++) { // choose weird color values that are unlikely to already be in the // colormap. color.red = ((i + 41) % MAX_VGA_COLORS) << 8; color.green = ((i + 42) % MAX_VGA_COLORS) << 8; color.blue = ((i + 43) % MAX_VGA_COLORS) << 8; pixel_valid[i] = false; if (XAllocColor(bx_x_display, cmap, &color)) { pixel[i] = color.pixel; pixel_valid[i] = true; n_allocated++; } } BX_INFO(("test_alloc_colors: %d colors available out of %d colors tried", n_allocated, n_tries)); // now free them all for (i = 0; i < n_tries; i++) { if (pixel_valid[i]) XFreeColors(bx_x_display, cmap, &pixel[i], 1, 0); } return (n_allocated == n_tries); } void bx_x11_gui_c::specific_init(unsigned tilewidth, unsigned tileheight) { unsigned i; int x; int y; /* window position */ unsigned int border_width = 4; /* four pixels */ const char *window_name = "ES40 Emulator"; const char *icon_name = "ES40"; XSizeHints size_hints; char *display_name = NULL; /* create GC for text and drawing */ unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */ XGCValues values; int default_depth; XEvent report; XSetWindowAttributes win_attr; unsigned long plane_masks_return[1]; XColor color; x_tilesize = tilewidth; y_tilesize = tileheight; /* connect to X server */ if ((bx_x_display = XOpenDisplay(display_name)) == NULL && progname != nullptr) { BX_PANIC(("%s: cannot connect to X server %s", progname, XDisplayName(display_name))); } /* get screen size from display structure macro */ bx_x_screen_num = DefaultScreen(bx_x_display); /* Note that in a real application, x and y would default to 0 * but would be settable from the command line or resource database. */ x = y = 0; // Temporary values so we can create the window font_width = 8; font_height = 16; dimension_x = text_cols * font_width; dimension_y = text_rows * font_height; /* create opaque window */ win = XCreateSimpleWindow(bx_x_display, RootWindow(bx_x_display, bx_x_screen_num), x, y, dimension_x, dimension_y, border_width, BlackPixel(bx_x_display, bx_x_screen_num), BlackPixel(bx_x_display, bx_x_screen_num)); // (attempt to) enable backing store win_attr.save_under = 1; win_attr.backing_store = Always; XChangeWindowAttributes(bx_x_display, win, CWSaveUnder | CWBackingStore, &win_attr); default_depth = DefaultDepth(bx_x_display, bx_x_screen_num); default_visual = DefaultVisual(bx_x_display, bx_x_screen_num); x_private_colormap = myCfg->get_bool_value("private_colormap", true); if (!x_private_colormap) { // if (!SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) { default_cmap = DefaultColormap(bx_x_display, bx_x_screen_num); // try to use default colormap. If not enough colors are available, // then switch to private colormap despite the user setting. There // are too many cases when no colors are available and ES40 Emulator // simply draws everything in black on black. if (!test_alloc_colors(default_cmap, 16)) { printf("xxx: I can't allocate 16 colors\n"); x_private_colormap = true; } col_vals[0] = BlackPixel(bx_x_display, bx_x_screen_num); col_vals[15] = WhitePixel(bx_x_display, bx_x_screen_num); for (i = 1; i < MAX_VGA_COLORS; i++) { if (i == 15) continue; col_vals[i] = col_vals[0]; } } if (x_private_colormap) { // if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) { default_cmap = XCreateColormap(bx_x_display, DefaultRootWindow(bx_x_display), default_visual, AllocNone); if (XAllocColorCells(bx_x_display, default_cmap, False, plane_masks_return, 0, col_vals, MAX_VGA_COLORS) == 0) { BX_PANIC(("XAllocColorCells returns error. Maybe your screen does not " "support a private colormap?")); } win_attr.colormap = default_cmap; XChangeWindowAttributes(bx_x_display, win, CWColormap, &win_attr); color.flags = DoRed | DoGreen | DoBlue; for (i = 0; i < MAX_VGA_COLORS; i++) { color.pixel = i; if (i == 15) { color.red = 0xffff; color.green = 0xffff; color.blue = 0xffff; } else { color.red = 0; color.green = 0; color.blue = 0; } XStoreColor(bx_x_display, default_cmap, &color); } } // convenience variables which hold the black & white color indeces black_pixel = col_vals[0]; white_pixel = col_vals[15]; BX_INFO(("font %u wide x %u high, display depth = %d", (unsigned)font_width, (unsigned)font_height, default_depth)); // select_visual(); /* Set size hints for window manager. The window manager may * override these settings. Note that in a real * application if size or position were set by the user * the flags would be UPosition and USize, and these would * override the window manager's preferences for this window. */ /* x, y, width, and height hints are now taken from * the actual settings of the window when mapped. Note * that PPosition and PSize must be specified anyway. */ size_hints.flags = PPosition | PSize | PMinSize | PMaxSize; size_hints.max_width = size_hints.min_width = dimension_x; size_hints.max_height = size_hints.min_height = dimension_y; { XWMHints wm_hints; XClassHint class_hints; /* format of the window name and icon name * arguments has changed in R4 */ XTextProperty windowName; /* format of the window name and icon name * arguments has changed in R4 */ XTextProperty iconName; /* These calls store window_name and icon_name into * XTextProperty structures and set their other * fields properly. */ if (XStringListToTextProperty((char **)&window_name, 1, &windowName) == 0 && progname != nullptr) { BX_PANIC(("%s: structure allocation for windowName failed.", progname)); } if (XStringListToTextProperty((char **)&icon_name, 1, &iconName) == 0 && progname != nullptr) { BX_PANIC(("%s: structure allocation for iconName failed.", progname)); } wm_hints.initial_state = NormalState; wm_hints.input = True; class_hints.res_name = progname; class_hints.res_class = (char *)"ES40 Emulator"; XSetWMProperties(bx_x_display, win, &windowName, &iconName, NULL /*argv*/, 0 /*argc*/, &size_hints, &wm_hints, &class_hints); XFree(windowName.value); XFree(iconName.value); Atom wm_delete = XInternAtom(bx_x_display, "WM_DELETE_WINDOW", 1); XSetWMProtocols(bx_x_display, win, &wm_delete, 1); } /* Select event types wanted */ XSelectInput(bx_x_display, win, ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask); /* Create default Graphics Context */ gc = XCreateGC(bx_x_display, win, valuemask, &values); gc_inv = XCreateGC(bx_x_display, win, valuemask, &values); XSetState(bx_x_display, gc, white_pixel, black_pixel, GXcopy, AllPlanes); XSetState(bx_x_display, gc_inv, black_pixel, white_pixel, GXinvert, AllPlanes); /* Display window */ XMapWindow(bx_x_display, win); XSync(bx_x_display, /* no discard */ 0); BX_DEBUG(("waiting for MapNotify")); while (1) { XNextEvent(bx_x_display, &report); if (report.type == MapNotify) break; } BX_DEBUG(("MapNotify found.")); // Create the VGA font create_internal_vga_font(); { char *imagedata; ximage = XCreateImage(bx_x_display, default_visual, default_depth, // depth of image (bitplanes) ZPixmap, 0, // offset NULL, // malloc() space after x_tilesize, y_tilesize, // x & y size of image 32, // # bits of padding 0); // bytes_per_line, let X11 calculate if (!ximage) BX_PANIC(("vga: couldn't XCreateImage()")); imDepth = default_depth; imWide = ximage->bytes_per_line; imBPP = ximage->bits_per_pixel; imagedata = (char *)malloc((size_t)(ximage->bytes_per_line * y_tilesize)); if (!imagedata) BX_PANIC(("imagedata: malloc returned error")); ximage->data = imagedata; if (imBPP < imDepth) { BX_PANIC(("vga_x: bits_per_pixel < depth ?")); } x_init_done = true; } curr_background = 0; XSetBackground(bx_x_display, gc, col_vals[curr_background]); curr_foreground = 1; XSetForeground(bx_x_display, gc, col_vals[curr_foreground]); // XGrabPointer( bx_x_display, win, True, 0, GrabModeAsync, GrabModeAsync, // win, None, CurrentTime ); XFlush(bx_x_display); // loads keymap for x11 x_keymapping = myCfg->get_bool_value("keyboard.use_mapping", false); if (x_keymapping) { // if (SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { bx_keymap->loadKeymap(convertStringToXKeysym); } new_gfx_api = 1; } // This is called whenever the mouse_enabled parameter changes. It // can change because of a gui event such as clicking on the mouse-enable // bitmap or pressing the middle button, or from the configuration interface. // In all those cases, setting the parameter value will get you here. void bx_x11_gui_c::mouse_enabled_changed_specific(bool val) { BX_DEBUG(("mouse_enabled=%d, x11 specific code", val ? 1 : 0)); if (val) { BX_INFO(("[x] Mouse on")); mouse_enable_x = current_x; mouse_enable_y = current_y; disable_cursor(); // Move the cursor to a 'safe' place warp_cursor(warp_home_x - current_x, warp_home_y - current_y); } else { BX_INFO(("[x] Mouse off")); enable_cursor(); warp_cursor(mouse_enable_x - current_x, mouse_enable_y - current_y); } } /** * Create a bitmap for VGA font data **/ void create_internal_vga_font(void) { // Default values font_width = 8; font_height = 16; for (int i = 0; i < 256; i++) { vgafont[i] = XCreateBitmapFromData(bx_x_display, win, (const char *)bx_vgafont[i].data, font_width, font_height); if (vgafont[i] == None) BX_PANIC(("Can't create vga font [%d]", i)); } } void bx_x11_gui_c::handle_events(void) { XEvent report; XKeyEvent *key_event; KeySym keysym; XComposeStatus compose; char buffer[MAX_MAPPED_STRING_LENGTH]; int bufsize = MAX_MAPPED_STRING_LENGTH; int charcount; bool mouse_update; int y; int height; XPointerMovedEvent *pointer_event; XEnterWindowEvent *enter_event; XLeaveWindowEvent *leave_event; XButtonEvent *button_event; XExposeEvent *expose_event; // current_x = -1; // current_y = -1; mouse_update = 0; while (XPending(bx_x_display) > 0) { XNextEvent(bx_x_display, &report); current_z = 0; switch (report.type) { case Expose: expose_event = &report.xexpose; /* Adjust y.*/ y = expose_event->y; height = expose_event->height; if (y < 0) { height += y; y = 0; } theVGA->redraw_area((unsigned)expose_event->x, y, (unsigned)expose_event->width, height); break; case ConfigureNotify: BX_DEBUG(("ConfigureNotify Xevent")); break; case ButtonPress: button_event = (XButtonEvent *)&report; BX_DEBUG(("xxx: buttonpress")); current_x = button_event->x; current_y = button_event->y; mouse_update = 1; BX_DEBUG(("xxx: x,y=(%d,%d)", current_x, current_y)); switch (button_event->button) { case Button1: mouse_button_state |= 0x01; send_keyboard_mouse_status(); mouse_update = 0; break; case Button2: if (CTRL_pressed) { // toggle_mouse_enable(); } else { mouse_button_state |= 0x04; send_keyboard_mouse_status(); mouse_update = 0; } break; case Button3: mouse_button_state |= 0x02; send_keyboard_mouse_status(); mouse_update = 0; break; } break; case ButtonRelease: button_event = (XButtonEvent *)&report; current_x = button_event->x; current_y = button_event->y; mouse_update = 1; switch (button_event->button) { case Button1: mouse_button_state &= ~0x01; send_keyboard_mouse_status(); mouse_update = 0; break; case Button2: mouse_button_state &= ~0x04; send_keyboard_mouse_status(); mouse_update = 0; break; case Button3: mouse_button_state &= ~0x02; send_keyboard_mouse_status(); mouse_update = 0; break; case Button4: current_z = 1; send_keyboard_mouse_status(); mouse_update = 0; break; case Button5: current_z = -1; send_keyboard_mouse_status(); mouse_update = 0; break; } break; case KeyPress: key_event = (XKeyEvent *)&report; charcount = XLookupString(key_event, buffer, bufsize, &keysym, &compose); xkeypress(keysym, 0); break; case KeyRelease: key_event = (XKeyEvent *)&report; charcount = XLookupString(key_event, buffer, bufsize, &keysym, &compose); xkeypress(keysym, 1); break; case MotionNotify: pointer_event = (XPointerMovedEvent *)&report; current_x = pointer_event->x; current_y = pointer_event->y; mouse_update = 1; break; case EnterNotify: enter_event = (XEnterWindowEvent *)&report; prev_x = current_x = enter_event->x; prev_y = current_y = enter_event->y; break; case LeaveNotify: leave_event = (XLeaveWindowEvent *)&report; prev_x = current_x = -1; prev_y = current_y = -1; break; case MapNotify: /* screen needs redraw, since X would have tossed previous * requests before window mapped */ // retval = 1; break; case ClientMessage: if (!strcmp(XGetAtomName(bx_x_display, report.xclient.message_type), "WM_PROTOCOLS")) { FAILURE(Graceful, "Emulator stopped from X"); // bx_stop_simulation(); } break; default: // (mch) Ignore... BX_DEBUG(("XXX: default Xevent type")); /* all events selected by StructureNotifyMask are thrown away here, * since nothing is done with them */ break; } /* end switch */ } /* end while */ if (mouse_update) { BX_DEBUG(("handle_events(): send mouse status")); send_keyboard_mouse_status(); } } void send_keyboard_mouse_status(void) { BX_DEBUG( ("XXX: prev=(%d,%d) curr=(%d,%d)", prev_x, prev_y, current_x, current_y)); if (((prev_x != -1) && (current_x != -1) && (prev_y != -1) && (current_y != -1)) || (current_z != 0)) { int dx; int dy; int dz; // (mch) consider warping here dx = current_x - prev_x - warp_dx; dy = -(current_y - prev_y - warp_dy); dz = current_z; warp_cursor(warp_home_x - current_x, warp_home_y - current_y); // DEV_mouse_motion_ext (dx, dy, dz, mouse_button_state); prev_x = current_x; prev_y = current_y; } else { if ((current_x != -1) && (current_y != -1)) { prev_x = current_x; prev_y = current_y; } else { prev_x = current_x = -1; prev_y = current_y = -1; } } } void bx_x11_gui_c::flush(void) { if (bx_x_display) XFlush(bx_x_display); } void xkeypress(KeySym keysym, int press_release) { u32 key_event; if ((keysym == XK_Control_L) || (keysym == XK_Control_R)) { CTRL_pressed = !press_release; } /* Old (no mapping) behavior */ if (!x_keymapping) { // if (!SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { // this depends on the fact that the X11 keysyms which // correspond to the ascii characters space .. tilde // are in consequtive order. if ((keysym >= XK_space) && (keysym <= XK_asciitilde)) { key_event = ascii_to_key_event[keysym - XK_space]; } else { switch (keysym) { case XK_KP_1: #ifdef XK_KP_End case XK_KP_End: #endif key_event = BX_KEY_KP_END; break; case XK_KP_2: #ifdef XK_KP_Down case XK_KP_Down: #endif key_event = BX_KEY_KP_DOWN; break; case XK_KP_3: #ifdef XK_KP_Page_Down case XK_KP_Page_Down: #endif key_event = BX_KEY_KP_PAGE_DOWN; break; case XK_KP_4: #ifdef XK_KP_Left case XK_KP_Left: #endif key_event = BX_KEY_KP_LEFT; break; case XK_KP_5: #ifdef XK_KP_Begin case XK_KP_Begin: #endif key_event = BX_KEY_KP_5; break; case XK_KP_6: #ifdef XK_KP_Right case XK_KP_Right: #endif key_event = BX_KEY_KP_RIGHT; break; case XK_KP_7: #ifdef XK_KP_Home case XK_KP_Home: #endif key_event = BX_KEY_KP_HOME; break; case XK_KP_8: #ifdef XK_KP_Up case XK_KP_Up: #endif key_event = BX_KEY_KP_UP; break; case XK_KP_9: #ifdef XK_KP_Page_Up case XK_KP_Page_Up: #endif key_event = BX_KEY_KP_PAGE_UP; break; case XK_KP_0: #ifdef XK_KP_Insert case XK_KP_Insert: #endif key_event = BX_KEY_KP_INSERT; break; case XK_KP_Decimal: #ifdef XK_KP_Delete case XK_KP_Delete: #endif key_event = BX_KEY_KP_DELETE; break; #ifdef XK_KP_Enter case XK_KP_Enter: key_event = BX_KEY_KP_ENTER; break; #endif case XK_KP_Subtract: key_event = BX_KEY_KP_SUBTRACT; break; case XK_KP_Add: key_event = BX_KEY_KP_ADD; break; case XK_KP_Multiply: key_event = BX_KEY_KP_MULTIPLY; break; case XK_KP_Divide: key_event = BX_KEY_KP_DIVIDE; break; case XK_Up: key_event = BX_KEY_UP; break; case XK_Down: key_event = BX_KEY_DOWN; break; case XK_Left: key_event = BX_KEY_LEFT; break; case XK_Right: key_event = BX_KEY_RIGHT; break; case XK_Delete: key_event = BX_KEY_DELETE; break; case XK_BackSpace: key_event = BX_KEY_BACKSPACE; break; case XK_Tab: key_event = BX_KEY_TAB; break; #ifdef XK_ISO_Left_Tab case XK_ISO_Left_Tab: key_event = BX_KEY_TAB; break; #endif case XK_Return: key_event = BX_KEY_ENTER; break; case XK_Escape: key_event = BX_KEY_ESC; break; case XK_F1: key_event = BX_KEY_F1; break; case XK_F2: key_event = BX_KEY_F2; break; case XK_F3: key_event = BX_KEY_F3; break; case XK_F4: key_event = BX_KEY_F4; break; case XK_F5: key_event = BX_KEY_F5; break; case XK_F6: key_event = BX_KEY_F6; break; case XK_F7: key_event = BX_KEY_F7; break; case XK_F8: key_event = BX_KEY_F8; break; case XK_F9: key_event = BX_KEY_F9; break; case XK_F10: key_event = BX_KEY_F10; break; case XK_F11: key_event = BX_KEY_F11; break; case XK_F12: key_event = BX_KEY_F12; break; case XK_Control_L: key_event = BX_KEY_CTRL_L; break; #ifdef XK_Control_R case XK_Control_R: key_event = BX_KEY_CTRL_R; break; #endif case XK_Shift_L: key_event = BX_KEY_SHIFT_L; break; case XK_Shift_R: key_event = BX_KEY_SHIFT_R; break; case XK_Alt_L: key_event = BX_KEY_ALT_L; break; #ifdef XK_Alt_R case XK_Alt_R: key_event = BX_KEY_ALT_R; break; #endif case XK_Caps_Lock: key_event = BX_KEY_CAPS_LOCK; break; case XK_Num_Lock: key_event = BX_KEY_NUM_LOCK; break; #ifdef XK_Scroll_Lock case XK_Scroll_Lock: key_event = BX_KEY_SCRL_LOCK; break; #endif #ifdef XK_Print case XK_Print: key_event = BX_KEY_PRINT; break; #endif #ifdef XK_Pause case XK_Pause: key_event = BX_KEY_PAUSE; break; #endif case XK_Insert: key_event = BX_KEY_INSERT; break; case XK_Home: key_event = BX_KEY_HOME; break; case XK_End: key_event = BX_KEY_END; break; case XK_Page_Up: key_event = BX_KEY_PAGE_UP; break; case XK_Page_Down: key_event = BX_KEY_PAGE_DOWN; break; default: BX_ERROR(("xkeypress(): keysym %x unhandled!", (unsigned)keysym)); return; break; } } } else { /* use mapping */ BXKeyEntry *entry = bx_keymap->findHostKey(keysym); if (!entry) { BX_ERROR(("xkeypress(): keysym %x unhandled!", (unsigned)keysym)); return; } key_event = entry->baseKey; } if (press_release) key_event |= BX_KEY_RELEASED; theKeyboard->gen_scancode(key_event); } void bx_x11_gui_c::clear_screen(void) { XClearArea(bx_x_display, win, 0, 0, dimension_x, dimension_y, 0); } void bx_x11_gui_c::text_update(u8 *old_text, u8 *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned nrows) { u8 *old_line; u8 *new_line; u8 *text_base; u8 cChar; unsigned int curs; unsigned int hchars; unsigned int i; unsigned int j; unsigned int offset; unsigned int rows; unsigned int x; unsigned int y; unsigned int xc; unsigned int yc; unsigned int yc2; unsigned int cs_y; unsigned new_foreground; unsigned new_background; u8 cfwidth; u8 cfheight; u8 cfheight2; u8 font_col; u8 font_row; u8 font_row2; u8 split_textrow; u8 split_fontrows; bool forceUpdate = 0; bool split_screen; unsigned char cell[64]; unsigned long text_palette[16]; if (charmap_updated) { BX_INFO(("charmap update. Font Height is %d", font_height)); for (unsigned c = 0; c < 256; c++) { if (char_changed[c]) { XFreePixmap(bx_x_display, vgafont[c]); bool gfxchar = tm_info.line_graphics && ((c & 0xE0) == 0xC0); j = 0; memset(cell, 0, sizeof(cell)); for (i = 0; i < font_height * 2; i += 2) { cell[i] |= ((vga_charmap[(c << 5) + j] & 0x01) << 7); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x02) << 5); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x04) << 3); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x08) << 1); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x10) >> 1); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x20) >> 3); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x40) >> 5); cell[i] |= ((vga_charmap[(c << 5) + j] & 0x80) >> 7); if (gfxchar) { cell[i + 1] = (vga_charmap[(c << 5) + j] & 0x01); } j++; } vgafont[c] = XCreateBitmapFromData(bx_x_display, win, (const char *)cell, 9, font_height); if (vgafont[c] == None) BX_PANIC(("Can't create vga font [%d]", c)); char_changed[c] = 0; } } forceUpdate = 1; charmap_updated = 0; } for (i = 0; i < 16; i++) { text_palette[i] = col_vals[theVGA->get_actl_palette_idx(i)]; } if ((tm_info.h_panning != h_panning) || (tm_info.v_panning != v_panning)) { forceUpdate = 1; h_panning = tm_info.h_panning; v_panning = tm_info.v_panning; } if (tm_info.line_compare != line_compare) { forceUpdate = 1; line_compare = tm_info.line_compare; } // first invalidate character at previous and new cursor location if ((prev_cursor_y < text_rows) && (prev_cursor_x < text_cols)) { curs = prev_cursor_y * tm_info.line_offset + prev_cursor_x * 2; old_text[curs] = ~new_text[curs]; } if ((tm_info.cs_start <= tm_info.cs_end) && (tm_info.cs_start < font_height) && (cursor_y < text_rows) && (cursor_x < text_cols)) { curs = cursor_y * tm_info.line_offset + cursor_x * 2; old_text[curs] = ~new_text[curs]; } else { curs = 0xffff; } rows = text_rows; if (v_panning) rows++; y = 0; cs_y = 0; text_base = new_text - tm_info.start_address; split_textrow = (line_compare + v_panning) / font_height; split_fontrows = ((line_compare + v_panning) % font_height) + 1; split_screen = 0; do { hchars = text_cols; if (h_panning) hchars++; if (split_screen) { yc = line_compare + cs_y * font_height + 1; font_row = 0; if (rows == 1) { cfheight = (dimension_y - line_compare - 1) % font_height; if (cfheight == 0) cfheight = font_height; } else { cfheight = font_height; } } else if (v_panning) { if (y == 0) { yc = 0; font_row = v_panning; cfheight = font_height - v_panning; } else { yc = y * font_height - v_panning; font_row = 0; if (rows == 1) { cfheight = v_panning; } else { cfheight = font_height; } } } else { yc = y * font_height; font_row = 0; cfheight = font_height; } if (!split_screen && (y == split_textrow)) { if (split_fontrows < cfheight) cfheight = split_fontrows; } new_line = new_text; old_line = old_text; x = 0; offset = cs_y * tm_info.line_offset; do { if (h_panning) { if (hchars > text_cols) { xc = 0; font_col = h_panning; cfwidth = font_width - h_panning; } else { xc = x * font_width - h_panning; font_col = 0; if (hchars == 1) { cfwidth = h_panning; } else { cfwidth = font_width; } } } else { xc = x * font_width; font_col = 0; cfwidth = font_width; } if (forceUpdate || (old_text[0] != new_text[0]) || (old_text[1] != new_text[1])) { cChar = new_text[0]; new_foreground = new_text[1] & 0x0f; new_background = (new_text[1] & 0xf0) >> 4; XSetForeground(bx_x_display, gc, text_palette[new_foreground]); XSetBackground(bx_x_display, gc, text_palette[new_background]); XCopyPlane(bx_x_display, vgafont[cChar], win, gc, font_col, font_row, cfwidth, cfheight, xc, yc, 1); if (offset == curs) { XSetForeground(bx_x_display, gc, text_palette[new_background]); XSetBackground(bx_x_display, gc, text_palette[new_foreground]); if (font_row == 0) { yc2 = yc + tm_info.cs_start; font_row2 = tm_info.cs_start; cfheight2 = tm_info.cs_end - tm_info.cs_start + 1; if ((yc2 + cfheight2) > (dimension_y)) { cfheight2 = dimension_y - yc2; } } else { if (v_panning > tm_info.cs_start) { yc2 = yc; font_row2 = font_row; cfheight2 = tm_info.cs_end - v_panning + 1; } else { yc2 = yc + tm_info.cs_start - v_panning; font_row2 = tm_info.cs_start; cfheight2 = tm_info.cs_end - tm_info.cs_start + 1; } } if (yc2 < (dimension_y)) { XCopyPlane(bx_x_display, vgafont[cChar], win, gc, font_col, font_row2, cfwidth, cfheight2, xc, yc2, 1); } } } x++; new_text += 2; old_text += 2; offset += 2; } while (--hchars); if (!split_screen && (y == split_textrow)) { new_text = text_base; forceUpdate = 1; cs_y = 0; if (tm_info.split_hpanning) h_panning = 0; rows = ((dimension_y - line_compare + font_height - 2) / font_height) + 1; split_screen = 1; } else { y++; cs_y++; new_text = new_line + tm_info.line_offset; old_text = old_line + tm_info.line_offset; } } while (--rows); h_panning = tm_info.h_panning; prev_cursor_x = cursor_x; prev_cursor_y = cursor_y; XFlush(bx_x_display); } void bx_x11_gui_c::graphics_tile_update(u8 *tile, unsigned x0, unsigned y0) { unsigned x; unsigned y; unsigned y_size; unsigned color; unsigned offset; u8 b0; u8 b1; u8 b2; u8 b3; if ((y0 + y_tilesize) > dimension_y) { y_size = dimension_y - y0; } else { y_size = y_tilesize; } switch (vga_bpp) { case 8: // 8 bits per pixel for (y = 0; y < y_size; y++) { for (x = 0; x < x_tilesize; x++) { color = col_vals[tile[y * x_tilesize + x]]; switch (imBPP) { case 8: // 8 bits per pixel ximage->data[imWide * y + x] = color; break; case 16: // 16 bits per pixel offset = imWide * y + 2 * x; b0 = color >> 0; b1 = color >> 8; if (ximage->byte_order == LSBFirst) { ximage->data[offset + 0] = b0; ximage->data[offset + 1] = b1; } else { // MSBFirst ximage->data[offset + 0] = b1; ximage->data[offset + 1] = b0; } break; case 24: // 24 bits per pixel offset = imWide * y + 3 * x; b0 = color >> 0; b1 = color >> 8; b2 = color >> 16; if (ximage->byte_order == LSBFirst) { ximage->data[offset + 0] = b0; ximage->data[offset + 1] = b1; ximage->data[offset + 2] = b2; } else { // MSBFirst ximage->data[offset + 0] = b2; ximage->data[offset + 1] = b1; ximage->data[offset + 2] = b0; } break; case 32: // 32 bits per pixel offset = imWide * y + 4 * x; b0 = color >> 0; b1 = color >> 8; b2 = color >> 16; b3 = color >> 24; if (ximage->byte_order == LSBFirst) { ximage->data[offset + 0] = b0; ximage->data[offset + 1] = b1; ximage->data[offset + 2] = b2; ximage->data[offset + 3] = b3; } else { // MSBFirst ximage->data[offset + 0] = b3; ximage->data[offset + 1] = b2; ximage->data[offset + 2] = b1; ximage->data[offset + 3] = b0; } break; default: BX_PANIC(("X_graphics_tile_update: bits_per_pixel %u not implemented", (unsigned)imBPP)); return; } } } break; default: BX_PANIC(( "X_graphics_tile_update: bits_per_pixel %u handled by new graphics API", (unsigned)vga_bpp)); return; } XPutImage(bx_x_display, win, gc, ximage, 0, 0, x0, y0, x_tilesize, y_size); } bx_svga_tileinfo_t *bx_x11_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info) { if (!info) { info = (bx_svga_tileinfo_t *)malloc(sizeof(bx_svga_tileinfo_t)); if (!info) { return NULL; } } info->bpp = ximage->bits_per_pixel; info->pitch = ximage->bytes_per_line; info->red_shift = 0; info->green_shift = 0; info->blue_shift = 0; info->red_mask = ximage->red_mask; info->green_mask = ximage->green_mask; info->blue_mask = ximage->blue_mask; int i; int rf; int gf; int bf; unsigned long red; unsigned long green; unsigned long blue; i = rf = gf = bf = 0; red = ximage->red_mask; green = ximage->green_mask; blue = ximage->blue_mask; while (red || rf || green || gf || blue || bf) { if (rf) { if (!(red & 1)) { info->red_shift = i; rf = 0; } } else { if (red & 1) { rf = 1; } } if (gf) { if (!(green & 1)) { info->green_shift = i; gf = 0; } } else { if (green & 1) { gf = 1; } } if (bf) { if (!(blue & 1)) { info->blue_shift = i; bf = 0; } } else { if (blue & 1) { bf = 1; } } i++; red >>= 1; green >>= 1; blue >>= 1; } info->is_indexed = (default_visual->c_class != TrueColor) && (default_visual->c_class != DirectColor); info->is_little_endian = (ximage->byte_order == LSBFirst); return info; } u8 *bx_x11_gui_c::graphics_tile_get(unsigned x0, unsigned y0, unsigned *w, unsigned *h) { if (x0 + x_tilesize > dimension_x) { *w = dimension_x - x0; } else { *w = x_tilesize; } if (y0 + y_tilesize > dimension_y) { *h = dimension_y - y0; } else { *h = y_tilesize; } return (u8 *)ximage->data + ximage->xoffset * ximage->bits_per_pixel / 8; } void bx_x11_gui_c::graphics_tile_update_in_place(unsigned x0, unsigned y0, unsigned w, unsigned h) { XPutImage(bx_x_display, win, gc, ximage, 0, 0, x0, y0, w, h); } bool bx_x11_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue) { // returns: 0=no screen update needed (color map change has direct effect) // 1=screen updated needed (redraw using current colormap) XColor color; color.flags = DoRed | DoGreen | DoBlue; color.red = red << 8; color.green = green << 8; color.blue = blue << 8; if (x_private_colormap) { // if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) { color.pixel = index; XStoreColor(bx_x_display, default_cmap, &color); return (0); // no screen update needed } else { XAllocColor(bx_x_display, DefaultColormap(bx_x_display, bx_x_screen_num), &color); col_vals[index] = color.pixel; return (1); // screen update needed } } void bx_x11_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp) { if ((bpp == 8) || (bpp == 15) || (bpp == 16) || (bpp == 24) || (bpp == 32)) { vga_bpp = bpp; } else { BX_PANIC(("%d bpp graphics mode not supported", bpp)); } if (fheight > 0) { font_height = fheight; font_width = fwidth; text_cols = x / font_width; text_rows = y / font_height; } if ((x != dimension_x) || (y != dimension_y)) { XSizeHints hints; long supplied_return; if (XGetWMNormalHints(bx_x_display, win, &hints, &supplied_return) && supplied_return & PMaxSize) { hints.max_width = hints.min_width = x; hints.max_height = hints.min_height = y; XSetWMNormalHints(bx_x_display, win, &hints); } XResizeWindow(bx_x_display, win, x, y); dimension_x = x; dimension_y = y; } } void bx_x11_gui_c::exit(void) { if (!x_init_done) return; // Delete the font bitmaps for (int i = 0; i < 256; i++) { // if (vgafont[i] != NULL) XFreePixmap(bx_x_display, vgafont[i]); } if (bx_x_display) XCloseDisplay(bx_x_display); BX_INFO(("Exit.")); } static void warp_cursor(int dx, int dy) { if (warp_dx || warp_dy || dx || dy) { warp_dx = dx; warp_dy = dy; XWarpPointer(bx_x_display, None, None, 0, 0, 0, 0, dx, dy); } } static void disable_cursor() { static Cursor cursor; static unsigned cursor_created = 0; static int shape_width = 16; static int shape_height = 16; static int mask_width = 16; static int mask_height = 16; static u32 shape_bits[(16 * 16) / 32] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static u32 mask_bits[(16 * 16) / 32] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; if (!cursor_created) { Pixmap shape; Pixmap mask; XColor white; XColor black; shape = XCreatePixmapFromBitmapData( bx_x_display, RootWindow(bx_x_display, bx_x_screen_num), (char *)shape_bits, shape_width, shape_height, 1, 0, 1); mask = XCreatePixmapFromBitmapData( bx_x_display, RootWindow(bx_x_display, bx_x_screen_num), (char *)mask_bits, mask_width, mask_height, 1, 0, 1); XParseColor(bx_x_display, default_cmap, "black", &black); XParseColor(bx_x_display, default_cmap, "white", &white); cursor = XCreatePixmapCursor(bx_x_display, shape, mask, &white, &black, 1, 1); cursor_created = 1; } XDefineCursor(bx_x_display, win, cursor); } static void enable_cursor() { XUndefineCursor(bx_x_display, win); } /* convertStringToXKeysym is a keymap callback * used when reading the keymap file. * It converts a Symblic String to a GUI Constant * * It returns a u32 constant or BX_KEYMAP_UNKNOWN if it fails */ static u32 convertStringToXKeysym(const char *string) { if (strncmp("XK_", string, 3) != 0) return BX_KEYMAP_UNKNOWN; KeySym keysym = XStringToKeysym(string + 3); // failure, return unknown if (keysym == NoSymbol) return BX_KEYMAP_UNKNOWN; return ((u32)keysym); } void bx_x11_gui_c::get_capabilities(u16 *xres, u16 *yres, u16 *bpp) { *xres = 1024; *yres = 768; *bpp = 32; } #endif /* if BX_WITH_X11 */ ================================================ FILE: src/gui/keymap.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "keymap.hpp" #include "../StdAfx.hpp" #include "../System.hpp" #include "gui.hpp" const char *bx_key_symbol[BX_KEY_NBKEYS] = { "BX_KEY_CTRL_L", "BX_KEY_SHIFT_L", "BX_KEY_F1", "BX_KEY_F2", "BX_KEY_F3", "BX_KEY_F4", "BX_KEY_F5", "BX_KEY_F6", "BX_KEY_F7", "BX_KEY_F8", "BX_KEY_F9", "BX_KEY_F10", "BX_KEY_F11", "BX_KEY_F12", "BX_KEY_CTRL_R", "BX_KEY_SHIFT_R", "BX_KEY_CAPS_LOCK", "BX_KEY_NUM_LOCK", "BX_KEY_ALT_L", "BX_KEY_ALT_R", "BX_KEY_A", "BX_KEY_B", "BX_KEY_C", "BX_KEY_D", "BX_KEY_E", "BX_KEY_F", "BX_KEY_G", "BX_KEY_H", "BX_KEY_I", "BX_KEY_J", "BX_KEY_K", "BX_KEY_L", "BX_KEY_M", "BX_KEY_N", "BX_KEY_O", "BX_KEY_P", "BX_KEY_Q", "BX_KEY_R", "BX_KEY_S", "BX_KEY_T", "BX_KEY_U", "BX_KEY_V", "BX_KEY_W", "BX_KEY_X", "BX_KEY_Y", "BX_KEY_Z", "BX_KEY_0", "BX_KEY_1", "BX_KEY_2", "BX_KEY_3", "BX_KEY_4", "BX_KEY_5", "BX_KEY_6", "BX_KEY_7", "BX_KEY_8", "BX_KEY_9", "BX_KEY_ESC", "BX_KEY_SPACE", "BX_KEY_SINGLE_QUOTE", "BX_KEY_COMMA", "BX_KEY_PERIOD", "BX_KEY_SLASH", "BX_KEY_SEMICOLON", "BX_KEY_EQUALS", "BX_KEY_LEFT_BRACKET", "BX_KEY_BACKSLASH", "BX_KEY_RIGHT_BRACKET", "BX_KEY_MINUS", "BX_KEY_GRAVE", "BX_KEY_BACKSPACE", "BX_KEY_ENTER", "BX_KEY_TAB", "BX_KEY_LEFT_BACKSLASH", "BX_KEY_PRINT", "BX_KEY_SCRL_LOCK", "BX_KEY_PAUSE", "BX_KEY_INSERT", "BX_KEY_DELETE", "BX_KEY_HOME", "BX_KEY_END", "BX_KEY_PAGE_UP", "BX_KEY_PAGE_DOWN", "BX_KEY_KP_ADD", "BX_KEY_KP_SUBTRACT", "BX_KEY_KP_END", "BX_KEY_KP_DOWN", "BX_KEY_KP_PAGE_DOWN", "BX_KEY_KP_LEFT", "BX_KEY_KP_RIGHT", "BX_KEY_KP_HOME", "BX_KEY_KP_UP", "BX_KEY_KP_PAGE_UP", "BX_KEY_KP_INSERT", "BX_KEY_KP_DELETE", "BX_KEY_KP_5", "BX_KEY_UP", "BX_KEY_DOWN", "BX_KEY_LEFT", "BX_KEY_RIGHT", "BX_KEY_KP_ENTER", "BX_KEY_KP_MULTIPLY", "BX_KEY_KP_DIVIDE", "BX_KEY_WIN_L", "BX_KEY_WIN_R", "BX_KEY_MENU", "BX_KEY_ALT_SYSREQ", "BX_KEY_CTRL_BREAK", "BX_KEY_INT_BACK", "BX_KEY_INT_FORWARD", "BX_KEY_INT_STOP", "BX_KEY_INT_MAIL", "BX_KEY_INT_SEARCH", "BX_KEY_INT_FAV", "BX_KEY_INT_HOME", "BX_KEY_POWER_MYCOMP", "BX_KEY_POWER_CALC", "BX_KEY_POWER_SLEEP", "BX_KEY_POWER_POWER", "BX_KEY_POWER_WAKE", }; bx_keymap_c *bx_keymap; bx_keymap_c::bx_keymap_c(CConfigurator *cfg) { keymapCount = 0; keymapTable = (BXKeyEntry *)NULL; myCfg = cfg; } bx_keymap_c::~bx_keymap_c(void) { if (keymapTable != NULL) { free(keymapTable); keymapTable = (BXKeyEntry *)NULL; } keymapCount = 0; } /** * Loads the configuration specified keymap file if keymapping is enabled * using convertStringToSymbol to convert strings to client constants **/ void bx_keymap_c::loadKeymap(u32 stringToSymbol(const char *)) { if (myCfg->get_bool_value("keyboard.use_mapping", false)) loadKeymap(stringToSymbol, myCfg->get_text_value("keyboard.map", "keys.map")); } /** * Returns true if the keymap contains any valid key entries. **/ bool bx_keymap_c::isKeymapLoaded() { return (keymapCount > 0); } /////////////////// // I'll add these to the keymap object in a minute. static unsigned char *lineptr = NULL; static int lineCount; static void init_parse() { lineCount = 0; } static void init_parse_line(char *line_to_parse) { // chop off newline lineptr = (unsigned char *)line_to_parse; char *nl; if ((nl = strchr(line_to_parse, '\n')) != NULL) { *nl = 0; } } static s32 get_next_word(char *output) { char *copyp = output; // find first nonspace while (*lineptr && isspace(*lineptr)) lineptr++; if (!*lineptr) return -1; // nothing but spaces until end of line if (*lineptr == '#') return -1; // nothing but a comment // copy nonspaces into the output while (*lineptr && !isspace(*lineptr)) *copyp++ = *lineptr++; *copyp = 0; // null terminate the copy // there must be at least one nonspace, since that's why we stopped the // first loop! // BX_ASSERT (copyp != output); return 0; } static s32 get_next_keymap_line(FILE *fp, char *bxsym, char *modsym, s32 *ascii, char *hostsym) { char line[256]; char buf[256]; line[0] = 0; while (1) { lineCount++; if (!fgets(line, sizeof(line) - 1, fp)) return -1; // EOF init_parse_line(line); if (get_next_word(bxsym) >= 0) { modsym[0] = 0; char *p; if ((p = strchr(bxsym, '+')) != NULL) { *p = 0; // truncate bxsym. p++; // move one char beyond the + strcpy(modsym, p); // copy the rest to modsym } if (get_next_word(buf) < 0) { BX_PANIC(("keymap line %d: expected 3 columns", lineCount)); return -1; } if (buf[0] == '\'' && buf[2] == '\'' && buf[3] == 0) { *ascii = (u8)buf[1]; } else if (!strcmp(buf, "space")) { *ascii = ' '; } else if (!strcmp(buf, "return")) { *ascii = '\n'; } else if (!strcmp(buf, "tab")) { *ascii = '\t'; } else if (!strcmp(buf, "backslash")) { *ascii = '\\'; } else if (!strcmp(buf, "apostrophe")) { *ascii = '\''; } else if (!strcmp(buf, "none")) { *ascii = -1; } else { BX_PANIC(("keymap line %d: ascii equivalent is \"%s\" but it must be " "char constant like 'x', or one of space,tab,return,none", lineCount, buf)); } if (get_next_word(hostsym) < 0) { BX_PANIC(("keymap line %d: expected 3 columns", lineCount)); return -1; } return 0; } // no words on this line, keep reading. } } /** * Loads the specified keymap file using convertStringToSymbol to convert *strings to client constants. **/ void bx_keymap_c::loadKeymap(u32 stringToSymbol(const char *), const char *filename) { FILE *keymapFile; char baseSym[256]; char modSym[256]; char hostSym[256]; s32 ascii = 0; u32 baseKey; u32 modKey; u32 hostKey; if ((keymapFile = fopen(filename, "r")) == NULL) { BX_PANIC(("Can not open keymap file '%s'.", filename)); return; } BX_INFO(("Loading keymap from '%s'", filename)); init_parse(); // Read keymap file one line at a time while (1) { if (get_next_keymap_line(keymapFile, baseSym, modSym, &ascii, hostSym) < 0) { break; } // convert X_KEY_* symbols to values baseKey = convertStringToBXKey(baseSym); modKey = convertStringToBXKey(modSym); hostKey = 0; if (stringToSymbol != NULL) hostKey = stringToSymbol(hostSym); BX_DEBUG(("baseKey='%s' (%d), modSym='%s' (%d), ascii=%d, guisym='%s' (%d)", baseSym, baseKey, modSym, modKey, ascii, hostSym, hostKey)); // Check if data is valid if (baseKey == BX_KEYMAP_UNKNOWN) { BX_PANIC(("line %d: unknown BX_KEY constant '%s'", lineCount, baseSym)); continue; } if (hostKey == BX_KEYMAP_UNKNOWN) { BX_PANIC(("line %d: unknown host key name '%s' (wrong keymap ?)", lineCount, hostSym)); continue; } CHECK_REALLOCATION( keymapTable, realloc(keymapTable, (keymapCount + 1) * sizeof(BXKeyEntry)), BXKeyEntry); if (keymapTable == NULL) BX_PANIC(("Can not allocate memory for keymap table.")); keymapTable[keymapCount].baseKey = baseKey; keymapTable[keymapCount].modKey = modKey; keymapTable[keymapCount].ascii = ascii; keymapTable[keymapCount].hostKey = hostKey; keymapCount++; } BX_INFO(("Loaded %d symbols", keymapCount)); fclose(keymapFile); } /** * Convert a null-terminate string to a BX_KEY code. **/ u32 bx_keymap_c::convertStringToBXKey(const char *string) { // We look through the bx_key_symbol table to find the searched string for (u16 i = 0; i < BX_KEY_NBKEYS; i++) { if (strcmp(string, bx_key_symbol[i]) == 0) { return i; } } // Key is not known return BX_KEYMAP_UNKNOWN; } /** * Finds an entry whose hostKey value matches the target value, and * returns a pointer to the corresponging BXKeyEntry structure. **/ BXKeyEntry *bx_keymap_c::findHostKey(u32 key) { // We look through the keymap table to find the searched key for (u16 i = 0; i < keymapCount; i++) { if (keymapTable[i].hostKey == key) { BX_DEBUG(("key 0x%02x matches hostKey for entry #%d", key, i)); return &keymapTable[i]; } } BX_DEBUG(("key %02x matches no entries", key)); // Return default return NULL; } /** * Finds an entry whose ASCII character matches the target value, and * returns a pointer to the corresponging BXKeyEntry structure. **/ BXKeyEntry *bx_keymap_c::findAsciiChar(u8 ch) { BX_DEBUG(("findAsciiChar (0x%02x)", ch)); // We look through the keymap table to find the searched key for (u16 i = 0; i < keymapCount; i++) { if (keymapTable[i].ascii == ch) { BX_DEBUG(("key %02x matches ascii for entry #%d", ch, i)); return &keymapTable[i]; } } BX_DEBUG(("key 0x%02x matches no entries", ch)); // Return default return NULL; } const char *bx_keymap_c::getBXKeyName(u32 key) { return bx_key_symbol[key & 0x7fffffff]; } ================================================ FILE: src/gui/keymap.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../Configurator.hpp" // In case of unknown symbol #define BX_KEYMAP_UNKNOWN 0xFFFFFFFF /// Structure of an element of the keymap table typedef struct { u32 baseKey; // base key u32 modKey; // modifier key that must be held down s32 ascii; // ascii equivalent, if any u32 hostKey; // value that the host's OS or library recognizes } BXKeyEntry; /** * \brief Keymap, used to map host keys to scancodes. **/ class bx_keymap_c { public: bx_keymap_c(CConfigurator *cfg); ~bx_keymap_c(void); void loadKeymap(u32 stringToSymbol(const char *)); void loadKeymap(u32 stringToSymbol(const char *), const char *filename); bool isKeymapLoaded(); BXKeyEntry *findHostKey(u32 hostkeynum); BXKeyEntry *findAsciiChar(u8 ascii); const char *getBXKeyName(u32 key); private: u32 convertStringToBXKey(const char *); CConfigurator *myCfg; BXKeyEntry *keymapTable; u16 keymapCount; }; extern bx_keymap_c *bx_keymap; ================================================ FILE: src/gui/plugin.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ ///////////////////////////////////////////////////////////////////////// // // This file provides macros and types needed for plugins. It is based on // the plugin.h file from plex86, but with significant changes to make // it work in Bochs. // Plex86 is Copyright (C) 1999-2000 The plex86 developers team // ///////////////////////////////////////////////////////////////////////// #ifndef __PLUGIN_H #define __PLUGIN_H #define PLUG_load_plugin(cfg, name) \ { lib##name##_LTX_plugin_init(cfg); } #define PLUG_unload_plugin(name) \ { lib##name##_LTX_plugin_fini(); } #define DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(mod) \ int lib##mod##_LTX_plugin_init(CConfigurator *cfg); \ void lib##mod##_LTX_plugin_fini(void); #if defined(HAVE_SDL) DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(sdl) #endif #if defined(_WIN32) DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(win32) #endif #if defined(HAVE_X11) DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(x11) #endif #endif /* __PLUGIN_H */ ================================================ FILE: src/gui/scancodes.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../StdAfx.hpp" //#include "bochs.h" #include "scancodes.hpp" unsigned char translation8042[256] = { 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b, 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d, 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; // Definition of scancodes make and break, // for each set (mf1/xt , mf2/at , mf3/ps2) // The table must be in BX_KEY order // scancode scancodes[BX_KEY_NBKEYS][3] = { { // BX_KEY_CTRL_L ( ibm 58) {"\x1D", "\x9D"}, {"\x14", "\xF0\x14"}, {"\x11", "\xF0\x11"}, }, { // BX_KEY_SHIFT_L ( ibm 44) {"\x2A", "\xAA"}, {"\x12", "\xF0\x12"}, {"\x12", "\xF0\x12"}, }, { // BX_KEY_F1 ( ibm 112 ) {"\x3B", "\xBB"}, {"\x05", "\xF0\x05"}, {"\x07", "\xF0\x07"}, }, { // BX_KEY_F2 ( ibm 113 ) {"\x3C", "\xBC"}, {"\x06", "\xF0\x06"}, {"\x0F", "\xF0\x0F"}, }, { // BX_KEY_F3 ( ibm 114 ) {"\x3D", "\xBD"}, {"\x04", "\xF0\x04"}, {"\x17", "\xF0\x17"}, }, { // BX_KEY_F4 ( ibm 115 ) {"\x3E", "\xBE"}, {"\x0C", "\xF0\x0C"}, {"\x1F", "\xF0\x1F"}, }, { // BX_KEY_F5 ( ibm 116 ) {"\x3F", "\xBF"}, {"\x03", "\xF0\x03"}, {"\x27", "\xF0\x27"}, }, { // BX_KEY_F6 ( ibm 117 ) {"\x40", "\xC0"}, {"\x0B", "\xF0\x0B"}, {"\x2F", "\xF0\x2F"}, }, { // BX_KEY_F7 ( ibm 118 ) {"\x41", "\xC1"}, {"\x83", "\xF0\x83"}, {"\x37", "\xF0\x37"}, }, { // BX_KEY_F8 ( ibm 119 ) {"\x42", "\xC2"}, {"\x0A", "\xF0\x0A"}, {"\x3F", "\xF0\x3F"}, }, { // BX_KEY_F9 ( ibm 120 ) {"\x43", "\xC3"}, {"\x01", "\xF0\x01"}, {"\x47", "\xF0\x47"}, }, { // BX_KEY_F10 ( ibm 121 ) {"\x44", "\xC4"}, {"\x09", "\xF0\x09"}, {"\x4F", "\xF0\x4F"}, }, { // BX_KEY_F11 ( ibm 122 ) {"\x57", "\xD7"}, {"\x78", "\xF0\x78"}, {"\x56", "\xF0\x56"}, }, { // BX_KEY_F12 ( ibm 123 ) {"\x58", "\xD8"}, {"\x07", "\xF0\x07"}, {"\x5E", "\xF0\x5E"}, }, { // BX_KEY_CTRL_R ( ibm 64 ) {"\xE0\x1D", "\xE0\x9D"}, {"\xE0\x14", "\xE0\xF0\x14"}, {"\x58", "\xF0x58"}, }, { // BX_KEY_SHIFT_R ( ibm 57 ) {"\x36", "\xB6"}, {"\x59", "\xF0\x59"}, {"\x59", "\xF0\x59"}, }, { // BX_KEY_CAPS_LOCK ( ibm 30 ) {"\x3A", "\xBA"}, {"\x58", "\xF0\x58"}, {"\x14", "\xF0\x14"}, }, { // BX_KEY_NUM_LOCK ( ibm 90 ) {"\x45", "\xC5"}, {"\x77", "\xF0\x77"}, {"\x76", "\xF0\x76"}, }, { // BX_KEY_ALT_L ( ibm 60 ) {"\x38", "\xB8"}, {"\x11", "\xF0\x11"}, {"\x19", "\xF0\x19"}, }, { // BX_KEY_ALT_R ( ibm 62 ) {"\xE0\x38", "\xE0\xB8"}, {"\xE0\x11", "\xE0\xF0\x11"}, {"\x39", "\xF0\x39"}, }, { // BX_KEY_A ( ibm 31 ) {"\x1E", "\x9E"}, {"\x1C", "\xF0\x1C"}, {"\x1C", "\xF0\x1C"}, }, { // BX_KEY_B ( ibm 50 ) {"\x30", "\xB0"}, {"\x32", "\xF0\x32"}, {"\x32", "\xF0\x32"}, }, { // BX_KEY_C ( ibm 48 ) {"\x2E", "\xAE"}, {"\x21", "\xF0\x21"}, {"\x21", "\xF0\x21"}, }, { // BX_KEY_D ( ibm 33 ) {"\x20", "\xA0"}, {"\x23", "\xF0\x23"}, {"\x23", "\xF0\x23"}, }, { // BX_KEY_E ( ibm 19 ) {"\x12", "\x92"}, {"\x24", "\xF0\x24"}, {"\x24", "\xF0\x24"}, }, { // BX_KEY_F ( ibm 34 ) {"\x21", "\xA1"}, {"\x2B", "\xF0\x2B"}, {"\x2B", "\xF0\x2B"}, }, { // BX_KEY_G ( ibm 35 ) {"\x22", "\xA2"}, {"\x34", "\xF0\x34"}, {"\x34", "\xF0\x34"}, }, { // BX_KEY_H ( ibm 36 ) {"\x23", "\xA3"}, {"\x33", "\xF0\x33"}, {"\x33", "\xF0\x33"}, }, { // BX_KEY_I ( ibm 24 ) {"\x17", "\x97"}, {"\x43", "\xF0\x43"}, {"\x43", "\xF0\x43"}, }, { // BX_KEY_J ( ibm 37 ) {"\x24", "\xA4"}, {"\x3B", "\xF0\x3B"}, {"\x3B", "\xF0\x3B"}, }, { // BX_KEY_K ( ibm 38 ) {"\x25", "\xA5"}, {"\x42", "\xF0\x42"}, {"\x42", "\xF0\x42"}, }, { // BX_KEY_L ( ibm 39 ) {"\x26", "\xA6"}, {"\x4B", "\xF0\x4B"}, {"\x4B", "\xF0\x4B"}, }, { // BX_KEY_M ( ibm 52 ) {"\x32", "\xB2"}, {"\x3A", "\xF0\x3A"}, {"\x3A", "\xF0\x3A"}, }, { // BX_KEY_N ( ibm 51 ) {"\x31", "\xB1"}, {"\x31", "\xF0\x31"}, {"\x31", "\xF0\x31"}, }, { // BX_KEY_O ( ibm 25 ) {"\x18", "\x98"}, {"\x44", "\xF0\x44"}, {"\x44", "\xF0\x44"}, }, { // BX_KEY_P ( ibm 26 ) {"\x19", "\x99"}, {"\x4D", "\xF0\x4D"}, {"\x4D", "\xF0\x4D"}, }, { // BX_KEY_Q ( ibm 17 ) {"\x10", "\x90"}, {"\x15", "\xF0\x15"}, {"\x15", "\xF0\x15"}, }, { // BX_KEY_R ( ibm 20 ) {"\x13", "\x93"}, {"\x2D", "\xF0\x2D"}, {"\x2D", "\xF0\x2D"}, }, { // BX_KEY_S ( ibm 32 ) {"\x1F", "\x9F"}, {"\x1B", "\xF0\x1B"}, {"\x1B", "\xF0\x1B"}, }, { // BX_KEY_T ( ibm 21 ) {"\x14", "\x94"}, {"\x2C", "\xF0\x2C"}, {"\x2C", "\xF0\x2C"}, }, { // BX_KEY_U ( ibm 23 ) {"\x16", "\x96"}, {"\x3C", "\xF0\x3C"}, {"\x3C", "\xF0\x3C"}, }, { // BX_KEY_V ( ibm 49 ) {"\x2F", "\xAF"}, {"\x2A", "\xF0\x2A"}, {"\x2A", "\xF0\x2A"}, }, { // BX_KEY_W ( ibm 18 ) {"\x11", "\x91"}, {"\x1D", "\xF0\x1D"}, {"\x1D", "\xF0\x1D"}, }, { // BX_KEY_X ( ibm 47 ) {"\x2D", "\xAD"}, {"\x22", "\xF0\x22"}, {"\x22", "\xF0\x22"}, }, { // BX_KEY_Y ( ibm 22 ) {"\x15", "\x95"}, {"\x35", "\xF0\x35"}, {"\x35", "\xF0\x35"}, }, { // BX_KEY_Z ( ibm 46 ) {"\x2C", "\xAC"}, {"\x1A", "\xF0\x1A"}, {"\x1A", "\xF0\x1A"}, }, { // BX_KEY_0 ( ibm 11 ) {"\x0B", "\x8B"}, {"\x45", "\xF0\x45"}, {"\x45", "\xF0\x45"}, }, { // BX_KEY_1 ( ibm 2 ) {"\x02", "\x82"}, {"\x16", "\xF0\x16"}, {"\x16", "\xF0\x16"}, }, { // BX_KEY_2 ( ibm 3 ) {"\x03", "\x83"}, {"\x1E", "\xF0\x1E"}, {"\x1E", "\xF0\x1E"}, }, { // BX_KEY_3 ( ibm 4 ) {"\x04", "\x84"}, {"\x26", "\xF0\x26"}, {"\x26", "\xF0\x26"}, }, { // BX_KEY_4 ( ibm 5 ) {"\x05", "\x85"}, {"\x25", "\xF0\x25"}, {"\x25", "\xF0\x25"}, }, { // BX_KEY_5 ( ibm 6 ) {"\x06", "\x86"}, {"\x2E", "\xF0\x2E"}, {"\x2E", "\xF0\x2E"}, }, { // BX_KEY_6 ( ibm 7 ) {"\x07", "\x87"}, {"\x36", "\xF0\x36"}, {"\x36", "\xF0\x36"}, }, { // BX_KEY_7 ( ibm 8 ) {"\x08", "\x88"}, {"\x3D", "\xF0\x3D"}, {"\x3D", "\xF0\x3D"}, }, { // BX_KEY_8 ( ibm 9 ) {"\x09", "\x89"}, {"\x3E", "\xF0\x3E"}, {"\x3E", "\xF0\x3E"}, }, { // BX_KEY_9 ( ibm 10 ) {"\x0A", "\x8A"}, {"\x46", "\xF0\x46"}, {"\x46", "\xF0\x46"}, }, { // BX_KEY_ESC ( ibm 110 ) {"\x01", "\x81"}, {"\x76", "\xF0\x76"}, {"\x08", "\xF0\x08"}, }, { // BX_KEY_SPACE ( ibm 61 ) {"\x39", "\xB9"}, {"\x29", "\xF0\x29"}, {"\x29", "\xF0\x29"}, }, { // BX_KEY_SINGLE_QUOTE ( ibm 41 ) {"\x28", "\xA8"}, {"\x52", "\xF0\x52"}, {"\x52", "\xF0\x52"}, }, { // BX_KEY_COMMA ( ibm 53 ) {"\x33", "\xB3"}, {"\x41", "\xF0\x41"}, {"\x41", "\xF0\x41"}, }, { // BX_KEY_PERIOD ( ibm 54 ) {"\x34", "\xB4"}, {"\x49", "\xF0\x49"}, {"\x49", "\xF0\x49"}, }, { // BX_KEY_SLASH ( ibm 55 ) {"\x35", "\xB5"}, {"\x4A", "\xF0\x4A"}, {"\x4A", "\xF0\x4A"}, }, { // BX_KEY_SEMICOLON ( ibm 40 ) {"\x27", "\xA7"}, {"\x4C", "\xF0\x4C"}, {"\x4C", "\xF0\x4C"}, }, { // BX_KEY_EQUALS ( ibm 13 ) {"\x0D", "\x8D"}, {"\x55", "\xF0\x55"}, {"\x55", "\xF0\x55"}, }, { // BX_KEY_LEFT_BRACKET ( ibm 27 ) {"\x1A", "\x9A"}, {"\x54", "\xF0\x54"}, {"\x54", "\xF0\x54"}, }, { // BX_KEY_BACKSLASH ( ibm 42, 29) {"\x2B", "\xAB"}, {"\x5D", "\xF0\x5D"}, {"\x53", "\xF0\x53"}, }, { // BX_KEY_RIGHT_BRACKET ( ibm 28 ) {"\x1B", "\x9B"}, {"\x5B", "\xF0\x5B"}, {"\x5B", "\xF0\x5B"}, }, { // BX_KEY_MINUS ( ibm 12 ) {"\x0C", "\x8C"}, {"\x4E", "\xF0\x4E"}, {"\x4E", "\xF0\x4E"}, }, { // BX_KEY_GRAVE ( ibm 1 ) {"\x29", "\xA9"}, {"\x0E", "\xF0\x0E"}, {"\x0E", "\xF0\x0E"}, }, { // BX_KEY_BACKSPACE ( ibm 15 ) {"\x0E", "\x8E"}, {"\x66", "\xF0\x66"}, {"\x66", "\xF0\x66"}, }, { // BX_KEY_ENTER ( ibm 43 ) {"\x1C", "\x9C"}, {"\x5A", "\xF0\x5A"}, {"\x5A", "\xF0\x5A"}, }, { // BX_KEY_TAB ( ibm 16 ) {"\x0F", "\x8F"}, {"\x0D", "\xF0\x0D"}, {"\x0D", "\xF0\x0D"}, }, { // BX_KEY_LEFT_BACKSLASH ( ibm 45 ) {"\x56", "\xD6"}, {"\x61", "\xF0\x61"}, {"\x13", "\xF0\x13"}, }, { // BX_KEY_PRINT ( ibm 124 ) {"\xE0\x2A\xE0\x37", "\xE0\xB7\xE0\xAA"}, {"\xE0\x12\xE0\x7C", "\xE0\xF0\x7C\xE0\xF0\x12"}, {"\x57", "\xF0\x57"}, }, { // BX_KEY_SCRL_LOCK ( ibm 125 ) {"\x46", "\xC6"}, {"\x7E", "\xF0\x7E"}, {"\x5F", "\xF0\x5F"}, }, { // BX_KEY_PAUSE ( ibm 126 ) {"\xE1\x1D\x45\xE1\x9D\xC5", ""}, {"\xE1\x14\x77\xE1\xF0\x14\xF0\x77", ""}, {"\x62", "\xF0\x62"}, }, { // BX_KEY_INSERT ( ibm 75 ) {"\xE0\x52", "\xE0\xD2"}, {"\xE0\x70", "\xE0\xF0\x70"}, {"\x67", "\xF0\x67"}, }, { // BX_KEY_DELETE ( ibm 76 ) {"\xE0\x53", "\xE0\xD3"}, {"\xE0\x71", "\xE0\xF0\x71"}, {"\x64", "\xF0\x64"}, }, { // BX_KEY_HOME ( ibm 80 ) {"\xE0\x47", "\xE0\xC7"}, {"\xE0\x6C", "\xE0\xF0\x6C"}, {"\x6E", "\xF0\x6E"}, }, { // BX_KEY_END ( ibm 81 ) {"\xE0\x4F", "\xE0\xCF"}, {"\xE0\x69", "\xE0\xF0\x69"}, {"\x65", "\xF0\x65"}, }, { // BX_KEY_PAGE_UP ( ibm 85 ) {"\xE0\x49", "\xE0\xC9"}, {"\xE0\x7D", "\xE0\xF0\x7D"}, {"\x6F", "\xF0\x6F"}, }, { // BX_KEY_PAGE_DOWN ( ibm 86 ) {"\xE0\x51", "\xE0\xD1"}, {"\xE0\x7A", "\xE0\xF0\x7A"}, {"\x6D", "\xF0\x6D"}, }, { // BX_KEY_KP_ADD ( ibm 106 ) {"\x4E", "\xCE"}, {"\x79", "\xF0\x79"}, {"\x7C", "\xF0\x7C"}, }, { // BX_KEY_KP_SUBTRACT ( ibm 105 ) {"\x4A", "\xCA"}, {"\x7B", "\xF0\x7B"}, {"\x84", "\xF0\x84"}, }, { // BX_KEY_KP_END ( ibm 93 ) {"\x4F", "\xCF"}, {"\x69", "\xF0\x69"}, {"\x69", "\xF0\x69"}, }, { // BX_KEY_KP_DOWN ( ibm 98 ) {"\x50", "\xD0"}, {"\x72", "\xF0\x72"}, {"\x72", "\xF0\x72"}, }, { // BX_KEY_KP_PAGE_DOWN ( ibm 103 ) {"\x51", "\xD1"}, {"\x7A", "\xF0\x7A"}, {"\x7A", "\xF0\x7A"}, }, { // BX_KEY_KP_LEFT ( ibm 92 ) {"\x4B", "\xCB"}, {"\x6B", "\xF0\x6B"}, {"\x6B", "\xF0\x6B"}, }, { // BX_KEY_KP_RIGHT ( ibm 102 ) {"\x4D", "\xCD"}, {"\x74", "\xF0\x74"}, {"\x74", "\xF0\x74"}, }, { // BX_KEY_KP_HOME ( ibm 91 ) {"\x47", "\xC7"}, {"\x6C", "\xF0\x6C"}, {"\x6C", "\xF0\x6C"}, }, { // BX_KEY_KP_UP ( ibm 96 ) {"\x48", "\xC8"}, {"\x75", "\xF0\x75"}, {"\x75", "\xF0\x75"}, }, { // BX_KEY_KP_PAGE_UP ( ibm 101 ) {"\x49", "\xC9"}, {"\x7D", "\xF0\x7D"}, {"\x7D", "\xF0\x7D"}, }, { // BX_KEY_KP_INSERT ( ibm 99 ) {"\x52", "\xD2"}, {"\x70", "\xF0\x70"}, {"\x70", "\xF0\x70"}, }, { // BX_KEY_KP_DELETE ( ibm 104 ) {"\x53", "\xD3"}, {"\x71", "\xF0\x71"}, {"\x71", "\xF0\x71"}, }, { // BX_KEY_KP_5 ( ibm 97 ) {"\x4C", "\xCC"}, {"\x73", "\xF0\x73"}, {"\x73", "\xF0\x73"}, }, { // BX_KEY_UP ( ibm 83 ) {"\xE0\x48", "\xE0\xC8"}, {"\xE0\x75", "\xE0\xF0\x75"}, {"\x63", "\xF0\x63"}, }, { // BX_KEY_DOWN ( ibm 84 ) {"\xE0\x50", "\xE0\xD0"}, {"\xE0\x72", "\xE0\xF0\x72"}, {"\x60", "\xF0\x60"}, }, { // BX_KEY_LEFT ( ibm 79 ) {"\xE0\x4B", "\xE0\xCB"}, {"\xE0\x6B", "\xE0\xF0\x6B"}, {"\x61", "\xF0\x61"}, }, { // BX_KEY_RIGHT ( ibm 89 ) {"\xE0\x4D", "\xE0\xCD"}, {"\xE0\x74", "\xE0\xF0\x74"}, {"\x6A", "\xF0\x6A"}, }, { // BX_KEY_KP_ENTER ( ibm 108 ) {"\xE0\x1C", "\xE0\x9C"}, {"\xE0\x5A", "\xE0\xF0\x5A"}, {"\x79", "\xF0\x79"}, }, { // BX_KEY_KP_MULTIPLY ( ibm 100 ) {"\x37", "\xB7"}, {"\x7C", "\xF0\x7C"}, {"\x7E", "\xF0\x7E"}, }, { // BX_KEY_KP_DIVIDE ( ibm 95 ) {"\xE0\x35", "\xE0\xB5"}, {"\xE0\x4A", "\xE0\xF0\x4A"}, {"\x77", "\xF0\x77"}, }, { // BX_KEY_WIN_L {"\xE0\x5B", "\xE0\xDB"}, {"\xE0\x1F", "\xE0\xF0\x1F"}, {"\x8B", "\xF0\x8B"}, }, { // BX_KEY_WIN_R {"\xE0\x5C", "\xE0\xDC"}, {"\xE0\x27", "\xE0\xF0\x27"}, {"\x8C", "\xF0\x8C"}, }, { // BX_KEY_MENU {"\xE0\x5D", "\xE0\xDD"}, {"\xE0\x2F", "\xE0\xF0\x2F"}, {"\x8D", "\xF0\x8D"}, }, { // BX_KEY_ALT_SYSREQ {"\x54", "\xD4"}, {"\x84", "\xF0\x84"}, {"\x57", "\xF0\x57"}, }, { // BX_KEY_CTRL_BREAK {"\xE0\x46", "\xE0\xC6"}, {"\xE0\x7E", "\xE0\xF0\x7E"}, {"\x62", "\xF0\x62"}, }, { // BX_KEY_INT_BACK {"\xE0\x6A", "\xE0\xEA"}, {"\xE0\x38", "\xE0\xF0\x38"}, {"\x38", "\xF0\x38"}, }, { // BX_KEY_INT_FORWARD {"\xE0\x69", "\xE0\xE9"}, {"\xE0\x30", "\xE0\xF0\x30"}, {"\x30", "\xF0\x30"}, }, { // BX_KEY_INT_STOP {"\xE0\x68", "\xE0\xE8"}, {"\xE0\x28", "\xE0\xF0\x28"}, {"\x28", "\xF0\x28"}, }, { // BX_KEY_INT_MAIL {"\xE0\x6C", "\xE0\xEC"}, {"\xE0\x48", "\xE0\xF0\x48"}, {"\x48", "\xF0\x48"}, }, { // BX_KEY_INT_SEARCH {"\xE0\x65", "\xE0\xE5"}, {"\xE0\x10", "\xE0\xF0\x10"}, {"\x10", "\xF0\x10"}, }, { // BX_KEY_INT_FAV {"\xE0\x66", "\xE0\xE6"}, {"\xE0\x18", "\xE0\xF0\x18"}, {"\x18", "\xF0\x18"}, }, { // BX_KEY_INT_HOME {"\xE0\x32", "\xE0\xB2"}, {"\xE0\x3A", "\xE0\xF0\x3A"}, {"\x97", "\xF0\x97"}, }, { // BX_KEY_POWER_MYCOMP {"\xE0\x6B", "\xE0\xEB"}, {"\xE0\x40", "\xE0\xF0\x40"}, {"\x40", "\xF0\x40"}, }, { // BX_KEY_POWER_CALC {"\xE0\x21", "\xE0\xA1"}, {"\xE0\x2B", "\xE0\xF0\x2B"}, {"\x99", "\xF0\x99"}, }, { // BX_KEY_POWER_SLEEP {"\xE0\x5F", "\xE0\xDF"}, {"\xE0\x3F", "\xE0\xF0\x3F"}, {"\x7F", "\xF0\x7F"}, }, { // BX_KEY_POWER_POWER {"\xE0\x5E", "\xE0\xDE"}, {"\xE0\x37", "\xE0\xF0\x37"}, {"", ""}, }, { // BX_KEY_POWER_WAKE {"\xE0\x63", "\xE0\xE3"}, {"\xE0\x5E", "\xE0\xF0\x5E"}, {"", ""}, }, }; ================================================ FILE: src/gui/scancodes.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gui.hpp" #ifndef BX_SCANCODES_H #define BX_SCANCODES_H // Translation table of the 8042 extern unsigned char translation8042[256]; typedef struct { const char *make; const char *brek; } scancode; // Scancodes table extern scancode scancodes[BX_KEY_NBKEYS][3]; #endif ================================================ FILE: src/gui/sdl.cpp ================================================ /* ES40 emulator. * Copyright (C) 2007-2008 by the ES40 Emulator Project * * WWW : http://es40.org * E-mail : camiel@camicom.com * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../StdAfx.hpp" #if defined(HAVE_SDL) #include "../System.hpp" #include "../VGA.hpp" #include "gui.hpp" #include "keymap.hpp" #include "../Configurator.hpp" #include "../Keyboard.hpp" #define _MULTI_THREAD // Define BX_PLUGGABLE in files that can be compiled into plugins. For // platforms that require a special tag on exported symbols, BX_PLUGGABLE // is used to know when we are exporting symbols and when we are importing. #define BX_PLUGGABLE #include #include #include #include #include "sdl_fonts.hpp" /** * \brief GUI implementation using SDL. **/ class bx_sdl_gui_c : public bx_gui_c { public: bx_sdl_gui_c(CConfigurator *cfg); virtual void specific_init(unsigned x_tilesize, unsigned y_tilesize); virtual void text_update(u8 *old_text, u8 *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned rows); virtual void graphics_tile_update(u8 *snapshot, unsigned x, unsigned y); virtual void handle_events(void); virtual void flush(void); virtual void clear_screen(void); virtual bool palette_change(unsigned index, unsigned red, unsigned green, unsigned blue); virtual void dimension_update(unsigned x, unsigned y, unsigned fheight = 0, unsigned fwidth = 0, unsigned bpp = 8); virtual void mouse_enabled_changed_specific(bool val); virtual void exit(void); virtual bx_svga_tileinfo_t *graphics_tile_info(bx_svga_tileinfo_t *info); virtual u8 *graphics_tile_get(unsigned x, unsigned y, unsigned *w, unsigned *h); virtual void graphics_tile_update_in_place(unsigned x, unsigned y, unsigned w, unsigned h); private: CConfigurator *myCfg; }; // declare one instance of the gui object and call macro to insert the // plugin code static bx_sdl_gui_c *theGui = NULL; IMPLEMENT_GUI_PLUGIN_CODE(sdl) static unsigned prev_cursor_x = 0; static unsigned prev_cursor_y = 0; static u32 convertStringToSDLKey(const char *string); SDL_Thread *sdl_thread; SDL_Surface *sdl_screen; SDL_Event sdl_event; int sdl_grab; unsigned res_x, res_y; unsigned half_res_x, half_res_y; static unsigned int text_cols = 80, text_rows = 25; u8 h_panning = 0, v_panning = 0; u16 line_compare = 1023; int fontwidth = 8, fontheight = 16; static unsigned vga_bpp = 8; unsigned tilewidth, tileheight; u32 palette[256]; u8 old_mousebuttons = 0, new_mousebuttons = 0; int old_mousex = 0, new_mousex = 0; int old_mousey = 0, new_mousey = 0; bool just_warped = false; bx_sdl_gui_c::bx_sdl_gui_c(CConfigurator *cfg) { myCfg = cfg; bx_keymap = new bx_keymap_c(cfg); } #ifdef __MORPHOS__ void bx_sdl_morphos_exit(void) { SDL_Quit(); if (PowerSDLBase) CloseLibrary(PowerSDLBase); } #endif void bx_sdl_gui_c::specific_init(unsigned x_tilesize, unsigned y_tilesize) { int i; int j; u32 flags; tilewidth = x_tilesize; tileheight = y_tilesize; for (i = 0; i < 256; i++) for (j = 0; j < 16; j++) vga_charmap[i * 32 + j] = sdl_font8x16[i][j]; #ifdef __MORPHOS__ if (!(PowerSDLBase = OpenLibrary("powersdl.library", 0))) { BX_PANIC(("Unable to open SDL libraries")); return; } #endif flags = SDL_INIT_VIDEO; if (SDL_Init(flags) < 0) { FAILURE(SDL, "Unable to initialize SDL libraries"); } #ifdef __MORPHOS__ atexit(bx_sdl_morphos_exit); #else atexit(SDL_Quit); #endif sdl_screen = NULL; // sdl_fullscreen_toggle = 0; dimension_update(640, 480); SDL_EnableKeyRepeat(250, 50); SDL_WarpMouse(half_res_x, half_res_y); // load keymap for sdl if (myCfg->get_bool_value("keyboard.use_mapping", false)) { bx_keymap->loadKeymap(convertStringToSDLKey); } new_gfx_api = 1; } void bx_sdl_gui_c::text_update(u8 *old_text, u8 *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned nrows) { u8 *pfont_row; u8 *old_line; u8 *new_line; u8 *text_base; unsigned int cs_y; unsigned int i; unsigned int x; unsigned int y; unsigned int curs; unsigned int hchars; unsigned int offset; u8 fontline; u8 fontpixels; u8 fontrows; int rows; u32 fgcolor; u32 bgcolor; u32 *buf; u32 *buf_row; u32 *buf_char; u32 disp; u16 font_row; u16 mask; u8 cfstart; u8 cfwidth; u8 cfheight; u8 split_fontrows; u8 split_textrow; bool cursor_visible; bool gfxcharw9; bool invert; bool forceUpdate; bool split_screen; u32 text_palette[16]; // UNUSED(nrows); forceUpdate = 0; if (charmap_updated) { forceUpdate = 1; charmap_updated = 0; } for (i = 0; i < 16; i++) { text_palette[i] = palette[theVGA->get_actl_palette_idx(i)]; } if ((tm_info.h_panning != h_panning) || (tm_info.v_panning != v_panning)) { forceUpdate = 1; h_panning = tm_info.h_panning; v_panning = tm_info.v_panning; } if (tm_info.line_compare != line_compare) { forceUpdate = 1; line_compare = tm_info.line_compare; } disp = sdl_screen->pitch / 4; buf_row = (u32 *)sdl_screen->pixels; // first invalidate character at previous and new cursor location if ((prev_cursor_y < text_rows) && (prev_cursor_x < text_cols)) { curs = prev_cursor_y * tm_info.line_offset + prev_cursor_x * 2; old_text[curs] = ~new_text[curs]; } cursor_visible = ((tm_info.cs_start <= tm_info.cs_end) && (tm_info.cs_start < fontheight)); if ((cursor_visible) && (cursor_y < text_rows) && (cursor_x < text_cols)) { curs = cursor_y * tm_info.line_offset + cursor_x * 2; old_text[curs] = ~new_text[curs]; } else { curs = 0xffff; } rows = text_rows; if (v_panning) rows++; y = 0; cs_y = 0; text_base = new_text - tm_info.start_address; split_textrow = (line_compare + v_panning) / fontheight; split_fontrows = ((line_compare + v_panning) % fontheight) + 1; split_screen = 0; do { buf = buf_row; hchars = text_cols; if (h_panning) hchars++; cfheight = fontheight; cfstart = 0; if (split_screen) { if (rows == 1) { cfheight = (res_y - line_compare - 1) % fontheight; if (cfheight == 0) cfheight = fontheight; } } else if (v_panning) { if (y == 0) { cfheight -= v_panning; cfstart = v_panning; } else if (rows == 1) { cfheight = v_panning; } } if (!split_screen && (y == split_textrow)) { if ((split_fontrows - cfstart) < cfheight) { cfheight = split_fontrows - cfstart; } } new_line = new_text; old_line = old_text; x = 0; offset = cs_y * tm_info.line_offset; do { cfwidth = fontwidth; if (h_panning) { if (hchars > text_cols) { cfwidth -= h_panning; } else if (hchars == 1) { cfwidth = h_panning; } } // check if char needs to be updated if (forceUpdate || (old_text[0] != new_text[0]) || (old_text[1] != new_text[1])) { // Get Foreground/Background pixel colors fgcolor = text_palette[new_text[1] & 0x0F]; bgcolor = text_palette[(new_text[1] >> 4) & 0x0F]; invert = ((offset == curs) && (cursor_visible)); gfxcharw9 = ((tm_info.line_graphics) && ((new_text[0] & 0xE0) == 0xC0)); // Display this one char fontrows = cfheight; fontline = cfstart; if (y > 0) { pfont_row = (u8 *)&vga_charmap[(new_text[0] << 5)]; } else { pfont_row = (u8 *)&vga_charmap[(new_text[0] << 5) + cfstart]; } buf_char = buf; do { font_row = *pfont_row++; if (gfxcharw9) { font_row = (font_row << 1) | (font_row & 0x01); } else { font_row <<= 1; } if (hchars > text_cols) { font_row <<= h_panning; } fontpixels = cfwidth; if ((invert) && (fontline >= tm_info.cs_start) && (fontline <= tm_info.cs_end)) mask = 0x100; else mask = 0x00; do { if ((font_row & 0x100) == mask) *buf = bgcolor; else *buf = fgcolor; buf++; font_row <<= 1; } while (--fontpixels); buf -= cfwidth; buf += disp; fontline++; } while (--fontrows); // restore output buffer ptr to start of this char buf = buf_char; } // move to next char location on screen buf += cfwidth; // select next char in old/new text new_text += 2; old_text += 2; offset += 2; x++; // process one entire horizontal row } while (--hchars); // go to next character row location buf_row += disp * cfheight; if (!split_screen && (y == split_textrow)) { new_text = text_base; forceUpdate = 1; cs_y = 0; if (tm_info.split_hpanning) h_panning = 0; rows = ((res_y - line_compare + fontheight - 2) / fontheight) + 1; split_screen = 1; } else { new_text = new_line + tm_info.line_offset; old_text = old_line + tm_info.line_offset; cs_y++; y++; } } while (--rows); h_panning = tm_info.h_panning; prev_cursor_x = cursor_x; prev_cursor_y = cursor_y; } void bx_sdl_gui_c::graphics_tile_update(u8 *snapshot, unsigned x, unsigned y) { u32 *buf; u32 disp; u32 *buf_row; int i; int j; disp = sdl_screen->pitch / 4; buf = (u32 *)sdl_screen->pixels + /*(headerbar_height+y)*disp +*/ x; i = tileheight; if (i + y > res_y) i = res_y - y; // FIXME if (i <= 0) return; switch (vga_bpp) { case 8: /* 8 bpp */ do { buf_row = buf; j = tilewidth; do { *buf++ = palette[*snapshot++]; } while (--j); buf = buf_row + disp; } while (--i); break; default: BX_PANIC(("%u bpp modes handled by new graphics API", vga_bpp)); return; } } bx_svga_tileinfo_t *bx_sdl_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info) { if (!info) { info = (bx_svga_tileinfo_t *)malloc(sizeof(bx_svga_tileinfo_t)); if (!info) { return NULL; } } info->bpp = sdl_screen->format->BitsPerPixel; info->pitch = sdl_screen->pitch; info->red_shift = sdl_screen->format->Rshift + 8 - sdl_screen->format->Rloss; info->green_shift = sdl_screen->format->Gshift + 8 - sdl_screen->format->Gloss; info->blue_shift = sdl_screen->format->Bshift + 8 - sdl_screen->format->Bloss; info->red_mask = sdl_screen->format->Rmask; info->green_mask = sdl_screen->format->Gmask; info->blue_mask = sdl_screen->format->Bmask; info->is_indexed = (sdl_screen->format->palette != NULL); #ifdef BX_LITTLE_ENDIAN info->is_little_endian = 1; #else info->is_little_endian = 0; #endif return info; } u8 *bx_sdl_gui_c::graphics_tile_get(unsigned x0, unsigned y0, unsigned *w, unsigned *h) { if (x0 + tilewidth > res_x) *w = res_x - x0; else *w = tilewidth; if (y0 + tileheight > res_y) *h = res_y - y0; else *h = tileheight; return (u8 *)sdl_screen->pixels + sdl_screen->pitch * y0 + sdl_screen->format->BytesPerPixel * x0; } void bx_sdl_gui_c::graphics_tile_update_in_place(unsigned x0, unsigned y0, unsigned w, unsigned h) {} static u32 sdl_sym_to_bx_key(SDLKey sym) { switch (sym) { // case SDLK_UNKNOWN: return BX_KEY_UNKNOWN; // case SDLK_FIRST: return BX_KEY_FIRST; case SDLK_BACKSPACE: return BX_KEY_BACKSPACE; case SDLK_TAB: return BX_KEY_TAB; // case SDLK_CLEAR: return BX_KEY_CLEAR; case SDLK_RETURN: return BX_KEY_ENTER; case SDLK_PAUSE: return BX_KEY_PAUSE; case SDLK_ESCAPE: return BX_KEY_ESC; case SDLK_SPACE: return BX_KEY_SPACE; // case SDLK_EXCLAIM: return BX_KEY_EXCLAIM; // case SDLK_QUOTEDBL: return BX_KEY_QUOTEDBL; // case SDLK_HASH: return BX_KEY_HASH; // case SDLK_DOLLAR: return BX_KEY_DOLLAR; // case SDLK_AMPERSAND: return BX_KEY_AMPERSAND; case SDLK_QUOTE: return BX_KEY_SINGLE_QUOTE; // case SDLK_LEFTPAREN: return BX_KEY_LEFTPAREN; // case SDLK_RIGHTPAREN: return BX_KEY_RIGHTPAREN; // case SDLK_ASTERISK: return BX_KEY_ASTERISK; // case SDLK_PLUS: return BX_KEY_PLUS; case SDLK_COMMA: return BX_KEY_COMMA; case SDLK_MINUS: return BX_KEY_MINUS; case SDLK_PERIOD: return BX_KEY_PERIOD; case SDLK_SLASH: return BX_KEY_SLASH; case SDLK_0: return BX_KEY_0; case SDLK_1: return BX_KEY_1; case SDLK_2: return BX_KEY_2; case SDLK_3: return BX_KEY_3; case SDLK_4: return BX_KEY_4; case SDLK_5: return BX_KEY_5; case SDLK_6: return BX_KEY_6; case SDLK_7: return BX_KEY_7; case SDLK_8: return BX_KEY_8; case SDLK_9: return BX_KEY_9; // case SDLK_COLON: return BX_KEY_COLON; case SDLK_SEMICOLON: return BX_KEY_SEMICOLON; // case SDLK_LESS: return BX_KEY_LESS; case SDLK_EQUALS: return BX_KEY_EQUALS; // case SDLK_GREATER: return BX_KEY_GREATER; // case SDLK_QUESTION: return BX_KEY_QUESTION; // case SDLK_AT: return BX_KEY_AT; /* Skip uppercase letters */ case SDLK_LEFTBRACKET: return BX_KEY_LEFT_BRACKET; case SDLK_BACKSLASH: return BX_KEY_BACKSLASH; case SDLK_RIGHTBRACKET: return BX_KEY_RIGHT_BRACKET; // case SDLK_CARET: return BX_KEY_CARET; // case SDLK_UNDERSCORE: return BX_KEY_UNDERSCORE; case SDLK_BACKQUOTE: return BX_KEY_GRAVE; case SDLK_a: return BX_KEY_A; case SDLK_b: return BX_KEY_B; case SDLK_c: return BX_KEY_C; case SDLK_d: return BX_KEY_D; case SDLK_e: return BX_KEY_E; case SDLK_f: return BX_KEY_F; case SDLK_g: return BX_KEY_G; case SDLK_h: return BX_KEY_H; case SDLK_i: return BX_KEY_I; case SDLK_j: return BX_KEY_J; case SDLK_k: return BX_KEY_K; case SDLK_l: return BX_KEY_L; case SDLK_m: return BX_KEY_M; case SDLK_n: return BX_KEY_N; case SDLK_o: return BX_KEY_O; case SDLK_p: return BX_KEY_P; case SDLK_q: return BX_KEY_Q; case SDLK_r: return BX_KEY_R; case SDLK_s: return BX_KEY_S; case SDLK_t: return BX_KEY_T; case SDLK_u: return BX_KEY_U; case SDLK_v: return BX_KEY_V; case SDLK_w: return BX_KEY_W; case SDLK_x: return BX_KEY_X; case SDLK_y: return BX_KEY_Y; case SDLK_z: return BX_KEY_Z; case SDLK_DELETE: return BX_KEY_DELETE; /* End of ASCII mapped keysyms */ /* Numeric keypad */ case SDLK_KP0: return BX_KEY_KP_INSERT; case SDLK_KP1: return BX_KEY_KP_END; case SDLK_KP2: return BX_KEY_KP_DOWN; case SDLK_KP3: return BX_KEY_KP_PAGE_DOWN; case SDLK_KP4: return BX_KEY_KP_LEFT; case SDLK_KP5: return BX_KEY_KP_5; case SDLK_KP6: return BX_KEY_KP_RIGHT; case SDLK_KP7: return BX_KEY_KP_HOME; case SDLK_KP8: return BX_KEY_KP_UP; case SDLK_KP9: return BX_KEY_KP_PAGE_UP; case SDLK_KP_PERIOD: return BX_KEY_KP_DELETE; case SDLK_KP_DIVIDE: return BX_KEY_KP_DIVIDE; case SDLK_KP_MULTIPLY: return BX_KEY_KP_MULTIPLY; case SDLK_KP_MINUS: return BX_KEY_KP_SUBTRACT; case SDLK_KP_PLUS: return BX_KEY_KP_ADD; case SDLK_KP_ENTER: return BX_KEY_KP_ENTER; // case SDLK_KP_EQUALS: return BX_KEY_KP_EQUALS; /* Arrows + Home/End pad */ case SDLK_UP: return BX_KEY_UP; case SDLK_DOWN: return BX_KEY_DOWN; case SDLK_RIGHT: return BX_KEY_RIGHT; case SDLK_LEFT: return BX_KEY_LEFT; case SDLK_INSERT: return BX_KEY_INSERT; case SDLK_HOME: return BX_KEY_HOME; case SDLK_END: return BX_KEY_END; case SDLK_PAGEUP: return BX_KEY_PAGE_UP; case SDLK_PAGEDOWN: return BX_KEY_PAGE_DOWN; /* Function keys */ case SDLK_F1: return BX_KEY_F1; case SDLK_F2: return BX_KEY_F2; case SDLK_F3: return BX_KEY_F3; case SDLK_F4: return BX_KEY_F4; case SDLK_F5: return BX_KEY_F5; case SDLK_F6: return BX_KEY_F6; case SDLK_F7: return BX_KEY_F7; case SDLK_F8: return BX_KEY_F8; case SDLK_F9: return BX_KEY_F9; case SDLK_F10: return BX_KEY_F10; case SDLK_F11: return BX_KEY_F11; case SDLK_F12: return BX_KEY_F12; // case SDLK_F13: return BX_KEY_F13; // case SDLK_F14: return BX_KEY_F14; // case SDLK_F15: return BX_KEY_F15; /* Key state modifier keys */ case SDLK_NUMLOCK: return BX_KEY_NUM_LOCK; case SDLK_CAPSLOCK: return BX_KEY_CAPS_LOCK; case SDLK_SCROLLOCK: return BX_KEY_SCRL_LOCK; case SDLK_RSHIFT: return BX_KEY_SHIFT_R; case SDLK_LSHIFT: return BX_KEY_SHIFT_L; case SDLK_RCTRL: return BX_KEY_CTRL_R; case SDLK_LCTRL: return BX_KEY_CTRL_L; case SDLK_RALT: return BX_KEY_ALT_R; case SDLK_LALT: return BX_KEY_ALT_L; case SDLK_RMETA: return BX_KEY_ALT_R; case SDLK_LMETA: return BX_KEY_WIN_L; case SDLK_LSUPER: return BX_KEY_WIN_L; case SDLK_RSUPER: return BX_KEY_WIN_R; // case SDLK_MODE: return BX_KEY_MODE; // case SDLK_COMPOSE: return BX_KEY_COMPOSE; /* Miscellaneous function keys */ case SDLK_PRINT: return BX_KEY_PRINT; case SDLK_BREAK: return BX_KEY_PAUSE; case SDLK_MENU: return BX_KEY_MENU; #if 0 case SDLK_HELP: return BX_KEY_HELP; case SDLK_SYSREQ: return BX_KEY_SYSREQ; case SDLK_POWER: return BX_KEY_POWER; case SDLK_EURO: return BX_KEY_EURO; case SDLK_UNDO: return BX_KEY_UNDO; #endif default: BX_ERROR(("sdl keysym %d not mapped", (int)sym)); return BX_KEY_UNHANDLED; } } void bx_sdl_gui_c::handle_events(void) { u32 key_event; // u8 mouse_state; int wheel_status; while (SDL_PollEvent(&sdl_event)) { wheel_status = 0; switch (sdl_event.type) { case SDL_VIDEOEXPOSE: SDL_UpdateRect(sdl_screen, 0, 0, res_x, res_y); break; case SDL_MOUSEMOTION: // //fprintf (stderr, "mouse event to (%d,%d), relative (%d,%d)\n", //(int)(sdl_event.motion.x), (int)(sdl_event.motion.y), //(int)sdl_event.motion.xrel, (int)sdl_event.motion.yrel); if (!sdl_grab) { // //fprintf (stderr, "ignore mouse event because sdl_grab is off\n"); // break; // } // if (just_warped // && sdl_event.motion.x == half_res_x // && sdl_event.motion.y == half_res_y) { // // This event was generated as a side effect of the WarpMouse, // // and it must be ignored. // //fprintf (stderr, "ignore mouse event because it is a side effect of //SDL_WarpMouse\n"); just_warped = false; break; // } // //fprintf (stderr, "processing relative mouse event\n"); // new_mousebuttons = ((sdl_event.motion.state & // 0x01)|((sdl_event.motion.state>>1)&0x02) // |((sdl_event.motion.state<<1)&0x04)); // DEV_mouse_motion_ext( // sdl_event.motion.xrel, // -sdl_event.motion.yrel, // wheel_status, // new_mousebuttons); // old_mousebuttons = new_mousebuttons; // old_mousex = (int)(sdl_event.motion.x); // old_mousey = (int)(sdl_event.motion.y); // //fprintf (stderr, "warping mouse to center\n"); // SDL_WarpMouse(half_res_x, half_res_y); // just_warped = 1; // break; // case SDL_MOUSEBUTTONDOWN: // if( (sdl_event.button.button == SDL_BUTTON_MIDDLE) // && ((SDL_GetModState() & KMOD_CTRL) > 0) // && (sdl_fullscreen_toggle == 0) ) // { // if( sdl_grab == 0 ) // { // SDL_ShowCursor(0); // SDL_WM_GrabInput(SDL_GRAB_ON); // } // else // { // SDL_ShowCursor(1); // SDL_WM_GrabInput(SDL_GRAB_OFF); // } // sdl_grab = ~sdl_grab; // toggle_mouse_enable(); // break; // } //#ifdef SDL_BUTTON_WHEELUP // // get the wheel status // if (sdl_event.button.button == SDL_BUTTON_WHEELUP) { // wheel_status = 1; // } // if (sdl_event.button.button == SDL_BUTTON_WHEELDOWN) { // wheel_status = -1; // } //#endif case SDL_MOUSEBUTTONUP: // // figure out mouse state // new_mousex = (int)(sdl_event.button.x); // new_mousey = (int)(sdl_event.button.y); // // SDL_GetMouseState() returns the state of all buttons // mouse_state = SDL_GetMouseState(NULL, NULL); // new_mousebuttons = // (mouse_state & 0x01) | // ((mouse_state>>1)&0x02) | // ((mouse_state<<1)&0x04) ; // // filter out middle button if not fullscreen // if( sdl_fullscreen_toggle == 0 ) // new_mousebuttons &= 0x07; // // send motion information // DEV_mouse_motion_ext( // new_mousex - old_mousex, // -(new_mousey - old_mousey), // wheel_status, // new_mousebuttons); // // mark current state to diff with next packet // old_mousebuttons = new_mousebuttons; // old_mousex = new_mousex; // old_mousey = new_mousey; break; // case SDL_KEYDOWN: // convert sym->bochs code if (sdl_event.key.keysym.sym > SDLK_LAST) break; if (!myCfg->get_bool_value("keyboard.use_mapping", false)) // if (!SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { key_event = sdl_sym_to_bx_key(sdl_event.key.keysym.sym); #if defined(DEBUG_KBD) BX_DEBUG(("keypress scancode=%d, sym=%d, bx_key = %d", sdl_event.key.keysym.scancode, sdl_event.key.keysym.sym, key_event)); #endif } else { /* use mapping */ BXKeyEntry *entry = bx_keymap->findHostKey(sdl_event.key.keysym.sym); if (!entry) { BX_ERROR(("host key %d (0x%x) not mapped!", (unsigned)sdl_event.key.keysym.sym, (unsigned)sdl_event.key.keysym.sym)); break; } key_event = entry->baseKey; } if (key_event == BX_KEY_UNHANDLED) break; theKeyboard->gen_scancode(key_event); if ((key_event == BX_KEY_NUM_LOCK) || (key_event == BX_KEY_CAPS_LOCK)) { theKeyboard->gen_scancode(key_event | BX_KEY_RELEASED); } break; case SDL_KEYUP: // filter out release of Windows/Fullscreen toggle and unsupported keys if ((sdl_event.key.keysym.sym != SDLK_SCROLLOCK) && (sdl_event.key.keysym.sym < SDLK_LAST)) { // convert sym->bochs code if (!myCfg->get_bool_value("keyboard.use_mapping", false)) { // if (!SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { key_event = sdl_sym_to_bx_key(sdl_event.key.keysym.sym); } else { /* use mapping */ BXKeyEntry *entry = bx_keymap->findHostKey(sdl_event.key.keysym.sym); if (!entry) { BX_ERROR(("host key %d (0x%x) not mapped!", (unsigned)sdl_event.key.keysym.sym, (unsigned)sdl_event.key.keysym.sym)); break; } key_event = entry->baseKey; } if (key_event == BX_KEY_UNHANDLED) break; if ((key_event == BX_KEY_NUM_LOCK) || (key_event == BX_KEY_CAPS_LOCK)) { theKeyboard->gen_scancode(key_event); } theKeyboard->gen_scancode(key_event | BX_KEY_RELEASED); } break; case SDL_QUIT: FAILURE(Graceful, "User requested shutdown"); } } } /** * Flush any changes to sdl_screen to the actual window. **/ void bx_sdl_gui_c::flush(void) { SDL_UpdateRect(sdl_screen, 0, 0, res_x, res_y); } /** * Clear sdl_screen display, and flush it. **/ void bx_sdl_gui_c::clear_screen(void) { int i = res_y; int j; u32 color; u32 *buf; u32 *buf_row; u32 disp; if (!sdl_screen) return; color = SDL_MapRGB(sdl_screen->format, 0, 0, 0); disp = sdl_screen->pitch / 4; buf = (u32 *)sdl_screen->pixels; do { buf_row = buf; j = res_x; while (j--) *buf++ = color; buf = buf_row + disp; } while (--i); flush(); } /** * Set palette-entry index to the desired value. * * The palette is used in text-mode and in 8bpp VGA mode. **/ bool bx_sdl_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue) { unsigned char palred = red & 0xFF; unsigned char palgreen = green & 0xFF; unsigned char palblue = blue & 0xFF; if (index > 255) return 0; palette[index] = SDL_MapRGB(sdl_screen->format, palred, palgreen, palblue); return 1; } void bx_sdl_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp) { if ((bpp == 8) || (bpp == 15) || (bpp == 16) || (bpp == 24) || (bpp == 32)) { vga_bpp = bpp; } else { FAILURE_1(SDL, "%d bpp graphics mode not supported.", bpp); } if (fheight > 0) { fontheight = fheight; fontwidth = fwidth; text_cols = x / fontwidth; text_rows = y / fontheight; } if ((x == res_x) && (y == res_y)) return; if (sdl_screen) { SDL_FreeSurface(sdl_screen); sdl_screen = NULL; } sdl_screen = SDL_SetVideoMode(x, y /*+headerbar_height+statusbar_height*/, 32, SDL_SWSURFACE); if (!sdl_screen) { FAILURE_3(SDL, "Unable to set requested videomode: %ix%i: %s \n", x, y, SDL_GetError()); } res_x = x; res_y = y; half_res_x = x / 2; half_res_y = y / 2; } void bx_sdl_gui_c::mouse_enabled_changed_specific(bool val) { if (val == 1) { SDL_ShowCursor(0); SDL_WM_GrabInput(SDL_GRAB_ON); } else { SDL_ShowCursor(1); SDL_WM_GrabInput(SDL_GRAB_OFF); } sdl_grab = val; } void bx_sdl_gui_c::exit(void) { if (sdl_screen) SDL_FreeSurface(sdl_screen); } /// key mapping for SDL typedef struct { const char *name; u32 value; } keyTableEntry; #define DEF_SDL_KEY(key) {#key, key}, keyTableEntry keytable[] = { // this include provides all the entries. #include "sdlkeys.hpp" // one final entry to mark the end {NULL, 0}}; // function to convert key names into SDLKey values. // This first try will be horribly inefficient, but it only has // to be done while loading a keymap. Once the simulation starts, // this function won't be called. static u32 convertStringToSDLKey(const char *string) { keyTableEntry *ptr; for (ptr = &keytable[0]; ptr->name != NULL; ptr++) { #if defined(DEBUG_SDL_KEY) printf("SDL: comparing string '%s' to SDL key '%s' \n", string, ptr->name); #endif if (!strcmp(string, ptr->name)) return ptr->value; } return BX_KEYMAP_UNKNOWN; } #endif // defined(HAVE_SDL) ================================================ FILE: src/gui/sdl_fonts.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef BX_SDL_H #define BX_SDL_H unsigned char sdl_font8x16[256][16] = { {0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 126, 129, 165, 129, 129, 189, // 1 153, 129, 129, 126, 0, 0, 0, 0}, {0, 0, 126, 255, 219, 255, 255, 195, // 2 231, 255, 255, 126, 0, 0, 0, 0}, {0, 0, 0, 0, 108, 254, 254, 254, // 3 254, 124, 56, 16, 0, 0, 0, 0}, {0, 0, 0, 0, 16, 56, 124, 254, // 4 124, 56, 16, 0, 0, 0, 0, 0}, {0, 0, 0, 24, 60, 60, 231, 231, // 5 231, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 0, 24, 60, 126, 255, 255, // 6 126, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 24, 60, // 7 60, 24, 0, 0, 0, 0, 0, 0}, {255, 255, 255, 255, 255, 255, 231, 195, // 8 195, 231, 255, 255, 255, 255, 255, 255}, {0, 0, 0, 0, 0, 60, 102, 66, // 9 66, 102, 60, 0, 0, 0, 0, 0}, {255, 255, 255, 255, 255, 195, 153, 189, // 10 189, 153, 195, 255, 255, 255, 255, 255}, {0, 0, 30, 14, 26, 50, 120, 204, // 11 204, 204, 204, 120, 0, 0, 0, 0}, {0, 0, 60, 102, 102, 102, 102, 60, // 12 24, 126, 24, 24, 0, 0, 0, 0}, {0, 0, 63, 51, 63, 48, 48, 48, // 13 48, 112, 240, 224, 0, 0, 0, 0}, {0, 0, 127, 99, 127, 99, 99, 99, // 14 99, 103, 231, 230, 192, 0, 0, 0}, {0, 0, 0, 24, 24, 219, 60, 231, // 15 60, 219, 24, 24, 0, 0, 0, 0}, {0, 128, 192, 224, 240, 248, 254, 248, // 16 240, 224, 192, 128, 0, 0, 0, 0}, {0, 2, 6, 14, 30, 62, 254, 62, // 17 30, 14, 6, 2, 0, 0, 0, 0}, {0, 0, 24, 60, 126, 24, 24, 24, // 18 126, 60, 24, 0, 0, 0, 0, 0}, {0, 0, 102, 102, 102, 102, 102, 102, // 19 102, 0, 102, 102, 0, 0, 0, 0}, {0, 0, 127, 219, 219, 219, 123, 27, // 20 27, 27, 27, 27, 0, 0, 0, 0}, {0, 124, 198, 96, 56, 108, 198, 198, // 21 108, 56, 12, 198, 124, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 22 254, 254, 254, 254, 0, 0, 0, 0}, {0, 0, 24, 60, 126, 24, 24, 24, // 23 126, 60, 24, 126, 0, 0, 0, 0}, {0, 0, 24, 60, 126, 24, 24, 24, // 24 24, 24, 24, 24, 0, 0, 0, 0}, {0, 0, 24, 24, 24, 24, 24, 24, // 25 24, 126, 60, 24, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 24, 12, 254, // 26 12, 24, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 48, 96, 254, // 27 96, 48, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 192, 192, // 28 192, 254, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 40, 108, 254, // 29 108, 40, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 16, 56, 56, 124, // 30 124, 254, 254, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 254, 254, 124, 124, // 31 56, 56, 16, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 32 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 24, 60, 60, 60, 24, 24, // 33 24, 0, 24, 24, 0, 0, 0, 0}, {0, 102, 102, 102, 36, 0, 0, 0, // 34 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 108, 108, 254, 108, 108, // 35 108, 254, 108, 108, 0, 0, 0, 0}, {24, 24, 124, 198, 194, 192, 124, 6, // 36 6, 134, 198, 124, 24, 24, 0, 0}, {0, 0, 0, 0, 194, 198, 12, 24, // 37 48, 96, 198, 134, 0, 0, 0, 0}, {0, 0, 56, 108, 108, 56, 118, 220, // 38 204, 204, 204, 118, 0, 0, 0, 0}, {0, 48, 48, 48, 96, 0, 0, 0, // 39 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 12, 24, 48, 48, 48, 48, // 40 48, 48, 24, 12, 0, 0, 0, 0}, {0, 0, 48, 24, 12, 12, 12, 12, // 41 12, 12, 24, 48, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 102, 60, 255, // 42 60, 102, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 24, 24, 126, // 43 24, 24, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 44 0, 24, 24, 24, 48, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 254, // 45 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 46 0, 0, 24, 24, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 6, 12, 24, // 47 48, 96, 192, 128, 0, 0, 0, 0}, {0, 0, 56, 108, 198, 198, 214, 214, // 48 198, 198, 108, 56, 0, 0, 0, 0}, {0, 0, 24, 56, 120, 24, 24, 24, // 49 24, 24, 24, 126, 0, 0, 0, 0}, {0, 0, 124, 198, 6, 12, 24, 48, // 50 96, 192, 198, 254, 0, 0, 0, 0}, {0, 0, 124, 198, 6, 6, 60, 6, // 51 6, 6, 198, 124, 0, 0, 0, 0}, {0, 0, 12, 28, 60, 108, 204, 254, // 52 12, 12, 12, 30, 0, 0, 0, 0}, {0, 0, 254, 192, 192, 192, 252, 6, // 53 6, 6, 198, 124, 0, 0, 0, 0}, {0, 0, 56, 96, 192, 192, 252, 198, // 54 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 254, 198, 6, 6, 12, 24, // 55 48, 48, 48, 48, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 198, 124, 198, // 56 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 198, 126, 6, // 57 6, 6, 12, 120, 0, 0, 0, 0}, {0, 0, 0, 0, 24, 24, 0, 0, // 58 0, 24, 24, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 24, 24, 0, 0, // 59 0, 24, 24, 48, 0, 0, 0, 0}, {0, 0, 0, 6, 12, 24, 48, 96, // 60 48, 24, 12, 6, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 126, 0, 0, // 61 126, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 96, 48, 24, 12, 6, // 62 12, 24, 48, 96, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 12, 24, 24, // 63 24, 0, 24, 24, 0, 0, 0, 0}, {0, 0, 0, 124, 198, 198, 222, 222, // 64 222, 220, 192, 124, 0, 0, 0, 0}, {0, 0, 16, 56, 108, 198, 198, 254, // 65 198, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 252, 102, 102, 102, 124, 102, // 66 102, 102, 102, 252, 0, 0, 0, 0}, {0, 0, 60, 102, 194, 192, 192, 192, // 67 192, 194, 102, 60, 0, 0, 0, 0}, {0, 0, 248, 108, 102, 102, 102, 102, // 68 102, 102, 108, 248, 0, 0, 0, 0}, {0, 0, 254, 102, 98, 104, 120, 104, // 69 96, 98, 102, 254, 0, 0, 0, 0}, {0, 0, 254, 102, 98, 104, 120, 104, // 70 96, 96, 96, 240, 0, 0, 0, 0}, {0, 0, 60, 102, 194, 192, 192, 222, // 71 198, 198, 102, 58, 0, 0, 0, 0}, {0, 0, 198, 198, 198, 198, 254, 198, // 72 198, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 60, 24, 24, 24, 24, 24, // 73 24, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 30, 12, 12, 12, 12, 12, // 74 204, 204, 204, 120, 0, 0, 0, 0}, {0, 0, 230, 102, 102, 108, 120, 120, // 75 108, 102, 102, 230, 0, 0, 0, 0}, {0, 0, 240, 96, 96, 96, 96, 96, // 76 96, 98, 102, 254, 0, 0, 0, 0}, {0, 0, 198, 238, 254, 254, 214, 198, // 77 198, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 198, 230, 246, 254, 222, 206, // 78 198, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 198, 198, 198, // 79 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 252, 102, 102, 102, 124, 96, // 80 96, 96, 96, 240, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 198, 198, 198, // 81 198, 214, 222, 124, 12, 14, 0, 0}, {0, 0, 252, 102, 102, 102, 124, 108, // 82 102, 102, 102, 230, 0, 0, 0, 0}, {0, 0, 124, 198, 198, 96, 56, 12, // 83 6, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 126, 126, 90, 24, 24, 24, // 84 24, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 198, 198, 198, 198, 198, 198, // 85 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 198, 198, 198, 198, 198, 198, // 86 198, 108, 56, 16, 0, 0, 0, 0}, {0, 0, 198, 198, 198, 198, 214, 214, // 87 214, 254, 238, 108, 0, 0, 0, 0}, {0, 0, 198, 198, 108, 124, 56, 56, // 88 124, 108, 198, 198, 0, 0, 0, 0}, {0, 0, 102, 102, 102, 102, 60, 24, // 89 24, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 254, 198, 134, 12, 24, 48, // 90 96, 194, 198, 254, 0, 0, 0, 0}, {0, 0, 60, 48, 48, 48, 48, 48, // 91 48, 48, 48, 60, 0, 0, 0, 0}, {0, 0, 0, 128, 192, 224, 112, 56, // 92 28, 14, 6, 2, 0, 0, 0, 0}, {0, 0, 60, 12, 12, 12, 12, 12, // 93 12, 12, 12, 60, 0, 0, 0, 0}, {16, 56, 108, 198, 0, 0, 0, 0, // 94 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 95 0, 0, 0, 0, 0, 255, 0, 0}, {0, 48, 24, 12, 0, 0, 0, 0, // 96 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 120, 12, 124, // 97 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 224, 96, 96, 120, 108, 102, // 98 102, 102, 102, 124, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 198, 192, // 99 192, 192, 198, 124, 0, 0, 0, 0}, {0, 0, 28, 12, 12, 60, 108, 204, // 100 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 198, 254, // 101 192, 192, 198, 124, 0, 0, 0, 0}, {0, 0, 28, 54, 50, 48, 120, 48, // 102 48, 48, 48, 120, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 118, 204, 204, // 103 204, 204, 204, 124, 12, 204, 120, 0}, {0, 0, 224, 96, 96, 108, 118, 102, // 104 102, 102, 102, 230, 0, 0, 0, 0}, {0, 0, 24, 24, 0, 56, 24, 24, // 105 24, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 6, 6, 0, 14, 6, 6, // 106 6, 6, 6, 6, 102, 102, 60, 0}, {0, 0, 224, 96, 96, 102, 108, 120, // 107 120, 108, 102, 230, 0, 0, 0, 0}, {0, 0, 56, 24, 24, 24, 24, 24, // 108 24, 24, 24, 60, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 236, 254, 214, // 109 214, 214, 214, 198, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 220, 102, 102, // 110 102, 102, 102, 102, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 198, 198, // 111 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 220, 102, 102, // 112 102, 102, 102, 124, 96, 96, 240, 0}, {0, 0, 0, 0, 0, 118, 204, 204, // 113 204, 204, 204, 124, 12, 12, 30, 0}, {0, 0, 0, 0, 0, 220, 118, 102, // 114 96, 96, 96, 240, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 198, 96, // 115 56, 12, 198, 124, 0, 0, 0, 0}, {0, 0, 16, 48, 48, 252, 48, 48, // 116 48, 48, 54, 28, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 204, 204, 204, // 117 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 198, 198, 198, // 118 198, 198, 108, 56, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 198, 198, 214, // 119 214, 214, 254, 108, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 198, 108, 56, // 120 56, 56, 108, 198, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 198, 198, 198, // 121 198, 198, 198, 126, 6, 12, 248, 0}, {0, 0, 0, 0, 0, 254, 204, 24, // 122 48, 96, 198, 254, 0, 0, 0, 0}, {0, 0, 14, 24, 24, 24, 112, 24, // 123 24, 24, 24, 14, 0, 0, 0, 0}, {0, 0, 24, 24, 24, 24, 24, 24, // 124 24, 24, 24, 24, 0, 0, 0, 0}, {0, 0, 112, 24, 24, 24, 14, 24, // 125 24, 24, 24, 112, 0, 0, 0, 0}, {0, 118, 220, 0, 0, 0, 0, 0, // 126 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 16, 56, 108, 198, // 127 198, 198, 254, 0, 0, 0, 0, 0}, {0, 0, 60, 102, 194, 192, 192, 192, // 128 192, 194, 102, 60, 24, 112, 0, 0}, {0, 0, 204, 0, 0, 204, 204, 204, // 129 204, 204, 204, 118, 0, 0, 0, 0}, {0, 12, 24, 48, 0, 124, 198, 254, // 130 192, 192, 198, 124, 0, 0, 0, 0}, {0, 16, 56, 108, 0, 120, 12, 124, // 131 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 204, 0, 0, 120, 12, 124, // 132 204, 204, 204, 118, 0, 0, 0, 0}, {0, 96, 48, 24, 0, 120, 12, 124, // 133 204, 204, 204, 118, 0, 0, 0, 0}, {0, 56, 108, 56, 0, 120, 12, 124, // 134 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 198, 192, // 135 192, 192, 198, 124, 24, 112, 0, 0}, {0, 16, 56, 108, 0, 124, 198, 254, // 136 192, 192, 198, 124, 0, 0, 0, 0}, {0, 0, 198, 0, 0, 124, 198, 254, // 137 192, 192, 198, 124, 0, 0, 0, 0}, {0, 96, 48, 24, 0, 124, 198, 254, // 138 192, 192, 198, 124, 0, 0, 0, 0}, {0, 0, 102, 0, 0, 56, 24, 24, // 139 24, 24, 24, 60, 0, 0, 0, 0}, {0, 24, 60, 102, 0, 56, 24, 24, // 140 24, 24, 24, 60, 0, 0, 0, 0}, {0, 96, 48, 24, 0, 56, 24, 24, // 141 24, 24, 24, 60, 0, 0, 0, 0}, {0, 198, 0, 16, 56, 108, 198, 198, // 142 254, 198, 198, 198, 0, 0, 0, 0}, {56, 108, 56, 16, 56, 108, 198, 198, // 143 254, 198, 198, 198, 0, 0, 0, 0}, {12, 24, 0, 254, 102, 98, 104, 120, // 144 104, 98, 102, 254, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 236, 54, 54, // 145 126, 216, 216, 110, 0, 0, 0, 0}, {0, 0, 62, 108, 204, 204, 254, 204, // 146 204, 204, 204, 206, 0, 0, 0, 0}, {0, 16, 56, 108, 0, 124, 198, 198, // 147 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 198, 0, 0, 124, 198, 198, // 148 198, 198, 198, 124, 0, 0, 0, 0}, {0, 96, 48, 24, 0, 124, 198, 198, // 149 198, 198, 198, 124, 0, 0, 0, 0}, {0, 48, 120, 204, 0, 204, 204, 204, // 150 204, 204, 204, 118, 0, 0, 0, 0}, {0, 96, 48, 24, 0, 204, 204, 204, // 151 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 198, 0, 0, 198, 198, 198, // 152 198, 198, 198, 126, 6, 12, 120, 0}, {0, 198, 0, 124, 198, 198, 198, 198, // 153 198, 198, 198, 124, 0, 0, 0, 0}, {0, 198, 0, 198, 198, 198, 198, 198, // 154 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 124, 206, 222, // 155 246, 230, 198, 124, 0, 0, 0, 0}, {0, 56, 108, 100, 96, 240, 96, 96, // 156 96, 96, 230, 252, 0, 0, 0, 0}, {0, 4, 124, 206, 206, 214, 214, 214, // 157 214, 230, 230, 124, 64, 0, 0, 0}, {0, 0, 0, 0, 0, 198, 108, 56, // 158 56, 108, 198, 0, 0, 0, 0, 0}, {0, 14, 27, 24, 24, 24, 126, 24, // 159 24, 24, 216, 112, 0, 0, 0, 0}, {0, 24, 48, 96, 0, 120, 12, 124, // 160 204, 204, 204, 118, 0, 0, 0, 0}, {0, 12, 24, 48, 0, 56, 24, 24, // 161 24, 24, 24, 60, 0, 0, 0, 0}, {0, 24, 48, 96, 0, 124, 198, 198, // 162 198, 198, 198, 124, 0, 0, 0, 0}, {0, 24, 48, 96, 0, 204, 204, 204, // 163 204, 204, 204, 118, 0, 0, 0, 0}, {0, 0, 118, 220, 0, 220, 102, 102, // 164 102, 102, 102, 102, 0, 0, 0, 0}, {118, 220, 0, 198, 230, 246, 254, 222, // 165 206, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 60, 108, 108, 62, 0, 126, // 166 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 56, 108, 108, 56, 0, 124, // 167 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 48, 48, 0, 48, 48, 96, // 168 192, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 124, 130, 178, 170, 178, 170, // 169 170, 130, 124, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 254, 6, // 170 6, 6, 6, 0, 0, 0, 0, 0}, {0, 96, 224, 98, 102, 108, 24, 48, // 171 96, 220, 134, 12, 24, 62, 0, 0}, {0, 96, 224, 98, 102, 108, 24, 48, // 172 102, 206, 154, 63, 6, 6, 0, 0}, {0, 0, 24, 24, 0, 24, 24, 24, // 173 60, 60, 60, 24, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 54, 108, 216, // 174 108, 54, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 216, 108, 54, // 175 108, 216, 0, 0, 0, 0, 0, 0}, {17, 68, 17, 68, 17, 68, 17, 68, // 176 17, 68, 17, 68, 17, 68, 17, 68}, {85, 170, 85, 170, 85, 170, 85, 170, // 177 85, 170, 85, 170, 85, 170, 85, 170}, {221, 119, 221, 119, 221, 119, 221, 119, // 178 221, 119, 221, 119, 221, 119, 221, 119}, {24, 24, 24, 24, 24, 24, 24, 24, // 179 24, 24, 24, 24, 24, 24, 24, 24}, {24, 24, 24, 24, 24, 24, 24, 248, // 180 24, 24, 24, 24, 24, 24, 24, 24}, {96, 192, 16, 56, 108, 198, 198, 254, // 181 198, 198, 198, 198, 0, 0, 0, 0}, {124, 198, 16, 56, 108, 198, 198, 254, // 182 198, 198, 198, 198, 0, 0, 0, 0}, {12, 6, 16, 56, 108, 198, 198, 254, // 183 198, 198, 198, 198, 0, 0, 0, 0}, {0, 0, 124, 130, 154, 162, 162, 162, // 184 154, 130, 124, 0, 0, 0, 0, 0}, {54, 54, 54, 54, 54, 246, 6, 246, // 185 54, 54, 54, 54, 54, 54, 54, 54}, {54, 54, 54, 54, 54, 54, 54, 54, // 186 54, 54, 54, 54, 54, 54, 54, 54}, {0, 0, 0, 0, 0, 254, 6, 246, // 187 54, 54, 54, 54, 54, 54, 54, 54}, {54, 54, 54, 54, 54, 246, 6, 254, // 188 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 24, 24, 124, 198, 192, 192, // 189 198, 124, 24, 24, 0, 0, 0, 0}, {0, 0, 0, 102, 102, 60, 24, 126, // 190 24, 126, 24, 24, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 248, // 191 24, 24, 24, 24, 24, 24, 24, 24}, {24, 24, 24, 24, 24, 24, 24, 31, // 192 0, 0, 0, 0, 0, 0, 0, 0}, {24, 24, 24, 24, 24, 24, 24, 255, // 193 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 255, // 194 24, 24, 24, 24, 24, 24, 24, 24}, {24, 24, 24, 24, 24, 24, 24, 31, // 195 24, 24, 24, 24, 24, 24, 24, 24}, {0, 0, 0, 0, 0, 0, 0, 255, // 196 0, 0, 0, 0, 0, 0, 0, 0}, {24, 24, 24, 24, 24, 24, 24, 255, // 197 24, 24, 24, 24, 24, 24, 24, 24}, {0, 0, 118, 220, 0, 120, 12, 124, // 198 204, 204, 204, 118, 0, 0, 0, 0}, {118, 220, 0, 56, 108, 198, 198, 254, // 199 198, 198, 198, 198, 0, 0, 0, 0}, {54, 54, 54, 54, 54, 55, 48, 63, // 200 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 63, 48, 55, // 201 54, 54, 54, 54, 54, 54, 54, 54}, {54, 54, 54, 54, 54, 247, 0, 255, // 202 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 255, 0, 247, // 203 54, 54, 54, 54, 54, 54, 54, 54}, {54, 54, 54, 54, 54, 55, 48, 55, // 204 54, 54, 54, 54, 54, 54, 54, 54}, {0, 0, 0, 0, 0, 255, 0, 255, // 205 0, 0, 0, 0, 0, 0, 0, 0}, {54, 54, 54, 54, 54, 247, 0, 247, // 206 54, 54, 54, 54, 54, 54, 54, 54}, {0, 0, 0, 0, 198, 124, 198, 198, // 207 198, 198, 124, 198, 0, 0, 0, 0}, {0, 0, 52, 24, 44, 6, 62, 102, // 208 102, 102, 102, 60, 0, 0, 0, 0}, {0, 0, 248, 108, 102, 102, 246, 102, // 209 102, 102, 108, 248, 0, 0, 0, 0}, {56, 108, 0, 254, 102, 98, 104, 120, // 210 104, 98, 102, 254, 0, 0, 0, 0}, {0, 198, 0, 254, 102, 98, 104, 120, // 211 104, 98, 102, 254, 0, 0, 0, 0}, {48, 24, 0, 254, 102, 98, 104, 120, // 212 104, 98, 102, 254, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 56, 24, 24, // 213 24, 24, 24, 60, 0, 0, 0, 0}, {12, 24, 0, 60, 24, 24, 24, 24, // 214 24, 24, 24, 60, 0, 0, 0, 0}, {60, 102, 0, 60, 24, 24, 24, 24, // 215 24, 24, 24, 60, 0, 0, 0, 0}, {0, 102, 0, 60, 24, 24, 24, 24, // 216 24, 24, 24, 60, 0, 0, 0, 0}, {24, 24, 24, 24, 24, 24, 24, 248, // 217 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 31, // 218 24, 24, 24, 24, 24, 24, 24, 24}, {255, 255, 255, 255, 255, 255, 255, 255, // 219 255, 255, 255, 255, 255, 255, 255, 255}, {0, 0, 0, 0, 0, 0, 0, 255, // 220 255, 255, 255, 255, 255, 255, 255, 255}, {0, 24, 24, 24, 24, 24, 0, 0, // 221 24, 24, 24, 24, 24, 0, 0, 0}, {48, 24, 0, 60, 24, 24, 24, 24, // 222 24, 24, 24, 60, 0, 0, 0, 0}, {255, 255, 255, 255, 255, 255, 255, 0, // 223 0, 0, 0, 0, 0, 0, 0, 0}, {24, 48, 0, 124, 198, 198, 198, 198, // 224 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 120, 204, 204, 204, 216, 204, // 225 198, 198, 198, 204, 0, 0, 0, 0}, {56, 108, 0, 124, 198, 198, 198, 198, // 226 198, 198, 198, 124, 0, 0, 0, 0}, {48, 24, 0, 124, 198, 198, 198, 198, // 227 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 118, 220, 0, 124, 198, 198, // 228 198, 198, 198, 124, 0, 0, 0, 0}, {118, 220, 0, 124, 198, 198, 198, 198, // 229 198, 198, 198, 124, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 102, 102, 102, // 230 102, 102, 102, 124, 96, 96, 192, 0}, {0, 0, 224, 96, 96, 124, 102, 102, // 231 102, 102, 102, 124, 96, 96, 240, 0}, {0, 0, 240, 96, 124, 102, 102, 102, // 232 102, 124, 96, 240, 0, 0, 0, 0}, {24, 48, 0, 198, 198, 198, 198, 198, // 233 198, 198, 198, 124, 0, 0, 0, 0}, {56, 108, 0, 198, 198, 198, 198, 198, // 234 198, 198, 198, 124, 0, 0, 0, 0}, {48, 24, 0, 198, 198, 198, 198, 198, // 235 198, 198, 198, 124, 0, 0, 0, 0}, {0, 12, 24, 48, 0, 198, 198, 198, // 236 198, 198, 198, 126, 6, 12, 248, 0}, {12, 24, 0, 102, 102, 102, 102, 60, // 237 24, 24, 24, 60, 0, 0, 0, 0}, {0, 255, 0, 0, 0, 0, 0, 0, // 238 0, 0, 0, 0, 0, 0, 0, 0}, {0, 12, 24, 48, 0, 0, 0, 0, // 239 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 254, // 240 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 24, 24, 126, 24, // 241 24, 0, 0, 126, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 242 0, 0, 0, 0, 255, 0, 255, 0}, {0, 224, 48, 98, 54, 236, 24, 48, // 243 102, 206, 154, 63, 6, 6, 0, 0}, {0, 0, 127, 219, 219, 219, 123, 27, // 244 27, 27, 27, 27, 0, 0, 0, 0}, {0, 124, 198, 96, 56, 108, 198, 198, // 245 108, 56, 12, 198, 124, 0, 0, 0}, {0, 0, 0, 0, 0, 24, 0, 126, // 246 0, 24, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 247 0, 0, 0, 24, 12, 120, 0, 0}, {0, 56, 108, 108, 56, 0, 0, 0, // 248 0, 0, 0, 0, 0, 0, 0, 0}, {0, 198, 0, 0, 0, 0, 0, 0, // 249 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 24, // 250 0, 0, 0, 0, 0, 0, 0, 0}, {0, 24, 56, 24, 24, 24, 60, 0, // 251 0, 0, 0, 0, 0, 0, 0, 0}, {0, 124, 6, 60, 6, 6, 124, 0, // 252 0, 0, 0, 0, 0, 0, 0, 0}, {0, 60, 102, 12, 24, 50, 126, 0, // 253 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 126, 126, 126, 126, // 254 126, 126, 126, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, // 255 0, 0, 0, 0, 0, 0, 0, 0}}; unsigned char sdl_font8x8[256][8] = { {0, 0, 0, 0, 0, 0, 0, 0}, // 0 {126, 129, 165, 129, 189, 153, 129, 126}, // 1 {126, 255, 219, 255, 195, 231, 255, 126}, // 2 {108, 254, 254, 254, 124, 56, 16, 0}, // 3 {16, 56, 124, 254, 124, 56, 16, 0}, // 4 {56, 124, 56, 254, 254, 214, 16, 56}, // 5 {16, 56, 124, 254, 254, 124, 16, 56}, // 6 {0, 0, 24, 60, 60, 24, 0, 0}, // 7 {255, 255, 231, 195, 195, 231, 255, 255}, // 8 {0, 60, 102, 66, 66, 102, 60, 0}, // 9 {255, 195, 153, 189, 189, 153, 195, 255}, // 10 {15, 7, 15, 125, 204, 204, 204, 120}, // 11 {60, 102, 102, 102, 60, 24, 126, 24}, // 12 {63, 51, 63, 48, 48, 112, 240, 224}, // 13 {127, 99, 127, 99, 99, 103, 230, 192}, // 14 {24, 219, 60, 231, 231, 60, 219, 24}, // 15 {128, 224, 248, 254, 248, 224, 128, 0}, // 16 {2, 14, 62, 254, 62, 14, 2, 0}, // 17 {24, 60, 126, 24, 24, 126, 60, 24}, // 18 {102, 102, 102, 102, 102, 0, 102, 0}, // 19 {127, 219, 219, 123, 27, 27, 27, 0}, // 20 {62, 97, 60, 102, 102, 60, 134, 124}, // 21 {0, 0, 0, 0, 126, 126, 126, 0}, // 22 {24, 60, 126, 24, 126, 60, 24, 255}, // 23 {24, 60, 126, 24, 24, 24, 24, 0}, // 24 {24, 24, 24, 24, 126, 60, 24, 0}, // 25 {0, 24, 12, 254, 12, 24, 0, 0}, // 26 {0, 48, 96, 254, 96, 48, 0, 0}, // 27 {0, 0, 192, 192, 192, 254, 0, 0}, // 28 {0, 36, 102, 255, 102, 36, 0, 0}, // 29 {0, 24, 60, 126, 255, 255, 0, 0}, // 30 {0, 255, 255, 126, 60, 24, 0, 0}, // 31 {0, 0, 0, 0, 0, 0, 0, 0}, // 32 {24, 60, 60, 24, 24, 0, 24, 0}, // 33 {102, 102, 36, 0, 0, 0, 0, 0}, // 34 {108, 108, 254, 108, 254, 108, 108, 0}, // 35 {24, 62, 96, 60, 6, 124, 24, 0}, // 36 {0, 198, 204, 24, 48, 102, 198, 0}, // 37 {56, 108, 56, 118, 220, 204, 118, 0}, // 38 {24, 24, 48, 0, 0, 0, 0, 0}, // 39 {12, 24, 48, 48, 48, 24, 12, 0}, // 40 {48, 24, 12, 12, 12, 24, 48, 0}, // 41 {0, 102, 60, 231, 60, 102, 0, 0}, // 42 {0, 24, 24, 126, 24, 24, 0, 0}, // 43 {0, 0, 0, 0, 0, 24, 24, 48}, // 44 {0, 0, 0, 126, 0, 0, 0, 0}, // 45 {0, 0, 0, 0, 0, 24, 24, 0}, // 46 {6, 12, 24, 48, 96, 192, 128, 0}, // 47 {124, 198, 206, 222, 246, 230, 124, 0}, // 48 {24, 56, 24, 24, 24, 24, 126, 0}, // 49 {124, 198, 6, 28, 48, 102, 254, 0}, // 50 {124, 198, 6, 60, 6, 198, 124, 0}, // 51 {28, 60, 108, 204, 254, 12, 30, 0}, // 52 {254, 192, 192, 252, 6, 198, 124, 0}, // 53 {56, 96, 192, 252, 198, 198, 124, 0}, // 54 {254, 198, 12, 24, 48, 48, 48, 0}, // 55 {124, 198, 198, 124, 198, 198, 124, 0}, // 56 {124, 198, 198, 126, 6, 12, 120, 0}, // 57 {0, 24, 24, 0, 0, 24, 24, 0}, // 58 {0, 24, 24, 0, 0, 24, 24, 48}, // 59 {6, 12, 24, 48, 24, 12, 6, 0}, // 60 {0, 0, 126, 0, 0, 126, 0, 0}, // 61 {96, 48, 24, 12, 24, 48, 96, 0}, // 62 {124, 198, 12, 24, 24, 0, 24, 0}, // 63 {124, 198, 222, 222, 222, 192, 120, 0}, // 64 {56, 108, 198, 254, 198, 198, 198, 0}, // 65 {252, 102, 102, 124, 102, 102, 252, 0}, // 66 {60, 102, 192, 192, 192, 102, 60, 0}, // 67 {248, 108, 102, 102, 102, 108, 248, 0}, // 68 {254, 98, 104, 120, 104, 98, 254, 0}, // 69 {254, 98, 104, 120, 104, 96, 240, 0}, // 70 {60, 102, 192, 192, 206, 102, 58, 0}, // 71 {198, 198, 198, 254, 198, 198, 198, 0}, // 72 {60, 24, 24, 24, 24, 24, 60, 0}, // 73 {30, 12, 12, 12, 204, 204, 120, 0}, // 74 {230, 102, 108, 120, 108, 102, 230, 0}, // 75 {240, 96, 96, 96, 98, 102, 254, 0}, // 76 {198, 238, 254, 254, 214, 198, 198, 0}, // 77 {198, 230, 246, 222, 206, 198, 198, 0}, // 78 {124, 198, 198, 198, 198, 198, 124, 0}, // 79 {252, 102, 102, 124, 96, 96, 240, 0}, // 80 {124, 198, 198, 198, 198, 206, 124, 14}, // 81 {252, 102, 102, 124, 108, 102, 230, 0}, // 82 {60, 102, 48, 24, 12, 102, 60, 0}, // 83 {126, 126, 90, 24, 24, 24, 60, 0}, // 84 {198, 198, 198, 198, 198, 198, 124, 0}, // 85 {198, 198, 198, 198, 198, 108, 56, 0}, // 86 {198, 198, 198, 214, 214, 254, 108, 0}, // 87 {198, 198, 108, 56, 108, 198, 198, 0}, // 88 {102, 102, 102, 60, 24, 24, 60, 0}, // 89 {254, 198, 140, 24, 50, 102, 254, 0}, // 90 {60, 48, 48, 48, 48, 48, 60, 0}, // 91 {192, 96, 48, 24, 12, 6, 2, 0}, // 92 {60, 12, 12, 12, 12, 12, 60, 0}, // 93 {16, 56, 108, 198, 0, 0, 0, 0}, // 94 {0, 0, 0, 0, 0, 0, 0, 255}, // 95 {48, 24, 12, 0, 0, 0, 0, 0}, // 96 {0, 0, 120, 12, 124, 204, 118, 0}, // 97 {224, 96, 124, 102, 102, 102, 220, 0}, // 98 {0, 0, 124, 198, 192, 198, 124, 0}, // 99 {28, 12, 124, 204, 204, 204, 118, 0}, // 100 {0, 0, 124, 198, 254, 192, 124, 0}, // 101 {60, 102, 96, 248, 96, 96, 240, 0}, // 102 {0, 0, 118, 204, 204, 124, 12, 248}, // 103 {224, 96, 108, 118, 102, 102, 230, 0}, // 104 {24, 0, 56, 24, 24, 24, 60, 0}, // 105 {6, 0, 6, 6, 6, 102, 102, 60}, // 106 {224, 96, 102, 108, 120, 108, 230, 0}, // 107 {56, 24, 24, 24, 24, 24, 60, 0}, // 108 {0, 0, 236, 254, 214, 214, 214, 0}, // 109 {0, 0, 220, 102, 102, 102, 102, 0}, // 110 {0, 0, 124, 198, 198, 198, 124, 0}, // 111 {0, 0, 220, 102, 102, 124, 96, 240}, // 112 {0, 0, 118, 204, 204, 124, 12, 30}, // 113 {0, 0, 220, 118, 96, 96, 240, 0}, // 114 {0, 0, 126, 192, 124, 6, 252, 0}, // 115 {48, 48, 252, 48, 48, 54, 28, 0}, // 116 {0, 0, 204, 204, 204, 204, 118, 0}, // 117 {0, 0, 198, 198, 198, 108, 56, 0}, // 118 {0, 0, 198, 214, 214, 254, 108, 0}, // 119 {0, 0, 198, 108, 56, 108, 198, 0}, // 120 {0, 0, 198, 198, 198, 126, 6, 252}, // 121 {0, 0, 126, 76, 24, 50, 126, 0}, // 122 {14, 24, 24, 112, 24, 24, 14, 0}, // 123 {24, 24, 24, 24, 24, 24, 24, 0}, // 124 {112, 24, 24, 14, 24, 24, 112, 0}, // 125 {118, 220, 0, 0, 0, 0, 0, 0}, // 126 {0, 16, 56, 108, 198, 198, 254, 0}, // 127 {124, 198, 192, 192, 198, 124, 12, 120}, // 128 {204, 0, 204, 204, 204, 204, 118, 0}, // 129 {12, 24, 124, 198, 254, 192, 124, 0}, // 130 {124, 130, 120, 12, 124, 204, 118, 0}, // 131 {198, 0, 120, 12, 124, 204, 118, 0}, // 132 {48, 24, 120, 12, 124, 204, 118, 0}, // 133 {48, 48, 120, 12, 124, 204, 118, 0}, // 134 {0, 0, 126, 192, 192, 126, 12, 56}, // 135 {124, 130, 124, 198, 254, 192, 124, 0}, // 136 {198, 0, 124, 198, 254, 192, 124, 0}, // 137 {48, 24, 124, 198, 254, 192, 124, 0}, // 138 {102, 0, 56, 24, 24, 24, 60, 0}, // 139 {124, 130, 56, 24, 24, 24, 60, 0}, // 140 {48, 24, 0, 56, 24, 24, 60, 0}, // 141 {198, 56, 108, 198, 254, 198, 198, 0}, // 142 {56, 108, 124, 198, 254, 198, 198, 0}, // 143 {24, 48, 254, 192, 248, 192, 254, 0}, // 144 {0, 0, 126, 18, 254, 144, 254, 0}, // 145 {62, 108, 204, 254, 204, 204, 206, 0}, // 146 {124, 130, 124, 198, 198, 198, 124, 0}, // 147 {198, 0, 124, 198, 198, 198, 124, 0}, // 148 {48, 24, 124, 198, 198, 198, 124, 0}, // 149 {120, 132, 0, 204, 204, 204, 118, 0}, // 150 {96, 48, 204, 204, 204, 204, 118, 0}, // 151 {198, 0, 198, 198, 198, 126, 6, 252}, // 152 {198, 56, 108, 198, 198, 108, 56, 0}, // 153 {198, 0, 198, 198, 198, 198, 124, 0}, // 154 {0, 2, 124, 206, 214, 230, 124, 128}, // 155 {56, 108, 100, 240, 96, 102, 252, 0}, // 156 {58, 108, 206, 214, 230, 108, 184, 0}, // 157 {0, 198, 108, 56, 108, 198, 0, 0}, // 158 {14, 27, 24, 60, 24, 216, 112, 0}, // 159 {24, 48, 120, 12, 124, 204, 118, 0}, // 160 {12, 24, 0, 56, 24, 24, 60, 0}, // 161 {12, 24, 124, 198, 198, 198, 124, 0}, // 162 {24, 48, 204, 204, 204, 204, 118, 0}, // 163 {118, 220, 0, 220, 102, 102, 102, 0}, // 164 {118, 220, 0, 230, 246, 222, 206, 0}, // 165 {60, 108, 108, 62, 0, 126, 0, 0}, // 166 {56, 108, 108, 56, 0, 124, 0, 0}, // 167 {24, 0, 24, 24, 48, 99, 62, 0}, // 168 {126, 129, 185, 165, 185, 165, 129, 126}, // 169 {0, 0, 0, 254, 6, 6, 0, 0}, // 170 {99, 230, 108, 126, 51, 102, 204, 15}, // 171 {99, 230, 108, 122, 54, 106, 223, 6}, // 172 {24, 0, 24, 24, 60, 60, 24, 0}, // 173 {0, 51, 102, 204, 102, 51, 0, 0}, // 174 {0, 204, 102, 51, 102, 204, 0, 0}, // 175 {34, 136, 34, 136, 34, 136, 34, 136}, // 176 {85, 170, 85, 170, 85, 170, 85, 170}, // 177 {119, 221, 119, 221, 119, 221, 119, 221}, // 178 {24, 24, 24, 24, 24, 24, 24, 24}, // 179 {24, 24, 56, 248, 56, 24, 24, 24}, // 180 {48, 96, 56, 108, 198, 254, 198, 0}, // 181 {124, 130, 56, 108, 198, 254, 198, 0}, // 182 {24, 12, 56, 108, 198, 254, 198, 0}, // 183 {126, 129, 157, 161, 161, 157, 129, 126}, // 184 {54, 54, 246, 6, 246, 54, 54, 54}, // 185 {54, 54, 54, 54, 54, 54, 54, 54}, // 186 {0, 0, 254, 6, 246, 54, 54, 54}, // 187 {54, 54, 246, 6, 254, 0, 0, 0}, // 188 {24, 24, 126, 192, 192, 126, 24, 24}, // 189 {102, 102, 60, 126, 24, 126, 24, 24}, // 190 {0, 0, 0, 240, 56, 24, 24, 24}, // 191 {24, 24, 28, 15, 0, 0, 0, 0}, // 192 {24, 24, 60, 255, 0, 0, 0, 0}, // 193 {0, 0, 0, 255, 60, 24, 24, 24}, // 194 {48, 48, 56, 63, 56, 48, 48, 48}, // 195 {0, 0, 0, 255, 0, 0, 0, 0}, // 196 {24, 24, 24, 60, 231, 60, 24, 24}, // 197 {240, 120, 120, 120, 60, 60, 60, 28}, // 198 {30, 60, 60, 60, 120, 120, 120, 112}, // 199 {15, 63, 63, 120, 120, 0, 1, 3}, // 200 {192, 224, 240, 240, 240, 240, 240, 224}, // 201 {0, 0, 0, 0, 0, 0, 0, 0}, // 202 {0, 0, 0, 0, 0, 0, 0, 0}, // 203 {30, 30, 14, 15, 15, 7, 7, 0}, // 204 {240, 240, 224, 224, 192, 192, 192, 0}, // 205 {6, 13, 27, 55, 47, 127, 126, 30}, // 206 {0, 252, 255, 255, 143, 119, 243, 3}, // 207 {0, 1, 7, 143, 143, 207, 207, 199}, // 208 {0, 248, 254, 254, 31, 15, 192, 248}, // 209 {0, 0, 0, 0, 0, 0, 0, 0}, // 210 {0, 0, 0, 0, 0, 0, 0, 0}, // 211 {30, 30, 30, 31, 15, 7, 7, 1}, // 212 {3, 3, 3, 7, 143, 255, 254, 252}, // 213 {195, 192, 192, 207, 143, 7, 7, 1}, // 214 {254, 255, 31, 15, 143, 254, 254, 248}, // 215 {102, 0, 60, 24, 24, 24, 60, 0}, // 216 {24, 24, 56, 240, 0, 0, 0, 0}, // 217 {0, 0, 0, 15, 28, 24, 24, 24}, // 218 {255, 255, 255, 255, 255, 255, 255, 255}, // 219 {0, 0, 0, 0, 255, 255, 255, 255}, // 220 {24, 24, 24, 0, 0, 24, 24, 24}, // 221 {48, 24, 60, 24, 24, 24, 60, 0}, // 222 {255, 255, 255, 255, 0, 0, 0, 0}, // 223 {0, 0, 0, 0, 0, 0, 0, 255}, // 224 {0, 0, 0, 0, 0, 255, 0, 255}, // 225 {0, 0, 0, 255, 0, 255, 0, 255}, // 226 {0, 255, 0, 255, 0, 255, 0, 255}, // 227 {0, 255, 0, 255, 0, 255, 0, 0}, // 228 {0, 255, 0, 255, 0, 0, 0, 0}, // 229 {0, 255, 0, 0, 0, 0, 0, 0}, // 230 {224, 128, 0, 0, 0, 0, 128, 224}, // 231 {248, 254, 255, 255, 255, 255, 254, 248}, // 232 {24, 48, 198, 198, 198, 198, 124, 0}, // 233 {124, 130, 0, 198, 198, 198, 124, 0}, // 234 {96, 48, 198, 198, 198, 198, 124, 0}, // 235 {24, 48, 198, 198, 198, 126, 6, 252}, // 236 {12, 24, 102, 102, 60, 24, 60, 0}, // 237 {255, 0, 0, 0, 0, 0, 0, 0}, // 238 {12, 24, 48, 0, 0, 0, 0, 0}, // 239 {0, 0, 0, 126, 0, 0, 0, 0}, // 240 {24, 24, 126, 24, 24, 0, 126, 0}, // 241 {0, 0, 0, 0, 0, 255, 0, 255}, // 242 {225, 50, 228, 58, 246, 42, 95, 134}, // 243 {127, 219, 219, 123, 27, 27, 27, 0}, // 244 {62, 97, 60, 102, 102, 60, 134, 124}, // 245 {0, 24, 0, 126, 0, 24, 0, 0}, // 246 {0, 0, 0, 0, 0, 24, 12, 56}, // 247 {56, 108, 108, 56, 0, 0, 0, 0}, // 248 {0, 198, 0, 0, 0, 0, 0, 0}, // 249 {0, 0, 0, 24, 0, 0, 0, 0}, // 250 {24, 56, 24, 24, 60, 0, 0, 0}, // 251 {120, 12, 56, 12, 120, 0, 0, 0}, // 252 {120, 12, 24, 48, 124, 0, 0, 0}, // 253 {0, 0, 60, 60, 60, 60, 0, 0}, // 254 {0, 0, 0, 0, 0, 0, 0, 0}}; // 255 #endif ================================================ FILE: src/gui/sdlkeys.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ DEF_SDL_KEY(SDLK_UNKNOWN) DEF_SDL_KEY(SDLK_FIRST) DEF_SDL_KEY(SDLK_BACKSPACE) DEF_SDL_KEY(SDLK_TAB) DEF_SDL_KEY(SDLK_CLEAR) DEF_SDL_KEY(SDLK_RETURN) DEF_SDL_KEY(SDLK_PAUSE) DEF_SDL_KEY(SDLK_ESCAPE) DEF_SDL_KEY(SDLK_SPACE) DEF_SDL_KEY(SDLK_EXCLAIM) DEF_SDL_KEY(SDLK_QUOTEDBL) DEF_SDL_KEY(SDLK_HASH) DEF_SDL_KEY(SDLK_DOLLAR) DEF_SDL_KEY(SDLK_AMPERSAND) DEF_SDL_KEY(SDLK_QUOTE) DEF_SDL_KEY(SDLK_LEFTPAREN) DEF_SDL_KEY(SDLK_RIGHTPAREN) DEF_SDL_KEY(SDLK_ASTERISK) DEF_SDL_KEY(SDLK_PLUS) DEF_SDL_KEY(SDLK_COMMA) DEF_SDL_KEY(SDLK_MINUS) DEF_SDL_KEY(SDLK_PERIOD) DEF_SDL_KEY(SDLK_SLASH) DEF_SDL_KEY(SDLK_0) DEF_SDL_KEY(SDLK_1) DEF_SDL_KEY(SDLK_2) DEF_SDL_KEY(SDLK_3) DEF_SDL_KEY(SDLK_4) DEF_SDL_KEY(SDLK_5) DEF_SDL_KEY(SDLK_6) DEF_SDL_KEY(SDLK_7) DEF_SDL_KEY(SDLK_8) DEF_SDL_KEY(SDLK_9) DEF_SDL_KEY(SDLK_COLON) DEF_SDL_KEY(SDLK_SEMICOLON) DEF_SDL_KEY(SDLK_LESS) DEF_SDL_KEY(SDLK_EQUALS) DEF_SDL_KEY(SDLK_GREATER) DEF_SDL_KEY(SDLK_QUESTION) DEF_SDL_KEY(SDLK_AT) // DEF_SDL_KEY( /* ) // DEF_SDL_KEY( Skip uppercase letters ) // DEF_SDL_KEY( */ ) DEF_SDL_KEY(SDLK_LEFTBRACKET) DEF_SDL_KEY(SDLK_BACKSLASH) DEF_SDL_KEY(SDLK_RIGHTBRACKET) DEF_SDL_KEY(SDLK_CARET) DEF_SDL_KEY(SDLK_UNDERSCORE) DEF_SDL_KEY(SDLK_BACKQUOTE) DEF_SDL_KEY(SDLK_a) DEF_SDL_KEY(SDLK_b) DEF_SDL_KEY(SDLK_c) DEF_SDL_KEY(SDLK_d) DEF_SDL_KEY(SDLK_e) DEF_SDL_KEY(SDLK_f) DEF_SDL_KEY(SDLK_g) DEF_SDL_KEY(SDLK_h) DEF_SDL_KEY(SDLK_i) DEF_SDL_KEY(SDLK_j) DEF_SDL_KEY(SDLK_k) DEF_SDL_KEY(SDLK_l) DEF_SDL_KEY(SDLK_m) DEF_SDL_KEY(SDLK_n) DEF_SDL_KEY(SDLK_o) DEF_SDL_KEY(SDLK_p) DEF_SDL_KEY(SDLK_q) DEF_SDL_KEY(SDLK_r) DEF_SDL_KEY(SDLK_s) DEF_SDL_KEY(SDLK_t) DEF_SDL_KEY(SDLK_u) DEF_SDL_KEY(SDLK_v) DEF_SDL_KEY(SDLK_w) DEF_SDL_KEY(SDLK_x) DEF_SDL_KEY(SDLK_y) DEF_SDL_KEY(SDLK_z) DEF_SDL_KEY(SDLK_DELETE) DEF_SDL_KEY(SDLK_WORLD_0) DEF_SDL_KEY(SDLK_WORLD_1) DEF_SDL_KEY(SDLK_WORLD_2) DEF_SDL_KEY(SDLK_WORLD_3) DEF_SDL_KEY(SDLK_WORLD_4) DEF_SDL_KEY(SDLK_WORLD_5) DEF_SDL_KEY(SDLK_WORLD_6) DEF_SDL_KEY(SDLK_WORLD_7) DEF_SDL_KEY(SDLK_WORLD_8) DEF_SDL_KEY(SDLK_WORLD_9) DEF_SDL_KEY(SDLK_WORLD_10) DEF_SDL_KEY(SDLK_WORLD_11) DEF_SDL_KEY(SDLK_WORLD_12) DEF_SDL_KEY(SDLK_WORLD_13) DEF_SDL_KEY(SDLK_WORLD_14) DEF_SDL_KEY(SDLK_WORLD_15) DEF_SDL_KEY(SDLK_WORLD_16) DEF_SDL_KEY(SDLK_WORLD_17) DEF_SDL_KEY(SDLK_WORLD_18) DEF_SDL_KEY(SDLK_WORLD_19) DEF_SDL_KEY(SDLK_WORLD_20) DEF_SDL_KEY(SDLK_WORLD_21) DEF_SDL_KEY(SDLK_WORLD_22) DEF_SDL_KEY(SDLK_WORLD_23) DEF_SDL_KEY(SDLK_WORLD_24) DEF_SDL_KEY(SDLK_WORLD_25) DEF_SDL_KEY(SDLK_WORLD_26) DEF_SDL_KEY(SDLK_WORLD_27) DEF_SDL_KEY(SDLK_WORLD_28) DEF_SDL_KEY(SDLK_WORLD_29) DEF_SDL_KEY(SDLK_WORLD_30) DEF_SDL_KEY(SDLK_WORLD_31) DEF_SDL_KEY(SDLK_WORLD_32) DEF_SDL_KEY(SDLK_WORLD_33) DEF_SDL_KEY(SDLK_WORLD_34) DEF_SDL_KEY(SDLK_WORLD_35) DEF_SDL_KEY(SDLK_WORLD_36) DEF_SDL_KEY(SDLK_WORLD_37) DEF_SDL_KEY(SDLK_WORLD_38) DEF_SDL_KEY(SDLK_WORLD_39) DEF_SDL_KEY(SDLK_WORLD_40) DEF_SDL_KEY(SDLK_WORLD_41) DEF_SDL_KEY(SDLK_WORLD_42) DEF_SDL_KEY(SDLK_WORLD_43) DEF_SDL_KEY(SDLK_WORLD_44) DEF_SDL_KEY(SDLK_WORLD_45) DEF_SDL_KEY(SDLK_WORLD_46) DEF_SDL_KEY(SDLK_WORLD_47) DEF_SDL_KEY(SDLK_WORLD_48) DEF_SDL_KEY(SDLK_WORLD_49) DEF_SDL_KEY(SDLK_WORLD_50) DEF_SDL_KEY(SDLK_WORLD_51) DEF_SDL_KEY(SDLK_WORLD_52) DEF_SDL_KEY(SDLK_WORLD_53) DEF_SDL_KEY(SDLK_WORLD_54) DEF_SDL_KEY(SDLK_WORLD_55) DEF_SDL_KEY(SDLK_WORLD_56) DEF_SDL_KEY(SDLK_WORLD_57) DEF_SDL_KEY(SDLK_WORLD_58) DEF_SDL_KEY(SDLK_WORLD_59) DEF_SDL_KEY(SDLK_WORLD_60) DEF_SDL_KEY(SDLK_WORLD_61) DEF_SDL_KEY(SDLK_WORLD_62) DEF_SDL_KEY(SDLK_WORLD_63) DEF_SDL_KEY(SDLK_WORLD_64) DEF_SDL_KEY(SDLK_WORLD_65) DEF_SDL_KEY(SDLK_WORLD_66) DEF_SDL_KEY(SDLK_WORLD_67) DEF_SDL_KEY(SDLK_WORLD_68) DEF_SDL_KEY(SDLK_WORLD_69) DEF_SDL_KEY(SDLK_WORLD_70) DEF_SDL_KEY(SDLK_WORLD_71) DEF_SDL_KEY(SDLK_WORLD_72) DEF_SDL_KEY(SDLK_WORLD_73) DEF_SDL_KEY(SDLK_WORLD_74) DEF_SDL_KEY(SDLK_WORLD_75) DEF_SDL_KEY(SDLK_WORLD_76) DEF_SDL_KEY(SDLK_WORLD_77) DEF_SDL_KEY(SDLK_WORLD_78) DEF_SDL_KEY(SDLK_WORLD_79) DEF_SDL_KEY(SDLK_WORLD_80) DEF_SDL_KEY(SDLK_WORLD_81) DEF_SDL_KEY(SDLK_WORLD_82) DEF_SDL_KEY(SDLK_WORLD_83) DEF_SDL_KEY(SDLK_WORLD_84) DEF_SDL_KEY(SDLK_WORLD_85) DEF_SDL_KEY(SDLK_WORLD_86) DEF_SDL_KEY(SDLK_WORLD_87) DEF_SDL_KEY(SDLK_WORLD_88) DEF_SDL_KEY(SDLK_WORLD_89) DEF_SDL_KEY(SDLK_WORLD_90) DEF_SDL_KEY(SDLK_WORLD_91) DEF_SDL_KEY(SDLK_WORLD_92) DEF_SDL_KEY(SDLK_WORLD_93) DEF_SDL_KEY(SDLK_WORLD_94) DEF_SDL_KEY(SDLK_WORLD_95) DEF_SDL_KEY(SDLK_KP0) DEF_SDL_KEY(SDLK_KP1) DEF_SDL_KEY(SDLK_KP2) DEF_SDL_KEY(SDLK_KP3) DEF_SDL_KEY(SDLK_KP4) DEF_SDL_KEY(SDLK_KP5) DEF_SDL_KEY(SDLK_KP6) DEF_SDL_KEY(SDLK_KP7) DEF_SDL_KEY(SDLK_KP8) DEF_SDL_KEY(SDLK_KP9) DEF_SDL_KEY(SDLK_KP_PERIOD) DEF_SDL_KEY(SDLK_KP_DIVIDE) DEF_SDL_KEY(SDLK_KP_MULTIPLY) DEF_SDL_KEY(SDLK_KP_MINUS) DEF_SDL_KEY(SDLK_KP_PLUS) DEF_SDL_KEY(SDLK_KP_ENTER) DEF_SDL_KEY(SDLK_KP_EQUALS) DEF_SDL_KEY(SDLK_UP) DEF_SDL_KEY(SDLK_DOWN) DEF_SDL_KEY(SDLK_RIGHT) DEF_SDL_KEY(SDLK_LEFT) DEF_SDL_KEY(SDLK_INSERT) DEF_SDL_KEY(SDLK_HOME) DEF_SDL_KEY(SDLK_END) DEF_SDL_KEY(SDLK_PAGEUP) DEF_SDL_KEY(SDLK_PAGEDOWN) DEF_SDL_KEY(SDLK_F1) DEF_SDL_KEY(SDLK_F2) DEF_SDL_KEY(SDLK_F3) DEF_SDL_KEY(SDLK_F4) DEF_SDL_KEY(SDLK_F5) DEF_SDL_KEY(SDLK_F6) DEF_SDL_KEY(SDLK_F7) DEF_SDL_KEY(SDLK_F8) DEF_SDL_KEY(SDLK_F9) DEF_SDL_KEY(SDLK_F10) DEF_SDL_KEY(SDLK_F11) DEF_SDL_KEY(SDLK_F12) DEF_SDL_KEY(SDLK_F13) DEF_SDL_KEY(SDLK_F14) DEF_SDL_KEY(SDLK_F15) DEF_SDL_KEY(SDLK_NUMLOCK) DEF_SDL_KEY(SDLK_CAPSLOCK) DEF_SDL_KEY(SDLK_SCROLLOCK) DEF_SDL_KEY(SDLK_RSHIFT) DEF_SDL_KEY(SDLK_LSHIFT) DEF_SDL_KEY(SDLK_RCTRL) DEF_SDL_KEY(SDLK_LCTRL) DEF_SDL_KEY(SDLK_RALT) DEF_SDL_KEY(SDLK_LALT) DEF_SDL_KEY(SDLK_RMETA) DEF_SDL_KEY(SDLK_LMETA) DEF_SDL_KEY(SDLK_LSUPER) DEF_SDL_KEY(SDLK_RSUPER) DEF_SDL_KEY(SDLK_MODE) DEF_SDL_KEY(SDLK_COMPOSE) DEF_SDL_KEY(SDLK_HELP) DEF_SDL_KEY(SDLK_PRINT) DEF_SDL_KEY(SDLK_SYSREQ) DEF_SDL_KEY(SDLK_BREAK) DEF_SDL_KEY(SDLK_MENU) DEF_SDL_KEY(SDLK_POWER) DEF_SDL_KEY(SDLK_EURO) DEF_SDL_KEY(SDLK_UNDO) ================================================ FILE: src/gui/vga.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This file is based upon Bochs. * * Copyright (C) 2002 MandrakeSoft S.A. * * MandrakeSoft S.A. * 43, rue d'Aboukir * 75002 Paris - France * http://www.linux-mandrake.com/ * http://www.mandrakesoft.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef BX_IODEV_VGA_H #define BX_IODEV_VGA_H // Make colour #define MAKE_COLOUR \ (red, red_shiftfrom, red_shiftto, red_mask, green, green_shiftfrom, \ green_shiftto, green_mask, blue, blue_shiftfrom, blue_shiftto, \ blue_mask)(((((red_shiftto) > (red_shiftfrom)) \ ? (red) << ((red_shiftto) - (red_shiftfrom)) \ : (red) >> ((red_shiftfrom) - (red_shiftto))) & \ (red_mask)) | \ ((((green_shiftto) > (green_shiftfrom)) \ ? (green) << ((green_shiftto) - (green_shiftfrom)) \ : (green) >> ((green_shiftfrom) - (green_shiftto))) & \ (green_mask)) | \ ((((blue_shiftto) > (blue_shiftfrom)) \ ? (blue) << ((blue_shiftto) - (blue_shiftfrom)) \ : (blue) >> ((blue_shiftfrom) - (blue_shiftto))) & \ (blue_mask))) #if BX_SUPPORT_VBE #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8 #define VBE_DISPI_4BPP_PLANE_SHIFT 21 #define VBE_DISPI_BANK_ADDRESS 0xA0000 #define VBE_DISPI_BANK_SIZE_KB 64 #define VBE_DISPI_MAX_XRES 1600 #define VBE_DISPI_MAX_YRES 1200 #define VBE_DISPI_MAX_BPP 32 #define VBE_DISPI_IOPORT_INDEX 0x01CE #define VBE_DISPI_IOPORT_DATA 0x01CF #define VBE_DISPI_IOPORT_INDEX_OLD 0xFF80 #define VBE_DISPI_IOPORT_DATA_OLD 0xFF81 #define VBE_DISPI_INDEX_ID 0x0 #define VBE_DISPI_INDEX_XRES 0x1 #define VBE_DISPI_INDEX_YRES 0x2 #define VBE_DISPI_INDEX_BPP 0x3 #define VBE_DISPI_INDEX_ENABLE 0x4 #define VBE_DISPI_INDEX_BANK 0x5 #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 #define VBE_DISPI_INDEX_X_OFFSET 0x8 #define VBE_DISPI_INDEX_Y_OFFSET 0x9 #define VBE_DISPI_ID0 0xB0C0 #define VBE_DISPI_ID1 0xB0C1 #define VBE_DISPI_ID2 0xB0C2 #define VBE_DISPI_ID3 0xB0C3 #define VBE_DISPI_ID4 0xB0C4 #define VBE_DISPI_BPP_4 0x04 #define VBE_DISPI_BPP_8 0x08 #define VBE_DISPI_BPP_15 0x0F #define VBE_DISPI_BPP_16 0x10 #define VBE_DISPI_BPP_24 0x18 #define VBE_DISPI_BPP_32 0x20 #define VBE_DISPI_DISABLED 0x00 #define VBE_DISPI_ENABLED 0x01 #define VBE_DISPI_GETCAPS 0x02 #define VBE_DISPI_8BIT_DAC 0x20 #define VBE_DISPI_LFB_ENABLED 0x40 #define VBE_DISPI_NOCLEARMEM 0x80 #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 #define VBE_DISPI_TOTAL_VIDEO_MEMORY_KB (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024) #define VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES \ (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB * 1024) #define BX_MAX_XRES VBE_DISPI_MAX_XRES #define BX_MAX_YRES VBE_DISPI_MAX_YRES #elif BX_SUPPORT_CLGD54XX #define BX_MAX_XRES 1280 #define BX_MAX_YRES 1024 #else #define BX_MAX_XRES 800 #define BX_MAX_YRES 600 #endif // BX_SUPPORT_VBE #define X_TILESIZE 16 #define Y_TILESIZE 24 #define BX_NUM_X_TILES (BX_MAX_XRES / X_TILESIZE) #define BX_NUM_Y_TILES (BX_MAX_YRES / Y_TILESIZE) // Support varying number of rows of text. This used to // be limited to only 25 lines. #define BX_MAX_TEXT_LINES 100 // struct { // u16 vbe_cur_dispi; // u16 vbe_xres; // u16 vbe_yres; // u16 vbe_bpp; // u16 vbe_max_xres; // u16 vbe_max_yres; // u16 vbe_max_bpp; // u16 vbe_bank; // bool vbe_enabled; // u16 vbe_curindex; // u32 vbe_visible_screen_size; /**< in bytes */ // u16 vbe_offset_x; /**< Virtual screen x start (in pixels) // */ u16 vbe_offset_y; /**< Virtual screen y start (in pixels) // */ u16 vbe_virtual_xres; u16 vbe_virtual_yres; u32 vbe_virtual_start; // /**< For dealing with bpp>8, this is where the virtual screen starts. */ // u8 vbe_bpp_multiplier; /**< We have to save this b/c sometimes we need // to recalculate stuff with it. */ bool vbe_lfb_enabled; bool // vbe_get_capabilities; bool vbe_8bit_dac; // } s; // state information #endif ================================================ FILE: src/lockstep.cpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #include "StdAfx.hpp" #include "lockstep.hpp" #if defined(IDB) && (defined(LS_MASTER) || defined(LS_SLAVE)) int ls_Socket; #if defined(LS_MASTER) char ls_IP[30]; #else int ls_listenSocket; #endif void lockstep_init() { struct sockaddr_in Address; #if defined(_WIN32) // Windows Sockets only work after calling WSAStartup. WSADATA wsa; WSAStartup(0x0101, &wsa); #endif // defined (_WIN32) #if defined(LS_MASTER) int result = -1; printf("Please enter the IP address of the lockstep slave to connect to: "); scanf("%s", ls_IP); ls_Socket = socket(AF_INET, SOCK_STREAM, 0); Address.sin_family = AF_INET; Address.sin_port = htons(21260); Address.sin_addr.s_addr = inet_addr(ls_IP); printf("%%LST-I-WAIT: Waiting to initiate lockstep connection to %s.\n", ls_IP); while (result == -1) result = connect(ls_Socket, (struct sockaddr *)&Address, sizeof(struct sockaddr)); #else // defined(LS_MASTER) socklen_t nAddressSize = sizeof(struct sockaddr_in); ls_listenSocket = socket(AF_INET, SOCK_STREAM, 0); if (ls_listenSocket == INVALID_SOCKET) printf("%%LST-F-NOSOCK Could not open lockstep socket to listen on!\n"); Address.sin_addr.s_addr = INADDR_ANY; Address.sin_port = htons(21260); Address.sin_family = AF_INET; int optval = 1; setsockopt(ls_listenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)); bind(ls_listenSocket, (struct sockaddr *)&Address, sizeof(Address)); listen(ls_listenSocket, 1); printf("%%LST-I-WAIT: Waiting for lockstep connection on port %d.\n", 21260); // Wait until we have a connection ls_Socket = INVALID_SOCKET; while (ls_Socket == INVALID_SOCKET) ls_Socket = accept(ls_listenSocket, (struct sockaddr *)&Address, &nAddressSize); #endif printf("%%LST-I-INIT: Lock-step connection initialized.\n"); } void lockstep_sync_m2s(char *s) { #if defined(LS_MASTER) send(ls_Socket, s, strlen(s) + 1, 0); #else fd_set readset; unsigned char buffer[1000]; ssize_t size; struct timeval tv; buffer[0] = 0; while (strcmp((char *)buffer, s)) { FD_ZERO(&readset); FD_SET(ls_Socket, &readset); tv.tv_sec = 30; tv.tv_usec = 0; while (select(ls_Socket + 1, &readset, NULL, NULL, &tv) <= 0) ; size = recv(ls_Socket, (char *)buffer, 999, 0); buffer[size + 1] = 0; // force null termination. } #endif } void lockstep_sync_s2m(char *s) { #if defined(LS_SLAVE) send(ls_Socket, s, strlen(s) + 1, 0); #else fd_set readset; unsigned char buffer[1000]; ssize_t size; struct timeval tv; buffer[0] = 0; while (strcmp((char *)buffer, s)) { FD_ZERO(&readset); FD_SET(ls_Socket, &readset); tv.tv_sec = 30; tv.tv_usec = 0; while (select(ls_Socket + 1, &readset, NULL, NULL, &tv) <= 0) ; size = recv(ls_Socket, (char *)buffer, 999, 0); buffer[size + 1] = 0; // force null termination. } #endif } char cmpbuffer[10000]; void lockstep_compare(char *s) { #if defined(LS_SLAVE) send(ls_Socket, s, strlen(s) + 1, 0); #else fd_set readset; ssize_t size; struct timeval tv; char *b1; char *b2; char *n1; char *n2; FD_ZERO(&readset); FD_SET(ls_Socket, &readset); tv.tv_sec = 30; tv.tv_usec = 0; while (select(ls_Socket + 1, &readset, NULL, NULL, &tv) <= 0) ; size = recv(ls_Socket, cmpbuffer, 99999, 0); cmpbuffer[size + 1] = 0; // force null termination. // printf("Comparing <%s> AND <%s>\n",s,buffer); b1 = s; b2 = cmpbuffer; while (b1 && b2) { n1 = strchr(b1, '\n'); n2 = strchr(b2, '\n'); if (n1) *n1++ = '\0'; if (n2) *n2++ = '\0'; if (strcmp(b1, b2)) { printf( "*************** LOCKSTEP: DIFFERENCE ENCOUNTERED ***************\n"); printf(" local system: %s\n", b1); printf("remote system: %s\n", b2); printf( "*************** PRESS ENTER TO CONTINUE ***************\n"); getc(stdin); } b1 = n1; b2 = n2; } #endif } void lockstep_send(char *s) { // printf("",s); fd_set readset; ssize_t size; struct timeval tv; char sConf[100] = ""; while (strcmp(sConf, "ACK")) { send(ls_Socket, s, strlen(s) + 1, 0); FD_ZERO(&readset); FD_SET(ls_Socket, &readset); tv.tv_sec = 5; tv.tv_usec = 0; if (select(ls_Socket + 1, &readset, NULL, NULL, &tv) > 0) { size = recv(ls_Socket, sConf, 99, 0); sConf[size + 1] = 0; // force null termination. } } } void lockstep_receive(char *s, int sz) { fd_set readset; ssize_t size = 0; struct timeval tv; s[0] = 'R'; while (!size) { FD_ZERO(&readset); FD_SET(ls_Socket, &readset); tv.tv_sec = 30; tv.tv_usec = 0; if (select(ls_Socket + 1, &readset, NULL, NULL, &tv) > 0) { size = recv(ls_Socket, s, sz - 1, 0); s[size + 1] = 0; // force null termination. } } send(ls_Socket, "ACK", 4, 0); } #endif // defined(IDB) && (defined(LS_MASTER) || defined(LS_SLAVE)) ================================================ FILE: src/lockstep.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_LOCKSTEP_H) #define INCLUDED_LOCKSTEP_H #include "telnet.hpp" #if defined(IDB) && (defined(LS_MASTER) || defined(LS_SLAVE)) extern int ls_Socket; #if defined(LS_MASTER) extern char ls_IP[30]; #else extern int ls_listenSocket; #endif void lockstep_init(); void lockstep_sync_m2s(char *s); void lockstep_sync_s2m(char *s); void lockstep_compare(char *s); void lockstep_send(char *s); void lockstep_receive(char *s, int sz); #endif // defined(IDB) && (defined(LS_MASTER) || defined(LS_SLAVE)) #endif // !defined(INCLUDED_LOCKSTEP_H) ================================================ FILE: src/make_unique.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Copyright (C) 2020 Remy van Elst * Website: https://github.com/lenticularis39/axpbox * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #ifndef AXPBOX_MAKE_UNIQUE_H #define AXPBOX_MAKE_UNIQUE_H #include /* Allow C++11 code to use std::make_unique * example ifdef: #if __cplusplus < 201402L */ namespace std { template std::unique_ptr make_unique( Args&& ...args ) { return std::unique_ptr( new T( std::forward(args)... ) ); } } #endif // AXPBOX_MAKE_UNIQUE_H ================================================ FILE: src/telnet.hpp ================================================ /* AXPbox Alpha Emulator * Copyright (C) 2020 Tomáš Glozar * Website: https://github.com/lenticularis39/axpbox * * Forked from: ES40 emulator * Copyright (C) 2007-2008 by the ES40 Emulator Project * Copyright (C) 2007 by Camiel Vanderhoeven * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Although this is not required, the author would appreciate being notified of, * and receiving any modifications you may make to the source code that might * serve the general public. */ #if !defined(INCLUDED_TELNET_H) #define INCLUDED_TELNET_H #if defined(HAVE_WINSOCK2_H) #include #endif #if defined(HAVE_WS2TCPIP_H) #include #endif #if defined(HAVE_SYS_SOCKET_H) #include #endif #if defined(HAVE_SOCKET_H) #include #endif #if defined(HAVE_IN_H) #include #endif #if defined(HAVE_INET_H) #include #endif #if defined(HAVE_ARPA_INET_H) #include #endif #if defined(HAVE_ARPA_TELNET_H) #include #endif #if defined(HAVE_NETINET_IN_H) #include #endif #if defined(HAVE_SYS_SELECT_H) #include #endif #if defined(HAVE_ERRNO_H) #include #endif #if defined(HAVE_FCNTL_H) #include #endif #if defined(HAVE_SIGNAL_H) #include #endif #if defined(_WIN32) && !defined(__GNUWIN32__) typedef size_t ssize_t; typedef int socklen_t; #endif // _WIN32 #if defined(__VMS) #define INVALID_SOCKET -1 typedef unsigned int socklen_t; #endif // __VMS #if defined(_WIN32) || defined(__VMS) #define IAC 255 /* interpret as command: */ #define DONT 254 /* you are not to use option */ #define DO 253 /* please, you use option */ #define WONT 252 /* I won't use option */ #define WILL 251 /* I will use option */ #define SB 250 /* interpret as subnegotiation */ #define GA 249 /* you may reverse the line */ #define EL 248 /* erase the current line */ #define EC 247 /* erase the current character */ #define AYT 246 /* are you there */ #define AO 245 /* abort output--but let prog finish */ #define IP 244 /* interrupt process--permanently */ #define BREAK 243 /* break */ #define DM 242 /* data mark--for connect. cleaning */ #define NOP 241 /* nop */ #define SE 240 /* end sub negotiation */ #define EOR 239 /* end of record (transparent mode) */ #define ABORT 238 /* Abort process */ #define SUSP 237 /* Suspend process */ #define xEOF 236 /* End of file: EOF is already used... */ #define SYNCH 242 /* for telfunc calls */ #define TELOPT_ECHO 1 /* echo */ #define TELOPT_SGA 3 /* suppress go ahead */ #define TELOPT_NAWS 31 /* window size */ #define TELOPT_LFLOW 33 /* remote flow control */ #else // defined(_WIN32) || defined(__VMS) #define INVALID_SOCKET 1 #endif // defined (_WIN32) || defined(__VMS) /* inet_aton -- Emulate BSD inet_aton via inet_addr. * * Useful on systems that don't have inet_aton, such as Solaris, * to let your code use the better inet_aton interface and use autoconf * and AC_REPLACE_FUNCS([inet_aton]). * * Copyright (C) 2003 Matthias Andree */ #if !defined(HAVE_INET_ATON) inline int inet_aton(const char *name, struct in_addr *addr) { unsigned long a = inet_addr(name); addr->s_addr = a; return a != (unsigned int)-1; } #endif #endif // !defined(INCLUDED_TELNET_H) ================================================ FILE: test/disk/unwritable/axp_correct.log ================================================ %FLS-F-NOREST: Flash could not be restored from flash.rom Emulator Failure: Runtime exception: pci0.15(ali_ide).disk0.0(file): file disk-unwritable.img is not writable: DiskFile.cpp, line L Stop threads: Freeing memory in use by system... ================================================ FILE: test/disk/unwritable/es40.cfg ================================================ sys0 = tsunami { memory.bits = 26; rom.srm = "cl67srmrom.exe"; rom.decompressed = "decompressed.rom"; rom.flash = "flash.rom"; rom.dpr = "dpr.rom"; cpu0 = ev68cb { speed = 800M; icache = false; } serial0 = serial { address = "127.0.0.1"; port = 21000; } pci0.15 = ali_ide { disk0.0 = file { file = "disk-unwritable.img"; read_only = false; cdrom = false; autocreate_size = 100M; } } pci0.7 = ali { } pci0.19 = ali_usb { } } ================================================ FILE: test/disk/unwritable/test.sh ================================================ #!/bin/bash touch disk-unwritable.img chmod 400 disk-unwritable.img # Download the firmware if [[ ! -f "cl67srmrom.exe" ]]; then wget 'http://raymii.org/s/inc/downloads/es40-srmon/cl67srmrom.exe' fi # Start AXPbox if [[ -f ../../../build/axpbox ]]; then ../../../build/axpbox run | tee axp.log elif [[ -f ../../../../build/axpbox ]]; then ../../../../build/axpbox run | tee axp.log; else ../../build/axpbox run | tee axp.log fi chmod 700 disk-unwritable.img rm disk-unwritable.img # OS X github runner has weiro sed version, use gnu one there. SED=/usr/bin/sed if [[ -f "/usr/local/opt/gnu-sed/libexec/gnubin/sed" ]]; then SED=/usr/local/opt/gnu-sed/libexec/gnubin/sed fi ${SED} -i -e 's$/[^ ]*/DiskFile.cpp$DiskFile.cpp$g' -e 's/line [0-9]*/line L/g' -e '/$Id/d' axp.log echo -n -e '\033[1;31m' diff -c axp_correct.log axp.log && echo -e '\033[1;32mdiff clean\033[0m' result=$? echo -n -e '\033[0m' rm -f axp.log cl67* *.rom exit $result ================================================ FILE: test/rom/es40.cfg ================================================ sys0 = tsunami { memory.bits = 26; rom.srm = "cl67srmrom.exe"; rom.decompressed = "decompressed.rom"; rom.flash = "flash.rom"; rom.dpr = "dpr.rom"; cpu0 = ev68cb { speed = 800M; icache = false; } serial0 = serial { address = "127.0.0.1"; port = 21000; } pci0.15 = ali_ide { } pci0.7 = ali { } pci0.19 = ali_usb { } } ================================================ FILE: test/rom/test.ps1 ================================================ # Download the firmware Invoke-WebRequest -Uri 'http://raymii.org/s/inc/downloads/es40-srmon/cl67srmrom.exe' -OutFile 'cl67srmrom.exe' # Start AXPbox Start-Process '..\..\..\build\Release\axpbox' -ArgumentList 'run' -NoNewWindow -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt # Wait for AXPbox to start Start-Sleep -Seconds 5 # Connect to terminal Start-Process -FilePath 'nc' -ArgumentList '-t', '127.0.0.1', '21000' -NoNewWindow -RedirectStandardOutput 'axp.log' # Wait for the last line of log to become P00>>> $timeout = 300 while ($true) { if ($timeout -eq 0) { Write-Host "waiting for SRM prompt timed out" -ForegroundColor Red exit 1 } # echo "=== start axp.log ===" # Get-Content -Path 'axp.log' -Raw # echo "=== end axp.log ===" $content = Get-Content -Path 'axp.log' -Raw $contentWithoutNullBytes = $content -replace '\0', '' if ($contentWithoutNullBytes -match "P00>>>") { exit 0 } Start-Sleep -Seconds 1 $timeout-- } Stop-Process -Name 'nc' ================================================ FILE: test/rom/test.sh ================================================ #!/bin/bash export LC_CTYPE=C export LANG=C export LC_ALL=C # Download the firmware wget 'http://raymii.org/s/inc/downloads/es40-srmon/cl67srmrom.exe' # Start AXPbox if [[ -f ../../../build/axpbox ]]; then ../../../build/axpbox run & AXPBOX_PID=$! else # Travis ../../build/axpbox run & AXPBOX_PID=$! fi # Wait for AXPbox to start sleep 5 # Connect to terminal nc -t 127.0.0.1 21000 | tee axp.log & NETCAT_PID=$! # Wait for the last line of log to become P00>>> timeout=600 while true do if [ $timeout -eq 0 ] then echo "waiting for SRM prompt timed out" >&2 exit 1 fi # print last line and remove null byte from it if [ "$(LC_ALL=C sed -n '$p' axp.log | LC_ALL=C sed 's/\x00//g')" == "P00>>>" ] then echo break fi sleep 1 timeout=$(($timeout - 1)) done kill $NETCAT_PID kill $AXPBOX_PID echo -n -e '\033[1;31m' diff -c axp_correct.log axp.log && echo -e '\033[1;32mdiff clean\033[0m' result=$? echo -n -e '\033[0m' rm -f axp.log cl67* *.rom exit $result ================================================ FILE: test/run ================================================ #!/bin/bash success=0 function message() { echo -n -e '\033[1;36m' >&2 echo $1 >&2 echo -n -e '\033[0m' >&2 } function run_test() { local old_pwd=$(pwd) message "[test] Started test $1 at $(date)" cd "test/$1" ./test.sh local result=$? cd "$old_pwd" if [ "$result" -eq "0" ] then message "[test] Test $1 finished at $(date)" >&2 else success=1 message "[test] Test $1 failed at $(date)" >&2 fi } message "[test] This is the AXPbox test script" run_test rom run_test disk/unwritable if [ "$success" -ne "0" ] then message "[test] Some tests failed, please check the log" else message "[test] All tests passed" fi exit "$success"