diff --git a/amx-deps/src/.gitignore b/amx-deps/src/.gitignore index fd832a0..259fcde 100644 --- a/amx-deps/src/.gitignore +++ b/amx-deps/src/.gitignore @@ -353,4 +353,5 @@ MigrationBackup/ # End of https://www.gitignore.io/api/visualstudio -!amx/amxexec.obj +Build +./premake5.exe diff --git a/amx-deps/src/amx/CMakeCache.txt b/amx-deps/src/amx/CMakeCache.txt deleted file mode 100644 index 1ce4002..0000000 --- a/amx-deps/src/amx/CMakeCache.txt +++ /dev/null @@ -1,322 +0,0 @@ -# This is the CMakeCache file. -# For build in directory: /root/king/amx -# It was generated by CMake: /usr/bin/cmake -# You can edit this file to change values found and used by cmake. -# If you do not want to change any of the values, simply exit the editor. -# If you do want to change a value, simply edit, save, and exit the editor. -# The syntax for the file is as follows: -# KEY:TYPE=VALUE -# KEY is the name of a variable in the cache. -# TYPE is a hint to GUI's for the type of VALUE, DO NOT EDIT TYPE!. -# VALUE is the current value for the KEY. - -######################## -# EXTERNAL cache entries -######################## - -//Path to a program. -CMAKE_AR:FILEPATH=/usr/bin/ar - -//For backwards compatibility, what version of CMake commands and -// syntax should this version of CMake try to support. -CMAKE_BACKWARDS_COMPATIBILITY:STRING=2.4 - -//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or -// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel. -CMAKE_BUILD_TYPE:STRING= - -//Enable/Disable color output during build. -CMAKE_COLOR_MAKEFILE:BOOL=ON - -//CXX compiler. -CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ - -//Flags used by the compiler during all build types. -CMAKE_CXX_FLAGS:STRING= - -//Flags used by the compiler during debug builds. -CMAKE_CXX_FLAGS_DEBUG:STRING=-g - -//Flags used by the compiler during release minsize builds. -CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG - -//Flags used by the compiler during release builds (/MD /Ob1 /Oi -// /Ot /Oy /Gs will produce slightly less optimized but smaller -// files). -CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG - -//Flags used by the compiler during Release with Debug Info builds. -CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g - -//C compiler. -CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc - -//Flags used by the compiler during all build types. -CMAKE_C_FLAGS:STRING= - -//Flags used by the compiler during debug builds. -CMAKE_C_FLAGS_DEBUG:STRING=-g - -//Flags used by the compiler during release minsize builds. -CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG - -//Flags used by the compiler during release builds (/MD /Ob1 /Oi -// /Ot /Oy /Gs will produce slightly less optimized but smaller -// files). -CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG - -//Flags used by the compiler during Release with Debug Info builds. -CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g - -//Flags used by the linker. -CMAKE_EXE_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//Install path prefix, prepended onto install directories. -CMAKE_INSTALL_PREFIX:PATH=/usr/local - -//Path to a program. -CMAKE_LINKER:FILEPATH=/usr/bin/ld - -//Path to a program. -CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make - -//Flags used by the linker during the creation of modules. -CMAKE_MODULE_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//Path to a program. -CMAKE_NM:FILEPATH=/usr/bin/nm - -//Path to a program. -CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy - -//Path to a program. -CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump - -//Path to a program. -CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib - -//Flags used by the linker during the creation of dll's. -CMAKE_SHARED_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//If set, runtime paths are not added when using shared libraries. -CMAKE_SKIP_RPATH:BOOL=NO - -//Path to a program. -CMAKE_STRIP:FILEPATH=/usr/bin/strip - -//If true, cmake will use relative paths in makefiles and projects. -CMAKE_USE_RELATIVE_PATHS:BOOL=OFF - -//If this value is on, makefiles will be generated without the -// .SILENT directive, and all commands will be echoed to the console -// during the make. This is useful for debugging only. With Visual -// Studio IDE projects all commands are done without /nologo. -CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE - -//Single output directory for building all executables. -EXECUTABLE_OUTPUT_PATH:PATH= - -//Single output directory for building all libraries. -LIBRARY_OUTPUT_PATH:PATH= - -//Dependencies for target -amxArgs_LIB_DEPENDS:STATIC= - -//Dependencies for target -amxDGram_LIB_DEPENDS:STATIC= - -//Dependencies for target -amxFile_LIB_DEPENDS:STATIC= - -//Dependencies for the target -amxFixed_LIB_DEPENDS:STATIC=general;m; - -//Dependencies for the target -amxFloat_LIB_DEPENDS:STATIC=general;m; - -//Dependencies for the target -amxProcess_LIB_DEPENDS:STATIC=general;dl; - -//Dependencies for target -amxString_LIB_DEPENDS:STATIC= - -//Dependencies for target -amxTime_LIB_DEPENDS:STATIC= - -//Value Computed by CMake -pawnamx_BINARY_DIR:STATIC=/root/king/amx - -//Value Computed by CMake -pawnamx_SOURCE_DIR:STATIC=/root/king/amx - - -######################## -# INTERNAL cache entries -######################## - -//Advanced flag for variable: CMAKE_AR -CMAKE_AR-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_BUILD_TOOL -CMAKE_BUILD_TOOL-ADVANCED:INTERNAL=1 -//What is the target build tool cmake is generating for. -CMAKE_BUILD_TOOL:INTERNAL=/usr/bin/make -//This is the directory where this CMakeCahe.txt was created -CMAKE_CACHEFILE_DIR:INTERNAL=/root/king/amx -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_MAJOR_VERSION:INTERNAL=2 -//Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=6 -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_RELEASE_VERSION:INTERNAL=patch 0 -//Advanced flag for variable: CMAKE_COLOR_MAKEFILE -CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 -//Path to CMake executable. -CMAKE_COMMAND:INTERNAL=/usr/bin/cmake -//Path to cpack program executable. -CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack -//Path to ctest program executable. -CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest -//Advanced flag for variable: CMAKE_CXX_COMPILER -CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 -CMAKE_CXX_COMPILER_WORKS:INTERNAL=1 -//Advanced flag for variable: CMAKE_CXX_FLAGS -CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_CXX_FLAGS_DEBUG -CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_CXX_FLAGS_MINSIZEREL -CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_CXX_FLAGS_RELEASE -CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO -CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_COMPILER -CMAKE_C_COMPILER-ADVANCED:INTERNAL=1 -CMAKE_C_COMPILER_WORKS:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_FLAGS -CMAKE_C_FLAGS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_FLAGS_DEBUG -CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_FLAGS_MINSIZEREL -CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_FLAGS_RELEASE -CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_C_FLAGS_RELWITHDEBINFO -CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Result of TRY_COMPILE -CMAKE_DETERMINE_CXX_ABI_COMPILED:INTERNAL=TRUE -//Result of TRY_COMPILE -CMAKE_DETERMINE_C_ABI_COMPILED:INTERNAL=TRUE -//Executable file format -CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF -//Advanced flag for variable: CMAKE_EXE_LINKER_FLAGS -CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG -CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL -CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE -CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Name of generator. -CMAKE_GENERATOR:INTERNAL=Unix Makefiles -//Start directory with the top level CMakeLists.txt file for this -// project -CMAKE_HOME_DIRECTORY:INTERNAL=/root/king/amx -//Install .so files without execute permission. -CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1 -//Advanced flag for variable: CMAKE_LINKER -CMAKE_LINKER-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MAKE_PROGRAM -CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MODULE_LINKER_FLAGS -CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG -CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL -CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE -CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_NM -CMAKE_NM-ADVANCED:INTERNAL=1 -//number of local generators -CMAKE_NUMBER_OF_LOCAL_GENERATORS:INTERNAL=1 -//Advanced flag for variable: CMAKE_OBJCOPY -CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_OBJDUMP -CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_RANLIB -CMAKE_RANLIB-ADVANCED:INTERNAL=1 -//Path to CMake installation. -CMAKE_ROOT:INTERNAL=/usr/share/cmake-2.6 -//Advanced flag for variable: CMAKE_SHARED_LINKER_FLAGS -CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG -CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL -CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE -CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_SKIP_RPATH -CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_STRIP -CMAKE_STRIP-ADVANCED:INTERNAL=1 -//uname command -CMAKE_UNAME:INTERNAL=/bin/uname -//Advanced flag for variable: CMAKE_USE_RELATIVE_PATHS -CMAKE_USE_RELATIVE_PATHS-ADVANCED:INTERNAL=1 -//Advanced flag for variable: CMAKE_VERBOSE_MAKEFILE -CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 -//Have include alloca.h -HAVE_ALLOCA_H:INTERNAL=1 -//Have include curses.h -HAVE_CURSES_H:INTERNAL=1 -//Have include ffi.h -HAVE_FFI_H:INTERNAL=1 -//Have include inttypes.h -HAVE_INTTYPES_H:INTERNAL=1 -//Have include stdint.h -HAVE_STDINT_H:INTERNAL=1 -//Have include unistd.h -HAVE_UNISTD_H:INTERNAL=1 - diff --git a/amx-deps/src/amx/CMakeLists.txt b/amx-deps/src/amx/CMakeLists.txt index efc6f83..a404f9b 100644 --- a/amx-deps/src/amx/CMakeLists.txt +++ b/amx-deps/src/amx/CMakeLists.txt @@ -1,224 +1,223 @@ -#build file for CMake, see http://www.cmake.org/ - -PROJECT(pawnamx) - -# check for optional include files -INCLUDE(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) -CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H) -IF(HAVE_UNISTD_H) - ADD_DEFINITIONS(-DHAVE_UNISTD_H) -ENDIF(HAVE_UNISTD_H) -CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H) -IF(HAVE_INTTYPES_H) - ADD_DEFINITIONS(-DHAVE_INTTYPES_H) -ENDIF(HAVE_INTTYPES_H) -CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) -IF(HAVE_STDINT_H) - ADD_DEFINITIONS(-DHAVE_STDINT_H) -ENDIF(HAVE_STDINT_H) -CHECK_INCLUDE_FILE("curses.h" HAVE_CURSES_H) -IF(HAVE_CURSES_H) - ADD_DEFINITIONS(-DHAVE_CURSES_H) -ENDIF(HAVE_CURSES_H) -CHECK_INCLUDE_FILE("alloca.h" HAVE_ALLOCA_H) -IF(HAVE_ALLOCA_H) - ADD_DEFINITIONS(-DHAVE_ALLOCA_H) -ENDIF(HAVE_ALLOCA_H) - -ADD_DEFINITIONS(-DFLOATPOINT -DFIXEDPOINT) -IF (UNIX) - CHECK_INCLUDE_FILE("ffi.h" HAVE_FFI_H) - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../linux) -ENDIF (UNIX) -IF(WIN32) - ADD_DEFINITIONS(-DPAWN_DLL -DSTDECL) - IF(NOT BORLAND) - LINK_LIBRARIES(winmm) - ENDIF(NOT BORLAND) -ENDIF(WIN32) - -# -------------------------------------------------------------------------- -# Extension modules - -# amxArgs -SET(ARGS_SRCS amxargs.c amx.c) -ADD_LIBRARY(amxArgs SHARED ${ARGS_SRCS}) -SET_TARGET_PROPERTIES(amxArgs PROPERTIES PREFIX "") -IF(WIN32) - SET(ARGS_SRCS ${ARGS_SRCS} dllmain.c amxargs.rc) - IF(BORLAND) - # Borland linker uses a DEF file if one is in the output directory - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxargs.def ${CMAKE_BINARY_DIR}/amxargs.def COPY_ONLY) - ELSE(BORLAND) - # For Microsoft Visual C/C++ we can set explicit flags for exports - SET_TARGET_PROPERTIES(amxArgs PROPERTIES LINK_FLAGS "/export:amx_ArgsInit /export:amx_ArgsCleanup /export:amx_ArgsSetCmdLine") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET amxArgs POST_BUILD COMMAND strip ARGS -K amx_ArgsInit -K amx_ArgsCleanup -K amx_ArgsSetCmdLine ${CMAKE_BINARY_DIR}/amxArgs.so) -ENDIF(UNIX) - -# amxDGram -SET(DGRAM_SRCS amxdgram.c amx.c) -ADD_LIBRARY(amxDGram SHARED ${DGRAM_SRCS}) -SET_TARGET_PROPERTIES(amxDGram PROPERTIES PREFIX "") -IF(WIN32) - SET(DGRAM_SRCS ${DGRAM_SRCS} dllmain.c amxargs.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxdgram.def ${CMAKE_BINARY_DIR}/amxdgram.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxDGram PROPERTIES LINK_FLAGS "/export:amx_DGramInit /export:amx_DGramCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET amxDGram POST_BUILD COMMAND strip ARGS -K amx_DGramInit -K amx_DGramCleanup ${CMAKE_BINARY_DIR}/amxDGram.so) -ENDIF(UNIX) - -# amxFile -SET(FILE_SRCS amxfile.c amx.c) -ADD_LIBRARY(amxFile SHARED ${FILE_SRCS}) -SET_TARGET_PROPERTIES(amxFile PROPERTIES PREFIX "") -IF(WIN32) - SET(FILE_SRCS ${FILE_SRCS} dllmain.c amxfile.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfile.def ${CMAKE_BINARY_DIR}/amxfile.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxFile PROPERTIES LINK_FLAGS "/export:amx_FileInit /export:amx_FileCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET amxFile POST_BUILD COMMAND strip ARGS -K amx_FileInit -K amx_FileCleanup ${CMAKE_BINARY_DIR}/amxFile.so) -ENDIF(UNIX) - -# amxFixed -SET(FIXED_SRCS fixed.c amx.c) -ADD_LIBRARY(amxFixed SHARED ${FIXED_SRCS}) -SET_TARGET_PROPERTIES(amxFixed PROPERTIES PREFIX "") -IF(WIN32) - SET(FIXED_SRCS ${FIXED_SRCS} dllmain.c amxfixed.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfixed.def ${CMAKE_BINARY_DIR}/amxfixed.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxFixed PROPERTIES LINK_FLAGS "/export:amx_FixedInit /export:amx_FixedCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - TARGET_LINK_LIBRARIES(amxFixed m) - ADD_CUSTOM_COMMAND(TARGET amxFixed POST_BUILD COMMAND strip ARGS -K amx_FixedInit -K amx_FixedCleanup ${CMAKE_BINARY_DIR}/amxFixed.so) -ENDIF(UNIX) - -# amxFloat -SET(FLOAT_SRCS float.c amx.c) -ADD_LIBRARY(amxFloat SHARED ${FLOAT_SRCS}) -SET_TARGET_PROPERTIES(amxFloat PROPERTIES PREFIX "") -IF(WIN32) - SET(FLOAT_SRCS ${FLOAT_SRCS} dllmain.c amxfloat.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfloat.def ${CMAKE_BINARY_DIR}/amxfloat.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxFloat PROPERTIES LINK_FLAGS "/export:amx_FloatInit /export:amx_FloatCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - TARGET_LINK_LIBRARIES(amxFloat m) - ADD_CUSTOM_COMMAND(TARGET amxFloat POST_BUILD COMMAND strip ARGS -K amx_FloatInit -K amx_FloatInit ${CMAKE_BINARY_DIR}/amxFloat.so) -ENDIF(UNIX) - -# amxProcess -IF(UNIX) - IF(NOT HAVE_FFI_H) - MESSAGE(SEND_ERROR "amxProcess requires libffi; see http://sourceware.org/libffi/") - MESSAGE(SEND_ERROR "libffi is not available (foreign function interface)") - ENDIF(NOT HAVE_FFI_H) -ENDIF(UNIX) -IF(WIN32 OR HAVE_FFI_H) -SET(PROCESS_SRCS amxprocess.c amx.c) -ADD_LIBRARY(amxProcess SHARED ${PROCESS_SRCS}) -SET_TARGET_PROPERTIES(amxProcess PROPERTIES PREFIX "") -IF(WIN32) - SET(PROCESS_SRCS ${PROCESS_SRCS} dllmain.c amxprocess.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxprocess.def ${CMAKE_BINARY_DIR}/amxprocess.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxProcess PROPERTIES LINK_FLAGS "/export:amx_ProcessInit /export:amx_ProcessCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - TARGET_LINK_LIBRARIES(amxProcess dl) - ADD_CUSTOM_COMMAND(TARGET amxProcess POST_BUILD COMMAND strip ARGS -K amx_ProcessInit -K amx_ProcessCleanup ${CMAKE_BINARY_DIR}/amxProcess.so) -ENDIF(UNIX) -ENDIF(WIN32 OR HAVE_FFI_H) - -# amxString -SET(STRING_SRCS amxstring.c amx.c amxcons.c) -ADD_LIBRARY(amxString SHARED ${STRING_SRCS}) -SET_TARGET_PROPERTIES(amxString PROPERTIES PREFIX "") -IF(WIN32) - SET(STRING_SRCS ${STRING_SRCS} dllmain.c amxstring.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxstring.def ${CMAKE_BINARY_DIR}/amxstring.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxString PROPERTIES LINK_FLAGS "/export:amx_StringInit /export:amx_StringCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET amxString POST_BUILD COMMAND strip ARGS -K amx_StringInit -K amx_StringCleanup ${CMAKE_BINARY_DIR}/amxString.so) -ENDIF(UNIX) - -# amxTime -SET(TIME_SRCS amxtime.c amx.c) -ADD_LIBRARY(amxTime SHARED ${TIME_SRCS}) -SET_TARGET_PROPERTIES(amxTime PROPERTIES PREFIX "") -IF(WIN32) - SET(TIME_SRCS ${TIME_SRCS} dllmain.c amxtime.rc) - IF(BORLAND) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxtime.def ${CMAKE_BINARY_DIR}/amxtime.def COPY_ONLY) - ELSE(BORLAND) - SET_TARGET_PROPERTIES(amxTime PROPERTIES LINK_FLAGS "/export:amx_TimeInit /export:amx_TimeCleanup") - ENDIF(BORLAND) -ENDIF(WIN32) -IF(UNIX) - ADD_CUSTOM_COMMAND(TARGET amxTime POST_BUILD COMMAND strip ARGS -K amx_TimeInit -K amx_TimeCleanup ${CMAKE_BINARY_DIR}/amxTime.so) -ENDIF(UNIX) - -# -------------------------------------------------------------------------- -# Simple run-time (example program) - -SET(PAWNRUN_SRCS pawnrun.c amx.c amxcore.c amxcons.c amxdbg.c) -IF (UNIX) - SET(PAWNRUN_SRCS ${PAWNRUN_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c) - IF(NOT HAVE_CURSES_H) - SET(PAWNRUN_SRCS ${PAWNRUN_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c) - ENDIF(NOT HAVE_CURSES_H) -ENDIF (UNIX) -ADD_EXECUTABLE(pawnrun ${PAWNRUN_SRCS}) -SET_TARGET_PROPERTIES(pawnrun PROPERTIES COMPILE_FLAGS -DAMXDBG COMPILE_FLAGS -DENABLE_BINRELOC) -IF (UNIX) - IF(HAVE_CURSES_H) -# SET_TARGET_PROPERTIES(pawnrun PROPERTIES COMPILE_FLAGS -DUSE_CURSES) - TARGET_LINK_LIBRARIES(pawnrun dl curses) - ELSE(HAVE_CURSES_H) - TARGET_LINK_LIBRARIES(pawnrun dl) - ENDIF(HAVE_CURSES_H) -ENDIF (UNIX) - -# -------------------------------------------------------------------------- -# Simple console debugger - -SET(PAWNDBG_SRCS pawndbg.c amx.c amxcore.c amxcons.c amxdbg.c) -IF (UNIX) - SET(PAWNDBG_SRCS ${PAWNDBG_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c) - IF(NOT HAVE_CURSES_H) - SET(PAWNDBG_SRCS ${PAWNDBG_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c) - ENDIF(NOT HAVE_CURSES_H) -ENDIF (UNIX) -ADD_EXECUTABLE(pawndbg ${PAWNDBG_SRCS}) -SET_TARGET_PROPERTIES(pawndbg PROPERTIES COMPILE_FLAGS -DENABLE_BINRELOC) -IF (UNIX) - IF(HAVE_CURSES_H) -# SET_TARGET_PROPERTIES(pawndbg PROPERTIES COMPILE_FLAGS -DUSE_CURSES) - TARGET_LINK_LIBRARIES(pawndbg dl curses) - ELSE(HAVE_CURSES_H) - TARGET_LINK_LIBRARIES(pawndbg dl) - ENDIF(HAVE_CURSES_H) -ENDIF (UNIX) +project(pawnamx C) +cmake_minimum_required(VERSION 2.8) + +# check for optional include files +include(CheckIncludeFile) +check_include_file("unistd.h" HAVE_UNISTD_H) +if(HAVE_UNISTD_H) + add_definitions(-DHAVE_UNISTD_H) +endif() +check_include_file("inttypes.h" HAVE_INTTYPES_H) +if(HAVE_INTTYPES_H) + add_definitions(-DHAVE_INTTYPES_H) +endif() +check_include_file("stdint.h" HAVE_STDINT_H) +if(HAVE_STDINT_H) + add_definitions(-DHAVE_STDINT_H) +endif() +check_include_file("alloca.h" HAVE_ALLOCA_H) +if(HAVE_ALLOCA_H) + add_definitions(-DHAVE_ALLOCA_H) +endif() + +add_definitions(-DFLOATPOINT -DFIXEDPOINT) +if(UNIX) + add_definitions(-DLINUX) + check_include_file("ffi.h" HAVE_FFI_H) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../linux) +endif() +if(WIN32) + add_definitions(-DAMXEXPORT=__stdcall -DAMX_NATIVE_CALL=__stdcall -DSTDECL) + if(NOT BORLAND) + link_libraries(winmm) + endif() +endif() + +# -------------------------------------------------------------------------- +# Extension modules + +# amxArgs +set(ARGS_SRCS amxargs.c amx.c) +add_library(amxArgs SHARED ${ARGS_SRCS}) +set_target_properties(amxArgs PROPERTIES PREFIX "") +if(WIN32) + set(ARGS_SRCS ${ARGS_SRCS} dllmain.c amxargs.rc) + if(BORLAND) + # Borland linker uses a DEF file if one is in the output directory + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxargs.def + ${CMAKE_BINARY_DIR}/amxargs.def + COPYONLY) + else() + # For Microsoft Visual C/C++ we can set explicit flags for exports + set_target_properties(amxArgs PROPERTIES LINK_FLAGS + "/export:amx_ArgsInit /export:amx_ArgsCleanup /export:amx_ArgsSetCmdLine") + endif() +endif() + +# amxDGram +set(DGRAM_SRCS amxdgram.c amx.c) +add_library(amxDGram SHARED ${DGRAM_SRCS}) +set_target_properties(amxDGram PROPERTIES PREFIX "") +if(WIN32) + set(DGRAM_SRCS ${DGRAM_SRCS} dllmain.c amxargs.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxdgram.def + ${CMAKE_BINARY_DIR}/amxdgram.def + COPYONLY) + else() + set_target_properties(amxDGram PROPERTIES LINK_FLAGS + "/export:amx_DGramInit /export:amx_DGramCleanup") + endif() + target_link_libraries(amxDGram ws2_32) +endif() + +# amxFile +set(FILE_SRCS amxfile.c amx.c) +add_library(amxFile SHARED ${FILE_SRCS}) +set_target_properties(amxFile PROPERTIES PREFIX "") +if(WIN32) + set(FILE_SRCS ${FILE_SRCS} dllmain.c amxFile.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxfile.def + ${CMAKE_BINARY_DIR}/amxfile.def + COPYONLY) + else() + set_target_properties(amxFile PROPERTIES LINK_FLAGS + "/export:amx_FileInit /export:amx_FileCleanup") + endif() +endif() + +# amxFixed +set(FIXED_SRCS fixed.c fixed.c amx.c) +add_library(amxFixed SHARED ${FIXED_SRCS}) +set_target_properties(amxFixed PROPERTIES PREFIX "") +if(WIN32) + set(FIXED_SRCS ${FIXED_SRCS} dllmain.c amxfixed.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxFixed.def + ${CMAKE_BINARY_DIR}/amxFixed.def + COPYONLY) + else() + set_target_properties(amxFixed PROPERTIES LINK_FLAGS + "/export:amx_FixedInit /export:amx_FixedCleanup") + endif() +endif() +if(UNIX) + target_link_libraries(amxFixed m) +endif() + +# amxFloat +set(FLOAT_SRCS amxfloat.c amx.c) +add_library(amxFloat SHARED ${FLOAT_SRCS}) +set_target_properties(amxFloat PROPERTIES PREFIX "") +if(WIN32) + set(FLOAT_SRCS ${FLOAT_SRCS} dllmain.c amxfloat.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxfloat.def + ${CMAKE_BINARY_DIR}/amxfloat.def + COPYONLY) + else() + set_target_properties(amxFloat PROPERTIES LINK_FLAGS + "/export:amx_FloatInit /export:amx_FloatCleanup") + endif() +endif() +if(UNIX) + target_link_libraries(amxFloat m) +endif() + +# amxProcess +set(PROCESS_SRCS amxprocess.c amx.c) +if(WIN32) + add_library(amxProcess SHARED ${PROCESS_SRCS}) +else() + if(HAVE_FFI_H) + add_library(amxProcess SHARED ${PROCESS_SRCS}) + else() + MESSAGE(SEND_ERROR "amxProcess requires libffi," + "see http://sources.redhat.com/libffi/") + MESSAGE(SEND_ERROR "libffi is not available (foreign function interface)") + endif() +endif() +set_target_properties(amxProcess PROPERTIES PREFIX "") +if(WIN32) + set(PROCESS_SRCS ${PROCESS_SRCS} dllmain.c amxprocess.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxprocess.def + ${CMAKE_BINARY_DIR}/amxprocess.def + COPYONLY) + else() + set_target_properties(amxProcess PROPERTIES LINK_FLAGS + "/export:amx_ProcessInit /export:amx_ProcessCleanup") + endif() +endif() +if(UNIX) + target_link_libraries(amxProcess dl) +endif() + +# amxString +set(STRING_SRCS amxstring.c amx.c amxcons.c) +add_library(amxString SHARED ${STRING_SRCS}) +set_target_properties(amxString PROPERTIES PREFIX "") +if(WIN32) + set(STRING_SRCS ${STRING_SRCS} dllmain.c amxstring.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxstring.def + ${CMAKE_BINARY_DIR}/amxstring.def + COPYONLY) + else() + set_target_properties(amxString PROPERTIES LINK_FLAGS + "/export:amx_StringInit /export:amx_StringCleanup") + endif() +endif() + +# amxTime +set(TIME_SRCS amxtime.c amx.c) +add_library(amxTime SHARED ${TIME_SRCS}) +set_target_properties(amxTime PROPERTIES PREFIX "") +if(WIN32) + set(TIME_SRCS ${TIME_SRCS} dllmain.c amxtime.rc) + if(BORLAND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/amxtime.def + ${CMAKE_BINARY_DIR}/amxtime.def + COPYONLY) + else() + set_target_properties(amxTime PROPERTIES LINK_FLAGS + "/export:amx_TimeInit /export:amx_TimeCleanup") + endif() +endif() + +# -------------------------------------------------------------------------- +# Simple run-time (example program) + +set(PAWNRUN_SRCS pawnrun.c amx.c amxcore.c amxcons.c amxdbg.c) +if(UNIX) + set(PAWNRUN_SRCS + ${PAWNRUN_SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c + ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c + ) +endif() +ADD_EXECUTABLE(pawnrun ${PAWNRUN_SRCS}) +set_target_properties(pawnrun PROPERTIES + COMPILE_FLAGS -DAMXDBG + COMPILE_FLAGS -DENABLE_BINRELOC +) +if(UNIX) + target_link_libraries(pawnrun dl) +endif() + +# -------------------------------------------------------------------------- +# Simple console debugger + +set(PAWNDBG_SRCS pawndbg.c amx.c amxcore.c amxcons.c amxdbg.c) +if(UNIX) + set(PAWNDBG_SRCS + ${PAWNDBG_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c + ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c + ) +endif() +ADD_EXECUTABLE(pawndbg ${PAWNDBG_SRCS}) +set_target_properties(pawndbg PROPERTIES COMPILE_FLAGS -DENABLE_BINRELOC) +if(UNIX) + target_link_libraries(pawndbg dl) +endif() diff --git a/amx-deps/src/amx/Makefile b/amx-deps/src/amx/Makefile deleted file mode 100644 index de281e1..0000000 --- a/amx-deps/src/amx/Makefile +++ /dev/null @@ -1,571 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 2.6 - -# Default target executed when no arguments are given to make. -default_target: all -.PHONY : default_target - -#============================================================================= -# Special targets provided by cmake. - -# Disable implicit rules so canoncical targets will work. -.SUFFIXES: - -# Remove some rules from gmake that .SUFFIXES does not remove. -SUFFIXES = - -.SUFFIXES: .hpux_make_needs_suffix_list - -# Suppress display of executed commands. -$(VERBOSE).SILENT: - -# A target that is always out of date. -cmake_force: -.PHONY : cmake_force - -#============================================================================= -# Set environment variables for the build. - -# The shell in which to execute make rules. -SHELL = /bin/sh - -# The CMake executable. -CMAKE_COMMAND = /usr/bin/cmake - -# The command to remove a file. -RM = /usr/bin/cmake -E remove -f - -# The top-level source directory on which CMake was run. -CMAKE_SOURCE_DIR = /root/king/amx - -# The top-level build directory on which CMake was run. -CMAKE_BINARY_DIR = /root/king/amx - -#============================================================================= -# Targets provided globally by CMake. - -# Special rule for the target edit_cache -edit_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..." - /usr/bin/cmake -i . -.PHONY : edit_cache - -# Special rule for the target edit_cache -edit_cache/fast: edit_cache -.PHONY : edit_cache/fast - -# Special rule for the target rebuild_cache -rebuild_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." - /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) -.PHONY : rebuild_cache - -# Special rule for the target rebuild_cache -rebuild_cache/fast: rebuild_cache -.PHONY : rebuild_cache/fast - -# The main all target -all: cmake_check_build_system - $(CMAKE_COMMAND) -E cmake_progress_start /root/king/amx/CMakeFiles /root/king/amx/CMakeFiles/progress.make - $(MAKE) -f CMakeFiles/Makefile2 all - $(CMAKE_COMMAND) -E cmake_progress_start /root/king/amx/CMakeFiles 0 -.PHONY : all - -# The main clean target -clean: - $(MAKE) -f CMakeFiles/Makefile2 clean -.PHONY : clean - -# The main clean target -clean/fast: clean -.PHONY : clean/fast - -# Prepare targets for installation. -preinstall: all - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall - -# Prepare targets for installation. -preinstall/fast: - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall/fast - -# clear depends -depend: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 -.PHONY : depend - -#============================================================================= -# Target rules for targets named amxArgs - -# Build rule for target. -amxArgs: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxArgs -.PHONY : amxArgs - -# fast build rule for target. -amxArgs/fast: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/build -.PHONY : amxArgs/fast - -#============================================================================= -# Target rules for targets named amxDGram - -# Build rule for target. -amxDGram: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxDGram -.PHONY : amxDGram - -# fast build rule for target. -amxDGram/fast: - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/build -.PHONY : amxDGram/fast - -#============================================================================= -# Target rules for targets named amxFile - -# Build rule for target. -amxFile: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxFile -.PHONY : amxFile - -# fast build rule for target. -amxFile/fast: - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/build -.PHONY : amxFile/fast - -#============================================================================= -# Target rules for targets named amxFixed - -# Build rule for target. -amxFixed: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxFixed -.PHONY : amxFixed - -# fast build rule for target. -amxFixed/fast: - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/build -.PHONY : amxFixed/fast - -#============================================================================= -# Target rules for targets named amxFloat - -# Build rule for target. -amxFloat: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxFloat -.PHONY : amxFloat - -# fast build rule for target. -amxFloat/fast: - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/build -.PHONY : amxFloat/fast - -#============================================================================= -# Target rules for targets named amxProcess - -# Build rule for target. -amxProcess: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxProcess -.PHONY : amxProcess - -# fast build rule for target. -amxProcess/fast: - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/build -.PHONY : amxProcess/fast - -#============================================================================= -# Target rules for targets named amxString - -# Build rule for target. -amxString: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxString -.PHONY : amxString - -# fast build rule for target. -amxString/fast: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/build -.PHONY : amxString/fast - -#============================================================================= -# Target rules for targets named amxTime - -# Build rule for target. -amxTime: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 amxTime -.PHONY : amxTime - -# fast build rule for target. -amxTime/fast: - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/build -.PHONY : amxTime/fast - -#============================================================================= -# Target rules for targets named pawndbg - -# Build rule for target. -pawndbg: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 pawndbg -.PHONY : pawndbg - -# fast build rule for target. -pawndbg/fast: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/build -.PHONY : pawndbg/fast - -#============================================================================= -# Target rules for targets named pawnrun - -# Build rule for target. -pawnrun: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 pawnrun -.PHONY : pawnrun - -# fast build rule for target. -pawnrun/fast: - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/build -.PHONY : pawnrun/fast - -# target to build an object file -__/linux/binreloc.o: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/__/linux/binreloc.o - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/__/linux/binreloc.o -.PHONY : __/linux/binreloc.o - -# target to preprocess a source file -__/linux/binreloc.i: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/__/linux/binreloc.i - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/__/linux/binreloc.i -.PHONY : __/linux/binreloc.i - -# target to generate assembly for a file -__/linux/binreloc.s: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/__/linux/binreloc.s - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/__/linux/binreloc.s -.PHONY : __/linux/binreloc.s - -# target to build an object file -amx.o: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amx.o - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amx.o - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amx.o - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/amx.o - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/amx.o - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amx.o - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amx.o - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amx.o - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amx.o - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amx.o -.PHONY : amx.o - -# target to preprocess a source file -amx.i: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amx.i - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amx.i - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amx.i - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/amx.i - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/amx.i - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amx.i - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amx.i - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amx.i - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amx.i - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amx.i -.PHONY : amx.i - -# target to generate assembly for a file -amx.s: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amx.s - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amx.s - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amx.s - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/amx.s - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/amx.s - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amx.s - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amx.s - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amx.s - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amx.s - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amx.s -.PHONY : amx.s - -# target to build an object file -amxargs.o: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amxargs.o -.PHONY : amxargs.o - -# target to preprocess a source file -amxargs.i: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amxargs.i -.PHONY : amxargs.i - -# target to generate assembly for a file -amxargs.s: - $(MAKE) -f CMakeFiles/amxArgs.dir/build.make CMakeFiles/amxArgs.dir/amxargs.s -.PHONY : amxargs.s - -# target to build an object file -amxcons.o: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxcons.o - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcons.o - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcons.o -.PHONY : amxcons.o - -# target to preprocess a source file -amxcons.i: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxcons.i - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcons.i - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcons.i -.PHONY : amxcons.i - -# target to generate assembly for a file -amxcons.s: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxcons.s - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcons.s - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcons.s -.PHONY : amxcons.s - -# target to build an object file -amxcore.o: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcore.o - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcore.o -.PHONY : amxcore.o - -# target to preprocess a source file -amxcore.i: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcore.i - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcore.i -.PHONY : amxcore.i - -# target to generate assembly for a file -amxcore.s: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxcore.s - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxcore.s -.PHONY : amxcore.s - -# target to build an object file -amxdbg.o: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxdbg.o - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxdbg.o -.PHONY : amxdbg.o - -# target to preprocess a source file -amxdbg.i: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxdbg.i - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxdbg.i -.PHONY : amxdbg.i - -# target to generate assembly for a file -amxdbg.s: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/amxdbg.s - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/amxdbg.s -.PHONY : amxdbg.s - -# target to build an object file -amxdgram.o: - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amxdgram.o -.PHONY : amxdgram.o - -# target to preprocess a source file -amxdgram.i: - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amxdgram.i -.PHONY : amxdgram.i - -# target to generate assembly for a file -amxdgram.s: - $(MAKE) -f CMakeFiles/amxDGram.dir/build.make CMakeFiles/amxDGram.dir/amxdgram.s -.PHONY : amxdgram.s - -# target to build an object file -amxfile.o: - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amxfile.o -.PHONY : amxfile.o - -# target to preprocess a source file -amxfile.i: - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amxfile.i -.PHONY : amxfile.i - -# target to generate assembly for a file -amxfile.s: - $(MAKE) -f CMakeFiles/amxFile.dir/build.make CMakeFiles/amxFile.dir/amxfile.s -.PHONY : amxfile.s - -# target to build an object file -amxprocess.o: - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amxprocess.o -.PHONY : amxprocess.o - -# target to preprocess a source file -amxprocess.i: - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amxprocess.i -.PHONY : amxprocess.i - -# target to generate assembly for a file -amxprocess.s: - $(MAKE) -f CMakeFiles/amxProcess.dir/build.make CMakeFiles/amxProcess.dir/amxprocess.s -.PHONY : amxprocess.s - -# target to build an object file -amxstring.o: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxstring.o -.PHONY : amxstring.o - -# target to preprocess a source file -amxstring.i: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxstring.i -.PHONY : amxstring.i - -# target to generate assembly for a file -amxstring.s: - $(MAKE) -f CMakeFiles/amxString.dir/build.make CMakeFiles/amxString.dir/amxstring.s -.PHONY : amxstring.s - -# target to build an object file -amxtime.o: - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amxtime.o -.PHONY : amxtime.o - -# target to preprocess a source file -amxtime.i: - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amxtime.i -.PHONY : amxtime.i - -# target to generate assembly for a file -amxtime.s: - $(MAKE) -f CMakeFiles/amxTime.dir/build.make CMakeFiles/amxTime.dir/amxtime.s -.PHONY : amxtime.s - -# target to build an object file -fixed.o: - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/fixed.o -.PHONY : fixed.o - -# target to preprocess a source file -fixed.i: - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/fixed.i -.PHONY : fixed.i - -# target to generate assembly for a file -fixed.s: - $(MAKE) -f CMakeFiles/amxFixed.dir/build.make CMakeFiles/amxFixed.dir/fixed.s -.PHONY : fixed.s - -# target to build an object file -float.o: - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/float.o -.PHONY : float.o - -# target to preprocess a source file -float.i: - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/float.i -.PHONY : float.i - -# target to generate assembly for a file -float.s: - $(MAKE) -f CMakeFiles/amxFloat.dir/build.make CMakeFiles/amxFloat.dir/float.s -.PHONY : float.s - -# target to build an object file -pawndbg.o: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/pawndbg.o -.PHONY : pawndbg.o - -# target to preprocess a source file -pawndbg.i: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/pawndbg.i -.PHONY : pawndbg.i - -# target to generate assembly for a file -pawndbg.s: - $(MAKE) -f CMakeFiles/pawndbg.dir/build.make CMakeFiles/pawndbg.dir/pawndbg.s -.PHONY : pawndbg.s - -# target to build an object file -pawnrun.o: - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/pawnrun.o -.PHONY : pawnrun.o - -# target to preprocess a source file -pawnrun.i: - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/pawnrun.i -.PHONY : pawnrun.i - -# target to generate assembly for a file -pawnrun.s: - $(MAKE) -f CMakeFiles/pawnrun.dir/build.make CMakeFiles/pawnrun.dir/pawnrun.s -.PHONY : pawnrun.s - -# Help Target -help: - @echo "The following are some of the valid targets for this Makefile:" - @echo "... all (the default if no target is provided)" - @echo "... clean" - @echo "... depend" - @echo "... amxArgs" - @echo "... amxDGram" - @echo "... amxFile" - @echo "... amxFixed" - @echo "... amxFloat" - @echo "... amxProcess" - @echo "... amxString" - @echo "... amxTime" - @echo "... edit_cache" - @echo "... pawndbg" - @echo "... pawnrun" - @echo "... rebuild_cache" - @echo "... __/linux/binreloc.o" - @echo "... __/linux/binreloc.i" - @echo "... __/linux/binreloc.s" - @echo "... amx.o" - @echo "... amx.i" - @echo "... amx.s" - @echo "... amxargs.o" - @echo "... amxargs.i" - @echo "... amxargs.s" - @echo "... amxcons.o" - @echo "... amxcons.i" - @echo "... amxcons.s" - @echo "... amxcore.o" - @echo "... amxcore.i" - @echo "... amxcore.s" - @echo "... amxdbg.o" - @echo "... amxdbg.i" - @echo "... amxdbg.s" - @echo "... amxdgram.o" - @echo "... amxdgram.i" - @echo "... amxdgram.s" - @echo "... amxfile.o" - @echo "... amxfile.i" - @echo "... amxfile.s" - @echo "... amxprocess.o" - @echo "... amxprocess.i" - @echo "... amxprocess.s" - @echo "... amxstring.o" - @echo "... amxstring.i" - @echo "... amxstring.s" - @echo "... amxtime.o" - @echo "... amxtime.i" - @echo "... amxtime.s" - @echo "... fixed.o" - @echo "... fixed.i" - @echo "... fixed.s" - @echo "... float.o" - @echo "... float.i" - @echo "... float.s" - @echo "... pawndbg.o" - @echo "... pawndbg.i" - @echo "... pawndbg.s" - @echo "... pawnrun.o" - @echo "... pawnrun.i" - @echo "... pawnrun.s" -.PHONY : help - - - -#============================================================================= -# Special targets to cleanup operation of make. - -# Special rule to run CMake to check the build system integrity. -# No rule that depends on this can have commands that come from listfiles -# because they might be regenerated. -cmake_check_build_system: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 -.PHONY : cmake_check_build_system - diff --git a/amx-deps/src/amx/amx.c b/amx-deps/src/amx/amx.c index 40459e9..a144412 100644 --- a/amx-deps/src/amx/amx.c +++ b/amx-deps/src/amx/amx.c @@ -1,3 +1,4 @@ +<<<<<<< HEAD /* Pawn Abstract Machine (for the Pawn language) * * Copyright (c) ITB CompuPhase, 1997-2008 @@ -5544,3 +5545,4638 @@ int AMXAPI amx_UTF8Len(const cell *cstr, int *length) return err; } #endif /* AMX_UTF8XXX */ +======= +/* Pawn Abstract Machine (for the Pawn language) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amx.c 3662 2006-11-07 08:44:33Z thiadmer $ + */ + +#if BUILD_PLATFORM == WINDOWS && BUILD_TYPE == RELEASE && BUILD_COMPILER == MSVC && PAWN_CELL_SIZE == 64 + /* bad bad workaround but we have to prevent a compiler crash :/ */ + #pragma optimize("g",off) +#endif + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows API */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include /* for wchar_t */ +#include /* for getenv() */ +#include +#include "osdefs.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include + #if !defined AMX_NODYNALOAD + #include + #endif + #if defined JIT + #include + #include + #endif +#endif +#if defined __LCC__ || defined LINUX + #include /* for wcslen() */ +#endif +#include "amx.h" +#if (defined _Windows && !defined AMX_NODYNALOAD) || (defined JIT && __WIN32__) + #include +#endif + + +/* When one or more of the AMX_funcname macris are defined, we want + * to compile only those functions. However, when none of these macros + * is present, we want to compile everything. + */ +#if defined AMX_ALIGN || defined AMX_ALLOT || defined AMX_CLEANUP + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_CLONE || defined AMX_DEFCALLBACK || defined AMX_EXEC + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_FLAGS || defined AMX_GETADDR || defined AMX_INIT + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_MEMINFO || defined AMX_NAMELENGTH || defined AMX_NATIVEINFO + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_PUSHXXX || defined AMX_RAISEERROR || defined AMX_REGISTER + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_SETCALLBACK || defined AMX_SETDEBUGHOOK || defined AMX_XXXNATIVES + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_XXXPUBLICS || defined AMX_XXXPUBVARS || defined AMX_XXXSTRING + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_XXXTAGS || defined AMX_XXXUSERDATA || defined AMX_UTF8XXX + #define AMX_EXPLIT_FUNCTIONS +#endif +#if !defined AMX_EXPLIT_FUNCTIONS + /* no constant set, set them all */ + #define AMX_ALIGN /* amx_Align16(), amx_Align32() and amx_Align64() */ + #define AMX_ALLOT /* amx_Allot() and amx_Release() */ + #define AMX_DEFCALLBACK /* amx_Callback() */ + #define AMX_CLEANUP /* amx_Cleanup() */ + #define AMX_CLONE /* amx_Clone() */ + #define AMX_EXEC /* amx_Exec() */ + #define AMX_FLAGS /* amx_Flags() */ + #define AMX_GETADDR /* amx_GetAddr() */ + #define AMX_INIT /* amx_Init() and amx_InitJIT() */ + #define AMX_MEMINFO /* amx_MemInfo() */ + #define AMX_NAMELENGTH /* amx_NameLength() */ + #define AMX_NATIVEINFO /* amx_NativeInfo() */ + #define AMX_PUSHXXX /* amx_Push(), amx_PushArray() and amx_PushString() */ + #define AMX_RAISEERROR /* amx_RaiseError() */ + #define AMX_REGISTER /* amx_Register() */ + #define AMX_SETCALLBACK /* amx_SetCallback() */ + #define AMX_SETDEBUGHOOK /* amx_SetDebugHook() */ + #define AMX_XXXNATIVES /* amx_NumNatives(), amx_GetNative() and amx_FindNative() */ + #define AMX_XXXPUBLICS /* amx_NumPublics(), amx_GetPublic() and amx_FindPublic() */ + #define AMX_XXXPUBVARS /* amx_NumPubVars(), amx_GetPubVar() and amx_FindPubVar() */ + #define AMX_XXXSTRING /* amx_StrLen(), amx_GetString() and amx_SetString() */ + #define AMX_XXXTAGS /* amx_NumTags(), amx_GetTag() and amx_FindTagId() */ + #define AMX_XXXUSERDATA /* amx_GetUserData() and amx_SetUserData() */ + #define AMX_UTF8XXX /* amx_UTF8Get(), amx_UTF8Put(), amx_UTF8Check() */ +#endif +#undef AMX_EXPLIT_FUNCTIONS +#if defined AMX_ANSIONLY + #undef AMX_UTF8XXX /* no UTF-8 support in ANSI/ASCII-only version */ +#endif +#if defined AMX_NO_NATIVEINFO + #undef AMX_NATIVEINFO +#endif +#if AMX_USERNUM <= 0 + #undef AMX_XXXUSERDATA +#endif +#if defined JIT + #define AMX_NO_MACRO_INSTR /* JIT is incompatible with macro instructions */ +#endif + +typedef enum { + OP_NONE, /* invalid opcode */ + OP_LOAD_PRI, + OP_LOAD_ALT, + OP_LOAD_S_PRI, + OP_LOAD_S_ALT, + OP_LREF_PRI, + OP_LREF_ALT, + OP_LREF_S_PRI, + OP_LREF_S_ALT, + OP_LOAD_I, + OP_LODB_I, + OP_CONST_PRI, + OP_CONST_ALT, + OP_ADDR_PRI, + OP_ADDR_ALT, + OP_STOR_PRI, + OP_STOR_ALT, + OP_STOR_S_PRI, + OP_STOR_S_ALT, + OP_SREF_PRI, + OP_SREF_ALT, + OP_SREF_S_PRI, + OP_SREF_S_ALT, + OP_STOR_I, + OP_STRB_I, + OP_LIDX, + OP_LIDX_B, + OP_IDXADDR, + OP_IDXADDR_B, + OP_ALIGN_PRI, + OP_ALIGN_ALT, + OP_LCTRL, + OP_SCTRL, + OP_MOVE_PRI, + OP_MOVE_ALT, + OP_XCHG, + OP_PUSH_PRI, + OP_PUSH_ALT, + OP_PUSH_R, + OP_PUSH_C, + OP_PUSH, + OP_PUSH_S, + OP_POP_PRI, + OP_POP_ALT, + OP_STACK, + OP_HEAP, + OP_PROC, + OP_RET, + OP_RETN, + OP_CALL, + OP_CALL_PRI, + OP_JUMP, + OP_JREL, + OP_JZER, + OP_JNZ, + OP_JEQ, + OP_JNEQ, + OP_JLESS, + OP_JLEQ, + OP_JGRTR, + OP_JGEQ, + OP_JSLESS, + OP_JSLEQ, + OP_JSGRTR, + OP_JSGEQ, + OP_SHL, + OP_SHR, + OP_SSHR, + OP_SHL_C_PRI, + OP_SHL_C_ALT, + OP_SHR_C_PRI, + OP_SHR_C_ALT, + OP_SMUL, + OP_SDIV, + OP_SDIV_ALT, + OP_UMUL, + OP_UDIV, + OP_UDIV_ALT, + OP_ADD, + OP_SUB, + OP_SUB_ALT, + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + OP_NEG, + OP_INVERT, + OP_ADD_C, + OP_SMUL_C, + OP_ZERO_PRI, + OP_ZERO_ALT, + OP_ZERO, + OP_ZERO_S, + OP_SIGN_PRI, + OP_SIGN_ALT, + OP_EQ, + OP_NEQ, + OP_LESS, + OP_LEQ, + OP_GRTR, + OP_GEQ, + OP_SLESS, + OP_SLEQ, + OP_SGRTR, + OP_SGEQ, + OP_EQ_C_PRI, + OP_EQ_C_ALT, + OP_INC_PRI, + OP_INC_ALT, + OP_INC, + OP_INC_S, + OP_INC_I, + OP_DEC_PRI, + OP_DEC_ALT, + OP_DEC, + OP_DEC_S, + OP_DEC_I, + OP_MOVS, + OP_CMPS, + OP_FILL, + OP_HALT, + OP_BOUNDS, + OP_SYSREQ_PRI, + OP_SYSREQ_C, + OP_FILE, /* obsolete */ + OP_LINE, /* obsolete */ + OP_SYMBOL, /* obsolete */ + OP_SRANGE, /* obsolete */ + OP_JUMP_PRI, + OP_SWITCH, + OP_CASETBL, + OP_SWAP_PRI, + OP_SWAP_ALT, + OP_PUSH_ADR, + OP_NOP, + OP_SYSREQ_N, + OP_SYMTAG, /* obsolete */ + OP_BREAK, + OP_PUSH2_C, + OP_PUSH2, + OP_PUSH2_S, + OP_PUSH2_ADR, + OP_PUSH3_C, + OP_PUSH3, + OP_PUSH3_S, + OP_PUSH3_ADR, + OP_PUSH4_C, + OP_PUSH4, + OP_PUSH4_S, + OP_PUSH4_ADR, + OP_PUSH5_C, + OP_PUSH5, + OP_PUSH5_S, + OP_PUSH5_ADR, + OP_LOAD_BOTH, + OP_LOAD_S_BOTH, + OP_CONST, + OP_CONST_S, + /* ----- */ + OP_SYSREQ_D, + OP_SYSREQ_ND, + /* ----- */ + OP_NUM_OPCODES +} OPCODE; + +#define USENAMETABLE(hdr) \ + ((hdr)->defsize==sizeof(AMX_FUNCSTUBNT)) +#define NUMENTRIES(hdr,field,nextfield) \ + (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) +#define GETENTRY(hdr,table,index) \ + (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize) +#define GETENTRYNAME(hdr,entry) \ + ( USENAMETABLE(hdr) \ + ? (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUBNT*)(entry))->nameofs) \ + : ((AMX_FUNCSTUB*)(entry))->name ) + +#if !defined NDEBUG + static int check_endian(void) + { + uint16_t val=0x00ff; + unsigned char *ptr=(unsigned char *)&val; + /* "ptr" points to the starting address of "val". If that address + * holds the byte "0xff", the computer stored the low byte of "val" + * at the lower address, and so the memory lay out is Little Endian. + */ + assert(*ptr==0xff || *ptr==0x00); + #if BYTE_ORDER==BIG_ENDIAN + return *ptr==0x00; /* return "true" if big endian */ + #else + return *ptr==0xff; /* return "true" if little endian */ + #endif + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==16 + static void swap16(uint16_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert(sizeof(*v)==2); + /* swap two bytes */ + t=s[0]; + s[0]=s[1]; + s[1]=t; + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==32 + static void swap32(uint32_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert_static(sizeof(*v)==4); + /* swap outer two bytes */ + t=s[0]; + s[0]=s[3]; + s[3]=t; + /* swap inner two bytes */ + t=s[1]; + s[1]=s[2]; + s[2]=t; + } +#endif + +#if (BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==64) && (defined _I64_MAX || defined HAVE_I64) + static void swap64(uint64_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert(sizeof(*v)==8); + + t=s[0]; + s[0]=s[7]; + s[7]=t; + + t=s[1]; + s[1]=s[6]; + s[6]=t; + + t=s[2]; + s[2]=s[5]; + s[5]=t; + + t=s[3]; + s[3]=s[4]; + s[4]=t; + } +#endif + +#if defined AMX_ALIGN || defined AMX_INIT +uint16_t * AMXAPI amx_Align16(uint16_t *v) +{ + assert_static(sizeof(*v)==2); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap16(v); + #endif + return v; +} + +uint32_t * AMXAPI amx_Align32(uint32_t *v) +{ + assert_static(sizeof(*v)==4); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap32(v); + #endif + return v; +} + +#if defined _I64_MAX || defined HAVE_I64 +uint64_t * AMXAPI amx_Align64(uint64_t *v) +{ + assert(sizeof(*v)==8); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap64(v); + #endif + return v; +} +#endif /* _I64_MAX || HAVE_I64 */ +#endif /* AMX_ALIGN || AMX_INIT */ + +#if PAWN_CELL_SIZE==16 + #define swapcell swap16 +#elif PAWN_CELL_SIZE==32 + #define swapcell swap32 +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) + #define swapcell swap64 +#else + #error Unsupported cell size +#endif + +#if defined AMX_FLAGS +int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) +{ + AMX_HEADER *hdr; + + *flags=0; + if (amx==NULL) + return AMX_ERR_FORMAT; + hdr=(AMX_HEADER *)amx->base; + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versionflags; + return AMX_ERR_NONE; +} +#endif /* AMX_FLAGS */ + +#if defined AMX_DEFCALLBACK +int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, const cell *params) +{ +#if defined AMX_NATIVETABLE + extern AMX_NATIVE const AMX_NATIVETABLE[]; +#endif + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + AMX_NATIVE f; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->natives<=hdr->libraries); +#if defined AMX_NATIVETABLE + if (index<0) { + /* size of AMX_NATIVETABLE is unknown here, so we cannot verify index */ + f=(AMX_NATIVETABLE)[-(index+1)]; + } else { +#endif + assert(index>=0 && index<(cell)NUMENTRIES(hdr,natives,libraries)); + func=GETENTRY(hdr,natives,index); + f=(AMX_NATIVE)func->address; +#if defined AMX_NATIVETABLE + } /* if */ +#endif + assert(f!=NULL); + + /* Now that we have found the function, patch the program so that any + * subsequent call will call the function directly (bypassing this + * callback). + * This trick cannot work in the JIT, because the program would need to + * be re-JIT-compiled after patching a P-code instruction. + */ + #if defined JIT && !defined NDEBUG + if ((amx->flags & AMX_FLAG_JITC)!=0) + assert(amx->sysreq_d==0); + #endif + if (amx->sysreq_d!=0) { + /* at the point of the call, the CIP pseudo-register points directly + * behind the SYSREQ instruction and its parameter(s) + */ + unsigned char *code=amx->base+(int)hdr->cod+(int)amx->cip-sizeof(cell); + assert(amx->cip >= 4 && amx->cip < (hdr->dat - hdr->cod)); + assert_static(sizeof(f)<=sizeof(cell)); /* function pointer must fit in a cell */ + if (amx->flags & AMX_FLAG_SYSREQN) /* SYSREQ.N has 2 parameters */ + code-=sizeof(cell); +#if defined __GNUC__ || defined __ICC || defined ASM32 + if (*(cell*)code==index) { +#else + if (*(cell*)code!=OP_SYSREQ_PRI) { + assert(*(cell*)(code-sizeof(cell))==OP_SYSREQ_C || *(cell*)(code-sizeof(cell))==OP_SYSREQ_N); + assert(*(cell*)code==index); +#endif + *(cell*)(code-sizeof(cell))=amx->sysreq_d; + *(cell*)code=(cell)f; + } /* if */ + } /* if */ + + /* Note: + * params[0] == number of bytes for the additional parameters passed to the native function + * params[1] == first argument + * etc. + */ + + amx->error=AMX_ERR_NONE; + *result = f(amx,params); + return amx->error; +} +#endif /* defined AMX_DEFCALLBACK */ + + +#if defined JIT + extern int AMXAPI getMaxCodeSize(void); + extern int AMXAPI asm_runJIT(void *sourceAMXbase, void *jumparray, void *compiledAMXbase); +#endif + +#if PAWN_CELL_SIZE==16 || defined AMX_DONT_RELOCATE + #define JUMPABS(base,ip) ((cell *)((base) + *(ip))) + #define RELOC_ABS(base, off) + #define RELOC_VALUE(base, v) +#else + #define JUMPABS(base, ip) ((cell *)*(ip)) + #define RELOC_ABS(base, off) (*(ucell *)((base)+(int)(off)) += (ucell)(base)) + #define RELOC_VALUE(base, v) ((v)+((ucell)(base))) +#endif + +#define DBGPARAM(v) ( (v)=*(cell *)(code+(int)cip), cip+=sizeof(cell) ) + +#if defined AMX_INIT + +static int amx_BrowseRelocate(AMX *amx) +{ + AMX_HEADER *hdr; + unsigned char *code; + cell cip; + long codesize; + OPCODE op; + int sysreq_flg; + #if defined __GNUC__ || defined __ICC || defined ASM32 || defined JIT + cell *opcode_list; + #endif + #if defined JIT + int opcode_count = 0; + int reloc_count = 0; + #endif + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + code=amx->base+(int)hdr->cod; + codesize=hdr->dat - hdr->cod; + amx->flags|=AMX_FLAG_BROWSE; + + /* sanity checks */ + assert_static(OP_PUSH_PRI==36); + assert_static(OP_PROC==46); + assert_static(OP_SHL==65); + assert_static(OP_SMUL==72); + assert_static(OP_EQ==95); + assert_static(OP_INC_PRI==107); + assert_static(OP_MOVS==117); + assert_static(OP_SYMBOL==126); + assert_static(OP_LOAD_BOTH==154); + + amx->sysreq_d=0; /* preset */ + sysreq_flg=0; + #if defined __GNUC__ || defined __ICC || defined ASM32 || defined JIT + amx_Exec(amx, (cell*)(void*)&opcode_list, 0); + #endif + + /* start browsing code */ + for (cip=0; cip=OP_NUM_OPCODES) { + amx->flags &= ~AMX_FLAG_BROWSE; + return AMX_ERR_INVINSTR; + } /* if */ + #if defined __GNUC__ || defined __ICC || defined ASM32 || defined JIT + /* relocate opcode (only works if the size of an opcode is at least + * as big as the size of a pointer (jump address); so basically we + * rely on the opcode and a pointer being 32-bit + */ + *(cell *)(code+(int)cip) = opcode_list[op]; + #endif + #if defined JIT + opcode_count++; + #endif + cip+=sizeof(cell); + switch (op) { +#if !defined AMX_NO_MACRO_INSTR + case OP_PUSH5_C: /* instructions with 5 parameters */ + case OP_PUSH5: + case OP_PUSH5_S: + case OP_PUSH5_ADR: + cip+=sizeof(cell)*5; + break; + + case OP_PUSH4_C: /* instructions with 4 parameters */ + case OP_PUSH4: + case OP_PUSH4_S: + case OP_PUSH4_ADR: + #if defined AMX_NO_MACRO_INSTR + amx->flags &= ~AMX_FLAG_BROWSE; + return AMX_ERR_INVINSTR; + #endif + cip+=sizeof(cell)*4; + break; + + case OP_PUSH3_C: /* instructions with 3 parameters */ + case OP_PUSH3: + case OP_PUSH3_S: + case OP_PUSH3_ADR: + #if defined AMX_NO_MACRO_INSTR + amx->flags &= ~AMX_FLAG_BROWSE; + return AMX_ERR_INVINSTR; + #endif + cip+=sizeof(cell)*3; + break; + + case OP_PUSH2_C: /* instructions with 2 parameters */ + case OP_PUSH2: + case OP_PUSH2_S: + case OP_PUSH2_ADR: + case OP_LOAD_BOTH: + case OP_LOAD_S_BOTH: + case OP_CONST: + case OP_CONST_S: + #if defined AMX_NO_MACRO_INSTR + amx->flags &= ~AMX_FLAG_BROWSE; + return AMX_ERR_INVINSTR; + #endif + cip+=sizeof(cell)*2; + break; +#endif /* !defined AMX_NO_MACRO_INSTR */ + + case OP_LOAD_PRI: /* instructions with 1 parameter */ + case OP_LOAD_ALT: + case OP_LOAD_S_PRI: + case OP_LOAD_S_ALT: + case OP_LREF_PRI: + case OP_LREF_ALT: + case OP_LREF_S_PRI: + case OP_LREF_S_ALT: + case OP_LODB_I: + case OP_CONST_PRI: + case OP_CONST_ALT: + case OP_ADDR_PRI: + case OP_ADDR_ALT: + case OP_STOR_PRI: + case OP_STOR_ALT: + case OP_STOR_S_PRI: + case OP_STOR_S_ALT: + case OP_SREF_PRI: + case OP_SREF_ALT: + case OP_SREF_S_PRI: + case OP_SREF_S_ALT: + case OP_STRB_I: + case OP_LIDX_B: + case OP_IDXADDR_B: + case OP_ALIGN_PRI: + case OP_ALIGN_ALT: + case OP_LCTRL: + case OP_SCTRL: + case OP_PUSH_R: + case OP_PUSH_C: + case OP_PUSH: + case OP_PUSH_S: + case OP_STACK: + case OP_HEAP: + case OP_JREL: + case OP_SHL_C_PRI: + case OP_SHL_C_ALT: + case OP_SHR_C_PRI: + case OP_SHR_C_ALT: + case OP_ADD_C: + case OP_SMUL_C: + case OP_ZERO: + case OP_ZERO_S: + case OP_EQ_C_PRI: + case OP_EQ_C_ALT: + case OP_INC: + case OP_INC_S: + case OP_DEC: + case OP_DEC_S: + case OP_MOVS: + case OP_CMPS: + case OP_FILL: + case OP_HALT: + case OP_BOUNDS: + case OP_PUSH_ADR: + cip+=sizeof(cell); + break; + + case OP_LOAD_I: /* instructions without parameters */ + case OP_STOR_I: + case OP_LIDX: + case OP_IDXADDR: + case OP_MOVE_PRI: + case OP_MOVE_ALT: + case OP_XCHG: + case OP_PUSH_PRI: + case OP_PUSH_ALT: + case OP_POP_PRI: + case OP_POP_ALT: + case OP_PROC: + case OP_RET: + case OP_RETN: + case OP_CALL_PRI: + case OP_SHL: + case OP_SHR: + case OP_SSHR: + case OP_SMUL: + case OP_SDIV: + case OP_SDIV_ALT: + case OP_UMUL: + case OP_UDIV: + case OP_UDIV_ALT: + case OP_ADD: + case OP_SUB: + case OP_SUB_ALT: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_NOT: + case OP_NEG: + case OP_INVERT: + case OP_ZERO_PRI: + case OP_ZERO_ALT: + case OP_SIGN_PRI: + case OP_SIGN_ALT: + case OP_EQ: + case OP_NEQ: + case OP_LESS: + case OP_LEQ: + case OP_GRTR: + case OP_GEQ: + case OP_SLESS: + case OP_SLEQ: + case OP_SGRTR: + case OP_SGEQ: + case OP_INC_PRI: + case OP_INC_ALT: + case OP_INC_I: + case OP_DEC_PRI: + case OP_DEC_ALT: + case OP_DEC_I: + case OP_SYSREQ_PRI: + case OP_JUMP_PRI: + case OP_SWAP_PRI: + case OP_SWAP_ALT: + case OP_NOP: + case OP_BREAK: + break; + + case OP_CALL: /* opcodes that need relocation */ + case OP_JUMP: + case OP_JZER: + case OP_JNZ: + case OP_JEQ: + case OP_JNEQ: + case OP_JLESS: + case OP_JLEQ: + case OP_JGRTR: + case OP_JGEQ: + case OP_JSLESS: + case OP_JSLEQ: + case OP_JSGRTR: + case OP_JSGEQ: + case OP_SWITCH: + #if defined JIT + reloc_count++; + #endif + RELOC_ABS(code, cip); + cip+=sizeof(cell); + break; + + case OP_SYSREQ_C: + cip+=sizeof(cell); + sysreq_flg|=0x01; /* mark SYSREQ.C found */ + break; +#if !defined AMX_NO_MACRO_INSTR + case OP_SYSREQ_N: + cip+=sizeof(cell)*2; + sysreq_flg|=0x02; /* mark SYSREQ.N found */ + break; +#endif + + case OP_FILE: + case OP_SYMBOL: { + cell num; + DBGPARAM(num); + cip+=num; + break; + } /* case */ + case OP_LINE: + case OP_SRANGE: + cip+=2*sizeof(cell); + break; + case OP_SYMTAG: + cip+=sizeof(cell); + break; + case OP_CASETBL: { + cell num; + int i; + DBGPARAM(num); /* number of records follows the opcode */ + for (i=0; i<=num; i++) { + RELOC_ABS(code, cip+2*i*sizeof(cell)); + #if defined JIT + reloc_count++; + #endif + } /* for */ + cip+=(2*num + 1)*sizeof(cell); + break; + } /* case */ + default: + amx->flags &= ~AMX_FLAG_BROWSE; + return AMX_ERR_INVINSTR; + } /* switch */ + } /* for */ + + assert(sysreq_flg==0 || sysreq_flg==0x01 || sysreq_flg==0x02); + #if !defined AMX_DONT_RELOCATE + if (sysreq_flg==0x01 || sysreq_flg==0x02) { + /* only either type of system request opcode should be found (otherwise, + * we probably have a non-conforming compiler + */ + #if (defined __GNUC__ || defined __ICC || defined ASM32 || defined JIT) && !defined __64BIT__ + /* to use direct system requests, a function pointer must fit in a cell; + * because the native function's address will be stored as the parameter + * of SYSREQ.D + */ + if ((amx->flags & AMX_FLAG_JITC)==0 && sizeof(AMX_NATIVE)<=sizeof(cell)) + amx->sysreq_d=(sysreq_flg==0x01) ? opcode_list[OP_SYSREQ_D] : opcode_list[OP_SYSREQ_ND]; + #else + /* ANSI C + * to use direct system requests, a function pointer must fit in a cell; + * see the comment above + */ + if (sizeof(AMX_NATIVE)<=sizeof(cell)) + amx->sysreq_d=(sysreq_flg==0x01) ? OP_SYSREQ_D : OP_SYSREQ_ND; + #endif + } /* if */ + #endif + + #if defined JIT + amx->code_size = getMaxCodeSize()*opcode_count + hdr->cod + + (hdr->stp - hdr->dat); + amx->reloc_size = 2*sizeof(cell)*reloc_count; + #endif + + amx->flags &= ~AMX_FLAG_BROWSE; + amx->flags |= AMX_FLAG_RELOC; + if (sysreq_flg & 0x02) + amx->flags |= AMX_FLAG_SYSREQN; + return AMX_ERR_NONE; +} + +#if AMX_COMPACTMARGIN > 2 +static void expand(unsigned char *code, long codesize, long memsize) +{ + ucell c; + struct { + long memloc; + ucell c; + } spare[AMX_COMPACTMARGIN]; + int sh=0,st=0,sc=0; + int shift; + + /* for in-place expansion, move from the end backward */ + assert(memsize % sizeof(cell) == 0); + while (codesize>0) { + c=0; + shift=0; + do { + codesize--; + /* no input byte should be shifted out completely */ + assert(shift<8*sizeof(cell)); + /* we work from the end of a sequence backwards; the final code in + * a sequence may not have the continuation bit set */ + assert(shift>0 || (code[(size_t)codesize] & 0x80)==0); + c|=(ucell)(code[(size_t)codesize] & 0x7f) << shift; + shift+=7; + } while (codesize>0 && (code[(size_t)codesize-1] & 0x80)!=0); + /* sign expand */ + if ((code[(size_t)codesize] & 0x40)!=0) { + while (shift < (int)(8*sizeof(cell))) { + c|=(ucell)0xff << shift; + shift+=8; + } /* while */ + } /* if */ + /* store */ + while (sc && (spare[sh].memloc>codesize)) { + *(ucell *)(code+(int)spare[sh].memloc)=spare[sh].c; + sh=(sh+1)%AMX_COMPACTMARGIN; + sc--; + } /* while */ + memsize -= sizeof(cell); + assert(memsize>=0); + if ((memsize>codesize)||((memsize==codesize)&&(memsize==0))) { + *(ucell *)(code+(size_t)memsize)=c; + } else { + assert(sc 2 */ + +int AMXAPI amx_Init(AMX *amx,void *program) +{ + AMX_HEADER *hdr; + int err; + unsigned char *data; + #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + #if defined _Windows + char libname[sNAMEMAX+8]; /* +1 for '\0', +3 for 'amx' prefix, +4 for extension */ + typedef int (FAR WINAPI *AMX_ENTRY)(AMX _FAR *amx); + HINSTANCE hlib; + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + char libname[_MAX_PATH]; + char *root; + typedef int (*AMX_ENTRY)(AMX *amx); + void *hlib; + #if !defined AMX_LIBPATH + #define AMX_LIBPATH "AMXLIB" + #endif + #endif + int numlibraries,i; + AMX_FUNCSTUB *lib; + AMX_ENTRY libinit; + #endif + + if ((amx->flags & AMX_FLAG_RELOC)!=0) + return AMX_ERR_INIT; /* already initialized (may not do so twice) */ + + hdr=(AMX_HEADER *)program; + /* the header is in Little Endian, on a Big Endian machine, swap all + * multi-byte words + */ + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + amx_Align32((uint32_t*)&hdr->size); + amx_Align16(&hdr->magic); + amx_Align16((uint16_t*)&hdr->flags); + amx_Align16((uint16_t*)&hdr->defsize); + amx_Align32((uint32_t*)&hdr->cod); + amx_Align32((uint32_t*)&hdr->dat); + amx_Align32((uint32_t*)&hdr->hea); + amx_Align32((uint32_t*)&hdr->stp); + amx_Align32((uint32_t*)&hdr->cip); + amx_Align32((uint32_t*)&hdr->publics); + amx_Align32((uint32_t*)&hdr->natives); + amx_Align32((uint32_t*)&hdr->libraries); + amx_Align32((uint32_t*)&hdr->pubvars); + amx_Align32((uint32_t*)&hdr->tags); + #endif + + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versiondefsize!=sizeof(AMX_FUNCSTUB) && hdr->defsize!=sizeof(AMX_FUNCSTUBNT)) + return AMX_ERR_FORMAT; + if (USENAMETABLE(hdr)) { + uint16_t *namelength; + /* when there is a separate name table, check the maximum name length + * in that table + */ + amx_Align32((uint32_t*)&hdr->nametable); + namelength=(uint16_t*)((unsigned char*)program + (unsigned)hdr->nametable); + amx_Align16(namelength); + if (*namelength>sNAMEMAX) + return AMX_ERR_FORMAT; + } /* if */ + if (hdr->stp<=0) + return AMX_ERR_FORMAT; + #if BYTE_ORDER==BIG_ENDIAN + if ((hdr->flags & AMX_FLAG_COMPACT)==0) { + ucell *code=(ucell *)((unsigned char *)program+(int)hdr->cod); + while (code<(ucell *)((unsigned char *)program+(int)hdr->hea)) + swapcell(code++); + } /* if */ + #endif + assert((hdr->flags & AMX_FLAG_COMPACT)!=0 || hdr->hea == hdr->size); + if ((hdr->flags & AMX_FLAG_COMPACT)!=0) { + #if AMX_COMPACTMARGIN > 2 + expand((unsigned char *)program+(int)hdr->cod, + hdr->size - hdr->cod, hdr->hea - hdr->cod); + #else + return AMX_ERR_FORMAT; + #endif + } /* if */ + + amx->base=(unsigned char *)program; + + /* set initial values */ + amx->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */ + amx->stp=hdr->stp - hdr->dat - sizeof(cell); + amx->hea=amx->hlw; + amx->stk=amx->stp; + #if defined AMX_DEFCALLBACK + if (amx->callback==NULL) + amx->callback=amx_Callback; + #endif + /* to split the data segment off the code segment, the "data" field must + * be set to a non-NULL value on initialization, before calling amx_Init() + */ + if (amx->data!=NULL) { + data=amx->data; + memcpy(data,amx->base+(int)hdr->dat,(size_t)(hdr->hea-hdr->dat)); + } else { + data=amx->base+(int)hdr->dat; + } /* if */ + + /* Set a zero cell at the top of the stack, which functions + * as a sentinel for strings. + */ + * (cell *)(data+(int)(hdr->stp-hdr->dat-sizeof(cell)))=0; + + /* also align all addresses in the public function, public variable, + * public tag and native function tables --offsets into the name table + * (if present) must also be swapped. + */ + #if BYTE_ORDER==BIG_ENDIAN + { /* local */ + AMX_FUNCSTUB *fs; + int i,num; + + fs=GETENTRY(hdr,natives,0); + num=NUMENTRIES(hdr,natives,libraries); + for (i=0; iaddress); /* redundant, because it should be zero */ + if (USENAMETABLE(hdr)) + amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + + fs=GETENTRY(hdr,publics,0); + assert(hdr->publics<=hdr->natives); + num=NUMENTRIES(hdr,publics,natives); + for (i=0; iaddress); + if (USENAMETABLE(hdr)) + amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + + fs=GETENTRY(hdr,pubvars,0); + assert(hdr->pubvars<=hdr->tags); + num=NUMENTRIES(hdr,pubvars,tags); + for (i=0; iaddress); + if (USENAMETABLE(hdr)) + amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + + fs=GETENTRY(hdr,tags,0); + if (hdr->file_version<7) { + assert(hdr->tags<=hdr->cod); + num=NUMENTRIES(hdr,tags,cod); + } else { + assert(hdr->tags<=hdr->nametable); + num=NUMENTRIES(hdr,tags,nametable); + } /* if */ + for (i=0; iaddress); + if (USENAMETABLE(hdr)) + amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + } /* local */ + #endif + + /* relocate call and jump instructions */ + if ((err=amx_BrowseRelocate(amx))!=AMX_ERR_NONE) + return err; + + /* load any extension modules that the AMX refers to */ + #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + root=getenv("AMXLIB"); + #endif + hdr=(AMX_HEADER *)amx->base; + numlibraries=NUMENTRIES(hdr,libraries,pubvars); + for (i=0; iaddress=(ucell)hlib; + } /* for */ + #endif + + return AMX_ERR_NONE; +} + +#if defined JIT + + #define CODESIZE_JIT 8192 /* approximate size of the code for the JIT */ + + #if defined __WIN32__ /* this also applies to Win32 "console" applications */ + + #define ALIGN(addr) (addr) + + #define PROT_READ 0x1 /* page can be read */ + #define PROT_WRITE 0x2 /* page can be written */ + #define PROT_EXEC 0x4 /* page can be executed */ + #define PROT_NONE 0x0 /* page can not be accessed */ + + static int mprotect(void *addr, size_t len, int prot) + { + DWORD prev, p = 0; + if ((prot & PROT_WRITE)!=0) + p = PAGE_EXECUTE_READWRITE; + else + p |= PAGE_EXECUTE_READ; + return !VirtualProtect(addr, len, p, &prev); + } + + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + + /* Linux already has mprotect() */ + #define ALIGN(addr) (char *)(((long)addr + sysconf(_SC_PAGESIZE)-1) & ~(sysconf(_SC_PAGESIZE)-1)) + + #else + + // TODO: Add cases for Linux, Unix, OS/2, ... + + /* DOS32 has no imposed limits on its segments */ + #define ALIGN(addr) (addr) + #define mprotect(addr, len, prot) (0) + + #endif /* #if defined __WIN32 __ */ + +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) +{ + int res; + AMX_HEADER *hdr; + + if ((amx->flags & AMX_FLAG_JITC)==0) + return AMX_ERR_INIT_JIT; /* flag not set, this AMX is not prepared for JIT */ + if (hdr->file_version>MAX_FILE_VER_JIT) + return AMX_ERR_VERSION; /* JIT may not support the newest file version(s) */ + + /* Patching SYSREQ.C opcodes to SYSREQ.D cannot work in the JIT, because the + * program would need to be re-JIT-compiled after patching a P-code + * instruction. If this field is not zero, something went wrong with the + * amx_BrowseRelocate(). + */ + assert(amx->sysreq_d==0); + + if (mprotect(ALIGN(asm_runJIT), CODESIZE_JIT, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) + return AMX_ERR_INIT_JIT; + + /* copy the prefix */ + memcpy(native_code, amx->base, ((AMX_HEADER *)(amx->base))->cod); + hdr = native_code; + + /* JIT rulz! (TM) */ + /* MP: added check for correct compilation */ + if ((res = asm_runJIT(amx->base, reloc_table, native_code)) == 0) { + /* update the required memory size (the previous value was a + * conservative estimate, now we know the exact size) + */ + amx->code_size = (hdr->dat + hdr->stp + 3) & ~3; + /* The compiled code is relocatable, since only relative jumps are + * used for destinations within the generated code and absoulute + * addresses for jumps into the runtime, which is fixed in memory. + */ + amx->base = (unsigned char*) native_code; + amx->cip = hdr->cip; + amx->hea = hdr->hea; + amx->hlw = hdr->hea; + amx->stp = hdr->stp - sizeof(cell); + /* also put a sentinel for strings at the top the stack */ + *(cell *)((char*)native_code + hdr->dat + hdr->stp - sizeof(cell)) = 0; + amx->stk = amx->stp; + } /* if */ + + return (res == 0) ? AMX_ERR_NONE : AMX_ERR_INIT_JIT; +} + +#else /* #if defined JIT */ + +int AMXAPI amx_InitJIT(AMX *amx,void *compiled_program,void *reloc_table) +{ + (void)amx; + (void)compiled_program; + (void)reloc_table; + return AMX_ERR_INIT_JIT; +} + +#endif /* #if defined JIT */ + +#endif /* AMX_INIT */ + +#if defined AMX_CLEANUP +int AMXAPI amx_Cleanup(AMX *amx) +{ + #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + #if defined _Windows + typedef int (FAR WINAPI *AMX_ENTRY)(AMX FAR *amx); + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + typedef int (*AMX_ENTRY)(AMX *amx); + #endif + AMX_HEADER *hdr; + int numlibraries,i; + AMX_FUNCSTUB *lib; + AMX_ENTRY libcleanup; + #endif + + /* unload all extension modules */ + #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + hdr=(AMX_HEADER *)amx->base; + assert(hdr->magic==AMX_MAGIC); + numlibraries=NUMENTRIES(hdr,libraries,pubvars); + for (i=0; iaddress!=0) { + char funcname[sNAMEMAX+12]; /* +1 for '\0', +4 for 'amx_', +7 for 'Cleanup' */ + strcpy(funcname,"amx_"); + strcat(funcname,GETENTRYNAME(hdr,lib)); + strcat(funcname,"Cleanup"); + #if defined _Windows + libcleanup=(AMX_ENTRY)GetProcAddress((HINSTANCE)lib->address,funcname); + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + libcleanup=(AMX_ENTRY)dlsym((void*)lib->address,funcname); + #endif + if (libcleanup!=NULL) + libcleanup(amx); + #if defined _Windows + FreeLibrary((HINSTANCE)lib->address); + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + dlclose((void*)lib->address); + #endif + } /* if */ + } /* for */ + #else + (void)amx; + #endif + return AMX_ERR_NONE; +} +#endif /* AMX_CLEANUP */ + +#if defined AMX_CLONE +int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) +{ + AMX_HEADER *hdr; + unsigned char _FAR *dataSource; + + if (amxSource==NULL) + return AMX_ERR_FORMAT; + if (amxClone==NULL) + return AMX_ERR_PARAMS; + if ((amxSource->flags & AMX_FLAG_RELOC)==0) + return AMX_ERR_INIT; + hdr=(AMX_HEADER *)amxSource->base; + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versionbase=amxSource->base; + amxClone->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */ + amxClone->stp=hdr->stp - hdr->dat - sizeof(cell); + amxClone->hea=amxClone->hlw; + amxClone->stk=amxClone->stp; + if (amxClone->callback==NULL) + amxClone->callback=amxSource->callback; + if (amxClone->debug==NULL) + amxClone->debug=amxSource->debug; + amxClone->flags=amxSource->flags; + + /* copy the data segment; the stack and the heap can be left uninitialized */ + assert(data!=NULL); + amxClone->data=(unsigned char _FAR *)data; + dataSource=(amxSource->data!=NULL) ? amxSource->data : amxSource->base+(int)hdr->dat; + memcpy(amxClone->data,dataSource,(size_t)(hdr->hea-hdr->dat)); + + /* Set a zero cell at the top of the stack, which functions + * as a sentinel for strings. + */ + * (cell *)(amxClone->data+(int)amxClone->stp) = 0; + + return AMX_ERR_NONE; +} +#endif /* AMX_CLONE */ + +#if defined AMX_MEMINFO +int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) +{ + AMX_HEADER *hdr; + + if (amx==NULL) + return AMX_ERR_FORMAT; + hdr=(AMX_HEADER *)amx->base; + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versiondat - hdr->cod; + if (datasize!=NULL) + *datasize=hdr->hea - hdr->dat; + if (stackheap!=NULL) + *stackheap=hdr->stp - hdr->hea; + + return AMX_ERR_NONE; +} +#endif /* AMX_MEMINFO */ + +#if defined AMX_NAMELENGTH +int AMXAPI amx_NameLength(AMX *amx, int *length) +{ + AMX_HEADER *hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + if (USENAMETABLE(hdr)) { + uint16_t *namelength=(uint16_t*)(amx->base + (unsigned)hdr->nametable); + *length=*namelength; + assert(hdr->file_version>=7); /* name table exists only for file version 7+ */ + } else { + *length=hdr->defsize - sizeof(ucell); + } /* if */ + return AMX_ERR_NONE; +} +#endif /* AMX_NAMELENGTH */ + +#if defined AMX_XXXNATIVES +int AMXAPI amx_NumNatives(AMX *amx, int *number) +{ + AMX_HEADER *hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->natives<=hdr->libraries); + *number=NUMENTRIES(hdr,natives,libraries); + return AMX_ERR_NONE; +} + +int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->natives<=hdr->libraries); + if (index>=(cell)NUMENTRIES(hdr,natives,libraries)) + return AMX_ERR_INDEX; + + func=GETENTRY(hdr,natives,index); + strcpy(funcname,GETENTRYNAME(hdr,func)); + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) +{ + int first,last,mid,result; + char pname[sNAMEMAX+1]; + + amx_NumNatives(amx, &last); + last--; /* last valid index is 1 less than the number of functions */ + first=0; + /* binary search */ + while (first<=last) { + mid=(first+last)/2; + amx_GetNative(amx, mid, pname); + result=strcmp(pname,name); + if (result>0) { + last=mid-1; + } else if (result<0) { + first=mid+1; + } else { + *index=mid; + return AMX_ERR_NONE; + } /* if */ + } /* while */ + /* not found, set to an invalid index, so amx_Exec() will fail */ + *index=INT_MAX; + return AMX_ERR_NOTFOUND; +} +#endif /* AMX_XXXNATIVES */ + +#if defined AMX_XXXPUBLICS +int AMXAPI amx_NumPublics(AMX *amx, int *number) +{ + AMX_HEADER *hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->publics<=hdr->natives); + *number=NUMENTRIES(hdr,publics,natives); + return AMX_ERR_NONE; +} + +int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->publics<=hdr->natives); + if (index>=(cell)NUMENTRIES(hdr,publics,natives)) + return AMX_ERR_INDEX; + + func=GETENTRY(hdr,publics,index); + strcpy(funcname,GETENTRYNAME(hdr,func)); + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindPublic(AMX *amx, const char *name, int *index) +{ + int first,last,mid,result; + char pname[sNAMEMAX+1]; + + amx_NumPublics(amx, &last); + last--; /* last valid index is 1 less than the number of functions */ + first=0; + /* binary search */ + while (first<=last) { + mid=(first+last)/2; + amx_GetPublic(amx, mid, pname); + result=strcmp(pname,name); + if (result>0) { + last=mid-1; + } else if (result<0) { + first=mid+1; + } else { + *index=mid; + return AMX_ERR_NONE; + } /* if */ + } /* while */ + /* not found, set to an invalid index, so amx_Exec() will fail */ + *index=INT_MAX; + return AMX_ERR_NOTFOUND; +} +#endif /* AMX_XXXPUBLICS */ + +#if defined AMX_XXXPUBVARS +int AMXAPI amx_NumPubVars(AMX *amx, int *number) +{ + AMX_HEADER *hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->pubvars<=hdr->tags); + *number=NUMENTRIES(hdr,pubvars,tags); + return AMX_ERR_NONE; +} + +int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *var; + + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->pubvars<=hdr->tags); + if (index>=(cell)NUMENTRIES(hdr,pubvars,tags)) + return AMX_ERR_INDEX; + + var=GETENTRY(hdr,pubvars,index); + strcpy(varname,GETENTRYNAME(hdr,var)); + *amx_addr=var->address; + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) +{ + int first,last,mid,result; + char pname[sNAMEMAX+1]; + cell paddr; + + amx_NumPubVars(amx, &last); + last--; /* last valid index is 1 less than the number of functions */ + first=0; + /* binary search */ + while (first<=last) { + mid=(first+last)/2; + amx_GetPubVar(amx, mid, pname, &paddr); + result=strcmp(pname,varname); + if (result>0) { + last=mid-1; + } else if (result<0) { + first=mid+1; + } else { + *amx_addr=paddr; + return AMX_ERR_NONE; + } /* if */ + } /* while */ + /* not found */ + *amx_addr=0; + return AMX_ERR_NOTFOUND; +} +#endif /* AMX_XXXPUBVARS */ + +#if defined AMX_XXXTAGS +int AMXAPI amx_NumTags(AMX *amx, int *number) +{ + AMX_HEADER *hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + if (hdr->file_version<5) { /* the tagname table appeared in file format 5 */ + *number=0; + return AMX_ERR_VERSION; + } /* if */ + if (hdr->file_version<7) { + assert(hdr->tags<=hdr->cod); + *number=NUMENTRIES(hdr,tags,cod); + } else { + assert(hdr->tags<=hdr->nametable); + *number=NUMENTRIES(hdr,tags,nametable); + } /* if */ + return AMX_ERR_NONE; +} + +int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *tag; + + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + if (hdr->file_version<5) { /* the tagname table appeared in file format 5 */ + *tagname='\0'; + *tag_id=0; + return AMX_ERR_VERSION; + } /* if */ + + if (hdr->file_version<7) { + assert(hdr->tags<=hdr->cod); + if (index>=(cell)NUMENTRIES(hdr,tags,cod)) + return AMX_ERR_INDEX; + } else { + assert(hdr->tags<=hdr->nametable); + if (index>=(cell)NUMENTRIES(hdr,tags,nametable)) + return AMX_ERR_INDEX; + } /* if */ + + tag=GETENTRY(hdr,tags,index); + strcpy(tagname,GETENTRYNAME(hdr,tag)); + *tag_id=tag->address; + + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) +{ + int first,last,mid; + cell mid_id; + + #if !defined NDEBUG + /* verify that the tagname table is sorted on the tag_id */ + amx_NumTags(amx, &last); + if (last>0) { + cell cur_id; + amx_GetTag(amx,0,tagname,&cur_id); + for (first=1; firsttag_id) + last=mid-1; + else if (mid_idusertags[index]!=tag; index++) + /* nothing */; + if (index>=AMX_USERNUM) + return AMX_ERR_USERDATA; + *ptr=amx->userdata[index]; + return AMX_ERR_NONE; +} + +int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) +{ + int index; + + assert(amx!=NULL); + assert(tag!=0); + /* try to find existing tag */ + for (index=0; indexusertags[index]!=tag; index++) + /* nothing */; + /* if not found, try to find empty tag */ + if (index>=AMX_USERNUM) + for (index=0; indexusertags[index]!=0; index++) + /* nothing */; + /* if still not found, quit with error */ + if (index>=AMX_USERNUM) + return AMX_ERR_INDEX; + /* set the tag and the value */ + amx->usertags[index]=tag; + amx->userdata[index]=ptr; + return AMX_ERR_NONE; +} +#endif /* AMX_XXXUSERDATA */ + +#if defined AMX_REGISTER || defined AMX_EXEC || defined AMX_INIT +static AMX_NATIVE findfunction(const char *name, const AMX_NATIVE_INFO *list, int number) +{ + int i; + + assert(list!=NULL); + for (i=0; list[i].name!=NULL && (ibase; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->natives<=hdr->libraries); + numnatives=NUMENTRIES(hdr,natives,libraries); + + err=AMX_ERR_NONE; + func=GETENTRY(hdr,natives,0); + for (i=0; iaddress==0) { + /* this function is not yet located */ + funcptr=(list!=NULL) ? findfunction(GETENTRYNAME(hdr,func),list,number) : NULL; + if (funcptr!=NULL) + func->address=(ucell)funcptr; + else + err=AMX_ERR_NOTFOUND; + } /* if */ + func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize); + } /* for */ + if (err==AMX_ERR_NONE) + amx->flags|=AMX_FLAG_NTVREG; + return err; +} +#endif /* AMX_REGISTER || AMX_EXEC || AMX_INIT */ + +#if defined AMX_NATIVEINFO +AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) +{ + static AMX_NATIVE_INFO n; + n.name=name; + n.func=func; + return &n; +} +#endif /* AMX_NATIVEINFO */ + + +#define STKMARGIN ((cell)(16*sizeof(cell))) + +#if defined AMX_PUSHXXX + +int AMXAPI amx_Push(AMX *amx, cell value) +{ + AMX_HEADER *hdr; + unsigned char *data; + + if (amx->hea+STKMARGIN>amx->stk) + return AMX_ERR_STACKERR; + hdr=(AMX_HEADER *)amx->base; + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + amx->stk-=sizeof(cell); + amx->paramcount+=1; + *(cell *)(data+(int)amx->stk)=value; + return AMX_ERR_NONE; +} + +int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) +{ + cell *paddr,xaddr; + int err; + + assert(amx!=NULL); + assert(array!=NULL); + + err=amx_Allot(amx,numcells,&xaddr,&paddr); + if (err==AMX_ERR_NONE) { + if (amx_addr!=NULL) + *amx_addr=xaddr; + if (phys_addr!=NULL) + *phys_addr=paddr; + memcpy(paddr,array,numcells*sizeof(cell)); + err=amx_Push(amx,xaddr); + } /* if */ + return err; +} + +int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) +{ + cell *paddr, xaddr; + int numcells,err; + + assert(amx!=NULL); + assert(string!=NULL); + + #if defined AMX_ANSIONLY + numcells=strlen(string) + 1; + #else + numcells= (use_wchar ? wcslen((const wchar_t*)string) : strlen(string)) + 1; + #endif + if (pack) + numcells=(numcells+sizeof(cell)-1)/sizeof(cell); + err=amx_Allot(amx,numcells,&xaddr,&paddr); + if (err==AMX_ERR_NONE) { + if (amx_addr!=NULL) + *amx_addr=xaddr; + if (phys_addr!=NULL) + *phys_addr=paddr; + amx_SetString(paddr,string,pack,use_wchar,UNLIMITED); + err=amx_Push(amx,xaddr); + } /* if */ + return err; +} +#endif /* AMX_PUSHXXX */ + +#if defined AMX_EXEC || defined AMX_INIT + +/* It is assumed that the abstract machine can simply access the memory area + * for the global data and the stack. If this is not the case, you need to + * define the macro sets _R() and _W(), for reading and writing to memory. + */ +#if !defined _R + #define _R_DEFAULT /* mark default memory access */ + #define _R(base,addr) (* (cell *)((unsigned char*)(base)+(int)(addr))) + #define _R8(base,addr) (* (unsigned char *)((unsigned char*)(base)+(int)(addr))) + #define _R16(base,addr) (* (uint16_t *)((unsigned char*)(base)+(int)(addr))) + #define _R32(base,addr) (* (uint32_t *)((unsigned char*)(base)+(int)(addr))) +#endif +#if !defined _W + #define _W_DEFAULT /* mark default memory access */ + #define _W(base,addr,value) ((*(cell *)((unsigned char*)(base)+(int)(addr)))=(cell)(value)) + #define _W8(base,addr,value) ((*(unsigned char *)((unsigned char*)(base)+(int)(addr)))=(unsigned char)(value)) + #define _W16(base,addr,value) ((*(uint16_t *)((unsigned char*)(base)+(int)(addr)))=(uint16_t)(value)) + #define _W32(base,addr,value) ((*(uint32_t *)((unsigned char*)(base)+(int)(addr)))=(uint32_t)(value)) +#endif + +#if -8/3==-2 && 8/-3==-2 + #define TRUNC_SDIV /* signed divisions are truncated on this platform */ +#else + #define IABS(a) ((a)>=0 ? (a) : (-a)) +#endif + +/* The pseudo-instructions come from the code stream. Normally, these are just + * accessed from memory. When the instructions must be fetched in some other + * way, the definition below must be pre-defined. + * N.B.: + * - reading from a code address should increment the instruction pointer + * (called "cip") + * - only cell-sized accesses occur in code memory + */ +#if !defined _RCODE + #define _RCODE() ( *cip++ ) +#endif + +#if !defined GETPARAM + #define GETPARAM(v) ( v=_RCODE() ) /* read a parameter from the opcode stream */ +#endif +#define SKIPPARAM(n) ( cip=(cell *)cip+(n) ) /* for obsolete opcodes */ + +/* PUSH() and POP() are defined in terms of the _R() and _W() macros */ +#define PUSH(v) ( stk-=sizeof(cell), _W(data,stk,v) ) +#define POP(v) ( v=_R(data,stk), stk+=sizeof(cell) ) + +#define ABORT(amx,v) { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; } + +#define CHKMARGIN() if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR +#define CHKSTACK() if (stk>amx->stp) return AMX_ERR_STACKLOW +#define CHKHEAP() if (heahlw) return AMX_ERR_HEAPLOW + +#if (defined __GNUC__ || defined __ICC) && !(defined ASM32 || defined JIT) + /* GNU C version uses the "labels as values" extension to create + * fast "indirect threaded" interpreter. The Intel C/C++ compiler + * supports this too. + */ + +#define NEXT(cip) goto **(void **)cip++ + +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) +{ +static const void * const amx_opcodelist[] = { + &&op_none, &&op_load_pri, &&op_load_alt, &&op_load_s_pri, + &&op_load_s_alt,&&op_lref_pri, &&op_lref_alt, &&op_lref_s_pri, + &&op_lref_s_alt,&&op_load_i, &&op_lodb_i, &&op_const_pri, + &&op_const_alt, &&op_addr_pri, &&op_addr_alt, &&op_stor_pri, + &&op_stor_alt, &&op_stor_s_pri,&&op_stor_s_alt,&&op_sref_pri, + &&op_sref_alt, &&op_sref_s_pri,&&op_sref_s_alt,&&op_stor_i, + &&op_strb_i, &&op_lidx, &&op_lidx_b, &&op_idxaddr, + &&op_idxaddr_b, &&op_align_pri, &&op_align_alt, &&op_lctrl, + &&op_sctrl, &&op_move_pri, &&op_move_alt, &&op_xchg, + &&op_push_pri, &&op_push_alt, &&op_push_r, &&op_push_c, + &&op_push, &&op_push_s, &&op_pop_pri, &&op_pop_alt, + &&op_stack, &&op_heap, &&op_proc, &&op_ret, + &&op_retn, &&op_call, &&op_call_pri, &&op_jump, + &&op_jrel, &&op_jzer, &&op_jnz, &&op_jeq, + &&op_jneq, &&op_jless, &&op_jleq, &&op_jgrtr, + &&op_jgeq, &&op_jsless, &&op_jsleq, &&op_jsgrtr, + &&op_jsgeq, &&op_shl, &&op_shr, &&op_sshr, + &&op_shl_c_pri, &&op_shl_c_alt, &&op_shr_c_pri, &&op_shr_c_alt, + &&op_smul, &&op_sdiv, &&op_sdiv_alt, &&op_umul, + &&op_udiv, &&op_udiv_alt, &&op_add, &&op_sub, + &&op_sub_alt, &&op_and, &&op_or, &&op_xor, + &&op_not, &&op_neg, &&op_invert, &&op_add_c, + &&op_smul_c, &&op_zero_pri, &&op_zero_alt, &&op_zero, + &&op_zero_s, &&op_sign_pri, &&op_sign_alt, &&op_eq, + &&op_neq, &&op_less, &&op_leq, &&op_grtr, + &&op_geq, &&op_sless, &&op_sleq, &&op_sgrtr, + &&op_sgeq, &&op_eq_c_pri, &&op_eq_c_alt, &&op_inc_pri, + &&op_inc_alt, &&op_inc, &&op_inc_s, &&op_inc_i, + &&op_dec_pri, &&op_dec_alt, &&op_dec, &&op_dec_s, + &&op_dec_i, &&op_movs, &&op_cmps, &&op_fill, + &&op_halt, &&op_bounds, &&op_sysreq_pri,&&op_sysreq_c, + &&op_file, &&op_line, &&op_symbol, &&op_srange, + &&op_jump_pri, &&op_switch, &&op_casetbl, &&op_swap_pri, + &&op_swap_alt, &&op_push_adr, &&op_nop, &&op_sysreq_n, + &&op_symtag, &&op_break, &&op_push2_c, &&op_push2, + &&op_push2_s, &&op_push2_adr, &&op_push3_c, &&op_push3, + &&op_push3_s, &&op_push3_adr, &&op_push4_c, &&op_push4, + &&op_push4_s, &&op_push4_adr, &&op_push5_c, &&op_push5, + &&op_push5_s, &&op_push5_adr, &&op_load_both, &&op_load_s_both, + &&op_const, &&op_const_s, &&op_sysreq_d, &&op_sysreq_nd }; + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + unsigned char *code, *data; + cell pri,alt,stk,frm,hea; + cell reset_stk, reset_hea, *cip; + cell offs,val; + ucell codesize; + int num,i; + + /* HACK: return label table (for amx_BrowseRelocate) if amx structure + * has the AMX_FLAG_BROWSE flag set. + */ + assert(amx!=NULL); + if ((amx->flags & AMX_FLAG_BROWSE)==AMX_FLAG_BROWSE) { + assert(sizeof(cell)==sizeof(void *)); + assert(retval!=NULL); + *retval=(cell)amx_opcodelist; + return 0; + } /* if */ + + if (amx->callback==NULL) + return AMX_ERR_CALLBACK; + if ((amx->flags & AMX_FLAG_RELOC)==0) + return AMX_ERR_INIT; + if ((amx->flags & AMX_FLAG_NTVREG)==0) { + if ((num=amx_Register(amx,NULL,0))!=AMX_ERR_NONE) + return num; + } /* if */ + assert((amx->flags & AMX_FLAG_BROWSE)==0); + + /* set up the registers */ + hdr=(AMX_HEADER *)amx->base; + assert(hdr->magic==AMX_MAGIC); + codesize=(ucell)(hdr->dat-hdr->cod); + code=amx->base+(int)hdr->cod; + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + hea=amx->hea; + stk=amx->stk; + reset_stk=stk; + reset_hea=hea; + alt=frm=pri=0;/* just to avoid compiler warnings */ + num=0; /* just to avoid compiler warnings */ + + /* get the start address */ + if (index==AMX_EXEC_MAIN) { + if (hdr->cip<0) + return AMX_ERR_INDEX; + cip=(cell *)(code + (int)hdr->cip); + } else if (index==AMX_EXEC_CONT) { + /* all registers: pri, alt, frm, cip, hea, stk, reset_stk, reset_hea */ + frm=amx->frm; + stk=amx->stk; + hea=amx->hea; + pri=amx->pri; + alt=amx->alt; + reset_stk=amx->reset_stk; + reset_hea=amx->reset_hea; + cip=(cell *)(code + (int)amx->cip); + } else if (index<0) { + return AMX_ERR_INDEX; + } else { + if (index>=(int)NUMENTRIES(hdr,publics,natives)) + return AMX_ERR_INDEX; + func=GETENTRY(hdr,publics,index); + cip=(cell *)(code + (int)func->address); + } /* if */ + /* check values just copied */ + CHKSTACK(); + CHKHEAP(); + assert(check_endian()); + + /* sanity checks */ + assert(OP_PUSH_PRI==36); + assert(OP_PROC==46); + assert(OP_SHL==65); + assert(OP_SMUL==72); + assert(OP_EQ==95); + assert(OP_INC_PRI==107); + assert(OP_MOVS==117); + assert(OP_SYMBOL==126); + #if PAWN_CELL_SIZE==16 + assert(sizeof(cell)==2); + #elif PAWN_CELL_SIZE==32 + assert(sizeof(cell)==4); + #elif PAWN_CELL_SIZE==64 + assert(sizeof(cell)==8); + #else + #error Unsupported cell size + #endif + + if (index!=AMX_EXEC_CONT) { + reset_stk+=amx->paramcount*sizeof(cell); + PUSH(amx->paramcount*sizeof(cell)); + amx->paramcount=0; /* push the parameter count to the stack & reset */ + PUSH(0); /* zero return address */ + } /* if */ + /* check stack/heap before starting to run */ + CHKMARGIN(); + + /* start running */ + NEXT(cip); + + op_none: + ABORT(amx,AMX_ERR_INVINSTR); + op_load_pri: + GETPARAM(offs); + pri=_R(data,offs); + NEXT(cip); + op_load_alt: + GETPARAM(offs); + alt=_R(data,offs); + NEXT(cip); + op_load_s_pri: + GETPARAM(offs); + pri=_R(data,frm+offs); + NEXT(cip); + op_load_s_alt: + GETPARAM(offs); + alt=_R(data,frm+offs); + NEXT(cip); + op_lref_pri: + GETPARAM(offs); + offs=_R(data,offs); + pri=_R(data,offs); + NEXT(cip); + op_lref_alt: + GETPARAM(offs); + offs=_R(data,offs); + alt=_R(data,offs); + NEXT(cip); + op_lref_s_pri: + GETPARAM(offs); + offs=_R(data,frm+offs); + pri=_R(data,offs); + NEXT(cip); + op_lref_s_alt: + GETPARAM(offs); + offs=_R(data,frm+offs); + alt=_R(data,offs); + NEXT(cip); + op_load_i: + /* verify address */ + if ((pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,pri); + NEXT(cip); + op_lodb_i: + GETPARAM(offs); + /* verify address */ + if ((pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch (offs) { + case 1: + pri=_R8(data,pri); + break; + case 2: + pri=_R16(data,pri); + break; + case 4: + pri=_R32(data,pri); + break; + } /* switch */ + NEXT(cip); + op_const_pri: + GETPARAM(pri); + NEXT(cip); + op_const_alt: + GETPARAM(alt); + NEXT(cip); + op_addr_pri: + GETPARAM(pri); + pri+=frm; + NEXT(cip); + op_addr_alt: + GETPARAM(alt); + alt+=frm; + NEXT(cip); + op_stor_pri: + GETPARAM(offs); + _W(data,offs,pri); + NEXT(cip); + op_stor_alt: + GETPARAM(offs); + _W(data,offs,alt); + NEXT(cip); + op_stor_s_pri: + GETPARAM(offs); + _W(data,frm+offs,pri); + NEXT(cip); + op_stor_s_alt: + GETPARAM(offs); + _W(data,frm+offs,alt); + NEXT(cip); + op_sref_pri: + GETPARAM(offs); + offs=_R(data,offs); + _W(data,offs,pri); + NEXT(cip); + op_sref_alt: + GETPARAM(offs); + offs=_R(data,offs); + _W(data,offs,alt); + NEXT(cip); + op_sref_s_pri: + GETPARAM(offs); + offs=_R(data,frm+offs); + _W(data,offs,pri); + NEXT(cip); + op_sref_s_alt: + GETPARAM(offs); + offs=_R(data,frm+offs); + _W(data,offs,alt); + NEXT(cip); + op_stor_i: + /* verify address */ + if ((alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + _W(data,alt,pri); + NEXT(cip); + op_strb_i: + GETPARAM(offs); + /* verify address */ + if ((alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch (offs) { + case 1: + _W8(data,alt,pri); + break; + case 2: + _W16(data,alt,pri); + break; + case 4: + _W32(data,alt,pri); + break; + } /* switch */ + NEXT(cip); + op_lidx: + offs=pri*sizeof(cell)+alt; /* implicit shift value for a cell */ + /* verify address */ + if ((offs>=hea && offs=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + NEXT(cip); + op_lidx_b: + GETPARAM(offs); + offs=(pri << (int)offs)+alt; + /* verify address */ + if ((offs>=hea && offs=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + NEXT(cip); + op_idxaddr: + pri=pri*sizeof(cell)+alt; + NEXT(cip); + op_idxaddr_b: + GETPARAM(offs); + pri=(pri << (int)offs)+alt; + NEXT(cip); + op_align_pri: + GETPARAM(offs); + #if BYTE_ORDER==LITTLE_ENDIAN + if (offs<(int)sizeof(cell)) + pri ^= sizeof(cell)-offs; + #endif + NEXT(cip); + op_align_alt: + GETPARAM(offs); + #if BYTE_ORDER==LITTLE_ENDIAN + if (offs<(int)sizeof(cell)) + alt ^= sizeof(cell)-offs; + #endif + NEXT(cip); + op_lctrl: + GETPARAM(offs); + switch (offs) { + case 0: + pri=hdr->cod; + break; + case 1: + pri=hdr->dat; + break; + case 2: + pri=hea; + break; + case 3: + pri=amx->stp; + break; + case 4: + pri=stk; + break; + case 5: + pri=frm; + break; + case 6: + pri=(cell)((unsigned char *)cip - code); + break; + } /* switch */ + NEXT(cip); + op_sctrl: + GETPARAM(offs); + switch (offs) { + case 0: + case 1: + case 3: + /* cannot change these parameters */ + break; + case 2: + hea=pri; + break; + case 4: + stk=pri; + break; + case 5: + frm=pri; + break; + case 6: + cip=(cell *)(code + (int)pri); + break; + } /* switch */ + NEXT(cip); + op_move_pri: + pri=alt; + NEXT(cip); + op_move_alt: + alt=pri; + NEXT(cip); + op_xchg: + offs=pri; /* offs is a temporary variable */ + pri=alt; + alt=offs; + NEXT(cip); + op_push_pri: + PUSH(pri); + NEXT(cip); + op_push_alt: + PUSH(alt); + NEXT(cip); + op_push_c: + GETPARAM(offs); + PUSH(offs); + NEXT(cip); + op_push_r: + GETPARAM(offs); + while (offs--) + PUSH(pri); + NEXT(cip); + op_push: + GETPARAM(offs); + PUSH(_R(data,offs)); + NEXT(cip); + op_push_s: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + NEXT(cip); + op_pop_pri: + POP(pri); + NEXT(cip); + op_pop_alt: + POP(alt); + NEXT(cip); + op_stack: + GETPARAM(offs); + alt=stk; + stk+=offs; + CHKMARGIN(); + CHKSTACK(); + NEXT(cip); + op_heap: + GETPARAM(offs); + alt=hea; + hea+=offs; + CHKMARGIN(); + CHKHEAP(); + NEXT(cip); + op_proc: + PUSH(frm); + frm=stk; + CHKMARGIN(); + NEXT(cip); + op_ret: + POP(frm); + POP(offs); + /* verify the return address */ + if ((ucell)offs>=codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(code+(int)offs); + NEXT(cip); + op_retn: + POP(frm); + POP(offs); + /* verify the return address */ + if ((ucell)offs>=codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(code+(int)offs); + stk+= _R(data,stk) + sizeof(cell); /* remove parameters from the stack */ + NEXT(cip); + op_call: + PUSH(((unsigned char *)cip-code)+sizeof(cell));/* push address behind instruction */ + cip=JUMPABS(code, cip); /* jump to the address */ + NEXT(cip); + op_call_pri: + PUSH((unsigned char *)cip-code); + cip=(cell *)(code+(int)pri); + NEXT(cip); + op_jump: + /* since the GETPARAM() macro modifies cip, you cannot + * do GETPARAM(cip) directly */ + cip=JUMPABS(code, cip); + NEXT(cip); + op_jrel: + offs=*cip; + cip=(cell *)((unsigned char *)cip + (int)offs + sizeof(cell)); + NEXT(cip); + op_jzer: + if (pri==0) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jnz: + if (pri!=0) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jeq: + if (pri==alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jneq: + if (pri!=alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jless: + if ((ucell)pri < (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jleq: + if ((ucell)pri <= (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jgrtr: + if ((ucell)pri > (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jgeq: + if ((ucell)pri >= (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jsless: + if (prialt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_jsgeq: + if (pri>=alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + NEXT(cip); + op_shl: + pri<<=alt; + NEXT(cip); + op_shr: + pri=(ucell)pri >> (ucell)alt; + NEXT(cip); + op_sshr: + pri>>=alt; + NEXT(cip); + op_shl_c_pri: + GETPARAM(offs); + pri<<=offs; + NEXT(cip); + op_shl_c_alt: + GETPARAM(offs); + alt<<=offs; + NEXT(cip); + op_shr_c_pri: + GETPARAM(offs); + pri=(ucell)pri >> (ucell)offs; + NEXT(cip); + op_shr_c_alt: + GETPARAM(offs); + alt=(ucell)alt >> (ucell)offs; + NEXT(cip); + op_smul: + pri*=alt; + NEXT(cip); + op_sdiv: + if (alt==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=alt; + #if defined TRUNC_SDIV + pri=pri/offs; + alt=pri%offs; + #else + val=pri; /* portable routine for truncated division */ + pri=IABS(pri)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + NEXT(cip); + op_sdiv_alt: + if (pri==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=pri; + #if defined TRUNC_SDIV + pri=alt/offs; + alt=alt%offs; + #else + val=alt; /* portable routine for truncated division */ + pri=IABS(alt)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + NEXT(cip); + op_umul: + pri=(ucell)pri * (ucell)alt; + NEXT(cip); + op_udiv: + if (alt==0) + ABORT(amx,AMX_ERR_DIVIDE); + offs=(ucell)pri % (ucell)alt; /* temporary storage */ + pri=(ucell)pri / (ucell)alt; + alt=offs; + NEXT(cip); + op_udiv_alt: + if (pri==0) + ABORT(amx,AMX_ERR_DIVIDE); + offs=(ucell)alt % (ucell)pri; /* temporary storage */ + pri=(ucell)alt / (ucell)pri; + alt=offs; + NEXT(cip); + op_add: + pri+=alt; + NEXT(cip); + op_sub: + pri-=alt; + NEXT(cip); + op_sub_alt: + pri=alt-pri; + NEXT(cip); + op_and: + pri&=alt; + NEXT(cip); + op_or: + pri|=alt; + NEXT(cip); + op_xor: + pri^=alt; + NEXT(cip); + op_not: + pri=!pri; + NEXT(cip); + op_neg: + pri=-pri; + NEXT(cip); + op_invert: + pri=~pri; + NEXT(cip); + op_add_c: + GETPARAM(offs); + pri+=offs; + NEXT(cip); + op_smul_c: + GETPARAM(offs); + pri*=offs; + NEXT(cip); + op_zero_pri: + pri=0; + NEXT(cip); + op_zero_alt: + alt=0; + NEXT(cip); + op_zero: + GETPARAM(offs); + _W(data,offs,0); + NEXT(cip); + op_zero_s: + GETPARAM(offs); + _W(data,frm+offs,0); + NEXT(cip); + op_sign_pri: + if ((pri & 0xff)>=0x80) + pri|= ~ (ucell)0xff; + NEXT(cip); + op_sign_alt: + if ((alt & 0xff)>=0x80) + alt|= ~ (ucell)0xff; + NEXT(cip); + op_eq: + pri= pri==alt ? 1 : 0; + NEXT(cip); + op_neq: + pri= pri!=alt ? 1 : 0; + NEXT(cip); + op_less: + pri= (ucell)pri < (ucell)alt ? 1 : 0; + NEXT(cip); + op_leq: + pri= (ucell)pri <= (ucell)alt ? 1 : 0; + NEXT(cip); + op_grtr: + pri= (ucell)pri > (ucell)alt ? 1 : 0; + NEXT(cip); + op_geq: + pri= (ucell)pri >= (ucell)alt ? 1 : 0; + NEXT(cip); + op_sless: + pri= prialt ? 1 : 0; + NEXT(cip); + op_sgeq: + pri= pri>=alt ? 1 : 0; + NEXT(cip); + op_eq_c_pri: + GETPARAM(offs); + pri= pri==offs ? 1 : 0; + NEXT(cip); + op_eq_c_alt: + GETPARAM(offs); + pri= alt==offs ? 1 : 0; + NEXT(cip); + op_inc_pri: + pri++; + NEXT(cip); + op_inc_alt: + alt++; + NEXT(cip); + op_inc: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) += 1; + #else + val=_R(data,offs); + _W(data,offs,val+1); + #endif + NEXT(cip); + op_inc_s: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) += 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val+1); + #endif + NEXT(cip); + op_inc_i: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) += 1; + #else + val=_R(data,pri); + _W(data,pri,val+1); + #endif + NEXT(cip); + op_dec_pri: + pri--; + NEXT(cip); + op_dec_alt: + alt--; + NEXT(cip); + op_dec: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) -= 1; + #else + val=_R(data,offs); + _W(data,offs,val-1); + #endif + NEXT(cip); + op_dec_s: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) -= 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val-1); + #endif + NEXT(cip); + op_dec_i: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) -= 1; + #else + val=_R(data,pri); + _W(data,pri,val-1); + #endif + NEXT(cip); + op_movs: + GETPARAM(offs); + /* verify top & bottom memory addresses, for both source and destination + * addresses + */ + if ((pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + memcpy(data+(int)alt, data+(int)pri, (int)offs); + #else + for (i=0; i+4=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); + #else + pri=0; + for (i=0; i+4=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + for (i=(int)alt; offs>=(int)sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell)) + _W32(data,i,pri); + NEXT(cip); + op_halt: + GETPARAM(offs); + if (retval!=NULL) + *retval=pri; + /* store complete status (stk and hea are already set in the ABORT macro) */ + amx->frm=frm; + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + if (offs==AMX_ERR_SLEEP) { + amx->stk=stk; + amx->hea=hea; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return (int)offs; + } /* if */ + ABORT(amx,(int)offs); + op_bounds: + GETPARAM(offs); + if ((ucell)pri>(ucell)offs) { + amx->cip=(cell)((unsigned char *)cip-code); + ABORT(amx,AMX_ERR_BOUNDS); + } /* if */ + NEXT(cip); + op_sysreq_pri: + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + NEXT(cip); + op_sysreq_c: + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + NEXT(cip); + op_file: + assert(0); /* this code should not occur during execution */ + ABORT(amx,AMX_ERR_INVINSTR); + op_line: + SKIPPARAM(2); + NEXT(cip); + op_symbol: + GETPARAM(offs); + cip=(cell *)((unsigned char *)cip + (int)offs); + NEXT(cip); + op_srange: + SKIPPARAM(2); + NEXT(cip); + op_symtag: + SKIPPARAM(1); + NEXT(cip); + op_jump_pri: + cip=(cell *)(code+(int)pri); + NEXT(cip); + op_switch: { + cell *cptr; + cptr=JUMPABS(code,cip)+1; /* +1, to skip the "casetbl" opcode */ + cip=JUMPABS(code,cptr+1); /* preset to "none-matched" case */ + num=(int)*cptr; /* number of records in the case table */ + for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2) + /* nothing */; + if (num>0) + cip=JUMPABS(code,cptr+1); /* case found */ + NEXT(cip); + } + op_casetbl: + assert(0); /* this should not occur during execution */ + ABORT(amx,AMX_ERR_INVINSTR); + op_swap_pri: + offs=_R(data,stk); + _W(data,stk,pri); + pri=offs; + NEXT(cip); + op_swap_alt: + offs=_R(data,stk); + _W(data,stk,alt); + alt=offs; + NEXT(cip); + op_push_adr: + GETPARAM(offs); + PUSH(frm+offs); + NEXT(cip); + op_nop: + NEXT(cip); +#if !defined AMX_NO_MACRO_INSTR + op_sysreq_n: + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + stk+=val+4; + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + NEXT(cip); +#endif + op_break: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + if (amx->debug!=NULL) { + /* store status */ + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->cip=(cell)((unsigned char*)cip-code); + num=amx->debug(amx); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + } /* if */ + NEXT(cip); +#if !defined AMX_NO_MACRO_INSTR + op_push5: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + op_push4: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + op_push3: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + op_push2: + GETPARAM(offs); + PUSH(_R(data,offs)); + GETPARAM(offs); + PUSH(_R(data,offs)); + NEXT(cip); + op_push5_s: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + op_push4_s: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + op_push3_s: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + op_push2_s: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + NEXT(cip); + op_push5_c: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + op_push4_c: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + op_push3_c: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + op_push2_c: + GETPARAM(offs); + PUSH(offs); + GETPARAM(offs); + PUSH(offs); + NEXT(cip); + op_push5_adr: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + op_push4_adr: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + op_push3_adr: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + op_push2_adr: + GETPARAM(offs); + PUSH(frm+offs); + GETPARAM(offs); + PUSH(frm+offs); + NEXT(cip); + op_load_both: + GETPARAM(offs); + pri=_R(data,offs); + GETPARAM(offs); + alt=_R(data,offs); + NEXT(cip); + op_load_s_both: + GETPARAM(offs); + pri=_R(data,frm+offs); + GETPARAM(offs); + alt=_R(data,frm+offs); + NEXT(cip); + op_const: + GETPARAM(offs); + GETPARAM(val); + _W(data,offs,val); + NEXT(cip); + op_const_s: + GETPARAM(offs); + GETPARAM(val); + _W(data,frm+offs,val); + NEXT(cip); +#endif +#if !defined AMX_NO_MACRO_INSTR + op_sysreq_d: /* see op_sysreq_c */ + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); + if (amx->error!=AMX_ERR_NONE) { + if (amx->error==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + NEXT(cip); +#endif +#if !defined AMX_NO_MACRO_INSTR && !defined AMX_NO_MACRO_INSTR + op_sysreq_nd: /* see op_sysreq_n */ + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); + stk+=val+4; + if (amx->error!=AMX_ERR_NONE) { + if (amx->error==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + NEXT(cip); +#endif +} + +#else + /* ANSI C & assembler versions */ + +#if defined ASM32 || defined JIT + /* For Watcom C/C++ use register calling convention (faster); for + * Microsoft C/C++ (and most other C compilers) use "cdecl". + * The important point is that you assemble AMXEXEC.ASM with the matching + * calling convention, or the right JIT, respectively. + * AMXJITR.ASM is for Watcom's register calling convention, AMXJITS.ASM and + * AMXJITSN.ASM are for "cdecl". + */ + #if defined __WATCOMC__ + #if !defined STACKARGS /* for AMX32.DLL */ + extern cell amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); + /* The following pragma tells the compiler into which registers + * the parameters have to go. */ + #pragma aux amx_exec_asm parm [eax] [edx] [ebx] [ecx]; + extern cell amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); + #pragma aux amx_exec_jit parm [eax] [edx] [ebx] [ecx]; + #else + extern cell __cdecl amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); + extern cell __cdecl amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); + #endif + #elif defined __arm__ + /* AAPCS compliant */ + extern cell amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); + extern cell amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); + #elif defined __GNUC__ + /* force "cdecl" by adding an "attribute" to the declaration */ + extern cell amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea) __attribute__((cdecl)); + extern cell amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea) __attribute__((cdecl)); + #else + /* force "cdecl" by specifying it as a "function class" with the "__cdecl" keyword */ + extern cell __cdecl amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); + extern cell __cdecl amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); + #endif +#endif /* ASM32 || JIT */ + +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + unsigned char *code, *data; + cell pri,alt,stk,frm,hea; + cell reset_stk, reset_hea, *cip; + ucell codesize; + int i; + #if defined ASM32 || defined JIT + cell parms[9]; /* registers and parameters for assembler AMX */ + #else + OPCODE op; + cell offs,val; + int num; + #endif + #if defined ASM32 + extern void const *amx_opcodelist[]; + #ifdef __WATCOMC__ + #pragma aux amx_opcodelist "_*" + #endif + #endif + #if defined JIT + extern void const *amx_opcodelist_jit[]; + #ifdef __WATCOMC__ + #pragma aux amx_opcodelist_jit "_*" + #endif + #endif + + assert(amx!=NULL); + #if defined ASM32 || defined JIT + /* HACK: return label table (for amx_BrowseRelocate) if amx structure + * is not passed. + */ + if ((amx->flags & AMX_FLAG_BROWSE)==AMX_FLAG_BROWSE) { + assert(sizeof(cell)==sizeof(void *)); + assert(retval!=NULL); + #if defined ASM32 && defined JIT + if ((amx->flags & AMX_FLAG_JITC)!=0) + *retval=(cell)amx_opcodelist_jit; + else + *retval=(cell)amx_opcodelist; + #elif defined ASM32 + *retval=(cell)amx_opcodelist; + #else + *retval=(cell)amx_opcodelist_jit; + #endif + return 0; + } /* if */ + #endif + + if (amx->callback==NULL) + return AMX_ERR_CALLBACK; + if ((amx->flags & AMX_FLAG_RELOC)==0) + return AMX_ERR_INIT; + if ((amx->flags & AMX_FLAG_NTVREG)==0) { + if ((i=amx_Register(amx,NULL,0))!=AMX_ERR_NONE) + return i; + } /* if */ + assert((amx->flags & AMX_FLAG_BROWSE)==0); + + /* set up the registers */ + hdr=(AMX_HEADER *)amx->base; + assert(hdr->magic==AMX_MAGIC); + codesize=(ucell)(hdr->dat-hdr->cod); + code=amx->base+(int)hdr->cod; + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + hea=amx->hea; + stk=amx->stk; + reset_stk=stk; + reset_hea=hea; + alt=frm=pri=0;/* just to avoid compiler warnings */ + + /* get the start address */ + if (index==AMX_EXEC_MAIN) { + if (hdr->cip<0) + return AMX_ERR_INDEX; + cip=(cell *)(code + (int)hdr->cip); + } else if (index==AMX_EXEC_CONT) { + /* all registers: pri, alt, frm, cip, hea, stk, reset_stk, reset_hea */ + frm=amx->frm; + stk=amx->stk; + hea=amx->hea; + pri=amx->pri; + alt=amx->alt; + reset_stk=amx->reset_stk; + reset_hea=amx->reset_hea; + cip=(cell *)(code + (int)amx->cip); + } else if (index<0) { + return AMX_ERR_INDEX; + } else { + if (index>=(cell)NUMENTRIES(hdr,publics,natives)) + return AMX_ERR_INDEX; + func=GETENTRY(hdr,publics,index); + cip=(cell *)(code + (int)func->address); + } /* if */ + /* check values just copied */ + CHKSTACK(); + CHKHEAP(); + assert(check_endian()); + + /* sanity checks */ + assert_static(OP_PUSH_PRI==36); + assert_static(OP_PROC==46); + assert_static(OP_SHL==65); + assert_static(OP_SMUL==72); + assert_static(OP_EQ==95); + assert_static(OP_INC_PRI==107); + assert_static(OP_MOVS==117); + assert_static(OP_SYMBOL==126); + assert_static(OP_PUSH2_C==138); + assert_static(OP_LOAD_BOTH==154); + #if PAWN_CELL_SIZE==16 + assert_static(sizeof(cell)==2); + #elif PAWN_CELL_SIZE==32 + assert_static(sizeof(cell)==4); + #elif PAWN_CELL_SIZE==64 + assert_static(sizeof(cell)==8); + #else + #error Unsupported cell size + #endif + + if (index!=AMX_EXEC_CONT) { + reset_stk+=amx->paramcount*sizeof(cell); + PUSH(amx->paramcount*sizeof(cell)); + amx->paramcount=0; /* push the parameter count to the stack & reset */ + #if defined ASM32 || defined JIT + PUSH(RELOC_VALUE(code,0));/* relocated zero return address */ + #else + PUSH(0); /* zero return address */ + #endif + } /* if */ + /* check stack/heap before starting to run */ + CHKMARGIN(); + + /* start running */ +#if defined ASM32 || defined JIT + /* either the assembler abstract machine or the JIT; both by Marc Peter */ + + parms[0] = pri; + parms[1] = alt; + parms[2] = (cell)cip; + parms[3] = (cell)data; + parms[4] = stk; + parms[5] = frm; + parms[6] = (cell)amx; + parms[7] = (cell)code; + parms[8] = (cell)codesize; + + #if defined ASM32 && defined JIT + if ((amx->flags & AMX_FLAG_JITC)!=0) + i = amx_exec_jit(parms,retval,amx->stp,hea); + else + i = amx_exec_asm(parms,retval,amx->stp,hea); + #elif defined ASM32 + i = amx_exec_asm(parms,retval,amx->stp,hea); + #else + i = amx_exec_jit(parms,retval,amx->stp,hea); + #endif + if (i == AMX_ERR_SLEEP) { + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + } else { + /* remove parameters from the stack; do this the "hard" way, because + * the assembler version has no internal knowledge of the local + * variables, so any "clean" way would be a kludge anyway. + */ + amx->stk=reset_stk; + amx->hea=reset_hea; + } /* if */ + return i; + +#else + + for ( ;; ) { + op=(OPCODE) _RCODE(); + switch (op) { + case OP_LOAD_PRI: + GETPARAM(offs); + pri=_R(data,offs); + break; + case OP_LOAD_ALT: + GETPARAM(offs); + alt=_R(data,offs); + break; + case OP_LOAD_S_PRI: + GETPARAM(offs); + pri=_R(data,frm+offs); + break; + case OP_LOAD_S_ALT: + GETPARAM(offs); + alt=_R(data,frm+offs); + break; + case OP_LREF_PRI: + GETPARAM(offs); + offs=_R(data,offs); + pri=_R(data,offs); + break; + case OP_LREF_ALT: + GETPARAM(offs); + offs=_R(data,offs); + alt=_R(data,offs); + break; + case OP_LREF_S_PRI: + GETPARAM(offs); + offs=_R(data,frm+offs); + pri=_R(data,offs); + break; + case OP_LREF_S_ALT: + GETPARAM(offs); + offs=_R(data,frm+offs); + alt=_R(data,offs); + break; + case OP_LOAD_I: + /* verify address */ + if (pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,pri); + break; + case OP_LODB_I: + GETPARAM(offs); + /* verify address */ + if (pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch ((int)offs) { + case 1: + pri=_R8(data,pri); + break; + case 2: + pri=_R16(data,pri); + break; + case 4: + pri=_R32(data,pri); + break; + } /* switch */ + break; + case OP_CONST_PRI: + GETPARAM(pri); + break; + case OP_CONST_ALT: + GETPARAM(alt); + break; + case OP_ADDR_PRI: + GETPARAM(pri); + pri+=frm; + break; + case OP_ADDR_ALT: + GETPARAM(alt); + alt+=frm; + break; + case OP_STOR_PRI: + GETPARAM(offs); + _W(data,offs,pri); + break; + case OP_STOR_ALT: + GETPARAM(offs); + _W(data,offs,alt); + break; + case OP_STOR_S_PRI: + GETPARAM(offs); + _W(data,frm+offs,pri); + break; + case OP_STOR_S_ALT: + GETPARAM(offs); + _W(data,frm+offs,alt); + break; + case OP_SREF_PRI: + GETPARAM(offs); + offs=_R(data,offs); + _W(data,offs,pri); + break; + case OP_SREF_ALT: + GETPARAM(offs); + offs=_R(data,offs); + _W(data,offs,alt); + break; + case OP_SREF_S_PRI: + GETPARAM(offs); + offs=_R(data,frm+offs); + _W(data,offs,pri); + break; + case OP_SREF_S_ALT: + GETPARAM(offs); + offs=_R(data,frm+offs); + _W(data,offs,alt); + break; + case OP_STOR_I: + /* verify address */ + if (alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + _W(data,alt,pri); + break; + case OP_STRB_I: + GETPARAM(offs); + /* verify address */ + if (alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch ((int)offs) { + case 1: + _W8(data,alt,pri); + break; + case 2: + _W16(data,alt,pri); + break; + case 4: + _W32(data,alt,pri); + break; + } /* switch */ + break; + case OP_LIDX: + offs=pri*sizeof(cell)+alt; + /* verify address */ + if (offs>=hea && offs=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + break; + case OP_LIDX_B: + GETPARAM(offs); + offs=(pri << (int)offs)+alt; + /* verify address */ + if (offs>=hea && offs=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + break; + case OP_IDXADDR: + pri=pri*sizeof(cell)+alt; + break; + case OP_IDXADDR_B: + GETPARAM(offs); + pri=(pri << (int)offs)+alt; + break; + case OP_ALIGN_PRI: + GETPARAM(offs); + #if BYTE_ORDER==LITTLE_ENDIAN + if ((size_t)offscod; + break; + case 1: + pri=hdr->dat; + break; + case 2: + pri=hea; + break; + case 3: + pri=amx->stp; + break; + case 4: + pri=stk; + break; + case 5: + pri=frm; + break; + case 6: + pri=(cell)((unsigned char *)cip - code); + break; + } /* switch */ + break; + case OP_SCTRL: + GETPARAM(offs); + switch ((int)offs) { + case 0: + case 1: + case 3: + /* cannot change these parameters */ + break; + case 2: + hea=pri; + break; + case 4: + stk=pri; + break; + case 5: + frm=pri; + break; + case 6: + cip=(cell *)(code + (int)pri); + break; + } /* switch */ + break; + case OP_MOVE_PRI: + pri=alt; + break; + case OP_MOVE_ALT: + alt=pri; + break; + case OP_XCHG: + offs=pri; /* offs is a temporary variable */ + pri=alt; + alt=offs; + break; + case OP_PUSH_PRI: + PUSH(pri); + break; + case OP_PUSH_ALT: + PUSH(alt); + break; + case OP_PUSH_C: + GETPARAM(offs); + PUSH(offs); + break; + case OP_PUSH_R: + GETPARAM(offs); + while (offs--) + PUSH(pri); + break; + case OP_PUSH: + GETPARAM(offs); + PUSH(_R(data,offs)); + break; + case OP_PUSH_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + break; + case OP_POP_PRI: + POP(pri); + break; + case OP_POP_ALT: + POP(alt); + break; + case OP_STACK: + GETPARAM(offs); + alt=stk; + stk+=offs; + CHKMARGIN(); + CHKSTACK(); + break; + case OP_HEAP: + GETPARAM(offs); + alt=hea; + hea+=offs; + CHKMARGIN(); + CHKHEAP(); + break; + case OP_PROC: + PUSH(frm); + frm=stk; + CHKMARGIN(); + break; + case OP_RET: + POP(frm); + POP(offs); + /* verify the return address */ + if ((ucell)offs>=codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(code+(int)offs); + break; + case OP_RETN: + POP(frm); + POP(offs); + /* verify the return address */ + if ((ucell)offs>=codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(code+(int)offs); + stk+=_R(data,stk)+sizeof(cell); /* remove parameters from the stack */ + amx->stk=stk; + break; + case OP_CALL: + PUSH(((unsigned char *)cip-code)+sizeof(cell));/* skip address */ + cip=JUMPABS(code, cip); /* jump to the address */ + break; + case OP_CALL_PRI: + PUSH((unsigned char *)cip-code); + cip=(cell *)(code+(int)pri); + break; + case OP_JUMP: + /* since the GETPARAM() macro modifies cip, you cannot + * do GETPARAM(cip) directly */ + cip=JUMPABS(code, cip); + break; + case OP_JREL: + offs=*cip; + cip=(cell *)((unsigned char *)cip + (int)offs + sizeof(cell)); + break; + case OP_JZER: + if (pri==0) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JNZ: + if (pri!=0) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JEQ: + if (pri==alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JNEQ: + if (pri!=alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JLESS: + if ((ucell)pri < (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JLEQ: + if ((ucell)pri <= (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JGRTR: + if ((ucell)pri > (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JGEQ: + if ((ucell)pri >= (ucell)alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JSLESS: + if (prialt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_JSGEQ: + if (pri>=alt) + cip=JUMPABS(code, cip); + else + cip=(cell *)((unsigned char *)cip+sizeof(cell)); + break; + case OP_SHL: + pri<<=alt; + break; + case OP_SHR: + pri=(ucell)pri >> (int)alt; + break; + case OP_SSHR: + pri>>=alt; + break; + case OP_SHL_C_PRI: + GETPARAM(offs); + pri<<=offs; + break; + case OP_SHL_C_ALT: + GETPARAM(offs); + alt<<=offs; + break; + case OP_SHR_C_PRI: + GETPARAM(offs); + pri=(ucell)pri >> (int)offs; + break; + case OP_SHR_C_ALT: + GETPARAM(offs); + alt=(ucell)alt >> (int)offs; + break; + case OP_SMUL: + pri*=alt; + break; + case OP_SDIV: + if (alt==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=alt; + #if defined TRUNC_SDIV + pri=pri/offs; + alt=pri%offs; + #else + val=pri; /* portable routine for truncated division */ + pri=IABS(pri)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + break; + case OP_SDIV_ALT: + if (pri==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=pri; + #if defined TRUNC_SDIV + pri=alt/offs; + alt=alt%offs; + #else + val=alt; /* portable routine for truncated division */ + pri=IABS(alt)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + break; + case OP_UMUL: + pri=(ucell)pri * (ucell)alt; + break; + case OP_UDIV: + if (alt==0) + ABORT(amx,AMX_ERR_DIVIDE); + offs=(ucell)pri % (ucell)alt; /* temporary storage */ + pri=(ucell)pri / (ucell)alt; + alt=offs; + break; + case OP_UDIV_ALT: + if (pri==0) + ABORT(amx,AMX_ERR_DIVIDE); + offs=(ucell)alt % (ucell)pri; /* temporary storage */ + pri=(ucell)alt / (ucell)pri; + alt=offs; + break; + case OP_ADD: + pri+=alt; + break; + case OP_SUB: + pri-=alt; + break; + case OP_SUB_ALT: + pri=alt-pri; + break; + case OP_AND: + pri&=alt; + break; + case OP_OR: + pri|=alt; + break; + case OP_XOR: + pri^=alt; + break; + case OP_NOT: + pri=!pri; + break; + case OP_NEG: + pri=-pri; + break; + case OP_INVERT: + pri=~pri; + break; + case OP_ADD_C: + GETPARAM(offs); + pri+=offs; + break; + case OP_SMUL_C: + GETPARAM(offs); + pri*=offs; + break; + case OP_ZERO_PRI: + pri=0; + break; + case OP_ZERO_ALT: + alt=0; + break; + case OP_ZERO: + GETPARAM(offs); + _W(data,offs,0); + break; + case OP_ZERO_S: + GETPARAM(offs); + _W(data,frm+offs,0); + break; + case OP_SIGN_PRI: + if ((pri & 0xff)>=0x80) + pri|= ~ (ucell)0xff; + break; + case OP_SIGN_ALT: + if ((alt & 0xff)>=0x80) + alt|= ~ (ucell)0xff; + break; + case OP_EQ: + pri= pri==alt ? 1 : 0; + break; + case OP_NEQ: + pri= pri!=alt ? 1 : 0; + break; + case OP_LESS: + pri= (ucell)pri < (ucell)alt ? 1 : 0; + break; + case OP_LEQ: + pri= (ucell)pri <= (ucell)alt ? 1 : 0; + break; + case OP_GRTR: + pri= (ucell)pri > (ucell)alt ? 1 : 0; + break; + case OP_GEQ: + pri= (ucell)pri >= (ucell)alt ? 1 : 0; + break; + case OP_SLESS: + pri= prialt ? 1 : 0; + break; + case OP_SGEQ: + pri= pri>=alt ? 1 : 0; + break; + case OP_EQ_C_PRI: + GETPARAM(offs); + pri= pri==offs ? 1 : 0; + break; + case OP_EQ_C_ALT: + GETPARAM(offs); + pri= alt==offs ? 1 : 0; + break; + case OP_INC_PRI: + pri++; + break; + case OP_INC_ALT: + alt++; + break; + case OP_INC: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) += 1; + #else + val=_R(data,offs); + _W(data,offs,val+1); + #endif + break; + case OP_INC_S: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) += 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val+1); + #endif + break; + case OP_INC_I: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) += 1; + #else + val=_R(data,pri); + _W(data,pri,val+1); + #endif + break; + case OP_DEC_PRI: + pri--; + break; + case OP_DEC_ALT: + alt--; + break; + case OP_DEC: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) -= 1; + #else + val=_R(data,offs); + _W(data,offs,val-1); + #endif + break; + case OP_DEC_S: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) -= 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val-1); + #endif + break; + case OP_DEC_I: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) -= 1; + #else + val=_R(data,pri); + _W(data,pri,val-1); + #endif + break; + case OP_MOVS: + GETPARAM(offs); + /* verify top & bottom memory addresses, for both source and destination + * addresses + */ + if (pri>=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + memcpy(data+(int)alt, data+(int)pri, (int)offs); + #else + for (i=0; i+4=hea && pri=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (alt>=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); + #else + pri=0; + for (i=0; i+4=hea && alt=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + for (i=(int)alt; (size_t)offs>=sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell)) + _W32(data,i,pri); + break; + case OP_HALT: + GETPARAM(offs); + if (retval!=NULL) + *retval=pri; + /* store complete status (stk and hea are already set in the ABORT macro) */ + amx->frm=frm; + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + if (offs==AMX_ERR_SLEEP) { + amx->stk=stk; + amx->hea=hea; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return (int)offs; + } /* if */ + ABORT(amx,(int)offs); + case OP_BOUNDS: + GETPARAM(offs); + if ((ucell)pri>(ucell)offs) { + amx->cip=(cell)((unsigned char *)cip-code); + ABORT(amx,AMX_ERR_BOUNDS); + } /* if */ + break; + case OP_SYSREQ_PRI: + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + break; + case OP_SYSREQ_C: + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + break; + case OP_LINE: + SKIPPARAM(2); + break; + case OP_SYMBOL: + GETPARAM(offs); + cip=(cell *)((unsigned char *)cip + (int)offs); + break; + case OP_SRANGE: + SKIPPARAM(2); + break; + case OP_SYMTAG: + SKIPPARAM(1); + break; + case OP_JUMP_PRI: + cip=(cell *)(code+(int)pri); + break; + case OP_SWITCH: { + cell *cptr; + + cptr=JUMPABS(code,cip)+1; /* +1, to skip the "casetbl" opcode */ + cip=JUMPABS(code,cptr+1); /* preset to "none-matched" case */ + num=(int)*cptr; /* number of records in the case table */ + for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2) + /* nothing */; + if (num>0) + cip=JUMPABS(code,cptr+1); /* case found */ + break; + } /* case */ + case OP_SWAP_PRI: + offs=_R(data,stk); + _W32(data,stk,pri); + pri=offs; + break; + case OP_SWAP_ALT: + offs=_R(data,stk); + _W32(data,stk,alt); + alt=offs; + break; + case OP_PUSH_ADR: + GETPARAM(offs); + PUSH(frm+offs); + break; + case OP_NOP: + break; +#if !defined AMX_NO_MACRO_INSTR + case OP_SYSREQ_N: + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + stk+=val+4; + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + break; +#endif + case OP_BREAK: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + if (amx->debug!=NULL) { + /* store status */ + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->cip=(cell)((unsigned char*)cip-code); + num=amx->debug(amx); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return num; + } /* if */ + ABORT(amx,num); + } /* if */ + } /* if */ + break; +#if !defined AMX_NO_MACRO_INSTR + case OP_PUSH5: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + case OP_PUSH4: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + case OP_PUSH3: + GETPARAM(offs); + PUSH(_R(data,offs)); + /* drop through */ + case OP_PUSH2: + GETPARAM(offs); + PUSH(_R(data,offs)); + GETPARAM(offs); + PUSH(_R(data,offs)); + break; + case OP_PUSH5_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + case OP_PUSH4_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + case OP_PUSH3_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + /* drop through */ + case OP_PUSH2_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + break; + case OP_PUSH5_C: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + case OP_PUSH4_C: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + case OP_PUSH3_C: + GETPARAM(offs); + PUSH(offs); + /* drop through */ + case OP_PUSH2_C: + GETPARAM(offs); + PUSH(offs); + GETPARAM(offs); + PUSH(offs); + break; + case OP_PUSH5_ADR: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + case OP_PUSH4_ADR: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + case OP_PUSH3_ADR: + GETPARAM(offs); + PUSH(frm+offs); + /* drop through */ + case OP_PUSH2_ADR: + GETPARAM(offs); + PUSH(frm+offs); + GETPARAM(offs); + PUSH(frm+offs); + break; + case OP_LOAD_BOTH: + GETPARAM(offs); + pri=_R(data,offs); + GETPARAM(offs); + alt=_R(data,offs); + break; + case OP_LOAD_S_BOTH: + GETPARAM(offs); + pri=_R(data,frm+offs); + GETPARAM(offs); + alt=_R(data,frm+offs); + break; + case OP_CONST: + GETPARAM(offs); + GETPARAM(val); + _W32(data,offs,val); + break; + case OP_CONST_S: + GETPARAM(offs); + GETPARAM(val); + _W32(data,frm+offs,val); + break; +#endif +#if !defined AMX_DONT_RELOCATE + case OP_SYSREQ_D: /* see OP_SYSREQ_C */ + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); + if (amx->error!=AMX_ERR_NONE) { + if (amx->error==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + break; +#endif +#if !defined AMX_NO_MACRO_INSTR && !defined AMX_DONT_RELOCATE + case OP_SYSREQ_ND: /* see SYSREQ_N */ + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); + stk+=val+4; + if (amx->error!=AMX_ERR_NONE) { + if (amx->error==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + break; +#endif + default: + /* case OP_FILE: should not occur during execution + * case OP_CASETBL: should not occur during execution + */ + assert(0); + ABORT(amx,AMX_ERR_INVINSTR); + } /* switch */ + } /* for */ +#endif +} + +#endif /* __GNUC__ || __ICC */ + +#endif /* AMX_EXEC || AMX_INIT */ + +#if defined AMX_SETCALLBACK +int AMXAPI amx_SetCallback(AMX *amx,AMX_CALLBACK callback) +{ + assert(amx!=NULL); + assert(callback!=NULL); + amx->callback=callback; + return AMX_ERR_NONE; +} +#endif /* AMX_SETCALLBACK */ + +#if defined AMX_SETDEBUGHOOK +int AMXAPI amx_SetDebugHook(AMX *amx,AMX_DEBUG debug) +{ + assert(amx!=NULL); + amx->debug=debug; + return AMX_ERR_NONE; +} +#endif /* AMX_SETDEBUGHOOK */ + +#if defined AMX_RAISEERROR +int AMXAPI amx_RaiseError(AMX *amx, int error) +{ + assert(error>0); + amx->error=error; + return AMX_ERR_NONE; +} +#endif /* AMX_RAISEERROR */ + +#if defined AMX_GETADDR +int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) +{ + AMX_HEADER *hdr; + unsigned char *data; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + + assert(phys_addr!=NULL); + if ((amx_addr>=amx->hea && amx_addrstk) || amx_addr<0 || amx_addr>=amx->stp) { + *phys_addr=NULL; + return AMX_ERR_MEMACCESS; + } /* if */ + + *phys_addr=(cell *)(data + (int)amx_addr); + return AMX_ERR_NONE; +} +#endif /* AMX_GETADDR */ + +#if defined AMX_ALLOT || defined AMX_EXEC +int AMXAPI amx_Allot(AMX *amx,int cells,cell *amx_addr,cell **phys_addr) +{ + AMX_HEADER *hdr; + unsigned char *data; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + + if (amx->stk - amx->hea - cells*sizeof(cell) < STKMARGIN) + return AMX_ERR_MEMORY; + assert(amx_addr!=NULL); + assert(phys_addr!=NULL); + *amx_addr=amx->hea; + *phys_addr=(cell *)(data + (int)amx->hea); + amx->hea += cells*sizeof(cell); + return AMX_ERR_NONE; +} + +int AMXAPI amx_Release(AMX *amx,cell amx_addr) +{ + if (amx->hea > amx_addr) + amx->hea=amx_addr; + return AMX_ERR_NONE; +} +#endif /* AMX_ALLOT */ + +#if defined AMX_XXXSTRING || defined AMX_UTF8XXX + +#define CHARBITS (8*sizeof(char)) +#if PAWN_CELL_SIZE==16 + #define CHARMASK (0xffffu << 8*(2-sizeof(char))) +#elif PAWN_CELL_SIZE==32 + #define CHARMASK (0xffffffffuL << 8*(4-sizeof(char))) +#elif PAWN_CELL_SIZE==64 + #define CHARMASK (0xffffffffffffffffuLL << 8*(8-sizeof(char))) +#else + #error Unsupported cell size +#endif + +int AMXAPI amx_StrLen(const cell *cstr, int *length) +{ + int len; + #if BYTE_ORDER==LITTLE_ENDIAN + cell c; + #endif + + assert(length!=NULL); + if (cstr==NULL) { + *length=0; + return AMX_ERR_PARAMS; + } /* if */ + + if ((ucell)*cstr>UNPACKEDMAX) { + /* packed string */ + assert_static(sizeof(char)==1); + len=strlen((char *)cstr); /* find '\0' */ + assert(check_endian()); + #if BYTE_ORDER==LITTLE_ENDIAN + /* on Little Endian machines, toggle the last bytes */ + c=cstr[len/sizeof(cell)]; /* get last cell */ + len=len - len % sizeof(cell); /* len = multiple of "cell" bytes */ + while ((c & CHARMASK)!=0) { + len++; + c <<= 8*sizeof(char); + } /* if */ + #endif + } else { + for (len=0; cstr[len]!=0; len++) + /* nothing */; + } /* if */ + *length = len; + return AMX_ERR_NONE; +} +#endif + +#if defined AMX_XXXSTRING || defined AMX_EXEC +int AMXAPI amx_SetString(cell *dest,const char *source,int pack,int use_wchar,size_t size) +{ /* the memory blocks should not overlap */ + int len, i; + + assert_static(UNLIMITED>0); + #if defined AMX_ANSIONLY + (void)use_wchar; + len=strlen(source); + #else + len= use_wchar ? wcslen((const wchar_t*)source) : strlen(source); + #endif + if (pack) { + /* create a packed string */ + if (size=size*sizeof(cell)) + len=size*sizeof(cell)-1; + dest[len/sizeof(cell)]=0; /* clear last bytes of last (semi-filled) cell*/ + #if defined AMX_ANSIONLY + memcpy(dest,source,len); + #else + if (use_wchar) { + for (i=0; i=0) + swapcell((ucell *)&dest[len--]); + #endif + + } else { + /* create an unpacked string */ + if (size=size) + len=size-1; + #if defined AMX_ANSIONLY + for (i=0; iUNPACKEDMAX) { + /* source string is packed */ + cell c = 0; /* to avoid a compiler warning */ + int i=sizeof(cell)-1; + while ((size_t)len> i*CHARBITS); + #else + if (use_wchar) + ((wchar_t*)dest)[len++]=(char)(c >> i*CHARBITS); + else + dest[len++]=(char)(c >> i*CHARBITS); + #endif + if (dest[len-1]=='\0') + break; /* terminating zero character found */ + i=(i+sizeof(cell)-1) % sizeof(cell); + } /* while */ + } else { + /* source string is unpacked */ + #if defined AMX_ANSIONLY + while (*source!=0 && (size_t)len=size) + len=size-1; + if (len>=0) + dest[len]='\0'; /* store terminator */ + return AMX_ERR_NONE; +} +#endif /* AMX_XXXSTRING */ + +#if defined AMX_UTF8XXX + #if defined __BORLANDC__ + #pragma warn -amb -8000 /* ambiguous operators need parentheses */ + #endif +/* amx_UTF8Get() + * Extract a single UTF-8 encoded character from a string and return a pointer + * to the character just behind that UTF-8 character. The parameters "endptr" + * and "value" may be NULL. + * If the code is not valid UTF-8, "endptr" has the value of the input + * parameter "string" and "value" is zero. + */ +int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) +{ +static const char utf8_count[16]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4 }; +static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x4000000L }; + unsigned char c; + cell result; + int followup; + + assert(string!=NULL); + if (value!=NULL) /* preset, in case of an error */ + *value=0; + if (endptr!=NULL) + *endptr=string; + + c = *(const unsigned char*)string++; + if (c<0x80) { + /* ASCII */ + result=c; + } else { + if (c<0xc0 || c>=0xfe) + return AMX_ERR_PARAMS; /* invalid or "follower" code, quit with error */ + /* At this point we know that the two top bits of c are ones. The two + * bottom bits are always part of the code. We only need to consider + * the 4 remaining bits; i.e., a 16-byte table. This is "utf8_count[]". + * (Actually the utf8_count[] table records the number of follow-up + * bytes minus 1. This is just for convenience.) + */ + assert((c & 0xc0)==0xc0); + followup=(int)utf8_count[(c >> 2) & 0x0f]; + /* The mask depends on the code length; this is just a very simple + * relation. + */ + #define utf8_mask (0x1f >> followup) + result= c & utf8_mask; + /* Collect the follow-up codes using a drop-through switch statement; + * this avoids a loop. In each case, verify the two leading bits. + */ + assert(followup>=0 && followup<=4); + switch (followup) { + case 4: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | (c & 0x3f); + case 3: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | (c & 0x3f); + case 2: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | (c & 0x3f); + case 1: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | (c & 0x3f); + case 0: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | (c & 0x3f); + } /* switch */ + /* Do additional checks: shortest encoding & reserved positions. The + * lowmark limits also depends on the code length; it can be read from + * a table with 5 elements. This is "utf8_lowmark[]". + */ + if (result=0xd800 && result<=0xdfff) || result==0xfffe || result==0xffff) + goto error; + } /* if */ + + if (value!=NULL) + *value=result; + if (endptr!=NULL) + *endptr=string; + + return AMX_ERR_NONE; + +error: + return AMX_ERR_PARAMS; +} + +/* amx_UTF8Put() + * Encode a single character into a byte string. The character may result in + * a string of up to 6 bytes. The function returns an error code if "maxchars" + * is lower than the required number of characters; in this case nothing is + * stored. + * The function does not zero-terminate the string. + */ +int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) +{ + assert(string!=NULL); + if (endptr!=NULL) /* preset, in case of an error */ + *endptr=string; + + if (value<0x80) { + /* 0xxxxxxx */ + if (maxchars < 1) goto error; + *string++ = (char)value; + } else if (value<0x800) { + /* 110xxxxx 10xxxxxx */ + if (maxchars < 2) goto error; + *string++ = (char)(((value>>6) & 0x1f) | 0xc0); + *string++ = (char)((value & 0x3f) | 0x80); + } else if (value<0x10000) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + if (maxchars < 3) goto error; + if ((value>=0xd800 && value<=0xdfff) || value==0xfffe || value==0xffff) + goto error; /* surrogate pairs and invalid characters */ + *string++ = (char)(((value>>12) & 0x0f) | 0xe0); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); + } else if (value<0x200000) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (maxchars < 4) goto error; + *string++ = (char)(((value>>18) & 0x07) | 0xf0); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); + } else if (value<0x4000000) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (maxchars < 5) goto error; + *string++ = (char)(((value>>24) & 0x03) | 0xf8); + *string++ = (char)(((value>>18) & 0x3f) | 0x80); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); + } else { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + if (maxchars < 6) goto error; + *string++ = (char)(((value>>30) & 0x01) | 0xfc); + *string++ = (char)(((value>>24) & 0x3f) | 0x80); + *string++ = (char)(((value>>18) & 0x3f) | 0x80); + *string++ = (char)(((value>>12) & 0x3f) | 0x80); + *string++ = (char)(((value>>6) & 0x3f) | 0x80); + *string++ = (char)((value & 0x3f) | 0x80); + } /* if */ + + if (endptr!=NULL) + *endptr=string; + return AMX_ERR_NONE; + +error: + return AMX_ERR_PARAMS; +} + +/* amx_UTF8Check() + * Run through a zero-terminated string and check the validity of the UTF-8 + * encoding. The function returns an error code, it is AMX_ERR_NONE if the + * string is valid UTF-8 (or valid ASCII for that matter). + */ +int AMXAPI amx_UTF8Check(const char *string, int *length) +{ + int err=AMX_ERR_NONE; + int len=0; + while (err==AMX_ERR_NONE && *string!='\0') { + err=amx_UTF8Get(string,&string,NULL); + len++; + } /* while */ + if (length!=NULL) + *length=len; + return err; +} + +/* amx_UTF8Len() + * Run through a wide string and return how many 8-bit characters are needed to + * store the string in UTF-8 format. The returned cound excludes the terminating + * zero byte. The function returns an error code. + */ +int AMXAPI amx_UTF8Len(const cell *cstr, int *length) +{ + int err; + + assert(length!=NULL); + err=amx_StrLen(cstr, length); + if (err==AMX_ERR_NONE && (ucell)*cstr<=UNPACKEDMAX) { + char buffer[10]; /* maximum UTF-8 code is 6 characters */ + char *endptr; + int len=*length, count=0; + while (len-->0) { + amx_UTF8Put(buffer, &endptr, sizeof buffer, *cstr++); + count+=(int)(endptr-buffer); + } /* while */ + *length=count; + } /* while */ + return err; +} +#endif /* AMX_UTF8XXX */ +>>>>>>> feature/premake diff --git a/amx-deps/src/amx/amx.h b/amx-deps/src/amx/amx.h index 504c597..4f6c265 100644 --- a/amx-deps/src/amx/amx.h +++ b/amx-deps/src/amx/amx.h @@ -1,3 +1,4 @@ +<<<<<<< HEAD /* Pawn Abstract Machine (for the Pawn language) * * Copyright (c) ITB CompuPhase, 1997-2008 @@ -488,3 +489,551 @@ int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); #endif #endif /* AMX_H_INCLUDED */ +======= +/* Pawn Abstract Machine (for the Pawn language) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amx.h 3648 2006-10-12 11:24:50Z thiadmer $ + */ + +#ifndef AMX_H_INCLUDED +#define AMX_H_INCLUDED + +#include /* for size_t */ +#include + +#if (defined __linux || defined __linux__) && !defined __LINUX__ + #define __LINUX__ +#endif +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #include +#endif + +#if defined __GNUC__ + #define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#endif + +#if !defined HAVE_STDINT_H + #if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) \ + || defined __GNUC__ || defined __LCC__ || defined __DMC__ \ + || (defined __WATCOMC__ && __WATCOMC__ >= 1200) + #define HAVE_STDINT_H 1 + #endif +#endif +#if !defined HAVE_INTTYPES_H + #if defined __FreeBSD__ || defined __APPLE__ + #define HAVE_INTTYPES_H 1 + #endif +#endif +#if defined HAVE_STDINT_H + #include +#elif defined HAVE_INTTYPES_H + #include +#else + #if defined __MACH__ + #include + #endif + typedef short int int16_t; + typedef unsigned short int uint16_t; + #if defined SN_TARGET_PS2 + typedef int int32_t; + typedef unsigned int uint32_t; + #else + typedef long int int32_t; + typedef unsigned long int uint32_t; + #endif + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define HAVE_I64 + #endif + #if !defined _INTPTR_T_DEFINED + #if defined _LP64 || defined WIN64 || defined _WIN64 + typedef __int64 intptr_t; + #else + typedef int32_t intptr_t; + #endif + #endif +#endif +#if defined _LP64 || defined WIN64 || defined _WIN64 + #if !defined __64BIT__ + #define __64BIT__ + #endif +#endif + +#if !defined HAVE_ALLOCA_H + #if defined __GNUC__ || defined __LCC__ || defined __DMC__ || defined __ARMCC_VERSION + #define HAVE_ALLOCA_H 1 + #elif defined __WATCOMC__ && __WATCOMC__ >= 1200 + #define HAVE_ALLOCA_H 1 + #endif +#endif +#if defined HAVE_ALLOCA_H && HAVE_ALLOCA_H + #include +#elif defined __BORLANDC__ + #include +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ + #if !defined alloca + #define alloca(n) _alloca(n) + #endif +#endif + +#if !defined arraysize + #define arraysize(array) (sizeof(array) / sizeof((array)[0])) +#endif + +#if !defined assert_static + #if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112) || GCC_VERSION >= 40600 + #define assert_static(test) _Static_assert(test, "assert") + #else + /* see "Compile-Time Assertions" by Greg Miller, + * (with modifications to port it to C) + */ + #define _ASSERT_STATIC_SYMBOL_INNER(line) __ASSERT_STATIC_ ## line + #define _ASSERT_STATIC_SYMBOL(line) _ASSERT_STATIC_SYMBOL_INNER(line) + #define assert_static(test) \ + do { \ + typedef char _ASSERT_STATIC_SYMBOL(__LINE__)[ ((test) ? 1 : -1) ]; \ + } while (0) + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined PAWN_DLL + #if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL __stdcall + #endif + #if !defined AMXAPI + #define AMXAPI __stdcall + #endif +#endif + +/* calling convention for native functions */ +#if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL +#endif +/* calling convention for all interface functions and callback functions */ +#if !defined AMXAPI + #if defined STDECL + #define AMXAPI __stdcall + #elif defined CDECL + #define AMXAPI __cdecl + #elif defined GCC_HASCLASSVISIBILITY + #define AMXAPI __attribute__ ((visibility("default"))) + #else + #define AMXAPI + #endif +#endif +#if !defined AMXEXPORT + #define AMXEXPORT +#endif + +/* File format version (in CUR_FILE_VERSION) + * 0 (original version) + * 1 (opcodes JUMP.pri, SWITCH and CASETBL) + * 2 (compressed files) + * 3 (public variables) + * 4 (opcodes SWAP.pri/alt and PUSHADDR) + * 5 (tagnames table) + * 6 (reformatted header) + * 7 (name table, opcodes SYMTAG & SYSREQ.D) + * 8 (opcode STMT, renewed debug interface) + * 9 (macro opcodes) + * MIN_FILE_VERSION is the lowest file version number that the current AMX + * implementation supports. If the AMX file header gets new fields, this number + * often needs to be incremented. MAX_AMX_VERSION is the lowest AMX version that + * is needed to support the current file version. When there are new opcodes, + * this number needs to be incremented. + * The file version supported by the JIT may run behind MIN_AMX_VERSION. So + * there is an extra constant for it: MAX_FILE_VER_JIT. + */ +#define CUR_FILE_VERSION 9 /* current file version; also the current AMX version */ +#define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ +#define MIN_AMX_VERSION 9 /* minimum AMX version needed to support the current file format */ +#define MAX_FILE_VER_JIT 8 /* file version supported by the JIT */ +#define MIN_AMX_VER_JIT 8 /* AMX version supported by the JIT */ + +#if !defined PAWN_CELL_SIZE + #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ +#endif +#if PAWN_CELL_SIZE==16 + typedef uint16_t ucell; + typedef int16_t cell; +#elif PAWN_CELL_SIZE==32 + typedef uint32_t ucell; + typedef int32_t cell; +#elif PAWN_CELL_SIZE==64 + typedef uint64_t ucell; + typedef int64_t cell; +#else + #error Unsupported cell size (PAWN_CELL_SIZE) +#endif + +#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1) +#define UNLIMITED (~1u >> 1) + +struct tagAMX; +typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, const cell *params); +typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, + cell *result, const cell *params); +typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); +typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX *, cell *, int)); +#if !defined _FAR + #define _FAR +#endif + +#if defined _MSC_VER + #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ + #pragma warning(disable:4103) /* disable warning message 4103 that complains about pragma pack in a header file */ + #pragma warning(disable:4127) /* "conditional expression is constant" (needed for static_assert) */ + #pragma warning(disable:4996) /* POSIX name is deprecated */ +#elif defined __GNUC__ +#elif defined __clang__ + #pragma GCC diagnostic ignored "-Wlogical-op-parentheses" + #pragma GCC diagnostic ignored "-Wbitwise-op-parentheses" +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_NATIVE_INFO { + const char _FAR *name; + AMX_NATIVE func PACKED; +} AMX_NATIVE_INFO; + +#if !defined AMX_USERNUM +#define AMX_USERNUM 4 +#endif +#define sEXPMAX 19 /* maximum name length for file version <= 6 */ +#define sNAMEMAX 31 /* maximum name length of symbol name */ + +typedef struct tagAMX_FUNCSTUB { + ucell address PACKED; + char name[sEXPMAX+1]; +} AMX_FUNCSTUB; + +typedef struct tagFUNCSTUBNT { + ucell address PACKED; + uint32_t nameofs PACKED; +} AMX_FUNCSTUBNT; + +/* The AMX structure is the internal structure for many functions. Not all + * fields are valid at all times; many fields are cached in local variables. + */ +typedef struct tagAMX { + unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ + unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ + AMX_CALLBACK callback PACKED; + AMX_DEBUG debug PACKED; /* debug callback */ + /* for external functions a few registers must be accessible from the outside */ + cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ + cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ + cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ + cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ + cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ + cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ + int flags PACKED; /* current status, see amx_Flags() */ + /* user data */ + #if AMX_USERNUM > 0 + long usertags[AMX_USERNUM] PACKED; + void _FAR *userdata[AMX_USERNUM] PACKED; + #endif + /* native functions can raise an error */ + int error PACKED; + /* passing parameters requires a "count" field */ + int paramcount; + /* the sleep opcode needs to store the full AMX status */ + cell pri PACKED; + cell alt PACKED; + cell reset_stk PACKED; + cell reset_hea PACKED; + /* extra fields for increased performance */ + cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ + #if defined JIT + /* support variables for the JIT */ + int reloc_size PACKED; /* required temporary buffer for relocations */ + long code_size PACKED; /* estimated memory footprint of the native code */ + #endif +} AMX; + +/* The AMX_HEADER structure is both the memory format as the file format. The + * structure is used internaly. + */ +typedef struct tagAMX_HEADER { + int32_t size PACKED; /* size of the "file" */ + uint16_t magic PACKED; /* signature */ + char file_version; /* file format version */ + char amx_version; /* required version of the AMX */ + int16_t flags PACKED; + int16_t defsize PACKED; /* size of a definition record */ + int32_t cod PACKED; /* initial value of COD - code block */ + int32_t dat PACKED; /* initial value of DAT - data block */ + int32_t hea PACKED; /* initial value of HEA - start of the heap */ + int32_t stp PACKED; /* initial value of STP - stack top */ + int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ + int32_t publics PACKED; /* offset to the "public functions" table */ + int32_t natives PACKED; /* offset to the "native functions" table */ + int32_t libraries PACKED; /* offset to the table of libraries */ + int32_t pubvars PACKED; /* the "public variables" table */ + int32_t tags PACKED; /* the "public tagnames" table */ + int32_t nametable PACKED; /* name table */ +} AMX_HEADER; + +#define AMX_MAGIC_16 0xf1e2 +#define AMX_MAGIC_32 0xf1e0 +#define AMX_MAGIC_64 0xf1e1 +#if PAWN_CELL_SIZE==16 + #define AMX_MAGIC AMX_MAGIC_16 +#elif PAWN_CELL_SIZE==32 + #define AMX_MAGIC AMX_MAGIC_32 +#elif PAWN_CELL_SIZE==64 + #define AMX_MAGIC AMX_MAGIC_64 +#endif + +enum { + AMX_ERR_NONE, + /* reserve the first 15 error codes for exit codes of the abstract machine */ + AMX_ERR_EXIT, /* forced exit */ + AMX_ERR_ASSERT, /* assertion failed */ + AMX_ERR_STACKERR, /* stack/heap collision */ + AMX_ERR_BOUNDS, /* index out of bounds */ + AMX_ERR_MEMACCESS, /* invalid memory access */ + AMX_ERR_INVINSTR, /* invalid instruction */ + AMX_ERR_STACKLOW, /* stack underflow */ + AMX_ERR_HEAPLOW, /* heap underflow */ + AMX_ERR_CALLBACK, /* no callback, or invalid callback */ + AMX_ERR_NATIVE, /* native function failed */ + AMX_ERR_DIVIDE, /* divide by zero */ + AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ + AMX_ERR_INVSTATE, /* invalid state for this access */ + + AMX_ERR_MEMORY = 16, /* out of memory */ + AMX_ERR_FORMAT, /* invalid file format */ + AMX_ERR_VERSION, /* file is for a newer version of the AMX */ + AMX_ERR_NOTFOUND, /* function not found */ + AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ + AMX_ERR_DEBUG, /* debugger cannot run */ + AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ + AMX_ERR_USERDATA, /* unable to set user data field (table full) */ + AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ + AMX_ERR_PARAMS, /* parameter error */ + AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ + AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ +}; + +/* AMX_FLAG_CHAR16 0x01 no longer used */ +#define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ +#define AMX_FLAG_COMPACT 0x04 /* compact encoding */ +#define AMX_FLAG_SLEEP 0x08 /* script uses the sleep instruction (possible re-entry or power-down mode) */ +#define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no BREAK opcodes */ +#define AMX_FLAG_SYSREQN 0x800 /* script new (optimized) version of SYSREQ opcode */ +#define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ +#define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ +#define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ +#define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ + +#define AMX_EXEC_MAIN (-1) /* start at program entry point */ +#define AMX_EXEC_CONT (-2) /* continue from last address */ + +#define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) + +#if !defined AMX_COMPACTMARGIN + #define AMX_COMPACTMARGIN 64 +#endif + +/* for native functions that use floating point parameters, the following + * two macros are convenient for casting a "cell" into a "float" type _without_ + * changing the bit pattern + */ +#if PAWN_CELL_SIZE==32 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ +#elif PAWN_CELL_SIZE==64 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ +#else + // amx_ftoc() and amx_ctof() cannot be used +#endif + +/* when a pointer cannot be stored in a cell, cells that hold relocated + * addresses need to be expanded + */ +#if defined __64BIT__ && PAWN_CELL_SIZE<64 + #define CELLMASK (((int64_t)1 << PAWN_CELL_SIZE) - 1) + #define amx_Address(amx,addr) \ + (cell*)(((int64_t)((amx)->data ? (amx)->data : (amx)->code) & ~CELLMASK) | ((int64_t)(addr) & CELLMASK)) +#elif defined __32BIT__ && PAWN_CELL_SIZE<32 + #define CELLMASK ((1L << PAWN_CELL_SIZE) - 1) + #define amx_Address(amx,addr) \ + (cell*)(((int32_t)((amx)->data ? (amx)->data : (amx)->code) & ~CELLMASK) | ((int32_t)(addr) & CELLMASK)) +#else + #define amx_Address(amx,addr) \ + (cell*)(((int32_t)((amx)->data ? (amx)->data : (amx)->base+(int)((AMX_HEADER *)(amx)->base)->dat)) + ((int32_t)(addr))) +#endif + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + /* C99: use variable-length arrays */ + #define amx_StrParam_Type(amx,param,result,type) \ + int result##_length_; \ + amx_StrLen(amx_Address(amx,param),&result##_length_); \ + char result##_vla_[(result##_length_+1)*sizeof(*(result))]; \ + (result)=(type)result##_vla_; \ + amx_GetString((char*)(result),amx_Address(amx,param), \ + sizeof(*(result))>1,result##_length_+1) + #define amx_StrParam(amx,param,result) \ + amx_StrParam_Type(amx,param,result,void*) +#else + /* macro using alloca() */ + #define amx_StrParam_Type(amx,param,result,type) \ + do { \ + int result##_length_; \ + amx_StrLen(amx_Address(amx,param),&result##_length_); \ + if (result##_length_>0 && \ + ((result)=(type)alloca((result##_length_+1)*sizeof(*(result))))!=NULL) \ + amx_GetString((char*)(result),amx_Address(amx,param), \ + sizeof(*(result))>1,result##_length_+1); \ + else (result) = NULL; \ + } while (0) + #define amx_StrParam(amx,param,result) \ + amx_StrParam_Type(amx,param,result,void*) +#endif + +uint16_t * AMXAPI amx_Align16(uint16_t *v); +uint32_t * AMXAPI amx_Align32(uint32_t *v); +#if defined _I64_MAX || defined HAVE_I64 + uint64_t * AMXAPI amx_Align64(uint64_t *v); +#endif +int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); +int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, const cell *params); +int AMXAPI amx_Cleanup(AMX *amx); +int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); +int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); +int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); +int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); +int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); +int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); +int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); +int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); +int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); +int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); +int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); +int AMXAPI amx_Init(AMX *amx, void *program); +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); +int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); +int AMXAPI amx_NameLength(AMX *amx, int *length); +AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); +int AMXAPI amx_NumNatives(AMX *amx, int *number); +int AMXAPI amx_NumPublics(AMX *amx, int *number); +int AMXAPI amx_NumPubVars(AMX *amx, int *number); +int AMXAPI amx_NumTags(AMX *amx, int *number); +int AMXAPI amx_Push(AMX *amx, cell value); +int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); +int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); +int AMXAPI amx_RaiseError(AMX *amx, int error); +int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); +int AMXAPI amx_Release(AMX *amx, cell amx_addr); +int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); +int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); +int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); +int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); +int AMXAPI amx_StrLen(const cell *cstring, int *length); +int AMXAPI amx_UTF8Check(const char *string, int *length); +int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); +int AMXAPI amx_UTF8Len(const cell *cstr, int *length); +int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); + +#if PAWN_CELL_SIZE==16 + void amx_Swap16(uint16_t *v); +#endif +#if PAWN_CELL_SIZE==32 + void amx_Swap32(uint32_t *v); +#endif +#if PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined INT64_MAX || defined HAVE_I64) + void amx_Swap64(uint64_t *v); +#endif + +#if PAWN_CELL_SIZE==16 + #define amx_AlignCell(v) amx_Align16((uint16_t*)(v)) + #define amx_SwapCell(v) amx_Swap16((uint16_t*)(v)) +#elif PAWN_CELL_SIZE==32 + #define amx_AlignCell(v) amx_Align32((uint32_t*)(v)) + #define amx_SwapCell(v) amx_Swap32((uint32_t*)(v)) +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined INT64_MAX || defined HAVE_I64) + #define amx_AlignCell(v) amx_Align64((uint64_t*)(v)) + #define amx_SwapCell(v) amx_Swap64((uint64_t*)(v)) +#else + #error Unsupported cell size +#endif + +#define amx_RegisterFunc(amx, name, func) \ + amx_Register((amx), amx_NativeInfo((name),(func)), 1); + +#if !defined AMX_NO_ALIGN + #if defined __LINUX__ || defined __FreeBSD__ || defined __APPLE__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMX_H_INCLUDED */ +>>>>>>> feature/premake diff --git a/amx-deps/src/amx/amxDGram.def b/amx-deps/src/amx/amxDGram.def index 8f9a432..4c0876f 100644 --- a/amx-deps/src/amx/amxDGram.def +++ b/amx-deps/src/amx/amxDGram.def @@ -1,6 +1,6 @@ -NAME amxDGram -DESCRIPTION 'Pawn AMX: network datagram' - -EXPORTS - amx_DGramInit - amx_DGramCleanup +NAME amxDGram +DESCRIPTION 'Pawn AMX: network datagram' + +EXPORTS + amx_DGramInit + amx_DGramCleanup diff --git a/amx-deps/src/amx/amxDGram.rc b/amx-deps/src/amx/amxDGram.rc index 994636b..61dd47a 100644 --- a/amx-deps/src/amx/amxDGram.rc +++ b/amx-deps/src/amx/amxDGram.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" -#define VERSIONNAME "amxDGram.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: network datagram\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxDGram\0" -#define VERSIONCOPYRIGHT "Copyright \251 2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 1 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "1.0.0\0" +#define VERSIONNAME "amxDGram.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: network datagram\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxDGram\0" +#define VERSIONCOPYRIGHT "Copyright \251 2006 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxFile.rc b/amx-deps/src/amx/amxFile.rc index 43c413f..fa37997 100644 --- a/amx-deps/src/amx/amxFile.rc +++ b/amx-deps/src/amx/amxFile.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 1 -#define BUILD 0 -#define VERSIONSTR "1.1.0\0" -#define VERSIONNAME "amxFile.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: File I/O support\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxFile\0" -#define VERSIONCOPYRIGHT "Copyright \251 2004-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 4 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "4.0.0\0" +#define VERSIONNAME "amxFile.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: File I/O support\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxFile\0" +#define VERSIONCOPYRIGHT "Copyright \251 2004-2016 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxFixed.def b/amx-deps/src/amx/amxFixed.def index deb5826..c9e56e8 100644 --- a/amx-deps/src/amx/amxFixed.def +++ b/amx-deps/src/amx/amxFixed.def @@ -1,6 +1,6 @@ -NAME amxFixed -DESCRIPTION 'Pawn AMX: fixed-point arithmetic' - -EXPORTS - amx_FixedInit - amx_FixedCleanup +NAME amxFixed +DESCRIPTION 'Pawn AMX: fixed-point arithmetic' + +EXPORTS + amx_FixedInit + amx_FixedCleanup diff --git a/amx-deps/src/amx/amxFixed.rc b/amx-deps/src/amx/amxFixed.rc index e01b540..eb9af90 100644 --- a/amx-deps/src/amx/amxFixed.rc +++ b/amx-deps/src/amx/amxFixed.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" -#define VERSIONNAME "amxFixed.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: Fixed Point support\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxFixed\0" -#define VERSIONCOPYRIGHT "Copyright \251 2003-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 1 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "1.0.0\0" +#define VERSIONNAME "amxFixed.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: Fixed Point support\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxFixed\0" +#define VERSIONCOPYRIGHT "Copyright \251 2003-2006 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxFloat.def b/amx-deps/src/amx/amxFloat.def index aaff2f0..3d88b98 100644 --- a/amx-deps/src/amx/amxFloat.def +++ b/amx-deps/src/amx/amxFloat.def @@ -1,6 +1,6 @@ -NAME amxFloat -DESCRIPTION 'Pawn AMX: floating-point arithmetic' - -EXPORTS - amx_FloatInit - amx_FloatCleanup +NAME amxFloat +DESCRIPTION 'Pawn AMX: floating-point arithmetic' + +EXPORTS + amx_FloatInit + amx_FloatCleanup diff --git a/amx-deps/src/amx/amxFloat.rc b/amx-deps/src/amx/amxFloat.rc index 98020b8..205f9db 100644 --- a/amx-deps/src/amx/amxFloat.rc +++ b/amx-deps/src/amx/amxFloat.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" -#define VERSIONNAME "amxFloat.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: Floating Point support\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxFloat\0" -#define VERSIONCOPYRIGHT "Copyright \251 2003-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 4 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "4.0.0\0" +#define VERSIONNAME "amxFloat.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: Floating Point support\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxFloat\0" +#define VERSIONCOPYRIGHT "Copyright \251 2003-2013 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxProcess.def b/amx-deps/src/amx/amxProcess.def index 5aa303c..39e8017 100644 --- a/amx-deps/src/amx/amxProcess.def +++ b/amx-deps/src/amx/amxProcess.def @@ -1,6 +1,6 @@ -NAME amxProcess -DESCRIPTION 'Pawn AMX: process control and foreign function interface' - -EXPORTS - amx_ProcessInit - amx_ProcessCleanup +NAME amxProcess +DESCRIPTION 'Pawn AMX: process control and foreign function interface' + +EXPORTS + amx_ProcessInit + amx_ProcessCleanup diff --git a/amx-deps/src/amx/amxProcess.rc b/amx-deps/src/amx/amxProcess.rc index f7823ff..999e6cc 100644 --- a/amx-deps/src/amx/amxProcess.rc +++ b/amx-deps/src/amx/amxProcess.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" -#define VERSIONNAME "amxProcess.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: process control and foreign function interface\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxProcess\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 1 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "1.0.0\0" +#define VERSIONNAME "amxProcess.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: process control and foreign function interface\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxProcess\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxargs.c b/amx-deps/src/amx/amxargs.c index 5d40862..2a99062 100644 --- a/amx-deps/src/amx/amxargs.c +++ b/amx-deps/src/amx/amxargs.c @@ -1,400 +1,391 @@ -/* Script Arguments support module for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxargs.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "osdefs.h" -#if defined __WIN32__ || defined __MSDOS__ - #include -#endif -#if defined __WIN32__ || defined _Windows - #include -#endif -#include "amx.h" - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _istdigit isdigit -# define _tgetenv getenv -# define _tcscat strcat -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _tcsncmp strncmp -# define _tcspbrk strpbrk -# define _tcsrchr strrchr -# define _tcstol strtol -#endif - -#if !defined AMXARGS_COLON - #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ - /* A ':' is also a separator for filenames (the disk drive identifier), and - * therefore it is better not to use it as an name/value seperator for - * command line argiments as well. So, by default, the library uses only - * the '=' as the name/value separator. - */ - #define AMXARGS_COLON 0 - #else - #define AMXARGS_COLON 1 - #endif -#endif - -#if !defined AMXARGS_SKIPARG - /* The first option may be the name of the script (this is common if the - * host application takes the name of the script as the first parameter). - * This can optionally be ignored. - */ - #define AMXARGS_SKIPARG 0 -#endif - - -static const TCHAR *tokenize(const TCHAR *string, int index, int *length); -static const TCHAR *cmdline = NULL; - -static const TCHAR *rawcmdline(void) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - #elif defined _Windows || defined __MSDOS__ - static char cmdbuffer[128]; /* DOS & Windows 3.1 are never in Unicode mode */ - #elif defined __LINUX__ - static char cmdbuffer[1024]; /* some arbitrary maximum */ - #endif - const TCHAR *ptr; - int skip = 0; - - if (cmdline == NULL) { - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - cmdline = GetCommandLine(); - skip++; - #elif defined _Windows || defined __MSDOS__ - #if defined _Windows - unsigned short _psp = GetCurrentPDB(); - #endif - char _far *cmd = (char _far *)MK_FP(_psp, 128); - unsigned char length = (unsigned char)*cmd++; - assert(length < 128); - assert(length < sizeof cmdbuffer); - memcpy(cmdbuffer, cmd, length); - cmdbuffer[length] = '\0'; - if ((cmd == strchr(cmdbuffer, '\r')) != NULL) - *cmd = '\0'; /* also erase \r after the last option (if any) */ - cmdline = cmdbuffer; - #elif defined __LINUX__ - /* Options in /proc//cmdline are delimited with '\0' characters - * rather than spaces. - */ - FILE *fp; - size_t fsize; - sprintf(cmdbuffer, "/proc/%d/cmdline", getpid()); - if ((fp = fopen(cmdbuffer, "r")) != NULL) { - char *ptr; - fseek(fp, 0, SEEK_END); - fsize = ftell(fp); - fseek(fp, 0, SEEK_SET); - if (fsize >= sizeof cmdbuffer) - fsize = sizeof cmdbuffer - 1; - fread(cmdbuffer, 1, fsize, fp); - fclose(fp); - cmdbuffer[fsize] = '\0'; /* terminate with double-zero */ - /* convert '\0' characters to spaces, for uniform parsing */ - for (ptr = cmdbuffer; *ptr != ' '; ptr = strchr(ptr, '\0') + 1) - *ptr = ' '; - cmdline = cmdbuffer; - skip++; - } /* if */ - #else - #error Platform not supported - #endif - - /* skip leading white space */ - while (*cmdline <= __T(' ') && *cmdline != __T('\0')) - cmdline++; - - #if AMXARGS_SKIPARG - skip++; - #endif - /* skip the first option(s), because it is the name of the host program - * and the name of the script - */ - if ((ptr = tokenize(cmdline, skip, NULL)) != NULL) - cmdline = ptr; - else - cmdline = _tcschr(cmdline, __T('\0')); - - } /* if */ - - return cmdline; -} - -static const TCHAR *tokenize(const TCHAR *string, int index, int *length) -{ - const TCHAR *start = string; - TCHAR endchar; - assert(index >= 0); - assert(start != NULL); - while (*start == __T(' ') || *start == __T('\t')) - start++; - if (*start == __T('\0')) - return NULL; - if (*start == __T('"')) - endchar = *start++; - else - endchar = __T(' '); - while (index > 0 && start != NULL) { - start = _tcschr(start, endchar); - if (start != NULL) { - assert(*start == endchar); - if (endchar != __T(' ')) - start++; - while (*start == __T(' ') || *start == __T('\t')) - start++; - if (*start == __T('"')) - endchar = *start++; - else - endchar = __T(' '); - } /* if */ - index--; - } /* while */ - if (start != NULL && length != NULL) { - const TCHAR *end; - if ((end = _tcschr(start, endchar)) == NULL) - end = _tcschr(start, __T('\0')); - assert(end != NULL); - *length = (int)(end - start); - } /* if */ - return start; -} - -static const TCHAR *matcharg(const TCHAR *key, int skip, int *length) -{ - const TCHAR *cmdline = rawcmdline(); - int index, optlen, keylen; - const TCHAR *option, *vptr; - - keylen = (key != NULL) ? _tcslen(key) : 0; - index = 0; - while ((option = tokenize(cmdline, index, length)) != NULL) { - /* check for a colon or an equal sign (':' or '=') */ - vptr = _tcschr(option, __T('=')); - #if AMXARGS_COLON - if (vptr == NULL || (int)(vptr - option) > *length) - vptr = _tcschr(option, __T(':')); - #endif - if (vptr != NULL && (int)(vptr - option) > *length) - vptr = NULL; - optlen = (vptr != NULL) ? (int)(vptr - option) : 0; - if (keylen == 0 && vptr == NULL - || keylen > 0 && keylen == optlen && _tcsncmp(option, key, optlen) == 0) - { - if (vptr != NULL) - optlen++; /* if ':' or '=' was found, skip it too */ - option += optlen; /* point behind option */ - *length -= optlen; /* length of the value, not of the option */ - assert(length >= 0); - if (skip-- == 0) - break; - } /* if */ - index++; - } /* while */ - return option; -} - - -/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false) - * returns true if the option was found and false on error or if the parameter "index" is out of range - */ -static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params) -{ - const TCHAR *cmdline = rawcmdline(); - const TCHAR *option; - int length, max; - TCHAR *str; - cell *cptr; - - max = (int)params[3]; - if (max <= 0) - return 0; - amx_GetAddr(amx, params[2], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - if ((option = tokenize(cmdline, params[1], &length)) == NULL) { - /* option not found, return an empty string */ - *cptr = 0; - return 0; - } /* if */ - - if (params[4]) - max *= sizeof(cell); - if (max > length + 1) - max = length + 1; - str = (TCHAR *)alloca(max*sizeof(TCHAR)); - if (str == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - memcpy(str, option, (max - 1) * sizeof(TCHAR)); - str[max - 1] = __T('\0'); - amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max); - - return 1; -} - -/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false) - * returns true if the option was found and false otherwise - */ -static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params) -{ - const TCHAR *option, *key; - int length, max; - TCHAR *str; - cell *cptr; - - max = (int)params[4]; - if (max <= 0) - return 0; - amx_StrParam(amx, params[2], key); - amx_GetAddr(amx, params[3], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - option = matcharg(key, (int)params[1], &length); - if (option == NULL) - return 0; /* option not found */ - - /* check whether we must write the value of the option at all; in case the - * size is one cell and that cell is already zero, we do not write anything - * back - */ - assert(params[4] > 0); - if (params[4] > 1 || *cptr != 0) { - if (params[5]) - max *= sizeof(cell); - if (max > length + 1) - max = length + 1; - str = (TCHAR *)alloca(max*sizeof(TCHAR)); - if (str == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - memcpy(str, option, (max - 1) * sizeof(TCHAR)); - str[max - 1] = __T('\0'); - amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max); - } /* if */ - - return 1; -} - -/* bool: argvalue(index=0, const option[]="", &value=cellmin) - * returns true if the option was found and false otherwise - */ -static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params) -{ - const TCHAR *option, *key; - int length; - cell *cptr; - - amx_StrParam(amx, params[2], key); - amx_GetAddr(amx, params[3], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - option = matcharg(key, (int)params[1], &length); - if (option == NULL) - return 0; - - /* check whether we must write the value of the option at all */ - if (length > 0 && (_istdigit(*option) || *option == __T('-'))) - *cptr = _tcstol(option, NULL, 10); - - return 1; -} - -/* argcount() */ -static cell AMX_NATIVE_CALL n_argcount(AMX *amx, const cell *params) -{ - const TCHAR *cmdline = rawcmdline(); - cell count = 0; - while (tokenize(cmdline, count, NULL) != NULL) - count++; - (void)amx; - (void)params; - return count; -} - - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO args_Natives[] = { - { "argcount", n_argcount }, - { "argindex", n_argindex }, - { "argstr", n_argstr }, - { "argvalue", n_argvalue }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_ArgsInit(AMX *amx) -{ - return amx_Register(amx, args_Natives, -1); -} - -int AMXEXPORT amx_ArgsCleanup(AMX *amx) -{ - (void)amx; - return AMX_ERR_NONE; -} - -/* A host application calls this function to set the command line for the - * script. If the host does not do this, the library will use the global - * options for the application (provided that it can find these). The buffer - * that is passed in to this function is NOT copied, so it may not be freed - * after the call. - */ -int AMXEXPORT amx_ArgsSetCmdLine(const TCHAR *cmd) -{ - cmdline = cmd; - return AMX_ERR_NONE; -} +/* Script Arguments support module for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2005-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxargs.c 5588 2016-10-25 11:13:28Z $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include "osdefs.h" +#if defined __WIN32__ || defined __MSDOS__ + #include +#endif +#if defined __WIN32__ || defined _Windows + #include +#endif +#if defined __GNUC__ || defined __clang__ + #include +#endif +#include "amx.h" + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _istdigit isdigit +# define _tgetenv getenv +# define _tcscat strcat +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _tcsncmp strncmp +# define _tcspbrk strpbrk +# define _tcsrchr strrchr +# define _tcstol strtol +#endif + +#if !defined AMXARGS_COLON + #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + /* A ':' is also a separator for filenames (the disk drive identifier), and + * therefore it is better not to use it as an name/value seperator for + * command line argiments as well. So, by default, the library uses only + * the '=' as the name/value separator. + */ + #define AMXARGS_COLON 0 + #else + #define AMXARGS_COLON 1 + #endif +#endif + +#if !defined AMXARGS_SKIPARG + /* The first option may be the name of the script (this is common if the + * host application takes the name of the script as the first parameter). + * This can optionally be ignored. + */ + #define AMXARGS_SKIPARG 0 +#endif + + +static const TCHAR *tokenize(const TCHAR *string, int index, int *length); +static const TCHAR *cmdline = NULL; + +static const TCHAR *rawcmdline(void) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + #elif defined _Windows || defined __MSDOS__ + static char cmdbuffer[128]; /* DOS & Windows 3.1 are never in Unicode mode */ + #elif defined __LINUX__ + static char cmdbuffer[1024]; /* some arbitrary maximum */ + #endif + const TCHAR *ptr; + int skip = 0; + + if (cmdline == NULL) { + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + cmdline = GetCommandLine(); + skip++; + #elif defined _Windows || defined __MSDOS__ + #if defined _Windows + unsigned short _psp = GetCurrentPDB(); + #endif + char _far *cmd = (char _far *)MK_FP(_psp, 128); + unsigned char length = (unsigned char)*cmd++; + assert(length < 128); + assert(length < sizeof cmdbuffer); + memcpy(cmdbuffer, cmd, length); + cmdbuffer[length] = '\0'; + if ((cmd == strchr(cmdbuffer, '\r')) != NULL) + *cmd = '\0'; /* also erase \r after the last option (if any) */ + cmdline = cmdbuffer; + #elif defined __LINUX__ + /* Options in /proc//cmdline are delimited with '\0' characters + * rather than spaces. + */ + FILE *fp; + size_t fsize; + sprintf(cmdbuffer, "/proc/%d/cmdline", getpid()); + if ((fp = fopen(cmdbuffer, "r")) != NULL) { + char *ptr; + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (fsize >= sizeof cmdbuffer) + fsize = sizeof cmdbuffer - 1; + fread(cmdbuffer, 1, fsize, fp); + fclose(fp); + cmdbuffer[fsize] = '\0'; /* terminate with double-zero */ + /* convert '\0' characters to spaces, for uniform parsing */ + for (ptr = cmdbuffer; *ptr != ' '; ptr = strchr(ptr, '\0') + 1) + *ptr = ' '; + cmdline = cmdbuffer; + skip++; + } /* if */ + #else + /* no mechanism for determining the commandline, so it + * must be supplied with amx_ArgsSetCmdLine() instead. + */ + ptr = ""; + #endif + + /* skip leading white space */ + while (*cmdline <= __T(' ') && *cmdline != __T('\0')) + cmdline++; + + #if AMXARGS_SKIPARG + skip++; + #endif + /* skip the first option(s), because it is the name of the host program + * and the name of the script + */ + if ((ptr = tokenize(cmdline, skip, NULL)) != NULL) + cmdline = ptr; + else + cmdline = _tcschr(cmdline, __T('\0')); + + } /* if */ + + return cmdline; +} + +static const TCHAR *tokenize(const TCHAR *string, int index, int *length) +{ + const TCHAR *start = string; + TCHAR endchar; + assert(index >= 0); + assert(start != NULL); + while (*start == __T(' ') || *start == __T('\t')) + start++; + if (*start == __T('\0')) + return NULL; + if (*start == __T('"')) + endchar = *start++; + else + endchar = __T(' '); + while (index > 0 && start != NULL) { + start = _tcschr(start, endchar); + if (start != NULL) { + assert(*start == endchar); + if (endchar != __T(' ')) + start++; + while (*start == __T(' ') || *start == __T('\t')) + start++; + if (*start == __T('"')) + endchar = *start++; + else + endchar = __T(' '); + } /* if */ + index--; + } /* while */ + if (start != NULL && length != NULL) { + const TCHAR *end; + if ((end = _tcschr(start, endchar)) == NULL) + end = _tcschr(start, __T('\0')); + assert(end != NULL); + *length = (int)(end - start); + } /* if */ + return start; +} + +static const TCHAR *matcharg(const TCHAR *key, int skip, int *length) +{ + const TCHAR *cmdline = rawcmdline(); + int index, optlen, keylen; + const TCHAR *option, *vptr; + + keylen = (key != NULL) ? (int)_tcslen(key) : 0; + index = 0; + while ((option = tokenize(cmdline, index, length)) != NULL) { + /* check for a colon or an equal sign (':' or '=') */ + vptr = _tcschr(option, __T('=')); + #if AMXARGS_COLON + if (vptr == NULL || (int)(vptr - option) > *length) + vptr = _tcschr(option, __T(':')); + #endif + if (vptr != NULL && (int)(vptr - option) > *length) + vptr = NULL; + optlen = (vptr != NULL) ? (int)(vptr - option) : 0; + if (keylen == 0 && vptr == NULL + || keylen > 0 && keylen == optlen && _tcsncmp(option, key, optlen) == 0) + { + if (vptr != NULL) + optlen++; /* if ':' or '=' was found, skip it too */ + option += optlen; /* point behind option */ + *length -= optlen; /* length of the value, not of the option */ + assert(*length >= 0); + if (skip-- == 0) + break; + } /* if */ + index++; + } /* while */ + return option; +} + + +/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false) + * returns true if the option was found and false on error or if the parameter "index" is out of range + */ +static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params) +{ + const TCHAR *cmdline = rawcmdline(); + const TCHAR *option; + int length, max; + TCHAR *str; + cell *cptr; + + max = (int)params[3]; + if (max <= 0) + return 0; + cptr = amx_Address(amx, params[2]); + + if ((option = tokenize(cmdline, params[1], &length)) == NULL) { + /* option not found, return an empty string */ + *cptr = 0; + return 0; + } /* if */ + + if (params[4]) + max *= sizeof(cell); + if (max > length + 1) + max = length + 1; + str = (TCHAR *)alloca(max*sizeof(TCHAR)); + if (str == NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + memcpy(str, option, (max - 1) * sizeof(TCHAR)); + str[max - 1] = __T('\0'); + amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max); + + return 1; +} + +/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false) + * returns true if the option was found and false otherwise + */ +static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params) +{ + const TCHAR *option, *key; + int length, max; + TCHAR *str; + cell *cptr; + + max = (int)params[4]; + if (max <= 0) + return 0; + amx_StrParam(amx, params[2], key); + cptr = amx_Address(amx, params[3]); + + option = matcharg(key, (int)params[1], &length); + if (option == NULL) + return 0; /* option not found */ + + /* check whether we must write the value of the option at all; in case the + * size is one cell and that cell is already zero, we do not write anything + * back + */ + assert(params[4] > 0); + if (params[4] > 1 || *cptr != 0) { + if (params[5]) + max *= sizeof(cell); + if (max > length + 1) + max = length + 1; + str = (TCHAR *)alloca(max*sizeof(TCHAR)); + if (str == NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + memcpy(str, option, (max - 1) * sizeof(TCHAR)); + str[max - 1] = __T('\0'); + amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max); + } /* if */ + + return 1; +} + +/* bool: argvalue(index=0, const option[]="", &value=cellmin) + * returns true if the option was found and false otherwise + */ +static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params) +{ + const TCHAR *option, *key; + int length; + cell *cptr; + + amx_StrParam(amx, params[2], key); + cptr = amx_Address(amx, params[3]); + + option = matcharg(key, (int)params[1], &length); + if (option == NULL) + return 0; + + /* check whether we must write the value of the option at all */ + if (length > 0 && (_istdigit(*option) || *option == __T('-'))) + *cptr = _tcstol(option, NULL, 10); + + return 1; +} + +/* argcount() */ +static cell AMX_NATIVE_CALL n_argcount(AMX *amx, const cell *params) +{ + const TCHAR *cmdline = rawcmdline(); + cell count = 0; + while (tokenize(cmdline, count, NULL) != NULL) + count++; + (void)amx; + (void)params; + return count; +} + + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO args_Natives[] = { + { "argcount", n_argcount }, + { "argindex", n_argindex }, + { "argstr", n_argstr }, + { "argvalue", n_argvalue }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_ArgsInit(AMX *amx) +{ + return amx_Register(amx, args_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_ArgsCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} + +/* A host application calls this function to set the command line for the + * script. If the host does not do this, the library will use the global + * options for the application (provided that it can find these). The buffer + * that is passed in to this function is NOT copied, so it may not be freed + * after the call. + */ +int AMXEXPORT AMXAPI amx_ArgsSetCmdLine(const TCHAR *cmd) +{ + cmdline = cmd; + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxargs.def b/amx-deps/src/amx/amxargs.def new file mode 100644 index 0000000..5ec007a --- /dev/null +++ b/amx-deps/src/amx/amxargs.def @@ -0,0 +1,6 @@ +NAME amxArgs +DESCRIPTION 'Pawn AMX: Script Arguments support' + +EXPORTS + amx_ArgsInit + amx_ArgsCleanup diff --git a/amx-deps/src/amx/amxargs.rc b/amx-deps/src/amx/amxargs.rc index 12a089e..6c8fcd8 100644 --- a/amx-deps/src/amx/amxargs.rc +++ b/amx-deps/src/amx/amxargs.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" -#define VERSIONNAME "amxArgs.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: Script Arguments support\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxArgs\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2008 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 4 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "4.0.0\0" +#define VERSIONNAME "amxArgs.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: Script Arguments support\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxArgs\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxaux.c b/amx-deps/src/amx/amxaux.c index 156dee9..10a92ea 100644 --- a/amx-deps/src/amx/amxaux.c +++ b/amx-deps/src/amx/amxaux.c @@ -1,168 +1,178 @@ -/* Support routines for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2003-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxaux.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include -#include "amx.h" -#include "amxaux.h" - -size_t AMXAPI aux_ProgramSize(char *filename) -{ - FILE *fp; - AMX_HEADER hdr; - - if ((fp=fopen(filename,"rb")) == NULL) - return 0; - fread(&hdr, sizeof hdr, 1, fp); - fclose(fp); - - amx_Align16(&hdr.magic); - amx_Align32((uint32_t *)&hdr.stp); - return (hdr.magic==AMX_MAGIC) ? (size_t)hdr.stp : 0; -} - -int AMXAPI aux_LoadProgram(AMX *amx, const char *filename, void *memblock) -{ - FILE *fp; - AMX_HEADER hdr; - int result, didalloc; - - /* open the file, read and check the header */ - if ((fp = fopen(filename, "rb")) == NULL) - return AMX_ERR_NOTFOUND; - fread(&hdr, sizeof hdr, 1, fp); - amx_Align16(&hdr.magic); - amx_Align32((uint32_t *)&hdr.size); - amx_Align32((uint32_t *)&hdr.stp); - if (hdr.magic != AMX_MAGIC) { - fclose(fp); - return AMX_ERR_FORMAT; - } /* if */ - - /* allocate the memblock if it is NULL */ - didalloc = 0; - if (memblock == NULL) { - if ((memblock = malloc(hdr.stp)) == NULL) { - fclose(fp); - return AMX_ERR_MEMORY; - } /* if */ - didalloc = 1; - /* after amx_Init(), amx->base points to the memory block */ - } /* if */ - - /* read in the file */ - rewind(fp); - fread(memblock, 1, (size_t)hdr.size, fp); - fclose(fp); - - /* initialize the abstract machine */ - memset(amx, 0, sizeof *amx); - result = amx_Init(amx, memblock); - - /* free the memory block on error, if it was allocated here */ - if (result != AMX_ERR_NONE && didalloc) { - free(memblock); - amx->base = NULL; /* avoid a double free */ - } /* if */ - - return result; -} - -int AMXAPI aux_FreeProgram(AMX *amx) -{ - if (amx->base!=NULL) { - amx_Cleanup(amx); - free(amx->base); - memset(amx,0,sizeof(AMX)); - } /* if */ - return AMX_ERR_NONE; -} - -char * AMXAPI aux_StrError(int errnum) -{ -static char *messages[] = { - /* AMX_ERR_NONE */ "(none)", - /* AMX_ERR_EXIT */ "Forced exit", - /* AMX_ERR_ASSERT */ "Assertion failed", - /* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)", - /* AMX_ERR_BOUNDS */ "Array index out of bounds", - /* AMX_ERR_MEMACCESS */ "Invalid memory access", - /* AMX_ERR_INVINSTR */ "Invalid instruction", - /* AMX_ERR_STACKLOW */ "Stack underflow", - /* AMX_ERR_HEAPLOW */ "Heap underflow", - /* AMX_ERR_CALLBACK */ "No (valid) native function callback", - /* AMX_ERR_NATIVE */ "Native function failed", - /* AMX_ERR_DIVIDE */ "Divide by zero", - /* AMX_ERR_SLEEP */ "(sleep mode)", - /* AMX_ERR_INVSTATE */ "Invalid state", - /* 14 */ "(reserved)", - /* 15 */ "(reserved)", - /* AMX_ERR_MEMORY */ "Out of memory", - /* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format", - /* AMX_ERR_VERSION */ "File is for a newer version of the AMX", - /* AMX_ERR_NOTFOUND */ "File or function is not found", - /* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)", - /* AMX_ERR_DEBUG */ "Debugger cannot run", - /* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)", - /* AMX_ERR_USERDATA */ "Unable to set user data field (table full)", - /* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT", - /* AMX_ERR_PARAMS */ "Parameter error", - /* AMX_ERR_DOMAIN */ "Domain error, expression result does not fit in range", - /* AMX_ERR_GENERAL */ "General error (unknown or unspecific error)", - /* AMX_ERR_OVERLAY */ "Overlays are unsupported (JIT) or uninitialized", - }; - if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0]) - return "(unknown)"; - return messages[errnum]; -} - -int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size) -{ - AMX_HEADER *hdr; - - if (amx == NULL || start == NULL || size == NULL) - return AMX_ERR_PARAMS; - - hdr = (AMX_HEADER*)amx->base; - switch(section) { - case CODE_SECTION: - *start = (cell *)(amx->base + hdr->cod); - *size = hdr->dat - hdr->cod; - break; - case DATA_SECTION: - *start = (cell *)(amx->data); - *size = hdr->hea - hdr->dat; - break; - case HEAP_SECTION: - *start = (cell *)(amx->data + hdr->hea); - *size = amx->hea - hdr->hea; - break; - case STACK_SECTION: - *start = (cell *)(amx->data + amx->stk); - *size = amx->stp - amx->stk; - break; - default: - return AMX_ERR_PARAMS; - } /* switch */ - return AMX_ERR_NONE; -} +/* Support routines for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2003-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxaux.c 3612 2006-07-22 09:59:46Z thiadmer $ + */ + +#include +#include +#include +#include "amx.h" +#include "amxaux.h" + +size_t AMXAPI aux_ProgramSize(char *filename) +{ + FILE *fp; + size_t size; + AMX_HEADER hdr; + + if ((fp=fopen(filename,"rb")) == NULL) + return 0; + size = fread(&hdr, sizeof hdr, 1, fp); + fclose(fp); + if (size < 1) + return 0; + + amx_Align16(&hdr.magic); + amx_Align32((uint32_t *)&hdr.stp); + return (hdr.magic==AMX_MAGIC) ? (size_t)hdr.stp : 0; +} + +int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock) +{ + FILE *fp; + size_t size; + AMX_HEADER hdr; + int result, didalloc; + + /* open the file, read and check the header */ + if ((fp = fopen(filename, "rb")) == NULL) + return AMX_ERR_NOTFOUND; + size = fread(&hdr, sizeof hdr, 1, fp); + if (size < 1) { + fclose(fp); + return AMX_ERR_FORMAT; + } /* if */ + amx_Align16(&hdr.magic); + amx_Align32((uint32_t *)&hdr.size); + amx_Align32((uint32_t *)&hdr.stp); + if (hdr.magic != AMX_MAGIC) { + fclose(fp); + return AMX_ERR_FORMAT; + } /* if */ + + /* allocate the memblock if it is NULL */ + didalloc = 0; + if (memblock == NULL) { + if ((memblock = malloc(hdr.stp)) == NULL) { + fclose(fp); + return AMX_ERR_MEMORY; + } /* if */ + didalloc = 1; + /* after amx_Init(), amx->base points to the memory block */ + } /* if */ + + /* read in the file */ + rewind(fp); + size = fread(memblock, 1, (size_t)hdr.size, fp); + fclose(fp); + if (size < (size_t)hdr.size) + return AMX_ERR_FORMAT; + + /* initialize the abstract machine */ + memset(amx, 0, sizeof *amx); + result = amx_Init(amx, memblock); + + /* free the memory block on error, if it was allocated here */ + if (result != AMX_ERR_NONE && didalloc) { + free(memblock); + amx->base = NULL; /* avoid a double free */ + } /* if */ + + return result; +} + +int AMXAPI aux_FreeProgram(AMX *amx) +{ + if (amx->base!=NULL) { + amx_Cleanup(amx); + free(amx->base); + memset(amx,0,sizeof(AMX)); + } /* if */ + return AMX_ERR_NONE; +} + +char * AMXAPI aux_StrError(int errnum) +{ +static char *messages[] = { + /* AMX_ERR_NONE */ "(none)", + /* AMX_ERR_EXIT */ "Forced exit", + /* AMX_ERR_ASSERT */ "Assertion failed", + /* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)", + /* AMX_ERR_BOUNDS */ "Array index out of bounds", + /* AMX_ERR_MEMACCESS */ "Invalid memory access", + /* AMX_ERR_INVINSTR */ "Invalid instruction", + /* AMX_ERR_STACKLOW */ "Stack underflow", + /* AMX_ERR_HEAPLOW */ "Heap underflow", + /* AMX_ERR_CALLBACK */ "No (valid) native function callback", + /* AMX_ERR_NATIVE */ "Native function failed", + /* AMX_ERR_DIVIDE */ "Divide by zero", + /* AMX_ERR_SLEEP */ "(sleep mode)", + /* 13 */ "(reserved)", + /* 14 */ "(reserved)", + /* 15 */ "(reserved)", + /* AMX_ERR_MEMORY */ "Out of memory", + /* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format", + /* AMX_ERR_VERSION */ "File is for a newer version of the AMX", + /* AMX_ERR_NOTFOUND */ "File or function is not found", + /* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)", + /* AMX_ERR_DEBUG */ "Debugger cannot run", + /* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)", + /* AMX_ERR_USERDATA */ "Unable to set user data field (table full)", + /* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT", + /* AMX_ERR_PARAMS */ "Parameter error", + /* AMX_ERR_DOMAIN */ "Domain error, expression result does not fit in range", + /* AMX_ERR_GENERAL */ "General error (unknown or unspecific error)", + }; + if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0]) + return "(unknown)"; + return messages[errnum]; +} + +int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size) +{ + AMX_HEADER *hdr; + + if (amx == NULL || start == NULL || size == NULL) + return AMX_ERR_PARAMS; + + hdr = (AMX_HEADER*)amx->base; + switch(section) { + case CODE_SECTION: + *start = (cell *)(amx->base + hdr->cod); + *size = hdr->dat - hdr->cod; + break; + case DATA_SECTION: + *start = (cell *)(amx->data); + *size = hdr->hea - hdr->dat; + break; + case HEAP_SECTION: + *start = (cell *)(amx->data + hdr->hea); + *size = amx->hea - hdr->hea; + break; + case STACK_SECTION: + *start = (cell *)(amx->data + amx->stk); + *size = amx->stp - amx->stk; + break; + default: + return AMX_ERR_PARAMS; + } /* switch */ + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxaux.h b/amx-deps/src/amx/amxaux.h index e1c3a7b..b9d2148 100644 --- a/amx-deps/src/amx/amxaux.h +++ b/amx-deps/src/amx/amxaux.h @@ -1,54 +1,55 @@ -/* Support routines for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2003-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxaux.h 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#ifndef AMXAUX_H_INCLUDED -#define AMXAUX_H_INCLUDED - -#include "amx.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* loading and freeing programs */ -size_t AMXAPI aux_ProgramSize(char *filename); -int AMXAPI aux_LoadProgram(AMX *amx, const char *filename, void *memblock); -int AMXAPI aux_FreeProgram(AMX *amx); - -/* a readable error message from an error code */ -char * AMXAPI aux_StrError(int errnum); - -enum { - CODE_SECTION, - DATA_SECTION, - HEAP_SECTION, - STACK_SECTION, - /* ----- */ - NUM_SECTIONS -}; -int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size); - -#ifdef __cplusplus -} -#endif - -#endif /* AMXAUX_H_INCLUDED */ +/* Support routines for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2003-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxaux.h 3612 2006-07-22 09:59:46Z thiadmer $ + */ + +#ifndef AMXAUX_H_INCLUDED +#define AMXAUX_H_INCLUDED + +#include "amx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* loading and freeing programs */ +size_t AMXAPI aux_ProgramSize(char *filename); +int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock); +int AMXAPI aux_FreeProgram(AMX *amx); + +/* a readable error message from an error code */ +char * AMXAPI aux_StrError(int errnum); + +enum { + CODE_SECTION, + DATA_SECTION, + HEAP_SECTION, + STACK_SECTION, + /* ----- */ + NUM_SECTIONS +}; +int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size); + +#ifdef __cplusplus +} +#endif + +#endif /* AMXAUX_H_INCLUDED */ diff --git a/amx-deps/src/amx/amxcons.c b/amx-deps/src/amx/amxcons.c index b31658c..ef3d14a 100644 --- a/amx-deps/src/amx/amxcons.c +++ b/amx-deps/src/amx/amxcons.c @@ -1,1394 +1,1531 @@ -/* Console output module (terminal I/O) for the Pawn AMX - * - * Since some of these routines go further than those of standard C, they - * cannot always be implemented with portable C functions. In other words, - * these routines must be ported to other environments. - * - * Copyright (c) ITB CompuPhase, 1997-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxcons.c 3935 2008-03-06 13:18:13Z thiadmer $ - */ - -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include -#include -#include -#include -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ - #include - #include -#endif -#if defined USE_CURSES || defined HAVE_CURSES_H - #include - #if !defined CURSES - #define CURSES 1 - #endif -#endif -#include "osdefs.h" -#if defined __ECOS__ - /* eCos puts include files in cyg/package_name */ - #include - #include - #include - #include -#else - #include "amx.h" -#endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - #include -#endif - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _fgetts fgets -# define _puttchar putchar -# define _stprintf sprintf -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _tprintf printf -#endif -#include "amxcons.h" - -#if defined __MSDOS__ - #define EOL_CHAR '\r' -#endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - #define EOL_CHAR '\r' -#endif -#if !defined EOL_CHAR - /* if not a "known" operating system, assume Linux/Unix */ - #define EOL_CHAR '\n' -#endif - -#if !defined AMX_STRING_LIB - -#if defined AMX_TERMINAL - /* required functions are implemented elsewhere */ - int amx_putstr(const TCHAR *); - int amx_putchar(int); - int amx_fflush(void); - int amx_getch(void); - TCHAR *amx_gets(TCHAR *,int); - int amx_termctl(int,int); - void amx_clrscr(void); - void amx_clreol(void); - int amx_gotoxy(int x,int y); - void amx_wherexy(int *x,int *y); - unsigned int amx_setattr(int foregr,int backgr,int highlight); - void amx_console(int columns, int lines, int flags); - void amx_viewsize(int *width,int *height); - int amx_kbhit(void); -#elif defined CURSES && CURSES != 0 - /* Use the "curses" library to implement the console */ - static WINDOW *curseswin; - #define amx_putstr(s) printw("%s",(s)) - #define amx_putchar(c) addch(c) - #define amx_fflush() refresh() - #define amx_getch() getch() - #define amx_gets(s,n) getnstr((s),(n)) - #define amx_clrscr() clear() - #define amx_clreol() clrtoeol() - #define amx_gotoxy(x,y) move((y)-1,(x)-1) - #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) - unsigned int amx_setattr(int foregr,int backgr,int highlight) - { - int attribs=A_NORMAL; - if (highlight>0) - attribs=(attribs & ~A_NORMAL) | A_STANDOUT; - attrset(attribs); - //??? in future, also handle colours - } - void CreateConsole(void); - int amx_kbhit(void) - { - int result; - CreateConsole(); - nodelay(curseswin,TRUE); /* enter non-blocking state */ - result=getch(); /* read key (if any) */ - nodelay(curseswin,FALSE); /* leave non-blocking state */ - if (result!=ERR) - ungetch(result); /* a key is waiting, push it back */ - return (result==ERR) ? 0 : 1; - } - int amx_termctl(int code,int value) - { - switch (code) { - case 0: /* query terminal support */ - return 1; - /* case 1: */ /* switch auto-wrap on/off (not supported in curses!) */ - /* case 2: */ /* create/switch to another console */ - case 3: /* set emphasized font */ - if (value) - attron(A_BOLD); - else - attroff(A_BOLD); - return 1; - /* case 4: */ /* query whether a terminal is "open" */ - default: - return 0; - } /* switch */ - } - void amx_wherexy(int *x,int *y) - { - int row,col; - getyx(curseswin,row,col); - if (x!=NULL) - *x=col+1; - if (y!=NULL) - *y=row+1; - } - void amx_viewsize(int *width,int *height) - { - int row,col; - getmaxyx(curseswin,row,col); - if (width!=NULL) - *width=col; - if (height!=NULL) - *height=row; - } -#elif defined VT100 || defined __LINUX__ || defined ANSITERM || defined __ECOS__ - /* ANSI/VT100 terminal, or shell emulating "xterm" */ - #if defined __ECOS__ - #define AMXCONSOLE_NOIDLE - #endif - - #if CYGPKG_PAWN_AMXCONSOLE_DIAG==1 - /* eCos has basically two ways to make simple exchanges with a terminal: - * - with the diag_*() functions (no input provided!) - * - with f*() functions (fprintf(),fputs(), etc). - */ - #define amx_fflush() - - static int amx_putstr(TCHAR *s) - { - diag_write_string(s); - return 1; - } - static int amx_putchar(TCHAR c) - { - diag_write_char(c); - return c; - } - static char amx_getch(void) - { - char c=-1; - HAL_DIAG_READ_CHAR(c); - return c; - } - #else - - #define amx_putstr(s) fputs((s),stdout) - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) - #define amx_kbhit() kbhit() - #endif - - int amx_termctl(int code,int value) - { - switch (code) { - case 0: /* query terminal support */ - return 1; - - case 1: /* switch "auto-wrap" on or off */ - if (value) - amx_putstr("\033[?7h"); /* enable "auto-wrap" */ - else - amx_putstr("\033[?7l"); /* disable "auto-wrap" */ - return 1; - - #if 0 - /* next to swapping buffers, more information should be saved and swapped, - * such as the cursor position and the current terminal attributes - */ - case 2: /* swap console buffers */ - amx_fflush(); - if (value==1) { - amx_putstr("\033[?47h"); - } else { - amx_putstr("\033[?47l"); - } /* if */ - amx_fflush(); - return 1; - #endif - - case 3: /* set bold/highlighted font */ - return 0; - - default: - return 0; - } /* switch */ - } - void amx_clrscr(void) - { - amx_putstr("\033[2J"); - amx_fflush(); /* pump through the terminal codes */ - } - void amx_clreol(void) - { - amx_putstr("\033[K"); - amx_fflush(); /* pump through the terminal codes */ - } - int amx_gotoxy(int x,int y) - { - char str[30]; - _stprintf(str,"\033[%d;%dH",y,x); - amx_putstr(str); - amx_fflush(); /* pump through the terminal codes */ - return 1; - } - void amx_wherexy(int *x,int *y) - { - int val,i; - char str[10]; - - assert(x!=NULL && y!=NULL); - amx_putstr("\033[6n"); - amx_fflush(); - while (amx_getch()!='\033') - /* nothing */; - val=amx_getch(); - assert(val=='['); - for (i=0; i<8 && (val=amx_getch())!=';'; i++) - str[i]=(char)val; - str[i]='\0'; - if (y!=NULL) - *y=atoi(str); - for (i=0; i<8 && (val=amx_getch())!='R'; i++) - str[i]=(char)val; - str[i]='\0'; - if (x!=NULL) - *x=atoi(str); - #if defined ANSITERM - val=amx_getch(); - assert(val=='\r'); /* ANSI driver adds CR to the end of the command */ - #endif - } - unsigned int amx_setattr(int foregr,int backgr,int highlight) - { - static short current=(0 << 8) | 7; - short prev = current; - char str[30]; - - if (foregr>=0) { - _stprintf(str,"\x1b[%dm",foregr+30); - amx_putstr(str); - current=(current & 0xff00) | (foregr & 0x0f); - } /* if */ - if (backgr>=0) { - _stprintf(str,"\x1b[%dm",backgr+40); - amx_putstr(str); - current=(current & 0x00ff) | ((backgr & 0x0f) << 8); - } /* if */ - if (highlight>=0) { - _stprintf(str,"\x1b[%dm",highlight); - amx_putstr(str); - current=(current & 0x7fff) | ((highlight & 0x01) << 15); - } /* if */ - return prev; - } - void amx_console(int columns, int lines, int flags) - { - char str[30]; - - (void)flags; - /* There is no ANSI code (or VT100/VT220) to set the size of the console - * (indeed, the terminal was that of the alphanumeric display). In xterm (a - * terminal emulator) we can set the terminal size though, and most - * terminals that in use today are in fact emulators. - * Putty understands this code too, by many others do not. - */ - sprintf(str,"\033[8;%d;%dt",lines,columns); - amx_putstr(str); - amx_fflush(); - } - void amx_viewsize(int *width,int *height) - { - /* a trick to get the size of the terminal is to position the cursor far - * away and then read it back - */ - amx_gotoxy(999,999); - amx_wherexy(width,height); - } -#elif defined __WIN32__ || defined _WIN32 || defined WIN32 - /* Win32 console */ - #define amx_putstr(s) _tprintf("%s",(s)) - #define amx_putchar(c) _puttchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) _fgetts(s,n,stdin) - #define amx_kbhit() kbhit() - - int amx_termctl(int code,int value) - { - switch (code) { - case 0: /* query terminal support */ - return 1; - - case 1: { /* switch auto-wrap on/off */ - /* only works in Windows 2000/XP */ - HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - DWORD Flags=ENABLE_PROCESSED_OUTPUT; - if (value) - Flags |= ENABLE_WRAP_AT_EOL_OUTPUT; - SetConsoleMode(hConsole,Flags); - return 1; - } /* case */ - - /* case 2: */ /* create/switch to another console */ - /* case 3: */ /* set emphasized font */ - /* case 4: */ /* query whether a terminal is "open" */ - default: - return 0; - } /* switch */ - } - void amx_clrscr(void) - { - COORD coordScreen={0,0}; - DWORD cCharsWritten; - CONSOLE_SCREEN_BUFFER_INFO csbi; - DWORD dwConSize; - HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - - amx_fflush(); /* make sure the I/O buffer is empty */ - GetConsoleScreenBufferInfo(hConsole,&csbi); - dwConSize=csbi.dwSize.X*csbi.dwSize.Y; - FillConsoleOutputCharacter(hConsole,' ',dwConSize,coordScreen,&cCharsWritten); - FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,coordScreen, &cCharsWritten); - SetConsoleCursorPosition(hConsole,coordScreen); - } - void amx_clreol(void) - { - DWORD cCharsWritten; - CONSOLE_SCREEN_BUFFER_INFO csbi; - DWORD dwConSize; - HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - - amx_fflush(); /* make sure all output is written */ - GetConsoleScreenBufferInfo(hConsole,&csbi); - dwConSize=csbi.dwSize.X - csbi.dwCursorPosition.X; - FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten); - FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten); - } - int amx_gotoxy(int x,int y) - { - COORD point; - CONSOLE_SCREEN_BUFFER_INFO csbi; - HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - - GetConsoleScreenBufferInfo(hConsole, &csbi); - if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y) - return 0; - amx_fflush(); /* make sure all output is written */ - point.X=(short)(x-1); - point.Y=(short)(y-1); - SetConsoleCursorPosition(hConsole,point); - return 1; - } - void amx_wherexy(int *x,int *y) - { - CONSOLE_SCREEN_BUFFER_INFO csbi; - amx_fflush(); /* make sure all output is written */ - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); - if (x!=NULL) - *x=csbi.dwCursorPosition.X+1; - if (y!=NULL) - *y=csbi.dwCursorPosition.Y+1; - } - unsigned int amx_setattr(int foregr,int backgr,int highlight) - { - static int ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - CONSOLE_SCREEN_BUFFER_INFO csbi; - int f,b,h,prev; - HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - - amx_fflush(); /* make sure all output is written */ - GetConsoleScreenBufferInfo(hConsole,&csbi); - f=csbi.wAttributes & 0x07; - b=(csbi.wAttributes >> 4) & 0x0f; - h=(csbi.wAttributes & 0x08) ? 1 : 0; - prev=(b << 8) | f | (h << 15); - if (foregr>=0 && foregr<8) - f=ansi_colours[foregr]; - if (backgr>=0 && backgr<8) - b=ansi_colours[backgr]; - if (highlight>=0) - h=highlight!=0; - SetConsoleTextAttribute(hConsole, (WORD)((b << 4) | f | (h << 3))); - return prev; - } - void amx_console(int columns, int lines, int flags) - { - SMALL_RECT rect; - COORD dwSize; - (void)flags; - dwSize.X=(short)columns; - dwSize.Y=(short)lines; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),dwSize); - rect.Left=0; - rect.Top=0; - rect.Right=(short)(columns-1); - rect.Bottom=(short)(lines-1); - SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE),TRUE,&rect); - } - void amx_viewsize(int *width,int *height) - { - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi); - if (width!=NULL) - *width=(int)csbi.dwSize.X; - if (height!=NULL) - *height=(int)(csbi.srWindow.Bottom-csbi.srWindow.Top+1); - } -#else - /* assume a streaming terminal; limited features (no colour, no cursor - * control) - */ - #define amx_putstr(s) printf("%s",(s)) - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) - #define amx_clrscr() (void)(0) - #define amx_clreol() (void)(0) - #define amx_gotoxy(x,y) ((void)(x),(void)(y),(0)) - #define amx_wherexy(x,y) (*(x)=*(y)=0) - #define amx_setattr(c,b,h) ((void)(c),(void)(b),(void)(h),(0)) - #define amx_termctl(c,v) ((void)(c),(void)(v),(0)) - #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) - #define amx_viewsize (*(x)=80,*(y)=25) - #define amx_kbhit() kbhit() -#endif - -#if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32) - void CreateConsole(void) - { static int createdconsole=0; - if (!createdconsole) { - AllocConsole(); - createdconsole=1; - } /* if */ - } -#elif defined CURSES && CURSES != 0 - // The Mac OS X build variant uses curses. - void CreateConsole(void) - { static int createdconsole=0; - if (!createdconsole) { - curseswin=initscr(); - cbreak(); - noecho(); - nonl(); - scrollok(curseswin,TRUE); - intrflush(curseswin,FALSE); - keypad(curseswin,TRUE); - createdconsole=1; - } /* if */ - } -#else - #define CreateConsole() -#endif - -static int cons_putstr(void *dest,const TCHAR *str) -{ - (void)dest; - return amx_putstr(str); -} - -static int cons_putchar(void *dest,TCHAR ch) -{ - (void)dest; - return amx_putchar(ch); -} - -#endif /* AMX_STRING_LIB */ - -enum { - SV_DECIMAL, - SV_HEX -}; - -static TCHAR *reverse(TCHAR *string,int stop) -{ - int start=0; - TCHAR temp; - - /* swap the string */ - stop--; /* avoid swapping the '\0' byte to the first position */ - while (stop - start > 0) { - temp = string[start]; - string[start] = string[stop]; - string[stop] = temp; - start++; - stop--; - } /* while */ - return string; -} - -/* Converts an integral value to a string, with optional padding with spaces or - * zeros. - * The "format" must be decimal or hexadecimal - * The number is right-aligned in the field with the size of the absolute value - * of the "width" parameter. - * If the width value is positive, the string is padded with spaces; if it is - * negative, it is padded with zeros. - */ -static TCHAR *amx_strval(TCHAR buffer[], long value, int format, int width) -{ - int start, stop; - TCHAR temp; - - start = stop = 0; - if (format == SV_DECIMAL) { - if (value < 0) { - buffer[0] = __T('-'); - start = stop = 1; - value = -value; - } /* if */ - do { - buffer[stop++] = (TCHAR)((value % 10) + __T('0')); - value /= 10; - } while (value > 0); - } else { - /* hexadecimal */ - unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */ - do { - buffer[stop] = (TCHAR)((v & 0x0f) + __T('0')); - if (buffer[stop] > __T('9')) - buffer[stop] += (TCHAR)(__T('A') - __T('0') - 10); - v >>= 4; - stop++; - } while (v != 0); - } /* if */ - - /* pad to given width */ - if (width < 0) { - temp = __T('0'); - width = -width; - } else { - temp = __T(' '); - } /* if */ - while (stop < width) - buffer[stop++] = temp; - - buffer[stop] = __T('\0'); - - /* swap the string, and we are done */ - reverse(buffer+start,stop-start); - return buffer; -} - -#if defined FIXEDPOINT - #define FIXEDMULT 1000 - #define FIXEDDIGITS 3 - -static TCHAR *formatfixed(TCHAR *string,cell value,TCHAR align,int width,TCHAR decpoint,int digits,TCHAR filler) -{ - int i, len; - cell ipart,v; - TCHAR vsign=__T('\0'); - - /* make the value positive (but keep the sign) */ - if (value<0) { - value=-value; - vsign=__T('-'); - } /* if */ - - /* "prepare" the value so that when it is truncated to the requested - * number of digits, the result is rounded towards the dropped digits - */ - assert(digits=0); - assert(value>=0); - - /* truncate the fractional part to the requested number of digits */ - for (i=FIXEDDIGITS; i>digits; i--) - value/=10; - - string[0]=__T('\0'); - - /* add sign */ - i=_tcslen(string); - string[i]=vsign; - string[i+1]=__T('\0'); - - /* add integer part */ - amx_strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0); - - /* add fractional part */ - if (digits>0) { - i=_tcslen(string); - string[i]=decpoint; - amx_strval(string+i+1,(long)value,SV_DECIMAL,-digits); - } /* if */ - - len=_tcslen(string); - if (len=len); - reverse(string,len); - reverse(string+len,i-len); - reverse(string,i); - } /* if */ - } /* if */ - - return string; -} -#endif - - -static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int width,int digits,TCHAR filler, - int (*f_putstr)(void*,const TCHAR *),int (*f_putchar)(void*,TCHAR),void *user) -{ - cell *cptr; - TCHAR buffer[40]; - #if defined FLOATPOINT - TCHAR formatstring[40]; - #endif - - #if !defined FIXEDPOINT && !defined FLOATPOINT - (void)decpoint; - #endif - assert(f_putstr!=NULL); - assert(f_putchar!=NULL); - - switch (ch) { - case __T('c'): - amx_GetAddr(amx,param,&cptr); - width--; /* single character itself has a with of 1 */ - if (sign!=__T('-')) - while (width-->0) - f_putchar(user,filler); - f_putchar(user,(TCHAR)*cptr); - while (width-->0) - f_putchar(user,filler); - return 1; - - case __T('d'): { - cell value; - int length=1; - amx_GetAddr(amx,param,&cptr); - value=*cptr; - if (value<0 || sign==__T('+')) - length++; - if (value<0) - value=-value; - while (value>=10) { - length++; - value/=10; - } /* while */ - width-=length; - if (sign!=__T('-')) - while (width-->0) - f_putchar(user,filler); - amx_strval(buffer,*cptr,SV_DECIMAL,0); - if (sign==__T('+') && *cptr>=0) - f_putchar(user,sign); - f_putstr(user,buffer); - while (width-->0) - f_putchar(user,filler); - return 1; - } /* case */ - -#if defined FLOATPOINT - case __T('f'): /* 32-bit floating point number */ - case __T('r'): /* if floating point is enabled, %r == %f */ - /* build a format string */ - if (digits==INT_MAX) - digits=5; - else if (digits>25) - digits=25; - _tcscpy(formatstring,__T("%")); - if (sign!=__T('\0')) - _stprintf(formatstring+_tcslen(formatstring),__T("%c"),sign); - if (width>0) - _stprintf(formatstring+_tcslen(formatstring),__T("%d"),width); - _stprintf(formatstring+_tcslen(formatstring),__T(".%df"),digits); - amx_GetAddr(amx,param,&cptr); - #if PAWN_CELL_SIZE == 64 - _stprintf(buffer,formatstring,*(double*)cptr); - #else - _stprintf(buffer,formatstring,*(float*)cptr); - #endif - if (decpoint==__T(',')) { - TCHAR *ptr=_tcschr(buffer,__T('.')); - if (ptr!=NULL) - *ptr=__T(','); - } /* if */ - f_putstr(user,buffer); - return 1; -#endif - -#if defined FIXEDPOINT - #define FIXEDMULT 1000 - case __T('q'): /* 32-bit fixed point number */ -#if !defined FLOATPOINT - case __T('r'): /* if fixed point is enabled, and floating point is not, %r == %q */ -#endif - amx_GetAddr(amx,param,&cptr); - /* format the number */ - if (digits==INT_MAX) - digits=3; - else if (digits>25) - digits=25; - formatfixed(buffer,*cptr,sign,width,decpoint,digits,filler); - assert(_tcslen(buffer)=0x10) { - length++; - value>>=4; - } /* while */ - width-=length; - if (sign!=__T('-')) - while (width-->0) - f_putchar(user,filler); - amx_strval(buffer,(long)*cptr,SV_HEX,0); - f_putstr(user,buffer); - while (width-->0) - f_putchar(user,filler); - return 1; - } /* case */ - - } /* switch */ - /* error in the string format, try to repair */ - f_putchar(user,ch); - return 0; -} - -enum { - FMT_NONE, /* not in format state; accept '%' */ - FMT_START, /* found '%', accept '+', '-' (START), '0' (filler; START), digit (WIDTH), '.' (DECIM), or '%' or format letter (done) */ - FMT_WIDTH, /* found digit after '%' or sign, accept digit (WIDTH), '.' (DECIM) or format letter (done) */ - FMT_DECIM, /* found digit after '.', accept accept digit (DECIM) or format letter (done) */ -}; - -static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width,int *digits,TCHAR *filler) -{ - assert(state!=NULL && sign!=NULL && decpoint!=NULL && width!=NULL && digits!=NULL && filler!=NULL); - switch (*state) { - case FMT_NONE: - if (c==__T('%')) { - *state=FMT_START; - *sign=__T('\0'); - *decpoint=__T('.'); - *width=0; - *digits=INT_MAX; - *filler=__T(' '); - } else { - return -1; /* print a single character */ - } /* if */ - break; - case FMT_START: - if (c==__T('+') || c==__T('-')) { - *sign=c; - } else if (c==__T('0')) { - *filler=c; - } else if (c>=__T('1') && c<=__T('9')) { - *width=(int)(c-__T('0')); - *state=FMT_WIDTH; - } else if (c==__T('.') || c==__T(',')) { - *decpoint=c; - *digits=0; - *state=FMT_DECIM; - } else if (c==__T('%')) { - *state=FMT_NONE; - return -1; /* print literal '%' */ - } else { - return 1; /* print formatted character */ - } /* if */ - break; - case FMT_WIDTH: - if (c>=__T('0') && c<=__T('9')) { - *width=*width*10+(int)(c-__T('0')); - } else if (c==__T('.') || c==__T(',')) { - *decpoint=c; - *digits=0; - *state=FMT_DECIM; - } else { - return 1; /* print formatted character */ - } /* if */ - break; - case FMT_DECIM: - if (c>=__T('0') && c<=__T('9')) { - *digits=*digits*10+(int)(c-__T('0')); - } else { - return 1; /* print formatted character */ - } /* if */ - break; - } /* switch */ - - return 0; -} - -int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) -{ - int i,paramidx=0; - int fmtstate=FMT_NONE,width,digits; - TCHAR sign,decpoint,filler; - int (*f_putstr)(void*,const TCHAR *); - int (*f_putchar)(void*,TCHAR); - void *user; - int skip,length; - - if (info!=NULL) { - f_putstr=info->f_putstr; - f_putchar=info->f_putchar; - user=info->user; - skip=info->skip; - length=info->length; - } else { - f_putstr=NULL; - f_putchar=NULL; - user=NULL; - skip=0; - length=INT_MAX; - } /* if */ - #if !defined AMX_STRING_LIB - if (f_putstr==NULL) - f_putstr=cons_putstr; - if (f_putchar==NULL) - f_putchar=cons_putchar; - #else - assert(f_putstr!=NULL && f_putchar!=NULL); - #endif - - /* if no placeholders appear, we can use a quicker routine */ - if (info==NULL || info->params==NULL) { - - TCHAR cache[100]; - int idx=0; - - if ((ucell)*cstr>UNPACKEDMAX) { - int j=sizeof(cell)-sizeof(char); - char c; - /* the string is packed */ - i=0; - for ( ; ; ) { - c=(char)((ucell)cstr[i] >> 8*j); - if (c==0) - break; - if (skip>0) { - skip--; /* skip a number of characters */ - } else { - if (length--<=0) - break; /* print up to a certain length */ - assert(idx0) - continue; - assert(idx0) { - cache[idx]=__T('\0'); - f_putstr(user,cache); - } /* if */ - - } else { - - /* check whether this is a packed string */ - if ((ucell)*cstr>UNPACKEDMAX) { - int j=sizeof(cell)-sizeof(char); - char c; - /* the string is packed */ - i=0; - for ( ; ; ) { - c=(char)((ucell)cstr[i] >> 8*j); - if (c==0) - break; - switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits,&filler)) { - case -1: - f_putchar(user,c); - break; - case 0: - break; - case 1: - assert(info!=NULL && info->params!=NULL); - if (paramidx>=info->numparams) /* insufficient parameters passed */ - amx_RaiseError(amx, AMX_ERR_NATIVE); - else - paramidx+=dochar(amx,c,info->params[paramidx],sign,decpoint,width,digits,filler, - f_putstr,f_putchar,user); - fmtstate=FMT_NONE; - break; - default: - assert(0); - } /* switch */ - if (j==0) - i++; - j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell); - } /* for */ - } else { - /* the string is unpacked */ - for (i=0; cstr[i]!=0; i++) { - switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits,&filler)) { - case -1: - f_putchar(user,(TCHAR)cstr[i]); - break; - case 0: - break; - case 1: - assert(info!=NULL && info->params!=NULL); - if (paramidx>=info->numparams) /* insufficient parameters passed */ - amx_RaiseError(amx, AMX_ERR_NATIVE); - else - paramidx+=dochar(amx,(TCHAR)cstr[i],info->params[paramidx],sign,decpoint,width,digits,filler, - f_putstr,f_putchar,user); - fmtstate=FMT_NONE; - break; - default: - assert(0); - } /* switch */ - } /* for */ - } /* if */ - - } /* if (info==NULL || info->params==NULL) */ - - return paramidx; -} - -#if !defined AMX_STRING_LIB - -#if defined AMX_ALTPRINT -/* print(const string[], start=0, end=cellmax) */ -static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) -{ - cell *cstr; - AMX_FMTINFO info; - - memset(&info,0,sizeof info); - info.skip= ((size_t)params[0]>=2*sizeof(cell)) ? (int)params[2] : 0; - info.length= ((size_t)params[0]>=3*sizeof(cell)) ? (int)(params[3]-info.skip) : INT_MAX; - - amx_GetAddr(amx,params[1],&cstr); - amx_printstring(amx,cstr,&info); - amx_fflush(); - return 0; -} -#else -/* print(const string[], foreground=-1, background=-1, highlight=-1) */ -static cell AMX_NATIVE_CALL n_print(AMX *amx, const cell *params) -{ - cell *cstr; - - amx_GetAddr(amx,params[1],&cstr); - amx_printstring(amx,cstr,NULL); - printf("\n"); - amx_fflush(); - return 0; -} -#endif - -static cell AMX_NATIVE_CALL n_printf(AMX *amx,const cell *params) -{ - cell *cstr; - AMX_FMTINFO info; - - memset(&info,0,sizeof info); - info.params=params+2; - info.numparams=(int)(params[0]/sizeof(cell))-1; - info.skip=0; - info.length=INT_MAX; - - amx_GetAddr(amx,params[1],&cstr); - amx_printstring(amx,cstr,&info); - printf("\n"); - amx_fflush(); - return 0; -} - -/* getchar(bool:echo=true) */ -static cell AMX_NATIVE_CALL n_getchar(AMX *amx,const cell *params) -{ - int c; - - (void)amx; - CreateConsole(); - c=amx_getch(); - if (params[1]) { - #if defined(SUPPRESS_ECHO) - /* For Mac OS X, non-Curses, don't echo the character */ - #else - amx_putchar((TCHAR)c); - amx_fflush(); - #endif - } /* if */ - return c; -} - -/* getstring(string[], size=sizeof string, bool:pack=false) */ -static cell AMX_NATIVE_CALL n_getstring(AMX *amx,const cell *params) -{ - int c,chars,max; - cell *cptr; - - CreateConsole(); - chars=0; - max=(int)params[2]; - if (max>0) { - #if __STDC_VERSION__ >= 199901L - TCHAR str[max]; /* use C99 feature if available */ - #else - TCHAR *str=(TCHAR *)alloca(max*sizeof(TCHAR)); - if (str==NULL) - return chars; - #endif - - c=amx_getch(); - while (c!=EOF && c!=EOL_CHAR && chars1,max); - - } /* if */ - return chars; -} - -static void acceptchar(int c,int *num) -{ - switch (c) { - case '\b': - amx_putchar('\b'); - *num-=1; - #if defined amx_putchar && (defined __BORLANDC__ || defined __WATCOMC__) - /* the backspace key does not erase the - * character, so do this explicitly */ - amx_putchar(' '); /* erase */ - amx_putchar('\b'); /* go back */ - #endif - break; - case EOL_CHAR: - amx_putchar('\n'); - *num+=1; - break; - default: - #if defined(SUPPRESS_ECHO) - /* For Mac OS X, non-Curses, don't echo the character */ - #else - amx_putchar((TCHAR)c); - #endif - *num+=1; - } /* switch */ - amx_fflush(); -} - -static int inlist(AMX *amx,int c,const cell *params,int num) -{ - int i, key; - - for (i=0; i36) - return 0; - - chars=0; - value=0; - sign=1; /* to avoid a compiler warning (Microsoft Visual C/C++ 6.0) */ - - c=amx_getch(); - while (c!=EOF) { - /* check for sign (if any) */ - if (chars==0) { - if (c=='-') { - sign=-1; - acceptchar(c,&chars); - c=amx_getch(); - } else { - sign=1; - } /* if */ - } /* if */ - - /* check end of input */ - #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS - if (c=='\n' && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0) - c='\r'; - #endif - if ((chars>1 || chars>0 && sign>0) - && (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0) - { - if (n>0) - acceptchar(c,&chars); - break; - } /* if */ - - /* get value */ - d=base; /* by default, do not accept the character */ - if (c>='0' && c<='9') { - d=c-'0'; - } else if (c>='a' && c<='z') { - d=c-'a'+10; - } else if (c>='A' && c<='Z') { - d=c-'A'+10; - } else if (c=='\b') { - if (chars>0) { - value/=base; - acceptchar(c,&chars); - } /* if */ - } /* if */ - if (d= 0); - - if (PrevIdle != NULL) - PrevIdle(amx, Exec); - - if (amx_kbhit()) { - key = amx_getch(); - amx_Push(amx, key); - err = Exec(amx, NULL, idxKeyPressed); - while (err == AMX_ERR_SLEEP) - err = Exec(amx, NULL, AMX_EXEC_CONT); - } /* if */ - - return err; -} -#endif - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO console_Natives[] = { - { "getchar", n_getchar }, - { "getstring", n_getstring }, - { "getvalue", n_getvalue }, - { "print", n_print }, - { "printf", n_printf }, - { "clrscr", n_clrscr }, - { "clreol", n_clreol }, - { "gotoxy", n_gotoxy }, - { "wherexy", n_wherexy }, - { "setattr", n_setattr }, - { "console", n_console }, - { "consctrl", n_consctrl }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_ConsoleInit(AMX *amx) -{ - #if !defined AMXCONSOLE_NOIDLE - /* see whether there is an @keypressed() function */ - if (amx_FindPublic(amx, "@keypressed", &idxKeyPressed) == AMX_ERR_NONE) { - if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE) - PrevIdle = NULL; - amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), (void*)amx_ConsoleIdle); - } /* if */ - #endif - - return amx_Register(amx, console_Natives, -1); -} - -int AMXEXPORT amx_ConsoleCleanup(AMX *amx) -{ - (void)amx; - #if !defined AMXCONSOLE_NOIDLE - PrevIdle = NULL; - #endif - return AMX_ERR_NONE; -} - -#endif /* AMX_STRING_LIB */ +/* Console output module (terminal I/O) for the Pawn AMX + * + * Since some of these routines go further than those of standard C, they + * cannot always be implemented with portable C functions. In other words, + * these routines must be ported to other environments. + * + * Copyright (c) ITB CompuPhase, 1997-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxcons.c 5587 2016-10-25 09:59:46Z $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #define HAVE_CONIO + #include + #include +#endif +#if defined USE_CURSES || defined HAVE_CURSES_H + #include + #if !defined CURSES + #define CURSES 1 + #endif +#endif +#include "osdefs.h" +#if defined __ECOS__ + /* eCos puts include files in cyg/package_name */ + #include + #include + #include + #include +#else + #include "amx.h" +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + #include +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _fgetts fgets +# define _puttchar putchar +# define _stprintf sprintf +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _tprintf printf +#endif +#include "amxcons.h" + +#if defined AMX_TERMINAL + #define EOL_CHAR '\r' +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #define EOL_CHAR '\r' +#endif +#if !defined EOL_CHAR + /* if not a "known" operating system, assume Linux/Unix */ + #define EOL_CHAR '\n' +#endif + +#if !defined AMX_STRING_LIB + +#if defined AMX_TERMINAL + /* required functions are implemented elsewhere */ + int amx_putstr(const TCHAR *); + int amx_putchar(int); + int amx_fflush(void); + int amx_getch(void); + TCHAR *amx_gets(TCHAR *,int); + int amx_termctl(int,int); + void amx_clrscr(void); + void amx_clreol(void); + int amx_gotoxy(int x,int y); + void amx_wherexy(int *x,int *y); + unsigned int amx_setattr(int foregr,int backgr,int highlight); + void amx_console(int columns, int lines, int flags); + void amx_viewsize(int *width,int *height); + int amx_kbhit(void); +#elif defined CURSES && CURSES != 0 + /* Use the "curses" library to implement the console */ + static WINDOW *curseswin; + #define amx_putstr(s) printw("%s",(s)) + #define amx_putchar(c) addch(c) + #define amx_fflush() refresh() + #define amx_getch() getch() + #define amx_gets(s,n) getnstr((s),(n)) + #define amx_clrscr() clear() + #define amx_clreol() clrtoeol() + #define amx_gotoxy(x,y) move((y)-1,(x)-1) + #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + if (highlight>0) + attron(A_STANDOUT); + else + attroff(A_STANDOUT); + //??? in future, also handle colours + } + void CreateConsole(void); + int amx_kbhit(void) + { + int result; + CreateConsole(); + nodelay(curseswin,TRUE); /* enter non-blocking state */ + result=getch(); /* read key (if any) */ + nodelay(curseswin,FALSE); /* leave non-blocking state */ + if (result!=ERR) + ungetch(result); /* a key is waiting, push it back */ + return (result==ERR) ? 0 : 1; + } + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + /* case 1: */ /* switch auto-wrap on/off (not supported in curses!) */ + /* case 2: */ /* create/switch to another console */ + case 3: /* set emphasized font */ + if (value) + attron(A_BOLD); + else + attroff(A_BOLD); + return 1; + /* case 4: */ /* query whether a terminal is "open" */ + default: + return 0; + } /* switch */ + } + void amx_wherexy(int *x,int *y) + { + int row,col; + getyx(curseswin,row,col); + if (x!=NULL) + *x=col+1; + if (y!=NULL) + *y=row+1; + } + void amx_viewsize(int *width,int *height) + { + int row,col; + getmaxyx(curseswin,row,col); + if (width!=NULL) + *width=col; + if (height!=NULL) + *height=row; + } +#elif defined VT100 || defined __LINUX__ || defined ANSITERM || defined __ECOS__ + /* ANSI/VT100 terminal, or shell emulating "xterm" */ + #if defined __ECOS__ + #define AMXCONSOLE_NOIDLE + #endif + + #if CYGPKG_PAWN_AMXCONSOLE_DIAG==1 + /* eCos has basically two ways to make simple exchanges with a terminal: + * - with the diag_*() functions (no input provided!) + * - with f*() functions (fprintf(),fputs(), etc). + */ + #define amx_fflush() + + static int amx_putstr(TCHAR *s) + { + diag_write_string(s); + return 1; + } + static int amx_putchar(TCHAR c) + { + diag_write_char(c); + return c; + } + static char amx_getch(void) + { + char c=-1; + HAL_DIAG_READ_CHAR(c); + return c; + } + #else + + #define amx_putstr(s) fputs((s),stdout) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_kbhit() kbhit() + #endif + + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + + case 1: /* switch "auto-wrap" on or off */ + if (value) + amx_putstr("\033[?7h"); /* enable "auto-wrap" */ + else + amx_putstr("\033[?7l"); /* disable "auto-wrap" */ + return 1; + + #if 0 + /* next to swapping buffers, more information should be saved and swapped, + * such as the cursor position and the current terminal attributes + */ + case 2: /* swap console buffers */ + amx_fflush(); + if (value==1) { + amx_putstr("\033[?47h"); + } else { + amx_putstr("\033[?47l"); + } /* if */ + amx_fflush(); + return 1; + #endif + + case 3: /* set bold/highlighted font */ + return 0; + + default: + return 0; + } /* switch */ + } + void amx_clrscr(void) + { + amx_putstr("\033[2J"); + amx_fflush(); /* pump through the terminal codes */ + } + void amx_clreol(void) + { + amx_putstr("\033[K"); + amx_fflush(); /* pump through the terminal codes */ + } + int amx_gotoxy(int x,int y) + { + char str[30]; + _stprintf(str,"\033[%d;%dH",y,x); + amx_putstr(str); + amx_fflush(); /* pump through the terminal codes */ + return 1; + } + void amx_wherexy(int *x,int *y) + { + int val,i; + char str[10]; + + assert(x!=NULL && y!=NULL); + amx_putstr("\033[6n"); + amx_fflush(); + while (amx_getch()!='\033') + /* nothing */; + val=amx_getch(); + assert(val=='['); + for (i=0; i<8 && (val=amx_getch())!=';'; i++) + str[i]=(char)val; + str[i]='\0'; + if (y!=NULL) + *y=atoi(str); + for (i=0; i<8 && (val=amx_getch())!='R'; i++) + str[i]=(char)val; + str[i]='\0'; + if (x!=NULL) + *x=atoi(str); + #if defined ANSITERM + val=amx_getch(); + assert(val=='\r'); /* ANSI driver adds CR to the end of the command */ + #endif + } + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + static short current=(0 << 8) | 7; + short prev = current; + char str[30]; + + if (foregr>=0) { + _stprintf(str,"\x1b[%dm",foregr+30); + amx_putstr(str); + current=(current & 0xff00) | (foregr & 0x0f); + } /* if */ + if (backgr>=0) { + _stprintf(str,"\x1b[%dm",backgr+40); + amx_putstr(str); + current=(current & 0x00ff) | ((backgr & 0x0f) << 8); + } /* if */ + if (highlight>=0) { + _stprintf(str,"\x1b[%dm",highlight); + amx_putstr(str); + current=(current & 0x7fff) | ((highlight & 0x01) << 15); + } /* if */ + return prev; + } + void amx_console(int columns, int lines, int flags) + { + char str[30]; + + (void)flags; + /* There is no ANSI code (or VT100/VT220) to set the size of the console + * (indeed, the terminal was that of the alphanumeric display). In xterm (a + * terminal emulator) we can set the terminal size though, and most + * terminals that in use today are in fact emulators. + * Putty understands this code too, by many others do not. + */ + sprintf(str,"\033[8;%d;%dt",lines,columns); + amx_putstr(str); + amx_fflush(); + } + void amx_viewsize(int *width,int *height) + { + /* a trick to get the size of the terminal is to position the cursor far + * away and then read it back + */ + amx_gotoxy(999,999); + amx_wherexy(width,height); + } +#elif defined __WIN32__ || defined _WIN32 || defined WIN32 + /* Win32 console */ + #define amx_putstr(s) _tprintf("%s",(s)) + #define amx_putchar(c) _puttchar(c) + #define amx_fflush() fflush(stdout) + #define amx_gets(s,n) _fgetts(s,n,stdin) + + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + + case 1: { /* switch auto-wrap on/off */ + /* only works in Windows 2000/XP */ + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + DWORD mode; + GetConsoleMode(hConsole,&mode); + if (value) + mode |= ENABLE_WRAP_AT_EOL_OUTPUT; + else + mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; + SetConsoleMode(hConsole,mode); + return 1; + } /* case */ + + /* case 2: */ /* create/switch to another console */ + /* case 3: */ /* set emphasized font */ + /* case 4: */ /* query whether a terminal is "open" */ + default: + return 0; + } /* switch */ + } + void amx_clrscr(void) + { + COORD coordScreen={0,0}; + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure the I/O buffer is empty */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + dwConSize=csbi.dwSize.X*csbi.dwSize.Y; + FillConsoleOutputCharacter(hConsole,' ',dwConSize,coordScreen,&cCharsWritten); + FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,coordScreen, &cCharsWritten); + SetConsoleCursorPosition(hConsole,coordScreen); + } + void amx_clreol(void) + { + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + dwConSize=csbi.dwSize.X - csbi.dwCursorPosition.X; + FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten); + FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten); + } + int amx_gotoxy(int x,int y) + { + COORD point; + CONSOLE_SCREEN_BUFFER_INFO csbi; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleScreenBufferInfo(hConsole, &csbi); + if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y) + return 0; + amx_fflush(); /* make sure all output is written */ + point.X=(short)(x-1); + point.Y=(short)(y-1); + SetConsoleCursorPosition(hConsole,point); + return 1; + } + void amx_wherexy(int *x,int *y) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + if (x!=NULL) + *x=csbi.dwCursorPosition.X+1; + if (y!=NULL) + *y=csbi.dwCursorPosition.Y+1; + } + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + static int ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int f,b,h,prev; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + f=csbi.wAttributes & 0x07; + b=(csbi.wAttributes >> 4) & 0x0f; + h=(csbi.wAttributes & 0x08) ? 1 : 0; + prev=(b << 8) | f | (h << 15); + if (foregr>=0 && foregr<8) + f=ansi_colours[foregr]; + if (backgr>=0 && backgr<8) + b=ansi_colours[backgr]; + if (highlight>=0) + h=highlight!=0; + SetConsoleTextAttribute(hConsole, (WORD)((b << 4) | f | (h << 3))); + return prev; + } + void amx_console(int columns, int lines, int flags) + { + SMALL_RECT rect; + COORD dwSize; + HANDLE hConsole; + (void)flags; + dwSize.X=(short)columns; + dwSize.Y=(short)lines; + hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleScreenBufferSize(hConsole,dwSize); + rect.Left=0; + rect.Top=0; + rect.Right=(short)(columns-1); + rect.Bottom=(short)(lines-1); + SetConsoleWindowInfo(hConsole,TRUE,&rect); + } + void amx_viewsize(int *width,int *height) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi); + if (width!=NULL) + *width=(int)csbi.dwSize.X; + if (height!=NULL) + *height=(int)(csbi.srWindow.Bottom-csbi.srWindow.Top+1); + } + int amx_getch(void) + { + TCHAR ch; + DWORD count,mode; + HANDLE hConsole=GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hConsole,&mode); + SetConsoleMode(hConsole,mode & ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT)); + while (ReadFile(hConsole,&ch,1,&count,NULL) && count==0) + /* nothing */; + SetConsoleMode(hConsole,mode); + if (count>0) + return ch; + return EOF; + } + int amx_kbhit(void) + { + DWORD count=0; + HANDLE hConsole; + hConsole=GetStdHandle(STD_INPUT_HANDLE); + if (GetFileType(hConsole)==FILE_TYPE_PIPE) { + PeekNamedPipe(hConsole,NULL,0,NULL,&count,NULL); + } else { + INPUT_RECORD rec; + while (PeekConsoleInput(hConsole,&rec,1,&count)) { + if (count==0 || (rec.EventType==KEY_EVENT && rec.Event.KeyEvent.bKeyDown)) + break; + ReadConsoleInput(hConsole,&rec,1,&count); + } + } + return (count>0); + } +#else + /* assume a streaming terminal; limited features (no colour, no cursor + * control) + */ + #define amx_putstr(s) printf("%s",(s)) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_clrscr() (void)(0) + #define amx_clreol() (void)(0) + #define amx_gotoxy(x,y) ((void)(x),(void)(y),(0)) + #define amx_wherexy(x,y) (*(x)=*(y)=0) + #define amx_setattr(c,b,h) ((void)(c),(void)(b),(void)(h),(0)) + #define amx_termctl(c,v) ((void)(c),(void)(v),(0)) + #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) + #define amx_viewsize (*(x)=80,*(y)=25) + #if defined HAVE_CONIO + #define amx_getch() getch() + #define amx_kbhit() kbhit() + #else + #define amx_getch() getchar() + #define amx_kbhit() (0) + #endif +#endif + +#if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32) + void CreateConsole(void) + { static int createdconsole=0; + if (!createdconsole) { + AllocConsole(); + createdconsole=1; + } /* if */ + } +#elif defined CURSES && CURSES != 0 + // The Mac OS X build variant uses curses. + void CreateConsole(void) + { static int createdconsole=0; + if (!createdconsole) { + curseswin=initscr(); + if (has_colors()) + start_color(); + cbreak(); + noecho(); + nonl(); + scrollok(curseswin,TRUE); + intrflush(curseswin,FALSE); + keypad(curseswin,TRUE); + createdconsole=1; + } /* if */ + } +#else + #define CreateConsole() +#endif + +static int cons_putstr(void *dest,const TCHAR *str) +{ + (void)dest; + return amx_putstr(str); +} + +static int cons_putchar(void *dest,TCHAR ch) +{ + (void)dest; + return amx_putchar(ch); +} + +#endif /* AMX_STRING_LIB */ + +enum { + SV_DECIMAL, + SV_HEX, + SV_BIN +}; + +static TCHAR *reverse(TCHAR *string,int stop) +{ + int start=0; + TCHAR temp; + + /* swap the string */ + stop--; /* avoid swapping the '\0' byte to the first position */ + while (stop - start > 0) { + temp = string[start]; + string[start] = string[stop]; + string[stop] = temp; + start++; + stop--; + } /* while */ + return string; +} + +/* Converts an integral value to a string, with optional padding with spaces or + * zeros. + * The "format" must be decimal or hexadecimal + * The number is right-aligned in the field with the size of the absolute value + * of the "width" parameter. + * If the width value is positive, the string is padded with spaces; if it is + * negative, it is padded with zeros. + */ +static TCHAR *amx_strval(TCHAR buffer[], long value, int format, int width) +{ + int start, stop; + TCHAR temp; + + start = stop = 0; + if (format == SV_DECIMAL) { + if (value < 0) { + buffer[0] = __T('-'); + start = stop = 1; + do { + temp = (TCHAR)(value % 10); + if (temp > 0) + temp = (TCHAR)(temp - 10); + buffer[stop++] = (TCHAR)(__T('0') - temp); + value /= 10; + } while (value != 0); + } else { + do { + buffer[stop++] = (TCHAR)((value % 10) + __T('0')); + value /= 10; + } while (value != 0); + } + } else if (format == SV_HEX) { + /* hexadecimal */ + unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */ + do { + buffer[stop] = (TCHAR)((v & 0x0f) + __T('0')); + if (buffer[stop] > __T('9')) + buffer[stop] += (TCHAR)(__T('A') - __T('0') - 10); + v >>= 4; + stop++; + } while (v != 0); + } else { + /* binary */ + unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */ + do { + buffer[stop++] = (TCHAR)((v & 0x01) + __T('0')); + v >>= 1; + } while (v != 0); + } /* if */ + + /* pad to given width */ + if (width < 0) { + temp = __T('0'); + width = -width; + } else { + temp = __T(' '); + } /* if */ + while (stop < width) + buffer[stop++] = temp; + + buffer[stop] = __T('\0'); + + /* swap the string, and we are done */ + reverse(buffer+start,stop-start); + return buffer; +} + +#if defined FIXEDPOINT + #define FIXEDMULT 1000 + #define FIXEDDIGITS 3 + +static TCHAR *formatfixed(TCHAR *string,cell value,TCHAR align,int width,TCHAR decpoint,int digits,TCHAR filler) +{ + int i, len; + cell ipart,v; + TCHAR vsign=__T('\0'); + + /* make the value positive (but keep the sign) */ + if (value<0) { + value=-value; + vsign=__T('-'); + } /* if */ + + /* "prepare" the value so that when it is truncated to the requested + * number of digits, the result is rounded towards the dropped digits + */ + assert(digits=0); + assert(value>=0); + + /* truncate the fractional part to the requested number of digits */ + for (i=FIXEDDIGITS; i>digits; i--) + value/=10; + + string[0]=__T('\0'); + + /* add sign */ + i=(int)_tcslen(string); + string[i]=vsign; + string[i+1]=__T('\0'); + + /* add integer part */ + amx_strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0); + + /* add fractional part */ + if (digits>0) { + i=(int)_tcslen(string); + string[i]=decpoint; + amx_strval(string+i+1,(long)value,SV_DECIMAL,-digits); + } /* if */ + + len=(int)_tcslen(string); + if (len=len); + reverse(string,len); + reverse(string+len,i-len); + reverse(string,i); + } /* if */ + } /* if */ + + return string; +} +#endif + + +static int dochar(AMX *amx,TCHAR ch,const cell* params,int paramidx,TCHAR sign,TCHAR decpoint,int width,int digits,TCHAR filler, + int (*f_putstr)(void*,const TCHAR *),int (*f_putchar)(void*,TCHAR),void *user) +{ + cell *cptr; + int ret = 1; + TCHAR buffer[40]; + #if defined FLOATPOINT + TCHAR formatstring[40]; + #endif + + #if !defined FIXEDPOINT && !defined FLOATPOINT + (void)decpoint; + #endif + assert(f_putstr!=NULL); + assert(f_putchar!=NULL); + if (width < 0) { + width = *amx_Address(amx, params[paramidx]); + ++paramidx; + ++ret; + } + if (digits < 0) { + digits = *amx_Address(amx, params[paramidx]); + ++paramidx; + ++ret; + } + + switch (ch) { + case __T('c'): + cptr=amx_Address(amx, params[paramidx]); + width--; /* single character itself has a with of 1 */ + if (sign!=__T('-')) + while (width-->0) + f_putchar(user,filler); + f_putchar(user,(TCHAR)*cptr); + while (width-->0) + f_putchar(user,filler); + return ret; + + case __T('d'): + case __T('i'): { + cell value; + int length=1; + cptr=amx_Address(amx, params[paramidx]); + value=*cptr; + if (value<0 || sign==__T('+')) + length++; + if (value<0) + value=-value; + while (value>=10) { + length++; + value/=10; + } /* while */ + width-=length; + if (sign!=__T('-')) + while (width-->0) + f_putchar(user,filler); + amx_strval(buffer,*cptr,SV_DECIMAL,0); + if (sign==__T('+') && *cptr>=0) + f_putchar(user,sign); + f_putstr(user,buffer); + while (width-->0) + f_putchar(user,filler); + return ret; + } /* case */ + +#if defined FLOATPOINT + case __T('f'): /* 32-bit floating point number */ + case __T('r'): /* if floating point is enabled, %r == %f */ + /* build a format string */ + if (digits==INT_MAX) + digits=6; + else if (digits>25) + digits=25; + _tcscpy(formatstring,__T("%")); + if (sign!=__T('\0')) + _stprintf(formatstring+_tcslen(formatstring),__T("%c"),sign); + if (width>0) + _stprintf(formatstring+_tcslen(formatstring),__T("%d"),width); + _stprintf(formatstring+_tcslen(formatstring),__T(".%df"),digits); + cptr=amx_Address(amx, params[paramidx]); + #if PAWN_CELL_SIZE == 64 + _stprintf(buffer,formatstring,*(double*)cptr); + #else + _stprintf(buffer,formatstring,*(float*)cptr); + #endif + if (decpoint==__T(',')) { + TCHAR *ptr=_tcschr(buffer,__T('.')); + if (ptr!=NULL) + *ptr=__T(','); + } /* if */ + f_putstr(user,buffer); + return ret; +#endif + +#if defined FIXEDPOINT + #define FIXEDMULT 1000 + case __T('q'): /* 32-bit fixed point number */ +#if !defined FLOATPOINT + case __T('r'): /* if fixed point is enabled, and floating point is not, %r == %q */ +#endif + cptr=amx_Address(amx, params[paramidx]); + /* format the number */ + if (digits==INT_MAX) + digits=3; + else if (digits>25) + digits=25; + formatfixed(buffer,*cptr,sign,width,decpoint,digits,filler); + assert(_tcslen(buffer)=0x10) { + length++; + value>>=4; + } /* while */ + width-=length; + if (sign!=__T('-')) + while (width-->0) + f_putchar(user,filler); + amx_strval(buffer,(long)*cptr,SV_HEX,0); + f_putstr(user,buffer); + while (width-->0) + f_putchar(user,filler); + return ret; + } /* case */ + + case __T('b'): { + ucell value; + int length = 1; + cptr = amx_Address(amx, params[paramidx]); + value = *(ucell*)cptr; + while (value >= 0x1) { + length++; + value >>= 1; + } /* while */ + width -= length; + if (sign != __T('-')) + while (width-->0) + f_putchar(user, filler); + amx_strval(buffer, (long)*cptr, SV_BIN, 0); + f_putstr(user, buffer); + while (width-->0) + f_putchar(user, filler); + return ret; + } /* case */ + + } /* switch */ + /* error in the string format, try to repair */ + f_putchar(user,ch); + return 0; +} + +enum { + FMT_NONE = 0, /* not in format state; accept '%' */ + FMT_START_D = 1, /* found '%', accept '+', '-' (START), '0' (filler; START), digit (WIDTH), '.' (DECIM), or '%' or format letter (done) */ + FMT_WIDTH_D = 2, /* found digit after '%' or sign, accept digit (WIDTH), '.' (DECIM) or format letter (done) */ + FMT_DECIM_D = 3, /* found digit after '.', accept accept digit (DECIM) or format letter (done) */ + FMT_START = 4, /* above, without $ */ + FMT_WIDTH = 5, /* above, without $ */ + FMT_DECIM = 6, /* above, without $ */ +}; + +static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width,int *digits,int *paramidx,TCHAR *filler) +{ + assert(state!=NULL && sign!=NULL && decpoint!=NULL && width!=NULL && digits!=NULL && filler!=NULL); + switch (*state) { + case FMT_NONE: + if (c==__T('%')) { + *state=FMT_START; + *sign=__T('\0'); + *decpoint=__T('.'); + *width=0; + *digits=INT_MAX; + *filler=__T(' '); + } else { + return -1; /* print a single character */ + } /* if */ + break; + case FMT_START: + if (c == __T('$')) { + *paramidx=0; + *filler=__T(' '); + *state=FMT_START_D; + return 0; + } + /* fallthrough */ + case FMT_START_D: + if (c==__T('+') || c==__T('-')) { + *sign=c; + } else if (c==__T('0')) { + *filler=c; + } else if (c>=__T('1') && c<=__T('9')) { + *width=(int)(c-__T('0')); + *state+=1; /* FMT_WIDTH */ + } else if (c == __T('*')) { + *width = -1; + *state+=1; /* FMT_WIDTH */ + } else if (c==__T('.') || c==__T(',')) { + *decpoint=c; + *digits=0; + *state+=2; /* FMT_DECIM */ + } else if (c==__T('%')) { + *state=FMT_NONE; + return -1; /* print literal '%' */ + } else { + return 1; /* print formatted character */ + } /* if */ + break; + case FMT_WIDTH: + if (*width < 0) { + return 1; + } else if (c == __T('$')) { + *paramidx=*width; + *width=0; + *state = FMT_WIDTH_D; + return 0; + } + /* fallthrough */ + case FMT_WIDTH_D: + if (c==__T('.') || c==__T(',')) { + *decpoint=c; + *digits=0; + *state+=1; /* FMT_DECIM */ + } else if (*width < 0) { + return 1; + } else if (c >= __T('0') && c <= __T('9')) { + *width=*width*10+(int)(c-__T('0')); + } else { + return 1; /* print formatted character */ + } /* if */ + break; + case FMT_DECIM: + case FMT_DECIM_D: + if (*digits < 0) { + return 1; + } else if (c >= __T('0') && c <= __T('9')) { + *digits = *digits * 10 + (int)(c - __T('0')); + } else if (c == __T('*')) { + *digits = -1; + } else { + return 1; /* print formatted character */ + } /* if */ + break; + } /* switch */ + + return 0; +} + +int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) +{ + int i,paramidx=0,thisparam=0; + int fmtstate=FMT_NONE,width,digits; + TCHAR sign,decpoint,filler; + int (*f_putstr)(void*,const TCHAR *); + int (*f_putchar)(void*,TCHAR); + void *user; + int skip,length; + + if (info!=NULL) { + f_putstr=info->f_putstr; + f_putchar=info->f_putchar; + user=info->user; + skip=info->skip; + length=info->length; + } else { + f_putstr=NULL; + f_putchar=NULL; + user=NULL; + skip=0; + length=INT_MAX; + } /* if */ + #if !defined AMX_STRING_LIB + if (f_putstr==NULL) + f_putstr=cons_putstr; + if (f_putchar==NULL) + f_putchar=cons_putchar; + #else + assert(f_putstr!=NULL && f_putchar!=NULL); + #endif + + /* if no placeholders appear, we can use a quicker routine */ + if (info==NULL || info->params==NULL) { + + TCHAR cache[100]; + int idx=0; + + if ((ucell)*cstr>UNPACKEDMAX) { + int j=sizeof(cell)-sizeof(char); + /* the string is packed */ + i=0; + for ( ; ; ) { + char c=(char)((ucell)cstr[i] >> 8*j); + if (c==0) + break; + if (skip>0) { + skip--; /* skip a number of characters */ + } else { + if (length--<=0) + break; /* print up to a certain length */ + assert(idx0) + continue; + assert(idx0) { + cache[idx]=__T('\0'); + f_putstr(user,cache); + } /* if */ + + } else { + + /* check whether this is a packed string */ + if ((ucell)*cstr>UNPACKEDMAX) { + int j=sizeof(cell)-sizeof(char); + /* the string is packed */ + i=0; + for ( ; ; ) { + char c=(char)((ucell)cstr[i] >> 8*j); + if (c==0) + break; + switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits,&thisparam,&filler)) { + case -1: + f_putchar(user,c); + break; + case 0: + break; + case 1: + assert(info!=NULL && info->params!=NULL); + if (paramidx>=info->numparams) /* insufficient parameters passed */ + amx_RaiseError(amx, AMX_ERR_NATIVE); + else + thisparam=dochar(amx,c,info->params,thisparam,sign,decpoint,width,digits,filler, + f_putstr,f_putchar,user); + if (fmtstate & 4) + paramidx += thisparam; + thisparam = paramidx; + fmtstate=FMT_NONE; + break; + default: + assert(0); + } /* switch */ + if (j==0) + i++; + j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell); + } /* for */ + } else { + /* the string is unpacked */ + for (i=0; cstr[i]!=0; i++) { + switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits,&thisparam,&filler)) { + case -1: + f_putchar(user,(TCHAR)cstr[i]); + break; + case 0: + break; + case 1: + assert(info!=NULL && info->params!=NULL); + if (paramidx>=info->numparams) /* insufficient parameters passed */ + amx_RaiseError(amx, AMX_ERR_NATIVE); + else + thisparam=dochar(amx,(TCHAR)cstr[i],info->params,thisparam,sign,decpoint,width,digits,filler, + f_putstr,f_putchar,user); + if (fmtstate & 4) + paramidx += thisparam; + thisparam = paramidx; + fmtstate = FMT_NONE; + break; + default: + assert(0); + } /* switch */ + } /* for */ + } /* if */ + + } /* if (info==NULL || info->params==NULL) */ + + return paramidx; +} + +#if !defined AMX_STRING_LIB + +#if defined AMX_ALTPRINT +/* print(const string[], start=0, end=cellmax) */ +static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) +{ + cell *cstr; + AMX_FMTINFO info; + + memset(&info,0,sizeof info); + info.skip= ((size_t)params[0]>=2*sizeof(cell)) ? (int)params[2] : 0; + info.length= ((size_t)params[0]>=3*sizeof(cell)) ? (int)(params[3]-info.skip) : INT_MAX; + + CreateConsole(); + cstr=amx_Address(amx,params[1]); + amx_printstring(amx,cstr,&info); + amx_fflush(); + return 0; +} +#else +/* print(const string[], foreground=-1, background=-1, highlight=-1) */ +static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) +{ + cell *cstr; + int oldcolours; + + CreateConsole(); + + /* set the new colours */ + oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]); + + cstr=amx_Address(amx,params[1]); + amx_printstring(amx,cstr,NULL); + cons_putchar(NULL, __T('\n')); + + /* reset the colours */ + (void)amx_setattr(oldcolours & 0xff,(oldcolours >> 8) & 0x7f,(oldcolours >> 15) & 0x01); + amx_fflush(); + return 0; +} +#endif + +static cell AMX_NATIVE_CALL n_printf(AMX *amx,const cell *params) +{ + cell *cstr; + AMX_FMTINFO info; + + memset(&info,0,sizeof info); + info.params=params+2; + info.numparams=(int)(params[0]/sizeof(cell))-1; + info.skip=0; + info.length=INT_MAX; + + CreateConsole(); + cstr=amx_Address(amx,params[1]); + amx_printstring(amx,cstr,&info); + cons_putchar(NULL, __T('\n')); + amx_fflush(); + return 0; +} + +/* getchar(bool:echo=true) */ +static cell AMX_NATIVE_CALL n_getchar(AMX *amx,const cell *params) +{ + int c; + + (void)amx; + CreateConsole(); + c=amx_getch(); + if (params[1]) { + #if defined(SUPPRESS_ECHO) + /* For Mac OS X, non-Curses, don't echo the character */ + #else + amx_putchar((TCHAR)c); + amx_fflush(); + #endif + } /* if */ + return c; +} + +/* getstring(string[], size=sizeof string, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_getstring(AMX *amx,const cell *params) +{ + int c,chars,max; + cell *cptr; + + (void)amx; + CreateConsole(); + chars=0; + max=(int)params[2]; + if (max>0) { + #if __STDC_VERSION__ >= 199901L + TCHAR str[max]; /* use C99 feature if available */ + #else + TCHAR *str=(TCHAR *)alloca(max*sizeof(TCHAR)); + if (str==NULL) + return chars; + #endif + + c=amx_getch(); + while (c!=EOF && c!=EOL_CHAR && chars1,max); + + } /* if */ + return chars; +} + +static void acceptchar(int c,int *num) +{ + switch (c) { + case '\b': + amx_putchar('\b'); + *num-=1; + #if defined amx_putchar && (defined __BORLANDC__ || defined __WATCOMC__) + /* the backspace key does not erase the + * character, so do this explicitly */ + amx_putchar(' '); /* erase */ + amx_putchar('\b'); /* go back */ + #endif + break; + case EOL_CHAR: + amx_putchar('\n'); + *num+=1; + break; + default: + #if defined(SUPPRESS_ECHO) + /* For Mac OS X, non-Curses, don't echo the character */ + #else + amx_putchar((TCHAR)c); + #endif + *num+=1; + } /* switch */ + amx_fflush(); +} + +static int inlist(AMX *amx,int c,const cell *params,int num) +{ + int i, key; + + (void)amx; + for (i=0; i36) + return 0; + + chars=0; + value=0; + sign=1; /* to avoid a compiler warning (Microsoft Visual C/C++ 6.0) */ + + c=amx_getch(); + while (c!=EOF) { + /* check for sign (if any) */ + if (chars==0) { + if (c=='-') { + sign=-1; + acceptchar(c,&chars); + c=amx_getch(); + } else { + sign=1; + } /* if */ + } /* if */ + + /* check end of input */ + #if EOL_CHAR!='\r' + if (c==EOL_CHAR && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0) + c='\r'; + #endif + if ((chars>1 || (chars>0 && sign>0)) + && (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0) + { + if (n>0) + acceptchar(c,&chars); + break; + } /* if */ + #if EOL_CHAR!='\r' + if (c=='\r') + c=EOL_CHAR; + #endif + + /* get value */ + d=base; /* by default, do not accept the character */ + if (c>='0' && c<='9') { + d=c-'0'; + } else if (c>='a' && c<='z') { + d=c-'a'+10; + } else if (c>='A' && c<='Z') { + d=c-'A'+10; + } else if (c=='\b') { + if (chars>0) { + value/=base; + acceptchar(c,&chars); + } /* if */ + } /* if */ + if (d= 0); + + if (PrevIdle != NULL) + PrevIdle(amx, Exec); + + if (amx_kbhit()) { + key = amx_getch(); + amx_Push(amx, key); + err = Exec(amx, NULL, idxKeyPressed); + while (err == AMX_ERR_SLEEP) + err = Exec(amx, NULL, AMX_EXEC_CONT); + } /* if */ + + return err; +} +#endif + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO console_Natives[] = { + { "getchar", n_getchar }, + { "getstring", n_getstring }, + { "getvalue", n_getvalue }, + { "print", n_print }, + { "printf", n_printf }, + { "clrscr", n_clrscr }, + { "clreol", n_clreol }, + { "gotoxy", n_gotoxy }, + { "wherexy", n_wherexy }, + { "setattr", n_setattr }, + { "console", n_console }, + { "consctrl", n_consctrl }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_ConsoleInit(AMX *amx) +{ + #if !defined AMXCONSOLE_NOIDLE + /* see whether there is an @keypressed() function */ + if (amx_FindPublic(amx, "@keypressed", &idxKeyPressed) == AMX_ERR_NONE) { + if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE) + PrevIdle = NULL; + amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), (void*)amx_ConsoleIdle); + } /* if */ + #endif + + return amx_Register(amx, console_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_ConsoleCleanup(AMX *amx) +{ + (void)amx; + #if !defined AMXCONSOLE_NOIDLE + PrevIdle = NULL; + #endif + return AMX_ERR_NONE; +} + +#endif /* AMX_STRING_LIB */ diff --git a/amx-deps/src/amx/amxcons.h b/amx-deps/src/amx/amxcons.h index 6775bf0..f0c0def 100644 --- a/amx-deps/src/amx/amxcons.h +++ b/amx-deps/src/amx/amxcons.h @@ -1,17 +1,17 @@ -#ifndef AMXCONS_H_INCLUDED -#define AMXCONS_H_INCLUDED - -typedef struct tagFMTINFO { - const cell *params; - int numparams; - int skip; /* number of characters to skip from the beginning */ - int length; /* number of characters to print */ - /* helper functions */ - int (*f_putstr)(void *dest,const TCHAR *); - int (*f_putchar)(void *dest,TCHAR); - void *user; /* user data */ -} AMX_FMTINFO; - -int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info); - -#endif /* AMXCONS_H_INCLUDED */ +#ifndef AMXCONS_H_INCLUDED +#define AMXCONS_H_INCLUDED + +typedef struct tagFMTINFO { + const cell *params; + int numparams; + int skip; /* number of characters to skip from the beginning */ + int length; /* number of characters to print */ + /* helper functions */ + int (*f_putstr)(void *dest,const TCHAR *); + int (*f_putchar)(void *dest,TCHAR); + void *user; /* user data */ +} AMX_FMTINFO; + +int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info); + +#endif /* AMXCONS_H_INCLUDED */ diff --git a/amx-deps/src/amx/amxcore.c b/amx-deps/src/amx/amxcore.c index 9a6efc6..488b99b 100644 --- a/amx-deps/src/amx/amxcore.c +++ b/amx-deps/src/amx/amxcore.c @@ -1,521 +1,501 @@ -/* Core module for the Pawn AMX - * - * Copyright (c) ITB CompuPhase, 1997-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxcore.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include -#include -#include -#include "osdefs.h" -#if defined __ECOS__ - /* eCos puts include files in cyg/package_name */ - #include - #define stricmp(a,b) strcasecmp(a,b) -#else - #include "amx.h" -#endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - #include -#endif - -/* A few compilers do not provide the ANSI C standard "time" functions */ -#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__ - #include -#endif - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -#endif - - -#define CHARBITS (8*sizeof(char)) -typedef unsigned char uchar; - -#if !defined AMX_NOPROPLIST -typedef struct _property_list { - struct _property_list *next; - cell id; - char *name; - cell value; -} proplist; - -static proplist proproot = { NULL, 0, NULL, 0 }; - -static proplist *list_additem(proplist *root) -{ - proplist *item; - - assert(root!=NULL); - if ((item=(proplist *)malloc(sizeof(proplist)))==NULL) - return NULL; - item->name=NULL; - item->id=0; - item->value=0; - item->next=root->next; - root->next=item; - return item; -} -static void list_delete(proplist *pred,proplist *item) -{ - assert(pred!=NULL); - assert(item!=NULL); - pred->next=item->next; - assert(item->name!=NULL); - free(item->name); - free(item); -} -static void list_setitem(proplist *item,cell id,char *name,cell value) -{ - char *ptr; - - assert(item!=NULL); - if ((ptr=(char *)malloc(strlen(name)+1))==NULL) - return; - if (item->name!=NULL) - free(item->name); - strcpy(ptr,name); - item->name=ptr; - item->id=id; - item->value=value; -} -static proplist *list_finditem(proplist *root,cell id,char *name,cell value, - proplist **pred) -{ - proplist *item=root->next; - proplist *prev=root; - - /* check whether to find by name or by value */ - assert(name!=NULL); - if (strlen(name)>0) { - /* find by name */ - while (item!=NULL && (item->id!=id || stricmp(item->name,name)!=0)) { - prev=item; - item=item->next; - } /* while */ - } else { - /* find by value */ - while (item!=NULL && (item->id!=id || item->value!=value)) { - prev=item; - item=item->next; - } /* while */ - } /* if */ - if (pred!=NULL) - *pred=prev; - return item; -} -#endif - -static cell AMX_NATIVE_CALL numargs(AMX *amx,const cell *params) -{ - AMX_HEADER *hdr; - uchar *data; - cell bytes; - - (void)params; - hdr=(AMX_HEADER *)amx->base; - data=amx->data ? amx->data : amx->base+(int)hdr->dat; - /* the number of bytes is on the stack, at "frm + 2*cell" */ - bytes= * (cell *)(data+(int)amx->frm+2*sizeof(cell)); - /* the number of arguments is the number of bytes divided - * by the size of a cell */ - return bytes/sizeof(cell); -} - -static cell AMX_NATIVE_CALL getarg(AMX *amx,const cell *params) -{ - AMX_HEADER *hdr; - uchar *data; - cell value; - - hdr=(AMX_HEADER *)amx->base; - data=amx->data ? amx->data : amx->base+(int)hdr->dat; - /* get the base value */ - value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); - /* adjust the address in "value" in case of an array access */ - value+=params[2]*sizeof(cell); - /* get the value indirectly */ - value= * (cell *)(data+(int)value); - return value; -} - -static cell AMX_NATIVE_CALL setarg(AMX *amx,const cell *params) -{ - AMX_HEADER *hdr; - uchar *data; - cell value; - - hdr=(AMX_HEADER *)amx->base; - data=amx->data ? amx->data : amx->base+(int)hdr->dat; - /* get the base value */ - value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); - /* adjust the address in "value" in case of an array access */ - value+=params[2]*sizeof(cell); - /* verify the address */ - if (value<0 || value>=amx->hea && valuestk) - return 0; - /* set the value indirectly */ - * (cell *)(data+(int)value) = params[3]; - return 1; -} - -static cell AMX_NATIVE_CALL heapspace(AMX *amx,const cell *params) -{ - (void)params; - return amx->stk - amx->hea; -} - -static cell AMX_NATIVE_CALL funcidx(AMX *amx,const cell *params) -{ - char name[64]; - cell *cstr; - int index,err,len; - - amx_GetAddr(amx,params[1],&cstr); - - /* verify string length */ - amx_StrLen(cstr,&len); - if (len>=64) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; - } /* if */ - - amx_GetString(name,cstr,0,UNLIMITED); - err=amx_FindPublic(amx,name,&index); - if (err!=AMX_ERR_NONE) - index=-1; /* this is not considered a fatal error */ - return index; -} - -void amx_swapcell(cell *pc) -{ - union { - cell c; - #if PAWN_CELL_SIZE==16 - uchar b[2]; - #elif PAWN_CELL_SIZE==32 - uchar b[4]; - #elif PAWN_CELL_SIZE==64 - uchar b[8]; - #else - #error Unsupported cell size - #endif - } value; - uchar t; - - assert(pc!=NULL); - value.c = *pc; - #if PAWN_CELL_SIZE==16 - t = value.b[0]; - value.b[0] = value.b[1]; - value.b[1] = t; - #elif PAWN_CELL_SIZE==32 - t = value.b[0]; - value.b[0] = value.b[3]; - value.b[3] = t; - t = value.b[1]; - value.b[1] = value.b[2]; - value.b[2] = t; - #elif PAWN_CELL_SIZE==64 - t = value.b[0]; - value.b[0] = value.b[7]; - value.b[7] = t; - t = value.b[1]; - value.b[1] = value.b[6]; - value.b[6] = t; - t = value.b[2]; - value.b[2] = value.b[5]; - value.b[5] = t; - t = value.b[3]; - value.b[3] = value.b[4]; - value.b[4] = t; - #else - #error Unsupported cell size - #endif - *pc = value.c; -} - -static cell AMX_NATIVE_CALL swapchars(AMX *amx,const cell *params) -{ - cell c; - - (void)amx; - assert((size_t)params[0]==sizeof(cell)); - - c=params[1]; - amx_swapcell(&c); - return c; -} - -static cell AMX_NATIVE_CALL core_tolower(AMX *amx,const cell *params) -{ - (void)amx; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - return (cell)CharLower((LPTSTR)params[1]); - #elif defined _Windows - return (cell)AnsiLower((LPSTR)params[1]); - #else - if ((unsigned)(params[1]-'A')<26u) - return params[1]+'a'-'A'; - return params[1]; - #endif -} - -static cell AMX_NATIVE_CALL core_toupper(AMX *amx,const cell *params) -{ - (void)amx; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - return (cell)CharUpper((LPTSTR)params[1]); - #elif defined _Windows - return (cell)AnsiUpper((LPSTR)params[1]); - #else - if ((unsigned)(params[1]-'a')<26u) - return params[1]+'A'-'a'; - return params[1]; - #endif -} - -static cell AMX_NATIVE_CALL core_min(AMX *amx,const cell *params) -{ - (void)amx; - return params[1] <= params[2] ? params[1] : params[2]; -} - -static cell AMX_NATIVE_CALL core_max(AMX *amx,const cell *params) -{ - (void)amx; - return params[1] >= params[2] ? params[1] : params[2]; -} - -static cell AMX_NATIVE_CALL core_clamp(AMX *amx,const cell *params) -{ - cell value = params[1]; - if (params[2] > params[3]) /* minimum value > maximum value ! */ - amx_RaiseError(amx,AMX_ERR_NATIVE); - if (value < params[2]) - value = params[2]; - else if (value > params[3]) - value = params[3]; - return value; -} - -#if !defined AMX_NOPROPLIST -static char *MakePackedString(cell *cptr) -{ - int len; - char *dest; - - amx_StrLen(cptr,&len); - dest=(char *)malloc(len+sizeof(cell)); - amx_GetString(dest,cptr,0,UNLIMITED); - return dest; -} - -static int verify_addr(AMX *amx,cell addr) -{ - int err; - cell *cdest; - - err=amx_GetAddr(amx,addr,&cdest); - if (err!=AMX_ERR_NONE) - amx_RaiseError(amx,err); - return err; -} - -static cell AMX_NATIVE_CALL getproperty(AMX *amx,const cell *params) -{ - cell *cstr; - char *name; - proplist *item; - - amx_GetAddr(amx,params[2],&cstr); - name=MakePackedString(cstr); - item=list_finditem(&proproot,params[1],name,params[3],NULL); - /* if list_finditem() found the value, store the name */ - if (item!=NULL && item->value==params[3] && strlen(name)==0) { - int needed=(strlen(item->name)+sizeof(cell)-1)/sizeof(cell); /* # of cells needed */ - if (verify_addr(amx,(cell)(params[4]+needed))!=AMX_ERR_NONE) { - free(name); - return 0; - } /* if */ - amx_GetAddr(amx,params[4],&cstr); - amx_SetString(cstr,item->name,1,0,UNLIMITED); - } /* if */ - free(name); - return (item!=NULL) ? item->value : 0; -} - -static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) -{ - cell prev=0; - cell *cstr; - char *name; - proplist *item; - - amx_GetAddr(amx,params[2],&cstr); - name=MakePackedString(cstr); - item=list_finditem(&proproot,params[1],name,params[3],NULL); - if (item==NULL) - item=list_additem(&proproot); - if (item==NULL) { - amx_RaiseError(amx,AMX_ERR_MEMORY); - } else { - prev=item->value; - if (strlen(name)==0) { - free(name); - amx_GetAddr(amx,params[4],&cstr); - name=MakePackedString(cstr); - } /* if */ - list_setitem(item,params[1],name,params[3]); - } /* if */ - free(name); - return prev; -} - -static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params) -{ - cell prev=0; - cell *cstr; - char *name; - proplist *item,*pred; - - amx_GetAddr(amx,params[2],&cstr); - name=MakePackedString(cstr); - item=list_finditem(&proproot,params[1],name,params[3],&pred); - if (item!=NULL) { - prev=item->value; - list_delete(pred,item); - } /* if */ - free(name); - return prev; -} - -static cell AMX_NATIVE_CALL existproperty(AMX *amx,const cell *params) -{ - cell *cstr; - char *name; - proplist *item; - - amx_GetAddr(amx,params[2],&cstr); - name=MakePackedString(cstr); - item=list_finditem(&proproot,params[1],name,params[3],NULL); - free(name); - return (item!=NULL); -} -#endif - -#if !defined AMX_NORANDOM -/* This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley - * (ISBN 0-201-47960-5). This is a "multiplicative congruential random number - * generator" that has been extended to 31-bits (the standard C version returns - * only 15-bits). - */ -#define INITIAL_SEED 0xcaa938dbL -static unsigned long IL_StandardRandom_seed = INITIAL_SEED; /* always use a non-zero seed */ -#define IL_RMULT 1103515245L -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell AMX_NATIVE_CALL core_random(AMX *amx,const cell *params) -{ - unsigned long lo, hi, ll, lh, hh, hl; - unsigned long result; - - /* one-time initialization (or, mostly one-time) */ - #if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__ - if (IL_StandardRandom_seed == INITIAL_SEED) - IL_StandardRandom_seed=(unsigned long)time(NULL); - #endif - - (void)amx; - - lo = IL_StandardRandom_seed & 0xffff; - hi = IL_StandardRandom_seed >> 16; - IL_StandardRandom_seed = IL_StandardRandom_seed * IL_RMULT + 12345; - ll = lo * (IL_RMULT & 0xffff); - lh = lo * (IL_RMULT >> 16 ); - hl = hi * (IL_RMULT & 0xffff); - hh = hi * (IL_RMULT >> 16 ); - result = ((ll + 12345) >> 16) + lh + hl + (hh << 16); - result &= ~LONG_MIN; /* remove sign bit */ - if (params[1]!=0) - result %= params[1]; - return (cell)result; -} -#endif - - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO core_Natives[] = { - { "numargs", numargs }, - { "getarg", getarg }, - { "setarg", setarg }, - { "heapspace", heapspace }, - { "funcidx", funcidx }, - { "swapchars", swapchars }, - { "tolower", core_tolower }, - { "toupper", core_toupper }, - { "min", core_min }, - { "max", core_max }, - { "clamp", core_clamp }, -#if !defined AMX_NORANDOM - { "random", core_random }, -#endif -#if !defined AMX_NOPROPLIST - { "getproperty", getproperty }, - { "setproperty", setproperty }, - { "deleteproperty",delproperty }, - { "existproperty", existproperty }, -#endif - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_CoreInit(AMX *amx) -{ - return amx_Register(amx, core_Natives, -1); -} - -int AMXEXPORT amx_CoreCleanup(AMX *amx) -{ - (void)amx; - #if !defined AMX_NOPROPLIST - while (proproot.next!=NULL) - list_delete(&proproot,proproot.next); - #endif - return AMX_ERR_NONE; -} +/* Core module for the Pawn AMX + * + * Copyright (c) ITB CompuPhase, 1997-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxcore.c 5504 2016-05-15 13:42:30Z $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include "osdefs.h" +#if defined __ECOS__ + /* eCos puts include files in cyg/package_name */ + #include + #define stricmp(a,b) strcasecmp(a,b) +#else + #include "amx.h" +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif + +/* A few compilers do not provide the ANSI C standard "time" functions */ +#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__ + #include +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +#endif + + +#define CHARBITS (8*sizeof(char)) +typedef unsigned char uchar; + +#if !defined AMX_NOPROPLIST +typedef struct _property_list { + struct _property_list *next; + cell id; + char *name; + cell value; +} proplist; + +static proplist proproot = { NULL, 0, NULL, 0 }; + +static proplist *list_additem(proplist *root) +{ + proplist *item; + + assert(root!=NULL); + if ((item=(proplist *)malloc(sizeof(proplist)))==NULL) + return NULL; + item->name=NULL; + item->id=0; + item->value=0; + item->next=root->next; + root->next=item; + return item; +} +static void list_delete(proplist *pred,proplist *item) +{ + assert(pred!=NULL); + assert(item!=NULL); + pred->next=item->next; + assert(item->name!=NULL); + free(item->name); + free(item); +} +static void list_setitem(proplist *item,cell id,char *name,cell value) +{ + char *ptr; + + assert(item!=NULL); + if ((ptr=(char *)malloc(strlen(name)+1))==NULL) + return; + if (item->name!=NULL) + free(item->name); + strcpy(ptr,name); + item->name=ptr; + item->id=id; + item->value=value; +} +static proplist *list_finditem(proplist *root,cell id,char *name,cell value, + proplist **pred) +{ + proplist *item=root->next; + proplist *prev=root; + + /* check whether to find by name or by value */ + assert(name!=NULL); + if (strlen(name)>0) { + /* find by name */ + while (item!=NULL && (item->id!=id || stricmp(item->name,name)!=0)) { + prev=item; + item=item->next; + } /* while */ + } else { + /* find by value */ + while (item!=NULL && (item->id!=id || item->value!=value)) { + prev=item; + item=item->next; + } /* while */ + } /* if */ + if (pred!=NULL) + *pred=prev; + return item; +} +#endif + +static cell AMX_NATIVE_CALL numargs(AMX *amx,const cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell bytes; + + (void)params; + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* the number of bytes is on the stack, at "frm + 2*cell" */ + bytes= * (cell *)(data+(int)amx->frm+2*sizeof(cell)); + /* the number of arguments is the number of bytes divided + * by the size of a cell */ + return bytes/sizeof(cell); +} + +static cell AMX_NATIVE_CALL getarg(AMX *amx,const cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* get the value indirectly */ + value= * (cell *)(data+(int)value); + return value; +} + +static cell AMX_NATIVE_CALL setarg(AMX *amx,const cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* verify the address */ + if (value<0 || (value>=amx->hea && valuestk)) + return 0; + /* set the value indirectly */ + * (cell *)(data+(int)value) = params[3]; + return 1; +} + +static cell AMX_NATIVE_CALL heapspace(AMX *amx,const cell *params) +{ + (void)params; + return amx->stk - amx->hea; +} + +static cell AMX_NATIVE_CALL funcidx(AMX *amx,const cell *params) +{ + char name[64]; + cell *cstr; + int index,err; + + cstr=amx_Address(amx,params[1]); + amx_GetString(name,cstr,0,sizeof name); + err=amx_FindPublic(amx,name,&index); + if (err!=AMX_ERR_NONE) + index=-1; /* this is not considered a fatal error */ + return index; +} + +void amx_swapcell(cell *pc) +{ + union { + cell c; + #if PAWN_CELL_SIZE==16 + uchar b[2]; + #elif PAWN_CELL_SIZE==32 + uchar b[4]; + #elif PAWN_CELL_SIZE==64 + uchar b[8]; + #else + #error Unsupported cell size + #endif + } value; + uchar t; + + assert(pc!=NULL); + value.c = *pc; + #if PAWN_CELL_SIZE==16 + t = value.b[0]; + value.b[0] = value.b[1]; + value.b[1] = t; + #elif PAWN_CELL_SIZE==32 + t = value.b[0]; + value.b[0] = value.b[3]; + value.b[3] = t; + t = value.b[1]; + value.b[1] = value.b[2]; + value.b[2] = t; + #elif PAWN_CELL_SIZE==64 + t = value.b[0]; + value.b[0] = value.b[7]; + value.b[7] = t; + t = value.b[1]; + value.b[1] = value.b[6]; + value.b[6] = t; + t = value.b[2]; + value.b[2] = value.b[5]; + value.b[5] = t; + t = value.b[3]; + value.b[3] = value.b[4]; + value.b[4] = t; + #else + #error Unsupported cell size + #endif + *pc = value.c; +} + +static cell AMX_NATIVE_CALL swapchars(AMX *amx,const cell *params) +{ + cell c; + + (void)amx; + assert((size_t)params[0]==sizeof(cell)); + + c=params[1]; + amx_swapcell(&c); + return c; +} + +static cell AMX_NATIVE_CALL core_tolower(AMX *amx,const cell *params) +{ + (void)amx; + #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && !defined _WIN64 + return (cell)CharLower((LPTSTR)params[1]); + #elif defined _Windows && !defined _WIN64 + return (cell)AnsiLower((LPSTR)params[1]); + #else + if ((unsigned)(params[1]-'A')<26u) + return params[1]+'a'-'A'; + return params[1]; + #endif +} + +static cell AMX_NATIVE_CALL core_toupper(AMX *amx,const cell *params) +{ + (void)amx; + #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && !defined _WIN64 + return (cell)CharUpper((LPTSTR)params[1]); + #elif defined _Windows && !defined _WIN64 + return (cell)AnsiUpper((LPSTR)params[1]); + #else + if ((unsigned)(params[1]-'a')<26u) + return params[1]+'A'-'a'; + return params[1]; + #endif +} + +static cell AMX_NATIVE_CALL core_min(AMX *amx,const cell *params) +{ + (void)amx; + return params[1] <= params[2] ? params[1] : params[2]; +} + +static cell AMX_NATIVE_CALL core_max(AMX *amx,const cell *params) +{ + (void)amx; + return params[1] >= params[2] ? params[1] : params[2]; +} + +static cell AMX_NATIVE_CALL core_clamp(AMX *amx,const cell *params) +{ + cell value = params[1]; + if (params[2] > params[3]) /* minimum value > maximum value ! */ + amx_RaiseError(amx,AMX_ERR_NATIVE); + if (value < params[2]) + value = params[2]; + else if (value > params[3]) + value = params[3]; + return value; +} + +#if !defined AMX_NOPROPLIST +static char *MakePackedString(cell *cptr) +{ + int len; + char *dest; + + amx_StrLen(cptr,&len); + dest=(char *)malloc(len+sizeof(cell)); + amx_GetString(dest,cptr,0,len+sizeof(cell)); + return dest; +} + +/* getproperty(id=0, const name[]="", value=cellmin, string[]="", size=sizeof string) */ +static cell AMX_NATIVE_CALL getproperty(AMX *amx,const cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + (void)amx; + cstr=amx_Address(amx,params[2]); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + /* if list_finditem() found the value, store the name */ + if (item!=NULL && item->value==params[3] && strlen(name)==0) { + cstr=amx_Address(amx,params[4]); + amx_SetString(cstr,item->name,1,0,params[5]); + } /* if */ + free(name); + return (item!=NULL) ? item->value : 0; +} + +/* setproperty(id=0, const name[]="", value=cellmin, const string[]="") */ +static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item; + + cstr=amx_Address(amx,params[2]); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + if (item==NULL) + item=list_additem(&proproot); + if (item==NULL) { + amx_RaiseError(amx,AMX_ERR_MEMORY); + } else { + prev=item->value; + if (strlen(name)==0) { + free(name); + cstr=amx_Address(amx,params[4]); + name=MakePackedString(cstr); + } /* if */ + list_setitem(item,params[1],name,params[3]); + } /* if */ + free(name); + return prev; +} + +/* deleteproperty(id=0, const name[]="", value=cellmin) */ +static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item,*pred; + + (void)amx; + cstr=amx_Address(amx,params[2]); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],&pred); + if (item!=NULL) { + prev=item->value; + list_delete(pred,item); + } /* if */ + free(name); + return prev; +} + +/* existproperty(id=0, const name[]="", value=cellmin) */ +static cell AMX_NATIVE_CALL existproperty(AMX *amx,const cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + (void)amx; + cstr=amx_Address(amx,params[2]); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + free(name); + return (item!=NULL); +} +#endif + +#if !defined AMX_NORANDOM +/* This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley + * (ISBN 0-201-47960-5). This is a "multiplicative congruential random number + * generator" that has been extended to 31-bits (the standard C version returns + * only 15-bits). + */ +#define INITIAL_SEED 0xcaa938dbL +static unsigned long IL_StandardRandom_seed = INITIAL_SEED; /* always use a non-zero seed */ +#define IL_RMULT 1103515245L +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_random(AMX *amx,const cell *params) +{ + unsigned long lo, hi, ll, lh, hh, hl; + unsigned long result; + + /* one-time initialization (or, mostly one-time) */ + #if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__ + if (IL_StandardRandom_seed == INITIAL_SEED) + IL_StandardRandom_seed=(unsigned long)time(NULL); + #endif + + (void)amx; + + lo = IL_StandardRandom_seed & 0xffff; + hi = IL_StandardRandom_seed >> 16; + IL_StandardRandom_seed = IL_StandardRandom_seed * IL_RMULT + 12345; + ll = lo * (IL_RMULT & 0xffff); + lh = lo * (IL_RMULT >> 16 ); + hl = hi * (IL_RMULT & 0xffff); + hh = hi * (IL_RMULT >> 16 ); + result = ((ll + 12345) >> 16) + lh + hl + (hh << 16); + result &= ~LONG_MIN; /* remove sign bit */ + if (params[1]!=0) + result %= params[1]; + return (cell)result; +} +#endif + + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO core_Natives[] = { + { "numargs", numargs }, + { "getarg", getarg }, + { "setarg", setarg }, + { "heapspace", heapspace }, + { "funcidx", funcidx }, + { "swapchars", swapchars }, + { "tolower", core_tolower }, + { "toupper", core_toupper }, + { "min", core_min }, + { "max", core_max }, + { "clamp", core_clamp }, +#if !defined AMX_NORANDOM + { "random", core_random }, +#endif +#if !defined AMX_NOPROPLIST + { "getproperty", getproperty }, + { "setproperty", setproperty }, + { "deleteproperty",delproperty }, + { "existproperty", existproperty }, +#endif + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_CoreInit(AMX *amx) +{ + return amx_Register(amx, core_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_CoreCleanup(AMX *amx) +{ + (void)amx; + #if !defined AMX_NOPROPLIST + while (proproot.next!=NULL) + list_delete(&proproot,proproot.next); + #endif + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxdbg.c b/amx-deps/src/amx/amxdbg.c index 1201d17..b0adcd6 100644 --- a/amx-deps/src/amx/amxdbg.c +++ b/amx-deps/src/amx/amxdbg.c @@ -1,502 +1,615 @@ -/* Pawn debugger interface - * - * Support functions for debugger applications - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxdbg.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include -#include -#include "osdefs.h" /* for _MAX_PATH */ -#include "amx.h" -#include "amxdbg.h" - - -int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg) -{ - assert(amxdbg != NULL); - if (amxdbg->hdr != NULL) - free(amxdbg->hdr); - if (amxdbg->filetbl != NULL) - free(amxdbg->filetbl); - if (amxdbg->symboltbl != NULL) - free(amxdbg->symboltbl); - if (amxdbg->tagtbl != NULL) - free(amxdbg->tagtbl); - if (amxdbg->automatontbl != NULL) - free(amxdbg->automatontbl); - if (amxdbg->statetbl != NULL) - free(amxdbg->statetbl); - memset(amxdbg, 0, sizeof(AMX_DBG)); - return AMX_ERR_NONE; -} - -int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp) -{ - AMX_HEADER amxhdr; - AMX_DBG_HDR dbghdr; - unsigned char *ptr; - int index, dim; - AMX_DBG_SYMDIM *symdim; - - assert(fp != NULL); - assert(amxdbg != NULL); - - memset(&amxhdr, 0, sizeof amxhdr); - fseek(fp, 0L, SEEK_SET); - fread(&amxhdr, sizeof amxhdr, 1, fp); - #if BYTE_ORDER==BIG_ENDIAN - amx_Align32((uint32_t*)&amxhdr.size); - amx_Align16(&amxhdr.magic); - amx_Align16(&dbghdr.flags); - #endif - if (amxhdr.magic != AMX_MAGIC) - return AMX_ERR_FORMAT; - if ((amxhdr.flags & AMX_FLAG_DEBUG) == 0) - return AMX_ERR_DEBUG; - - fseek(fp, amxhdr.size, SEEK_SET); - memset(&dbghdr, 0, sizeof(AMX_DBG_HDR)); - fread(&dbghdr, sizeof(AMX_DBG_HDR), 1, fp); - - #if BYTE_ORDER==BIG_ENDIAN - amx_Align32((uint32_t*)&dbghdr.size); - amx_Align16(&dbghdr.magic); - amx_Align16(&dbghdr.files); - amx_Align16(&dbghdr.lines); - amx_Align16(&dbghdr.symbols); - amx_Align16(&dbghdr.tags); - amx_Align16(&dbghdr.automatons); - amx_Align16(&dbghdr.states); - #endif - if (dbghdr.magic != AMX_DBG_MAGIC) - return AMX_ERR_FORMAT; - - /* allocate all memory */ - memset(amxdbg, 0, sizeof(AMX_DBG)); - amxdbg->hdr = malloc((size_t)dbghdr.size); - if (dbghdr.files > 0) - amxdbg->filetbl = malloc(dbghdr.files * sizeof(AMX_DBG_FILE *)); - if (dbghdr.symbols > 0) - amxdbg->symboltbl = malloc(dbghdr.symbols * sizeof(AMX_DBG_SYMBOL *)); - if (dbghdr.tags > 0) - amxdbg->tagtbl = malloc(dbghdr.tags * sizeof(AMX_DBG_TAG *)); - if (dbghdr.automatons > 0) - amxdbg->automatontbl = malloc(dbghdr.automatons * sizeof(AMX_DBG_MACHINE *)); - if (dbghdr.states > 0) - amxdbg->statetbl = malloc(dbghdr.states * sizeof(AMX_DBG_STATE *)); - if (amxdbg->hdr == NULL - || (dbghdr.files > 0 && amxdbg->filetbl == NULL) - || (dbghdr.symbols > 0 && amxdbg->symboltbl == NULL) - || (dbghdr.tags > 0 && amxdbg->tagtbl == NULL) - || (dbghdr.states > 0 && amxdbg->statetbl == NULL) - || (dbghdr.automatons > 0 && amxdbg->automatontbl == NULL)) - { - dbg_FreeInfo(amxdbg); - return AMX_ERR_MEMORY; - } /* if */ - - /* load the entire symbolic information block into memory */ - memcpy(amxdbg->hdr, &dbghdr, sizeof dbghdr); - fread(amxdbg->hdr + 1, 1, (size_t)(dbghdr.size - sizeof dbghdr), fp); - - /* run through the file, fix alignment issues and set up table pointers */ - ptr = (unsigned char *)(amxdbg->hdr + 1); - - /* file table */ - for (index = 0; index < dbghdr.files; index++) { - assert(amxdbg->filetbl != NULL); - amxdbg->filetbl[index] = (AMX_DBG_FILE *)ptr; - #if BYTE_ORDER==BIG_ENDIAN - amx_AlignCell(&amxdbg->filetbl[index]->address); - #endif - for (ptr = ptr + sizeof(AMX_DBG_FILE); *ptr != '\0'; ptr++) - /* nothing */; - ptr++; /* skip '\0' too */ - } /* for */ - - /* line table */ - amxdbg->linetbl = (AMX_DBG_LINE*)ptr; - #if BYTE_ORDER==BIG_ENDIAN - for (index = 0; index < dbghdr.lines; index++) { - amx_AlignCell(&amxdbg->linetbl[index].address); - amx_Align32((uint32_t*)&amxdbg->linetbl[index].line); - } /* for */ - #endif - ptr += dbghdr.lines * sizeof(AMX_DBG_LINE); - - /* symbol table (plus index tags) */ - for (index = 0; index < dbghdr.symbols; index++) { - assert(amxdbg->symboltbl != NULL); - amxdbg->symboltbl[index] = (AMX_DBG_SYMBOL *)ptr; - #if BYTE_ORDER==BIG_ENDIAN - amx_AlignCell(&amxdbg->symboltbl[index]->address); - amx_Align16((uint16_t*)&amxdbg->symboltbl[index]->tag); - amx_AlignCell(&amxdbg->symboltbl[index]->codestart); - amx_AlignCell(&amxdbg->symboltbl[index]->codeend); - amx_Align16((uint16_t*)&amxdbg->symboltbl[index]->dim); - #endif - for (ptr = ptr + sizeof(AMX_DBG_SYMBOL); *ptr != '\0'; ptr++) - /* nothing */; - ptr++; /* skip '\0' too */ - for (dim = 0; dim < amxdbg->symboltbl[index]->dim; dim++) { - symdim = (AMX_DBG_SYMDIM *)ptr; - amx_Align16((uint16_t*)&symdim->tag); - amx_AlignCell(&symdim->size); - ptr += sizeof(AMX_DBG_SYMDIM); - } /* for */ - } /* for */ - - /* tag name table */ - for (index = 0; index < dbghdr.tags; index++) { - assert(amxdbg->tagtbl != NULL); - amxdbg->tagtbl[index] = (AMX_DBG_TAG *)ptr; - #if BYTE_ORDER==BIG_ENDIAN - amx_Align16(&amxdbg->tagtbl[index]->tag); - #endif - for (ptr = ptr + sizeof(AMX_DBG_TAG) - 1; *ptr != '\0'; ptr++) - /* nothing */; - ptr++; /* skip '\0' too */ - } /* for */ - - /* automaton name table */ - for (index = 0; index < dbghdr.automatons; index++) { - assert(amxdbg->automatontbl != NULL); - amxdbg->automatontbl[index] = (AMX_DBG_MACHINE *)ptr; - #if BYTE_ORDER==BIG_ENDIAN - amx_Align16(&amxdbg->automatontbl[index]->automaton); - amx_AlignCell(&amxdbg->automatontbl[index]->address); - #endif - for (ptr = ptr + sizeof(AMX_DBG_MACHINE) - 1; *ptr != '\0'; ptr++) - /* nothing */; - ptr++; /* skip '\0' too */ - } /* for */ - - /* state name table */ - for (index = 0; index < dbghdr.states; index++) { - assert(amxdbg->statetbl != NULL); - amxdbg->statetbl[index] = (AMX_DBG_STATE *)ptr; - #if BYTE_ORDER==BIG_ENDIAN - amx_Align16(&amxdbg->statetbl[index]->state); - amx_Align16(&amxdbg->automatontbl[index]->automaton); - #endif - for (ptr = ptr + sizeof(AMX_DBG_STATE) - 1; *ptr != '\0'; ptr++) - /* nothing */; - ptr++; /* skip '\0' too */ - } /* for */ - - return AMX_ERR_NONE; -} - -/* dbg_LinearAddress() returns the linear address that matches the given - * relative address for the current overlay. The linear address is relative - * to the code section (as if the code section were a single block). - * Linearizing addresses is only needed for debugging hooks when overlays - * are active. On a non-overlay file, dbg_LinearAddress() returns the same - * address as its input. - */ -int AMXAPI dbg_LinearAddress(AMX *amx, ucell relative_addr, ucell *linear_addr) -{ - AMX_HEADER *hdr; - - assert(amx!=NULL); - hdr=(AMX_HEADER *)amx->base; - assert(hdr!=NULL); - assert(linear_addr!=NULL); - if ((hdr->flags & AMX_FLAG_OVERLAY)==0) { - *linear_addr=relative_addr; - } else { - AMX_OVERLAYINFO *tbl; - assert(hdr->overlays!=0 && hdr->overlays!=hdr->nametable); - if ((size_t)amx->ovl_index >= (hdr->nametable-hdr->overlays)/sizeof(AMX_OVERLAYINFO)) - return AMX_ERR_OVERLAY; - tbl=(AMX_OVERLAYINFO*)(amx->base+hdr->overlays)+amx->ovl_index; - *linear_addr=tbl->offset+relative_addr; - } /* if */ - return AMX_ERR_NONE; -} - -int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename) -{ - int index; - - assert(amxdbg != NULL); - assert(filename != NULL); - *filename = NULL; - /* this is a simple linear look-up; a binary search would be possible too */ - for (index = 0; index < amxdbg->hdr->files && amxdbg->filetbl[index]->address <= address; index++) - /* nothing */; - /* reset for overrun */ - if (--index < 0) - return AMX_ERR_NOTFOUND; - - *filename = amxdbg->filetbl[index]->name; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line) -{ - int index; - - assert(amxdbg != NULL); - assert(line != NULL); - *line = 0; - /* this is a simple linear look-up; a binary search would be possible too */ - for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address <= address; index++) - /* nothing */; - /* reset for overrun */ - if (--index < 0) - return AMX_ERR_NOTFOUND; - - *line = (long)amxdbg->linetbl[index].line; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname) -{ - /* dbg_LookupFunction() finds the function a code address is in. It can be - * used for stack walking, and for stepping through a function while stepping - * over sub-functions - */ - int index; - - assert(amxdbg != NULL); - assert(funcname != NULL); - *funcname = NULL; - for (index = 0; index < amxdbg->hdr->symbols; index++) { - if (amxdbg->symboltbl[index]->ident == iFUNCTN - && amxdbg->symboltbl[index]->codestart <= address - && amxdbg->symboltbl[index]->codeend > address) - break; - } /* for */ - if (index >= amxdbg->hdr->symbols) - return AMX_ERR_NOTFOUND; - - *funcname = amxdbg->symboltbl[index]->name; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name) -{ - int index; - - assert(amxdbg != NULL); - assert(name != NULL); - *name = NULL; - for (index = 0; index < amxdbg->hdr->tags && amxdbg->tagtbl[index]->tag != tag; index++) - /* nothing */; - if (index >= amxdbg->hdr->tags) - return AMX_ERR_NOTFOUND; - - *name = amxdbg->tagtbl[index]->name; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name) -{ - int index; - - assert(amxdbg != NULL); - assert(name != NULL); - *name = NULL; - for (index = 0; index < amxdbg->hdr->automatons && amxdbg->automatontbl[index]->automaton != automaton; index++) - /* nothing */; - if (index >= amxdbg->hdr->automatons) - return AMX_ERR_NOTFOUND; - - *name = amxdbg->automatontbl[index]->name; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name) -{ - int index; - - assert(amxdbg != NULL); - assert(name != NULL); - *name = NULL; - for (index = 0; index < amxdbg->hdr->states && amxdbg->statetbl[index]->state != state; index++) - /* nothing */; - if (index >= amxdbg->hdr->states) - return AMX_ERR_NOTFOUND; - - *name = amxdbg->statetbl[index]->name; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address) -{ - /* Find a suitable "breakpoint address" close to the indicated line (and in - * the specified file). The address is moved up to the next "breakable" line - * if no "breakpoint" is available on the specified line. You can use function - * dbg_LookupLine() to find out at which precise line the breakpoint was set. - * - * The filename comparison is strict (case sensitive and path sensitive); the - * "filename" parameter should point into the "filetbl" of the AMX_DBG - * structure. - */ - int file, index; - ucell bottomaddr,topaddr; - - assert(amxdbg != NULL); - assert(filename != NULL); - assert(address != NULL); - *address = 0; - - index = 0; - for (file = 0; file < amxdbg->hdr->files; file++) { - /* find the (next) mathing instance of the file */ - if (strcmp(amxdbg->filetbl[file]->name, filename) != 0) - continue; - /* get address range for the current file */ - bottomaddr = amxdbg->filetbl[file]->address; - topaddr = (file + 1 < amxdbg->hdr->files) ? amxdbg->filetbl[file+1]->address : (ucell)(cell)-1; - /* go to the starting address in the line table */ - while (index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < bottomaddr) - index++; - /* browse until the line is found or until the top address is exceeded */ - while (index < amxdbg->hdr->lines - && amxdbg->linetbl[index].line < line - && amxdbg->linetbl[index].address < topaddr) - index++; - if (index >= amxdbg->hdr->lines) - return AMX_ERR_NOTFOUND; - if (amxdbg->linetbl[index].line >= line) - break; - /* if not found (and the line table is not yet exceeded) try the next - * instance of the same file (a file may appear twice in the file table) - */ - } /* for */ - - if (strcmp(amxdbg->filetbl[file]->name, filename) != 0) - return AMX_ERR_NOTFOUND; - - assert(index < amxdbg->hdr->lines); - *address = amxdbg->linetbl[index].address; - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address) -{ - /* Find a suitable "breakpoint address" close to the indicated line (and in - * the specified file). The address is moved up to the first "breakable" line - * in the function. You can use function dbg_LookupLine() to find out at which - * precise line the breakpoint was set. - * - * The filename comparison is strict (case sensitive and path sensitive); the - * "filename" parameter should point into the "filetbl" of the AMX_DBG - * structure. The function name comparison is case sensitive too. - */ - int index, err; - const char *tgtfile; - ucell funcaddr; - - assert(amxdbg != NULL); - assert(funcname != NULL); - assert(filename != NULL); - assert(address != NULL); - *address = 0; - - index = 0; - for ( ;; ) { - /* find (next) matching function */ - while (index < amxdbg->hdr->symbols - && (amxdbg->symboltbl[index]->ident != iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, funcname) != 0)) - index++; - if (index >= amxdbg->hdr->symbols) - return AMX_ERR_NOTFOUND; - /* verify that this line falls in the appropriate file */ - err = dbg_LookupFile(amxdbg, amxdbg->symboltbl[index]->address, &tgtfile); - if (err == AMX_ERR_NONE || strcmp(filename, tgtfile) == 0) - break; - index++; /* line is the wrong file, search further */ - } /* for */ - - /* now find the first line in the function where we can "break" on */ - assert(index < amxdbg->hdr->symbols); - funcaddr = amxdbg->symboltbl[index]->address; - for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < funcaddr; index++) - /* nothing */; - - if (index >= amxdbg->hdr->lines) - return AMX_ERR_NOTFOUND; - *address = amxdbg->linetbl[index].address; - - return AMX_ERR_NONE; -} - -int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym) -{ - ucell codestart,codeend; - int index; - - assert(amxdbg != NULL); - assert(symname != NULL); - assert(sym != NULL); - *sym = NULL; - - codestart = codeend = 0; - index = 0; - for ( ;; ) { - /* find (next) matching variable */ - while (index < amxdbg->hdr->symbols - && (amxdbg->symboltbl[index]->ident == iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, symname) != 0) - && (amxdbg->symboltbl[index]->codestart > scopeaddr || amxdbg->symboltbl[index]->codeend < scopeaddr)) - index++; - if (index >= amxdbg->hdr->symbols) - break; - /* check the range, keep a pointer to the symbol with the smallest range */ - if (strcmp(amxdbg->symboltbl[index]->name, symname) == 0 - && (codestart == 0 && codeend == 0 - || amxdbg->symboltbl[index]->codestart >= codestart && amxdbg->symboltbl[index]->codeend <= codeend)) - { - *sym = amxdbg->symboltbl[index]; - codestart = amxdbg->symboltbl[index]->codestart; - codeend = amxdbg->symboltbl[index]->codeend; - } /* if */ - index++; - } /* for */ - - return (*sym == NULL) ? AMX_ERR_NOTFOUND : AMX_ERR_NONE; -} - -int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim) -{ - /* retrieves a pointer to the array dimensions structures of an array symbol */ - const char *ptr; - - assert(amxdbg != NULL); - assert(sym != NULL); - assert(symdim != NULL); - *symdim = NULL; - - if (sym->ident != iARRAY && sym->ident != iREFARRAY) - return AMX_ERR_PARAMS; - assert(sym->dim > 0); /* array must have at least one dimension */ - - /* find the end of the symbol name */ - for (ptr = sym->name; *ptr != '\0'; ptr++) - /* nothing */; - *symdim = (AMX_DBG_SYMDIM *)(ptr + 1);/* skip '\0' too */ - - return AMX_ERR_NONE; -} +/* Pawn debugger interface + * + * Support functions for debugger applications + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxdbg.c 3612 2006-07-22 09:59:46Z thiadmer $ + */ + +#include +#include +#include +#include +#include "osdefs.h" /* for _MAX_PATH */ +#include "amx.h" +#include "amxdbg.h" + +#if PAWN_CELL_SIZE==16 + #define dbg_AlignCell(v) dbg_Align16(v) +#elif PAWN_CELL_SIZE==32 + #define dbg_AlignCell(v) dbg_Align32(v) +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) + #define dbg_AlignCell(v) dbg_Align64(v) +#else + #error Unsupported cell size +#endif + +#if !defined NDEBUG + static int check_endian(void) + { + uint16_t val=0x00ff; + unsigned char *ptr=(unsigned char *)&val; + /* "ptr" points to the starting address of "val". If that address + * holds the byte "0xff", the computer stored the low byte of "val" + * at the lower address, and so the memory lay out is Little Endian. + */ + assert(*ptr==0xff || *ptr==0x00); + #if BYTE_ORDER==BIG_ENDIAN + return *ptr==0x00; /* return "true" if big endian */ + #else + return *ptr==0xff; /* return "true" if little endian */ + #endif + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==16 + static void swap16(uint16_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert(sizeof(*v)==2); + /* swap two bytes */ + t=s[0]; + s[0]=s[1]; + s[1]=t; + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==32 + static void swap32(uint32_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert_static(sizeof(*v)==4); + /* swap outer two bytes */ + t=s[0]; + s[0]=s[3]; + s[3]=t; + /* swap inner two bytes */ + t=s[1]; + s[1]=s[2]; + s[2]=t; + } +#endif + +#if (BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==64) && (defined _I64_MAX || defined HAVE_I64) + static void swap64(uint64_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert(sizeof(*v)==8); + + t=s[0]; + s[0]=s[7]; + s[7]=t; + + t=s[1]; + s[1]=s[6]; + s[6]=t; + + t=s[2]; + s[2]=s[5]; + s[5]=t; + + t=s[3]; + s[3]=s[4]; + s[4]=t; + } +#endif + +uint16_t * AMXAPI dbg_Align16(uint16_t *v) +{ + assert_static(sizeof(*v)==2); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap16(v); + #endif + return v; +} + +uint32_t * AMXAPI dbg_Align32(uint32_t *v) +{ + assert_static(sizeof(*v)==4); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap32(v); + #endif + return v; +} + +#if defined _I64_MAX || defined HAVE_I64 +uint64_t * AMXAPI dbg_Align64(uint64_t *v) +{ + assert(sizeof(*v)==8); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap64(v); + #endif + return v; +} +#endif /* _I64_MAX || HAVE_I64 */ + +int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg) +{ + assert(amxdbg != NULL); + if (amxdbg->hdr != NULL) + free(amxdbg->hdr); + if (amxdbg->filetbl != NULL) + free(amxdbg->filetbl); + if (amxdbg->symboltbl != NULL) + free(amxdbg->symboltbl); + if (amxdbg->tagtbl != NULL) + free(amxdbg->tagtbl); + if (amxdbg->automatontbl != NULL) + free(amxdbg->automatontbl); + if (amxdbg->statetbl != NULL) + free(amxdbg->statetbl); + memset(amxdbg, 0, sizeof(AMX_DBG)); + return AMX_ERR_NONE; +} + +int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp) +{ + AMX_HEADER amxhdr; + AMX_DBG_HDR dbghdr; + size_t size; + unsigned char *ptr; + int index, dim; + AMX_DBG_LINE *line; + AMX_DBG_SYMDIM *symdim; + + assert(fp != NULL); + assert(amxdbg != NULL); + + memset(&amxhdr, 0, sizeof amxhdr); + fseek(fp, 0L, SEEK_SET); + if (fread(&amxhdr, sizeof amxhdr, 1, fp) == 0) + return AMX_ERR_FORMAT; + #if BYTE_ORDER==BIG_ENDIAN + dbg_Align32((uint32_t*)&amxhdr.size); + dbg_Align16(&amxhdr.magic); + dbg_Align16(&dbghdr.flags); + #endif + if (amxhdr.magic != AMX_MAGIC) + return AMX_ERR_FORMAT; + if ((amxhdr.flags & AMX_FLAG_DEBUG) == 0) + return AMX_ERR_DEBUG; + + fseek(fp, amxhdr.size, SEEK_SET); + memset(&dbghdr, 0, sizeof(AMX_DBG_HDR)); + if (fread(&dbghdr, sizeof(AMX_DBG_HDR), 1, fp) == 0) + return AMX_ERR_FORMAT; + + #if BYTE_ORDER==BIG_ENDIAN + dbg_Align32((uint32_t*)&dbghdr.size); + dbg_Align16(&dbghdr.magic); + dbg_Align16(&dbghdr.flags); + dbg_Align16(&dbghdr.files); + dbg_Align16(&dbghdr.lines); + dbg_Align16(&dbghdr.symbols); + dbg_Align16(&dbghdr.tags); + dbg_Align16(&dbghdr.automatons); + dbg_Align16(&dbghdr.states); + #endif + if (dbghdr.magic != AMX_DBG_MAGIC) + return AMX_ERR_FORMAT; + + /* allocate all memory */ + memset(amxdbg, 0, sizeof(AMX_DBG)); + amxdbg->hdr = malloc((size_t)dbghdr.size); + if (dbghdr.files > 0) + amxdbg->filetbl = malloc(dbghdr.files * sizeof(AMX_DBG_FILE *)); + if (dbghdr.symbols > 0) + amxdbg->symboltbl = malloc(dbghdr.symbols * sizeof(AMX_DBG_SYMBOL *)); + if (dbghdr.tags > 0) + amxdbg->tagtbl = malloc(dbghdr.tags * sizeof(AMX_DBG_TAG *)); + if (dbghdr.automatons > 0) + amxdbg->automatontbl = malloc(dbghdr.automatons * sizeof(AMX_DBG_MACHINE *)); + if (dbghdr.states > 0) + amxdbg->statetbl = malloc(dbghdr.states * sizeof(AMX_DBG_STATE *)); + if (amxdbg->hdr == NULL + || (dbghdr.files > 0 && amxdbg->filetbl == NULL) + || (dbghdr.symbols > 0 && amxdbg->symboltbl == NULL) + || (dbghdr.tags > 0 && amxdbg->tagtbl == NULL) + || (dbghdr.states > 0 && amxdbg->statetbl == NULL) + || (dbghdr.automatons > 0 && amxdbg->automatontbl == NULL)) + { + dbg_FreeInfo(amxdbg); + return AMX_ERR_MEMORY; + } /* if */ + + /* load the entire symbolic information block into memory */ + memcpy(amxdbg->hdr, &dbghdr, sizeof dbghdr); + size=(size_t)(dbghdr.size - sizeof dbghdr); + if (fread(amxdbg->hdr + 1, 1, size, fp) < size) { + dbg_FreeInfo(amxdbg); + return AMX_ERR_FORMAT; + } /* if */ + + /* run through the file, fix alignment issues and set up table pointers */ + ptr = (unsigned char *)(amxdbg->hdr + 1); + + /* file table */ + for (index = 0; index < dbghdr.files; index++) { + assert(amxdbg->filetbl != NULL); + amxdbg->filetbl[index] = (AMX_DBG_FILE *)ptr; + #if BYTE_ORDER==BIG_ENDIAN + dbg_AlignCell(&amxdbg->filetbl[index]->address); + #endif + for (ptr = ptr + sizeof(AMX_DBG_FILE); *ptr != '\0'; ptr++) + /* nothing */; + ptr++; /* skip '\0' too */ + } /* for */ + + /* line table */ + amxdbg->linetbl = (AMX_DBG_LINE*)ptr; + #if BYTE_ORDER==BIG_ENDIAN + for (index = 0; index < dbghdr.lines; index++) { + dbg_AlignCell(&amxdbg->linetbl[index].address); + dbg_Align32((uint32_t*)&amxdbg->linetbl[index].line); + } /* for */ + #endif + ptr += (uint16_t)dbghdr.lines * sizeof(AMX_DBG_LINE); + + /* detect dbghdr.lines overflow */ + while ((line = (AMX_DBG_LINE *)ptr) + && (cell)line->address > (cell)(line - 1)->address) { + #if BYTE_ORDER==BIG_ENDIAN + for (index = 0; index <= (uint32_t)(1u << 16) - 1; index++) { + dbg_AlignCell(&linetbl[index].address); + dbg_Align32((uint32_t*)&linetbl[index].line); + line++; + } /* for */ + #endif + ptr += (uint32_t)(1u << 16) * sizeof(AMX_DBG_LINE); + } /* while */ + + /* symbol table (plus index tags) */ + for (index = 0; index < dbghdr.symbols; index++) { + assert(amxdbg->symboltbl != NULL); + amxdbg->symboltbl[index] = (AMX_DBG_SYMBOL *)ptr; + #if BYTE_ORDER==BIG_ENDIAN + dbg_AlignCell(&amxdbg->symboltbl[index]->address); + dbg_Align16((uint16_t*)&amxdbg->symboltbl[index]->tag); + dbg_AlignCell(&amxdbg->symboltbl[index]->codestart); + dbg_AlignCell(&amxdbg->symboltbl[index]->codeend); + dbg_Align16((uint16_t*)&amxdbg->symboltbl[index]->dim); + #endif + for (ptr = ptr + sizeof(AMX_DBG_SYMBOL); *ptr != '\0'; ptr++) + /* nothing */; + ptr++; /* skip '\0' too */ + for (dim = 0; dim < amxdbg->symboltbl[index]->dim; dim++) { + symdim = (AMX_DBG_SYMDIM *)ptr; + dbg_Align16((uint16_t*)&symdim->tag); + dbg_AlignCell(&symdim->size); + ptr += sizeof(AMX_DBG_SYMDIM); + } /* for */ + } /* for */ + + /* tag name table */ + for (index = 0; index < dbghdr.tags; index++) { + assert(amxdbg->tagtbl != NULL); + amxdbg->tagtbl[index] = (AMX_DBG_TAG *)ptr; + #if BYTE_ORDER==BIG_ENDIAN + dbg_Align16(&amxdbg->tagtbl[index]->tag); + #endif + for (ptr = ptr + sizeof(AMX_DBG_TAG) - 1; *ptr != '\0'; ptr++) + /* nothing */; + ptr++; /* skip '\0' too */ + } /* for */ + + /* automaton name table */ + for (index = 0; index < dbghdr.automatons; index++) { + assert(amxdbg->automatontbl != NULL); + amxdbg->automatontbl[index] = (AMX_DBG_MACHINE *)ptr; + #if BYTE_ORDER==BIG_ENDIAN + dbg_Align16(&amxdbg->automatontbl[index]->automaton); + dbg_AlignCell(&amxdbg->automatontbl[index]->address); + #endif + for (ptr = ptr + sizeof(AMX_DBG_MACHINE) - 1; *ptr != '\0'; ptr++) + /* nothing */; + ptr++; /* skip '\0' too */ + } /* for */ + + /* state name table */ + for (index = 0; index < dbghdr.states; index++) { + assert(amxdbg->statetbl != NULL); + amxdbg->statetbl[index] = (AMX_DBG_STATE *)ptr; + #if BYTE_ORDER==BIG_ENDIAN + dbg_Align16(&amxdbg->statetbl[index]->state); + dbg_Align16(&amxdbg->automatontbl[index]->automaton); + #endif + for (ptr = ptr + sizeof(AMX_DBG_STATE) - 1; *ptr != '\0'; ptr++) + /* nothing */; + ptr++; /* skip '\0' too */ + } /* for */ + + return AMX_ERR_NONE; +} + +int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename) +{ + int index; + + assert(amxdbg != NULL); + assert(filename != NULL); + *filename = NULL; + /* this is a simple linear look-up; a binary search would be possible too */ + for (index = 0; index < amxdbg->hdr->files && amxdbg->filetbl[index]->address <= address; index++) + /* nothing */; + /* reset for overrun */ + if (--index < 0) + return AMX_ERR_NOTFOUND; + + *filename = amxdbg->filetbl[index]->name; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line) +{ + int index; + + assert(amxdbg != NULL); + assert(line != NULL); + *line = 0; + /* this is a simple linear look-up; a binary search would be possible too */ + for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address <= address; index++) + /* nothing */; + /* reset for overrun */ + if (--index < 0) + return AMX_ERR_NOTFOUND; + + *line = (long)amxdbg->linetbl[index].line; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname) +{ + /* dbg_LookupFunction() finds the function a code address is in. It can be + * used for stack walking, and for stepping through a function while stepping + * over sub-functions + */ + int index; + + assert(amxdbg != NULL); + assert(funcname != NULL); + *funcname = NULL; + for (index = 0; index < amxdbg->hdr->symbols; index++) { + if (amxdbg->symboltbl[index]->ident == iFUNCTN + && amxdbg->symboltbl[index]->codestart <= address + && amxdbg->symboltbl[index]->codeend > address + && amxdbg->symboltbl[index]->name[0] != '@') + break; + } /* for */ + if (index >= amxdbg->hdr->symbols) + return AMX_ERR_NOTFOUND; + + *funcname = amxdbg->symboltbl[index]->name; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name) +{ + int index; + + assert(amxdbg != NULL); + assert(name != NULL); + *name = NULL; + for (index = 0; index < amxdbg->hdr->tags && amxdbg->tagtbl[index]->tag != tag; index++) + /* nothing */; + if (index >= amxdbg->hdr->tags) + return AMX_ERR_NOTFOUND; + + *name = amxdbg->tagtbl[index]->name; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name) +{ + int index; + + assert(amxdbg != NULL); + assert(name != NULL); + *name = NULL; + for (index = 0; index < amxdbg->hdr->automatons && amxdbg->automatontbl[index]->automaton != automaton; index++) + /* nothing */; + if (index >= amxdbg->hdr->automatons) + return AMX_ERR_NOTFOUND; + + *name = amxdbg->automatontbl[index]->name; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name) +{ + int index; + + assert(amxdbg != NULL); + assert(name != NULL); + *name = NULL; + for (index = 0; index < amxdbg->hdr->states && amxdbg->statetbl[index]->state != state; index++) + /* nothing */; + if (index >= amxdbg->hdr->states) + return AMX_ERR_NOTFOUND; + + *name = amxdbg->statetbl[index]->name; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address) +{ + /* Find a suitable "breakpoint address" close to the indicated line (and in + * the specified file). The address is moved up to the next "breakable" line + * if no "breakpoint" is available on the specified line. You can use function + * dbg_LookupLine() to find out at which precise line the breakpoint was set. + * + * The filename comparison is strict (case sensitive and path sensitive); the + * "filename" parameter should point into the "filetbl" of the AMX_DBG + * structure. + */ + int file, index; + ucell bottomaddr,topaddr; + + assert(amxdbg != NULL); + assert(filename != NULL); + assert(address != NULL); + *address = 0; + + index = 0; + for (file = 0; file < amxdbg->hdr->files; file++) { + /* find the (next) mathing instance of the file */ + if (strcmp(amxdbg->filetbl[file]->name, filename) != 0) + continue; + /* get address range for the current file */ + bottomaddr = amxdbg->filetbl[file]->address; + topaddr = (file + 1 < amxdbg->hdr->files) ? amxdbg->filetbl[file+1]->address : (ucell)(cell)-1; + /* go to the starting address in the line table */ + while (index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < bottomaddr) + index++; + /* browse until the line is found or until the top address is exceeded */ + while (index < amxdbg->hdr->lines + && amxdbg->linetbl[index].line < line + && amxdbg->linetbl[index].address < topaddr) + index++; + if (index >= amxdbg->hdr->lines) + return AMX_ERR_NOTFOUND; + if (amxdbg->linetbl[index].line >= line) + break; + /* if not found (and the line table is not yet exceeded) try the next + * instance of the same file (a file may appear twice in the file table) + */ + } /* for */ + + if (strcmp(amxdbg->filetbl[file]->name, filename) != 0) + return AMX_ERR_NOTFOUND; + + assert(index < amxdbg->hdr->lines); + *address = amxdbg->linetbl[index].address; + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address) +{ + /* Find a suitable "breakpoint address" close to the indicated line (and in + * the specified file). The address is moved up to the first "breakable" line + * in the function. You can use function dbg_LookupLine() to find out at which + * precise line the breakpoint was set. + * + * The filename comparison is strict (case sensitive and path sensitive); the + * "filename" parameter should point into the "filetbl" of the AMX_DBG + * structure. The function name comparison is case sensitive too. + */ + int index, err; + const char *tgtfile; + ucell funcaddr; + + assert(amxdbg != NULL); + assert(funcname != NULL); + assert(filename != NULL); + assert(address != NULL); + *address = 0; + + index = 0; + for ( ;; ) { + /* find (next) matching function */ + while (index < amxdbg->hdr->symbols + && (amxdbg->symboltbl[index]->ident != iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, funcname) != 0)) + index++; + if (index >= amxdbg->hdr->symbols) + return AMX_ERR_NOTFOUND; + /* verify that this line falls in the appropriate file */ + err = dbg_LookupFile(amxdbg, amxdbg->symboltbl[index]->address, &tgtfile); + if (err == AMX_ERR_NONE || strcmp(filename, tgtfile) == 0) + break; + index++; /* line is the wrong file, search further */ + } /* for */ + + /* now find the first line in the function where we can "break" on */ + assert(index < amxdbg->hdr->symbols); + funcaddr = amxdbg->symboltbl[index]->address; + for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < funcaddr; index++) + /* nothing */; + + if (index >= amxdbg->hdr->lines) + return AMX_ERR_NOTFOUND; + *address = amxdbg->linetbl[index].address; + + return AMX_ERR_NONE; +} + +int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym) +{ + ucell codestart,codeend; + int index; + + assert(amxdbg != NULL); + assert(symname != NULL); + assert(sym != NULL); + *sym = NULL; + + codestart = codeend = 0; + index = 0; + for ( ;; ) { + /* find (next) matching variable */ + while (index < amxdbg->hdr->symbols + && (amxdbg->symboltbl[index]->ident == iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, symname) != 0) + && (amxdbg->symboltbl[index]->codestart > scopeaddr || amxdbg->symboltbl[index]->codeend < scopeaddr)) + index++; + if (index >= amxdbg->hdr->symbols) + break; + /* check the range, keep a pointer to the symbol with the smallest range */ + if (strcmp(amxdbg->symboltbl[index]->name, symname) == 0 + && ((codestart == 0 && codeend == 0) + || (amxdbg->symboltbl[index]->codestart >= codestart && amxdbg->symboltbl[index]->codeend <= codeend))) + { + *sym = amxdbg->symboltbl[index]; + codestart = amxdbg->symboltbl[index]->codestart; + codeend = amxdbg->symboltbl[index]->codeend; + } /* if */ + index++; + } /* for */ + + return (*sym == NULL) ? AMX_ERR_NOTFOUND : AMX_ERR_NONE; +} + +int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim) +{ + /* retrieves a pointer to the array dimensions structures of an array symbol */ + const char *ptr; + + assert(amxdbg != NULL); + assert(sym != NULL); + assert(symdim != NULL); + *symdim = NULL; + + if (sym->ident != iARRAY && sym->ident != iREFARRAY) + return AMX_ERR_PARAMS; + assert(sym->dim > 0); /* array must have at least one dimension */ + + /* find the end of the symbol name */ + for (ptr = sym->name; *ptr != '\0'; ptr++) + /* nothing */; + *symdim = (AMX_DBG_SYMDIM *)(ptr + 1);/* skip '\0' too */ + + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxdbg.h b/amx-deps/src/amx/amxdbg.h index a6da7d8..2e49cee 100644 --- a/amx-deps/src/amx/amxdbg.h +++ b/amx-deps/src/amx/amxdbg.h @@ -1,173 +1,171 @@ -/* Abstract Machine for the Pawn compiler, debugger support - * - * This file contains extra definitions that are convenient for debugger - * support. - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxdbg.h 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#ifndef AMXDBG_H_INCLUDED -#define AMXDBG_H_INCLUDED - -#ifndef AMX_H_INCLUDED - #include "amx.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Some compilers do not support the #pragma align, which should be fine. Some - * compilers give a warning on unknown #pragmas, which is not so fine... - */ -#if defined SN_TARGET_PS2 || defined __GNUC__ - #define AMX_NO_ALIGN -#endif - -#if defined __GNUC__ - #define PACKED __attribute__((packed)) -#else - #define PACKED -#endif - -#if !defined AMX_NO_ALIGN - #if defined __LINUX__ || defined __FreeBSD__ - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=mac68k - #else - #pragma pack(push) - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #if defined __TURBOC__ - #pragma option -a- /* "pack" pragma for older Borland compilers */ - #endif - #endif -#endif - -typedef struct tagAMX_DBG_HDR { - int32_t size PACKED; /* size of the debug information chunk */ - uint16_t magic PACKED; /* signature, must be 0xf1ef */ - char file_version; /* file format version */ - char amx_version; /* required version of the AMX */ - int16_t flags PACKED; /* currently unused */ - int16_t files PACKED; /* number of entries in the "file" table */ - int16_t lines PACKED; /* number of entries in the "line" table */ - int16_t symbols PACKED; /* number of entries in the "symbol" table */ - int16_t tags PACKED; /* number of entries in the "tag" table */ - int16_t automatons PACKED; /* number of entries in the "automaton" table */ - int16_t states PACKED; /* number of entries in the "state" table */ -} AMX_DBG_HDR; -#define AMX_DBG_MAGIC 0xf1ef - -typedef struct tagAMX_DBG_FILE { - ucell address PACKED; /* address in the code segment where generated code (for this file) starts */ - const char name[1]; /* ASCII string, zero-terminated */ -} AMX_DBG_FILE; - -typedef struct tagAMX_DBG_LINE { - ucell address PACKED; /* address in the code segment where generated code (for this line) starts */ - int32_t line PACKED; /* line number */ -} AMX_DBG_LINE; - -typedef struct tagAMX_DBG_SYMBOL { - ucell address PACKED; /* address in the data segment or relative to the frame */ - int16_t tag PACKED; /* tag for the symbol */ - ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */ - ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */ - char ident; /* kind of symbol (function/variable) */ - char vclass; /* class of symbol (global/local) */ - int16_t dim PACKED; /* number of dimensions */ - const char name[1]; /* ASCII string, zero-terminated */ -} AMX_DBG_SYMBOL; - -typedef struct tagAMX_DBG_SYMDIM { - int16_t tag PACKED; /* tag for the array dimension */ - ucell size PACKED; /* size of the array dimension */ -} AMX_DBG_SYMDIM; - -typedef struct tagAMX_DBG_TAG { - int16_t tag PACKED; /* tag id */ - const char name[1]; /* ASCII string, zero-terminated */ -} AMX_DBG_TAG; - -typedef struct tagAMX_DBG_MACHINE { - int16_t automaton PACKED; /* automaton id */ - ucell address PACKED; /* address of state variable */ - const char name[1]; /* ASCII string, zero-terminated */ -} AMX_DBG_MACHINE; - -typedef struct tagAMX_DBG_STATE { - int16_t state PACKED; /* state id */ - int16_t automaton PACKED; /* automaton id */ - const char name[1]; /* ASCII string, zero-terminated */ -} AMX_DBG_STATE; - -typedef struct tagAMX_DBG { - AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */ - AMX_DBG_FILE **filetbl PACKED; - AMX_DBG_LINE *linetbl PACKED; - AMX_DBG_SYMBOL **symboltbl PACKED; - AMX_DBG_TAG **tagtbl PACKED; - AMX_DBG_MACHINE **automatontbl PACKED; - AMX_DBG_STATE **statetbl PACKED; -} AMX_DBG; - -#if !defined iVARIABLE - #define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ - #define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ - #define iARRAY 3 - #define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ - #define iFUNCTN 9 -#endif - - -int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg); -int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp); - -int AMXAPI dbg_LinearAddress(AMX *amx, ucell relative_addr, ucell *linear_addr); -int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename); -int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname); -int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line); - -int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address); -int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address); -int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name); -int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name); -int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name); -int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym); -int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim); - - -#if !defined AMX_NO_ALIGN - #if defined __LINUX__ || defined __FreeBSD__ - #pragma pack() /* reset default packing */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=reset - #else - #pragma pack(pop) /* reset previous packing */ - #endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* AMXDBG_H_INCLUDED */ +/* Abstract Machine for the Pawn compiler, debugger support + * + * This file contains extra definitions that are convenient for debugger + * support. + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxdbg.h 3612 2006-07-22 09:59:46Z thiadmer $ + */ + +#ifndef AMXDBG_H_INCLUDED +#define AMXDBG_H_INCLUDED + +#ifndef AMX_H_INCLUDED + #include "amx.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if defined SN_TARGET_PS2 || defined __GNUC__ + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_DBG_HDR { + int32_t size PACKED; /* size of the debug information chunk */ + uint16_t magic PACKED; /* signature, must be 0xf1ef */ + char file_version; /* file format version */ + char amx_version; /* required version of the AMX */ + int16_t flags PACKED; /* currently unused */ + int16_t files PACKED; /* number of entries in the "file" table */ + int16_t lines PACKED; /* number of entries in the "line" table */ + int16_t symbols PACKED; /* number of entries in the "symbol" table */ + int16_t tags PACKED; /* number of entries in the "tag" table */ + int16_t automatons PACKED; /* number of entries in the "automaton" table */ + int16_t states PACKED; /* number of entries in the "state" table */ +} PACKED AMX_DBG_HDR; +#define AMX_DBG_MAGIC 0xf1ef + +typedef struct tagAMX_DBG_FILE { + ucell address PACKED; /* address in the code segment where generated code (for this file) starts */ + const char name[1]; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_FILE; + +typedef struct tagAMX_DBG_LINE { + ucell address PACKED; /* address in the code segment where generated code (for this line) starts */ + int32_t line PACKED; /* line number */ +} PACKED AMX_DBG_LINE; + +typedef struct tagAMX_DBG_SYMBOL { + ucell address PACKED; /* address in the data segment or relative to the frame */ + int16_t tag PACKED; /* tag for the symbol */ + ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */ + ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */ + char ident; /* kind of symbol (function/variable) */ + char vclass; /* class of symbol (global/local) */ + int16_t dim PACKED; /* number of dimensions */ + const char name[1]; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_SYMBOL; + +typedef struct tagAMX_DBG_SYMDIM { + int16_t tag PACKED; /* tag for the array dimension */ + ucell size PACKED; /* size of the array dimension */ +} PACKED AMX_DBG_SYMDIM; + +typedef struct tagAMX_DBG_TAG { + int16_t tag PACKED; /* tag id */ + const char name[1]; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_TAG; + +typedef struct tagAMX_DBG_MACHINE { + int16_t automaton PACKED; /* automaton id */ + ucell address PACKED; /* address of state variable */ + const char name[1]; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_MACHINE; + +typedef struct tagAMX_DBG_STATE { + int16_t state PACKED; /* state id */ + int16_t automaton PACKED; /* automaton id */ + const char name[1]; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_STATE; + +typedef struct tagAMX_DBG { + AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */ + AMX_DBG_FILE **filetbl PACKED; + AMX_DBG_LINE *linetbl PACKED; + AMX_DBG_SYMBOL **symboltbl PACKED; + AMX_DBG_TAG **tagtbl PACKED; + AMX_DBG_MACHINE **automatontbl PACKED; + AMX_DBG_STATE **statetbl PACKED; +} PACKED AMX_DBG; + +#if !defined iVARIABLE + #define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ + #define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ + #define iARRAY 3 + #define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ + #define iFUNCTN 9 +#endif + + +int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg); +int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp); + +int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename); +int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname); +int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line); + +int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address); +int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address); +int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name); +int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name); +int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name); +int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym); +int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim); + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMXDBG_H_INCLUDED */ diff --git a/amx-deps/src/amx/amxdef.asm b/amx-deps/src/amx/amxdef.asm index 5dba428..2d0543d 100644 --- a/amx-deps/src/amx/amxdef.asm +++ b/amx-deps/src/amx/amxdef.asm @@ -1,90 +1,86 @@ -; Definition of the AMX structure for assembler syntax (MASM/TASM/WASM) - -amx_s STRUC - _base DD ? - _codeseg DD ? - _dataseg DD ? - _callback DD ? - _debug DD ? - _overlay DD ? - _cip DD ? - _frm DD ? - _hea DD ? - _hlw DD ? - _stk DD ? - _stp DD ? - _flags DD ? - _usertags DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h) - _userdata DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h) - _error DD ? - _paramcount DD ? - _pri DD ? - _alt DD ? - _reset_stk DD ? - _reset_hea DD ? - _syscall_d DD ? - _ovl_index DD ? - _codesize DD ? ; memory size of the overlay or of the native code -IFDEF JIT - ; the two fields below are for the JIT; they do not exist in - ; the non-JIT version of the abstract machine - _reloc_size DD ? ; memory block for relocations -ENDIF -amx_s ENDS - -amxhead_s STRUC - _size DD ? ; size of the "file" - _magic DW ? ; signature - _file_version DB ? ;file format version - _amx_version DB ? ; required version of the AMX - _h_flags DW ? - _defsize DW ? ; size of one public/native function entry - _cod DD ? ; initial value of COD - code block - _dat DD ? ; initial value of DAT - data block - _h_hea DD ? ; initial value of HEA - start of the heap - _h_stp DD ? ; initial value of STP - stack top - _h_cip DD ? ; initial value of CIP - the instruction pointer - _publics DD ? ; offset to the "public functions" table - _natives DD ? ; offset to the "native functions" table - _libraries DD ? ; offset to the "library" table - _pubvars DD ? ; offset to the "public variables" table - _tags DD ? ; offset to the "public tagnames" table - _nametable DD ? ; offset to the name table, file version 7+ only - _overlaytbl DD ? ; offset to the overlay table, file version 10+ only -amxhead_s ENDS - - -AMX_ERR_NONE EQU 0 -AMX_ERR_EXIT EQU 1 -AMX_ERR_ASSERT EQU 2 -AMX_ERR_STACKERR EQU 3 -AMX_ERR_BOUNDS EQU 4 -AMX_ERR_MEMACCESS EQU 5 -AMX_ERR_INVINSTR EQU 6 -AMX_ERR_STACKLOW EQU 7 -AMX_ERR_HEAPLOW EQU 8 -AMX_ERR_CALLBACK EQU 9 -AMX_ERR_NATIVE EQU 10 -AMX_ERR_DIVIDE EQU 11 ; for catching divide errors -AMX_ERR_SLEEP EQU 12 - -AMX_ERR_MEMORY EQU 16 -AMX_ERR_FORMAT EQU 17 -AMX_ERR_VERSION EQU 18 -AMX_ERR_NOTFOUND EQU 19 -AMX_ERR_INDEX EQU 20 -AMX_ERR_DEBUG EQU 21 -AMX_ERR_INIT EQU 22 -AMX_ERR_USERDATA EQU 23 -AMX_ERR_INIT_JIT EQU 24 -AMX_ERR_PARAMS EQU 25 -AMX_ERR_DOMAIN EQU 26 -AMX_ERR_GENERAL EQU 27 - -AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available -AMX_FLAG_COMPACT EQU 0004h -AMX_FLAG_BYTEOPC EQU 0008h -AMX_FLAG_NOCHECKS EQU 0010h -AMX_FLAG_BROWSE EQU 4000h -AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated - +; Definition of the AMX structure for assembler syntax (MASM/TASM/WASM) + +amx_s STRUC + _base DD ? + _dataseg DD ? + _callback DD ? + _debug DD ? + _cip DD ? + _frm DD ? + _hea DD ? + _hlw DD ? + _stk DD ? + _stp DD ? + _flags DD ? + _usertags DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h) + _userdata DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h) + _error DD ? + _paramcount DD ? + _pri DD ? + _alt DD ? + _reset_stk DD ? + _reset_hea DD ? + _syscall_d DD ? +IFDEF JIT + ; the two fields below are for the JIT; they do not exist in + ; the non-JIT version of the abstract machine + _reloc_size DD ? ; memory block for relocations + _code_size DD ? ; memory size of the native code +ENDIF +amx_s ENDS + +amxhead_s STRUC + _size DD ? ; size of the "file" + _magic DW ? ; signature + _file_version DB ? ;file format version + _amx_version DB ? ; required version of the AMX + _h_flags DW ? + _defsize DW ? ; size of one public/native function entry + _cod DD ? ; initial value of COD - code block + _dat DD ? ; initial value of DAT - data block + _h_hea DD ? ; initial value of HEA - start of the heap + _h_stp DD ? ; initial value of STP - stack top + _h_cip DD ? ; initial value of CIP - the instruction pointer + _publics DD ? ; offset to the "public functions" table + _natives DD ? ; offset to the "native functions" table + _libraries DD ? ; offset to the "library" table + _pubvars DD ? ; offset to the "public variables" table + _tags DD ? ; offset to the "public tagnames" table + _nametable DD ? ; offset to the name table, file version 7 only +amxhead_s ENDS + + +AMX_ERR_NONE EQU 0 +AMX_ERR_EXIT EQU 1 +AMX_ERR_ASSERT EQU 2 +AMX_ERR_STACKERR EQU 3 +AMX_ERR_BOUNDS EQU 4 +AMX_ERR_MEMACCESS EQU 5 +AMX_ERR_INVINSTR EQU 6 +AMX_ERR_STACKLOW EQU 7 +AMX_ERR_HEAPLOW EQU 8 +AMX_ERR_CALLBACK EQU 9 +AMX_ERR_NATIVE EQU 10 +AMX_ERR_DIVIDE EQU 11 ; for catching divide errors +AMX_ERR_SLEEP EQU 12 + +AMX_ERR_MEMORY EQU 16 +AMX_ERR_FORMAT EQU 17 +AMX_ERR_VERSION EQU 18 +AMX_ERR_NOTFOUND EQU 19 +AMX_ERR_INDEX EQU 20 +AMX_ERR_DEBUG EQU 21 +AMX_ERR_INIT EQU 22 +AMX_ERR_USERDATA EQU 23 +AMX_ERR_INIT_JIT EQU 24 +AMX_ERR_PARAMS EQU 25 +AMX_ERR_DOMAIN EQU 26 +AMX_ERR_GENERAL EQU 27 + +AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available +AMX_FLAG_COMPACT EQU 0004h +AMX_FLAG_BYTEOPC EQU 0008h +AMX_FLAG_NOCHECKS EQU 0010h +AMX_FLAG_BROWSE EQU 4000h +AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated + diff --git a/amx-deps/src/amx/amxdefn.asm b/amx-deps/src/amx/amxdefn.asm index 24c4b51..450f6a0 100644 --- a/amx-deps/src/amx/amxdefn.asm +++ b/amx-deps/src/amx/amxdefn.asm @@ -1,90 +1,86 @@ -; Definition of the AMX structure for assembler syntax (NASM) - - struc amx_s -_base: resd 1 -_codeseg: resd 1 -_dataseg: resd 1 -_callback: resd 1 -_debug: resd 1 -_overlay: resd 1 -_cip: resd 1 -_frm: resd 1 -_hea: resd 1 -_hlw: resd 1 -_stk: resd 1 -_stp: resd 1 -_flags: resd 1 -_usertags: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) -_userdata: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) -_error: resd 1 -_paramcount: resd 1 -_pri: resd 1 -_alt: resd 1 -_reset_stk: resd 1 -_reset_hea: resd 1 -_syscall_d: resd 1 -_ovl_index: resd 1 -_codesize: resd 1 ; memory size of the overlay or of the native code -%ifdef JIT - ; the two fields below are for the JIT; they do not exist in - ; the non-JIT version of the abstract machine -_reloc_size: resd 1 ; memory block for relocations -%endif - endstruc - - struc amxhead_s -_size: resd 1 ; size of the "file" -_magic: resw 1 ; signature -_file_version: resb 1; file format version -_amx_version: resb 1 ; required version of the AMX -_h_flags: resw 1 -_defsize: resw 1 ; size of one public/native function entry -_cod: resd 1 ; initial value of COD - code block -_dat: resd 1 ; initial value of DAT - data block -_h_hea: resd 1 ; initial value of HEA - start of the heap -_h_stp: resd 1 ; initial value of STP - stack top -_h_cip: resd 1 ; initial value of CIP - the instruction pointer -_publics: resd 1 ; offset to the "public functions" table -_natives: resd 1 ; offset to the "native functions" table -_libraries: resd 1 ; offset to the "library" table -_pubvars: resd 1 ; offset to the "public variables" table -_tags: resd 1 ; offset to the "public tagnames" table -_nametable: resd 1 ; offset to the name table, file version 7+ only -_overlaytbl: resd 1 ; offset to the overlay table, file version 10+ only - endstruc - - -AMX_ERR_NONE EQU 0 -AMX_ERR_EXIT EQU 1 -AMX_ERR_ASSERT EQU 2 -AMX_ERR_STACKERR EQU 3 -AMX_ERR_BOUNDS EQU 4 -AMX_ERR_MEMACCESS EQU 5 -AMX_ERR_INVINSTR EQU 6 -AMX_ERR_STACKLOW EQU 7 -AMX_ERR_HEAPLOW EQU 8 -AMX_ERR_CALLBACK EQU 9 -AMX_ERR_NATIVE EQU 10 -AMX_ERR_DIVIDE EQU 11 ; for catching divide errors -AMX_ERR_SLEEP EQU 12 - -AMX_ERR_MEMORY EQU 16 -AMX_ERR_FORMAT EQU 17 -AMX_ERR_VERSION EQU 18 -AMX_ERR_NOTFOUND EQU 19 -AMX_ERR_INDEX EQU 20 -AMX_ERR_DEBUG EQU 21 -AMX_ERR_INIT EQU 22 -AMX_ERR_USERDATA EQU 23 -AMX_ERR_INIT_JIT EQU 24 -AMX_ERR_PARAMS EQU 25 -AMX_ERR_DOMAIN EQU 26 -AMX_ERR_GENERAL EQU 27 - -AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available -AMX_FLAG_COMPACT EQU 0004h -AMX_FLAG_BYTEOPC EQU 0008h -AMX_FLAG_NOCHECKS EQU 0010h -AMX_FLAG_BROWSE EQU 4000h -AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated - +; Definition of the AMX structure for assembler syntax (NASM) + + struc amx_s +_base: resd 1 +_dataseg: resd 1 +_callback: resd 1 +_debug: resd 1 +_cip: resd 1 +_frm: resd 1 +_hea: resd 1 +_hlw: resd 1 +_stk: resd 1 +_stp: resd 1 +_flags: resd 1 +_usertags: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) +_userdata: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) +_error: resd 1 +_paramcount: resd 1 +_pri: resd 1 +_alt: resd 1 +_reset_stk: resd 1 +_reset_hea: resd 1 +_syscall_d: resd 1 +%ifdef JIT + ; the two fields below are for the JIT; they do not exist in + ; the non-JIT version of the abstract machine +_reloc_size: resd 1 ; memory block for relocations +_code_size: resd 1 ; memory size of the native code +%endif + endstruc + + struc amxhead_s +_size: resd 1 ; size of the "file" +_magic: resw 1 ; signature +_file_version: resb 1; file format version +_amx_version: resb 1 ; required version of the AMX +_h_flags: resw 1 +_defsize: resw 1 ; size of one public/native function entry +_cod: resd 1 ; initial value of COD - code block +_dat: resd 1 ; initial value of DAT - data block +_h_hea: resd 1 ; initial value of HEA - start of the heap +_h_stp: resd 1 ; initial value of STP - stack top +_h_cip: resd 1 ; initial value of CIP - the instruction pointer +_publics: resd 1 ; offset to the "public functions" table +_natives: resd 1 ; offset to the "native functions" table +_libraries: resd 1 ; offset to the "library" table +_pubvars: resd 1 ; offset to the "public variables" table +_tags: resd 1 ; offset to the "public tagnames" table +_nametable: resd 1 ; offset to the name table, file version 7 only + endstruc + + +AMX_ERR_NONE EQU 0 +AMX_ERR_EXIT EQU 1 +AMX_ERR_ASSERT EQU 2 +AMX_ERR_STACKERR EQU 3 +AMX_ERR_BOUNDS EQU 4 +AMX_ERR_MEMACCESS EQU 5 +AMX_ERR_INVINSTR EQU 6 +AMX_ERR_STACKLOW EQU 7 +AMX_ERR_HEAPLOW EQU 8 +AMX_ERR_CALLBACK EQU 9 +AMX_ERR_NATIVE EQU 10 +AMX_ERR_DIVIDE EQU 11 ; for catching divide errors +AMX_ERR_SLEEP EQU 12 + +AMX_ERR_MEMORY EQU 16 +AMX_ERR_FORMAT EQU 17 +AMX_ERR_VERSION EQU 18 +AMX_ERR_NOTFOUND EQU 19 +AMX_ERR_INDEX EQU 20 +AMX_ERR_DEBUG EQU 21 +AMX_ERR_INIT EQU 22 +AMX_ERR_USERDATA EQU 23 +AMX_ERR_INIT_JIT EQU 24 +AMX_ERR_PARAMS EQU 25 +AMX_ERR_DOMAIN EQU 26 +AMX_ERR_GENERAL EQU 27 + +AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available +AMX_FLAG_COMPACT EQU 0004h +AMX_FLAG_BYTEOPC EQU 0008h +AMX_FLAG_NOCHECKS EQU 0010h +AMX_FLAG_BROWSE EQU 4000h +AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated + diff --git a/amx-deps/src/amx/amxdgram.c b/amx-deps/src/amx/amxdgram.c index 3db6c97..51345a2 100644 --- a/amx-deps/src/amx/amxdgram.c +++ b/amx-deps/src/amx/amxdgram.c @@ -1,358 +1,357 @@ -/* Datagram sending/receiving module for the Pawn Abstract Machine - * - * This module uses the UDP protocol (from the TCP/IP protocol suite). - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxdgram.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include -#include -#include "osdefs.h" -#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - #include - #include - #include - #include - #include - #include - #include -#else - #include - #include -#endif -#include "amx.h" - - -#define SRC_BUFSIZE 22 -#define BUFLEN 512 -#define AMX_DGRAMPORT 9930 /* default port */ - -#if !defined SOCKET_ERROR - #define SOCKET_ERROR -1 -#endif - -static int sLocal; - -static unsigned long udp_GetHostAddr(const char *host,int index) -{ - unsigned long addr=inet_addr(host); - if (addr==0xffffffffL) { - struct hostent *phost=gethostbyname(host); - if (phost!=NULL) { - /* count the number of addresses in the list */ - int count; - for (count=0; phost->h_addr_list[count]!=0; count++) - /* nothing */; - if (indexh_addr_list[index]; - } /* if */ - } /* if */ - return addr; -} - -static int udp_Open(void) -{ -#if defined __WIN32 || defined _WIN32 || defined WIN32 - WORD wVersionRequested = MAKEWORD(1,1); - WSADATA wsaData; -#endif - int optval = 1; - - #if defined __WIN32 || defined _WIN32 || defined WIN32 - WSAStartup(wVersionRequested, &wsaData); - #endif - - if ((sLocal=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - return -1; - - if (setsockopt(sLocal, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof optval) == -1) - return -1; - - return sLocal; -} - -static int udp_Close(void) -{ - if (sLocal>=0) { - #if defined __WIN32 || defined _WIN32 || defined WIN32 - closesocket(sLocal); - #else - close(sLocal); - #endif - } /* if */ - - #if defined __WIN32 || defined _WIN32 || defined WIN32 - WSACleanup(); - #endif - - return 0; -} - -static int udp_Send(const char *host,short port,const char *message,int size) -{ - struct sockaddr_in sRemote; - - if (sLocal<0) - return -1; - - memset((void *)&sRemote,sizeof sRemote,0); - sRemote.sin_family=AF_INET; - sRemote.sin_port=htons(port); - sRemote.sin_addr.s_addr= (host==NULL) ? htonl(INADDR_BROADCAST) : udp_GetHostAddr(host,0); - - if (sendto(sLocal,message,size,0,(struct sockaddr *)&sRemote,sizeof sRemote)==-1) - return -1; - - return size; -} - -/* This call is blocking - * if source is not NULL, it must point to a buffer that can contain at least - * 22 characters. - */ -static int udp_Receive(char *message,size_t maxmsg,char *source) -{ - struct sockaddr_in sSource; - int slen=sizeof(sSource); - int size; - - size=recvfrom(sLocal, message, maxmsg, 0, (struct sockaddr *)&sSource, &slen); - if (size==-1) - return -1; - if (source!=NULL) - sprintf(source, "%s:%d", inet_ntoa(sSource.sin_addr), ntohs(sSource.sin_port)); - - return size; -} - -static int udp_IsPacket(void) -{ - int result; - fd_set rdset; - struct timeval time; - - /* the select() function waits until the socket can be read, or until a - * time-out occurs; the time-out is set to 1 microsecond (the shortest - * delay possible). - */ - time.tv_sec=0; - time.tv_usec=1; - FD_ZERO(&rdset); - FD_SET(sLocal,&rdset); - result=select(0,&rdset,NULL,NULL,&time); - if (result==SOCKET_ERROR) - return -1; - - return result != 0; -} - -static int udp_Listen(short port) -{ - struct sockaddr_in sFrom; - - memset((void *)&sFrom,sizeof sFrom,0); - sFrom.sin_family=AF_INET; - sFrom.sin_port=htons(port); - sFrom.sin_addr.s_addr=htonl(INADDR_ANY); - if (bind(sLocal,(struct sockaddr *)&sFrom,sizeof sFrom)==-1) - return -1; - - return 0; -} - -static AMX_IDLE PrevIdle = NULL; -static int idxReceiveString = -1; -static int idxReceivePacket = -1; -static short dgramPort = 0; -static int dgramBound = 0; - -/* sendstring(const message[], const destination[]="") - * destination has the format "127.0.0.1:9930"; when set to an empty string, - * a broadcast is sent. - * To mark the text as a "string", the function inserts a "byte order mark" in - * front of it. It does this for Extended ASCII strings too, although this is - * not entirely correct. - * Returns true on success, false on failure. - */ -static cell AMX_NATIVE_CALL n_sendstring(AMX *amx, const cell *params) -{ - int r = 0, length; - cell *cstr; - char *host, *message, *ptr; - short port=AMX_DGRAMPORT; - - amx_GetAddr(amx, params[1], &cstr); - amx_UTF8Len(cstr, &length); - - if ((message = alloca(length + 3 + 1)) != NULL) { - /* insert the byte order mark (BOM) */ - message[0]='\xef'; - message[1]='\xbb'; - message[2]='\xbf'; - /* if this is a wide string, convert it to UTF-8 */ - if ((ucell)*cstr<=UNPACKEDMAX) { - ptr=message+3; - while (*cstr!=0) - amx_UTF8Put(ptr, &ptr, length - (ptr-message), *cstr++); - *ptr='\0'; - } else { - amx_GetString(message+3, cstr, 0, UNLIMITED); - } /* if */ - - amx_StrParam(amx, params[2], host); - if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { - *ptr++='\0'; - port=(short)atoi(ptr); - } /* if */ - r= (udp_Send(host,port,message,strlen(message)+1) > 0); - } /* if */ - - return r; -} - -/* sendpacket(const packet[], size, const destination[]="") - * destination has the format "127.0.0.1:9930"; when set to an empty string, - * a broadcast is sent. - * Returns true on success, false on failure. - */ -static cell AMX_NATIVE_CALL n_sendpacket(AMX *amx, const cell *params) -{ - cell *cstr; - char *host, *ptr; - short port=AMX_DGRAMPORT; - - amx_GetAddr(amx, params[1], &cstr); - amx_StrParam(amx, params[3], host); - if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { - *ptr++='\0'; - port=(short)atoi(ptr); - } /* if */ - return (udp_Send(host,port,(const char *)cstr,params[2] * sizeof(cell)) > 0); -} - -/* listenport(port) - * A program must call this function from main() or another start-up function - * because the module will use the default port 9930 otherwise. - */ -static cell AMX_NATIVE_CALL n_listenport(AMX *amx, const cell *params) -{ - (void)amx; - dgramPort = (short)params[1]; - return 0; -} - -static int AMXAPI amx_DGramIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) -{ - char message[BUFLEN], source[SRC_BUFSIZE]; - cell amx_addr_msg, amx_addr_src; - int len, chars; - int err=0; - - assert(idxReceiveString >= 0 || idxReceivePacket >= 0); - - if (PrevIdle != NULL) - PrevIdle(amx, Exec); - - /* set up listener (first call only) */ - if (!dgramBound) { - if (dgramPort==0) - dgramPort=AMX_DGRAMPORT; /* use default port if none was set */ - if (udp_Listen(dgramPort)==-1) - return AMX_ERR_GENERAL; - dgramBound=1; - } /* if */ - - if (udp_IsPacket()) { - len=udp_Receive(message, sizeof message / sizeof message[0], source); - amx_PushString(amx,&amx_addr_src,NULL,source,1,0); - /* check the presence of a byte order mark: if it is absent, the received - * packet is no string; also check the packet size against string length - */ - if ((message[0]!='\xef' || message[1]!='\xbb' || message[2]!='\xbf' - || len!=(int)strlen(message)+1 || idxReceiveString<0) && idxReceivePacket>=0) - { - /* receive as "packet" */ - amx_Push(amx,len); - amx_PushArray(amx,&amx_addr_msg,NULL,(cell*)message,len); - err=Exec(amx,NULL,idxReceivePacket); - } else { - const char *msg=message; - if (msg[0]=='\xef' && msg[1]=='\xbb' && msg[2]=='\xbf') - msg+=3; /* skip BOM */ - /* optionally convert from UTF-8 to a wide string */ - if (amx_UTF8Check(msg,&chars)==AMX_ERR_NONE) { - cell *array=alloca((chars+1)*sizeof(cell)); - cell *ptr=array; - if (array!=NULL) { - while (err==AMX_ERR_NONE && *msg!='\0') - amx_UTF8Get(msg,&msg,ptr++); - *ptr=0; /* zero-terminate */ - amx_PushArray(amx,&amx_addr_msg,NULL,array,chars+1); - } /* if */ - } else { - amx_PushString(amx,&amx_addr_msg,NULL,msg,1,0); - } /* if */ - err=Exec(amx,NULL,idxReceiveString); - } /* if */ - while (err==AMX_ERR_SLEEP) - err=Exec(amx,NULL,AMX_EXEC_CONT); - amx_Release(amx,amx_addr_msg); - amx_Release(amx,amx_addr_src); - } /* if */ - - return err; -} - -#if defined __cplusplus - extern "C" -#endif -AMX_NATIVE_INFO dgram_Natives[] = { - { "sendstring", n_sendstring }, - { "sendpacket", n_sendpacket }, - { "listenport", n_listenport }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_DGramInit(AMX *amx) -{ - dgramBound = 0; - if (udp_Open()==-1) - return AMX_ERR_GENERAL; - - /* see whether there is an @receivestring() function */ - if (amx_FindPublic(amx,"@receivestring",&idxReceiveString)==AMX_ERR_NONE - || amx_FindPublic(amx,"@receivepacket",&idxReceivePacket)==AMX_ERR_NONE) - { - if (amx_GetUserData(amx,AMX_USERTAG('I','d','l','e'),(void**)&PrevIdle)!=AMX_ERR_NONE) - PrevIdle=NULL; - amx_SetUserData(amx,AMX_USERTAG('I','d','l','e'),amx_DGramIdle); - } /* if */ - - return amx_Register(amx,dgram_Natives,-1); -} - -int AMXEXPORT amx_DGramCleanup(AMX *amx) -{ - (void)amx; - udp_Close(); - return AMX_ERR_NONE; -} +/* Datagram sending/receiving module for the Pawn Abstract Machine + * + * This module uses the UDP protocol (from the TCP/IP protocol suite). + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxdgram.c 3664 2006-11-08 12:09:25Z thiadmer $ + */ + +#include +#include +#if defined LINUX + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include +#endif +#include "osdefs.h" +#include "amx.h" + + +#define SRC_BUFSIZE 22 +#define BUFLEN 512 +#define AMX_DGRAMPORT 9930 /* default port */ + +#if !defined SOCKET_ERROR + #define SOCKET_ERROR -1 +#endif + +static int sLocal; + +static unsigned long udp_GetHostAddr(const char *host,int index) +{ + unsigned long addr=inet_addr(host); + if (addr==0xffffffffL) { + struct hostent *phost=gethostbyname(host); + if (phost!=NULL) { + /* count the number of addresses in the list */ + int count; + for (count=0; phost->h_addr_list[count]!=0; count++) + /* nothing */; + if (indexh_addr_list[index]; + } /* if */ + } /* if */ + return addr; +} + +static int udp_Open(void) +{ +#if defined __WIN32 || defined _WIN32 || defined WIN32 + WORD wVersionRequested = MAKEWORD(1,1); + WSADATA wsaData; +#endif + int optval = 1; + + #if defined __WIN32 || defined _WIN32 || defined WIN32 + WSAStartup(wVersionRequested, &wsaData); + #endif + + if ((sLocal=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (setsockopt(sLocal, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof optval) == -1) + return -1; + + return sLocal; +} + +static int udp_Close(void) +{ + if (sLocal>=0) { + #if defined __WIN32 || defined _WIN32 || defined WIN32 + closesocket(sLocal); + #else + close(sLocal); + #endif + } /* if */ + + #if defined __WIN32 || defined _WIN32 || defined WIN32 + WSACleanup(); + #endif + + return 0; +} + +static int udp_Send(const char *host,short port,const char *message,int size) +{ + struct sockaddr_in sRemote; + + if (sLocal<0) + return -1; + + memset((void *)&sRemote,sizeof sRemote,0); + sRemote.sin_family=AF_INET; + sRemote.sin_port=htons(port); + sRemote.sin_addr.s_addr= (host==NULL) ? htonl(INADDR_BROADCAST) : udp_GetHostAddr(host,0); + + if (sendto(sLocal,message,size,0,(struct sockaddr *)&sRemote,sizeof sRemote)==-1) + return -1; + + return size; +} + +/* This call is blocking + * if source is not NULL, it must point to a buffer that can contain at least + * 22 characters. + */ +static int udp_Receive(char *message,size_t maxmsg,char *source) +{ + struct sockaddr_in sSource; + int slen=sizeof(sSource); + int size; + + size=recvfrom(sLocal, message, maxmsg, 0, (struct sockaddr *)&sSource, &slen); + if (size==-1) + return -1; + if (source!=NULL) + sprintf(source, "%s:%d", inet_ntoa(sSource.sin_addr), ntohs(sSource.sin_port)); + + return size; +} + +static int udp_IsPacket(void) +{ + int result; + fd_set rdset; + struct timeval time; + + /* the select() function waits until the socket can be read, or until a + * time-out occurs; the time-out is set to 1 microsecond (the shortest + * delay possible). + */ + time.tv_sec=0; + time.tv_usec=1; + FD_ZERO(&rdset); + FD_SET(sLocal,&rdset); + result=select(0,&rdset,NULL,NULL,&time); + if (result==SOCKET_ERROR) + return -1; + + return result != 0; +} + +static int udp_Listen(short port) +{ + struct sockaddr_in sFrom; + + memset((void *)&sFrom,sizeof sFrom,0); + sFrom.sin_family=AF_INET; + sFrom.sin_port=htons(port); + sFrom.sin_addr.s_addr=htonl(INADDR_ANY); + if (bind(sLocal,(struct sockaddr *)&sFrom,sizeof sFrom)==-1) + return -1; + + return 0; +} + +static AMX_IDLE PrevIdle = NULL; +static int idxReceiveString = -1; +static int idxReceivePacket = -1; +static short dgramPort = 0; +static int dgramBound = 0; + +/* sendstring(const message[], const destination[]="") + * destination has the format "127.0.0.1:9930"; when set to an empty string, + * a broadcast is sent. + * To mark the text as a "string", the function inserts a "byte order mark" in + * front of it. It does this for Extended ASCII strings too, although this is + * not entirely correct. + * Returns true on success, false on failure. + */ +static cell AMX_NATIVE_CALL n_sendstring(AMX *amx, const cell *params) +{ + int r = 0, length; + cell *cstr; + char *host, *message, *ptr; + short port=AMX_DGRAMPORT; + + amx_GetAddr(amx, params[1], &cstr); + amx_UTF8Len(cstr, &length); + + if ((message = alloca(length + 3 + 1)) != NULL) { + /* insert the byte order mark (BOM) */ + message[0]='\xef'; + message[1]='\xbb'; + message[2]='\xbf'; + /* if this is a wide string, convert it to UTF-8 */ + if ((ucell)*cstr<=UNPACKEDMAX) { + ptr=message+3; + while (*cstr!=0) + amx_UTF8Put(ptr, &ptr, length - (ptr-message), *cstr++); + *ptr='\0'; + } else { + amx_GetString(message+3, cstr, 0, UNLIMITED); + } /* if */ + + amx_StrParam(amx, params[2], host); + if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { + *ptr++='\0'; + port=(short)atoi(ptr); + } /* if */ + r= (udp_Send(host,port,message,strlen(message)+1) > 0); + } /* if */ + + return r; +} + +/* sendpacket(const packet[], size, const destination[]="") + * destination has the format "127.0.0.1:9930"; when set to an empty string, + * a broadcast is sent. + * Returns true on success, false on failure. + */ +static cell AMX_NATIVE_CALL n_sendpacket(AMX *amx, const cell *params) +{ + cell *cstr; + char *host, *ptr; + short port=AMX_DGRAMPORT; + + amx_GetAddr(amx, params[1], &cstr); + amx_StrParam(amx, params[3], host); + if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { + *ptr++='\0'; + port=(short)atoi(ptr); + } /* if */ + return (udp_Send(host,port,(const char *)cstr,params[2] * sizeof(cell)) > 0); +} + +/* listenport(port) + * A program must call this function from main() or another start-up function + * because the module will use the default port 9930 otherwise. + */ +static cell AMX_NATIVE_CALL n_listenport(AMX *amx, const cell *params) +{ + (void)amx; + dgramPort = (short)params[1]; + return 0; +} + +static int AMXAPI amx_DGramIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) +{ + char message[BUFLEN], source[SRC_BUFSIZE]; + cell amx_addr_msg, amx_addr_src; + int len, chars; + int err=0; + + assert(idxReceiveString >= 0 || idxReceivePacket >= 0); + + if (PrevIdle != NULL) + PrevIdle(amx, Exec); + + /* set up listener (first call only) */ + if (!dgramBound) { + if (dgramPort==0) + dgramPort=AMX_DGRAMPORT; /* use default port if none was set */ + if (udp_Listen(dgramPort)==-1) + return AMX_ERR_GENERAL; + dgramBound=1; + } /* if */ + + if (udp_IsPacket()) { + len=udp_Receive(message, sizeof message / sizeof message[0], source); + amx_PushString(amx,&amx_addr_src,NULL,source,1,0); + /* check the presence of a byte order mark: if it is absent, the received + * packet is no string; also check the packet size against string length + */ + if ((message[0]!='\xef' || message[1]!='\xbb' || message[2]!='\xbf' + || len!=(int)strlen(message)+1 || idxReceiveString<0) && idxReceivePacket>=0) + { + /* receive as "packet" */ + amx_Push(amx,len); + amx_PushArray(amx,&amx_addr_msg,NULL,(cell*)message,len); + err=Exec(amx,NULL,idxReceivePacket); + } else { + const char *msg=message; + if (msg[0]=='\xef' && msg[1]=='\xbb' && msg[2]=='\xbf') + msg+=3; /* skip BOM */ + /* optionally convert from UTF-8 to a wide string */ + if (amx_UTF8Check(msg,&chars)==AMX_ERR_NONE) { + cell *array=alloca((chars+1)*sizeof(cell)); + cell *ptr=array; + if (array!=NULL) { + while (err==AMX_ERR_NONE && *msg!='\0') + amx_UTF8Get(msg,&msg,ptr++); + *ptr=0; /* zero-terminate */ + amx_PushArray(amx,&amx_addr_msg,NULL,array,chars+1); + } /* if */ + } else { + amx_PushString(amx,&amx_addr_msg,NULL,msg,1,0); + } /* if */ + err=Exec(amx,NULL,idxReceiveString); + } /* if */ + while (err==AMX_ERR_SLEEP) + err=Exec(amx,NULL,AMX_EXEC_CONT); + amx_Release(amx,amx_addr_msg); + amx_Release(amx,amx_addr_src); + } /* if */ + + return err; +} + +#if defined __cplusplus + extern "C" +#endif +AMX_NATIVE_INFO dgram_Natives[] = { + { "sendstring", n_sendstring }, + { "sendpacket", n_sendpacket }, + { "listenport", n_listenport }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_DGramInit(AMX *amx) +{ + dgramBound = 0; + if (udp_Open()==-1) + return AMX_ERR_GENERAL; + + /* see whether there is an @receivestring() function */ + if (amx_FindPublic(amx,"@receivestring",&idxReceiveString)==AMX_ERR_NONE + || amx_FindPublic(amx,"@receivepacket",&idxReceivePacket)==AMX_ERR_NONE) + { + if (amx_GetUserData(amx,AMX_USERTAG('I','d','l','e'),(void**)&PrevIdle)!=AMX_ERR_NONE) + PrevIdle=NULL; + amx_SetUserData(amx,AMX_USERTAG('I','d','l','e'),amx_DGramIdle); + } /* if */ + + return amx_Register(amx,dgram_Natives,-1); +} + +int AMXEXPORT amx_DGramCleanup(AMX *amx) +{ + (void)amx; + udp_Close(); + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxexec.asm b/amx-deps/src/amx/amxexec.asm index f990b54..6ae718e 100644 --- a/amx-deps/src/amx/amxexec.asm +++ b/amx-deps/src/amx/amxexec.asm @@ -1,2713 +1,2112 @@ -; AMXEXEC.ASM Abstract Machine for the "Pawn" language -; - -;Some notes: -; * Please also look at the "history" log (below) for more notes. -; * This file assembles with Watcom's WASM, Borland's TASM and Microsoft's -; MASM; see the file "amxexecn.asm" for a version that works with NASM. -; * There are two "calling convention" issues: -; o The convention with which amx_exec_asm() itself is called. The default -; calling convention is Watcom's register calling convention. Change -; this to __cdecl by setting the macro STACKARGS. -; o The convention for calling the "hook" functions (native functions and -; the debugger callback). Again, the default is Watcom's register calling -; convention. Use the macros CDECL or STDECL for __cdecl and __stdcall -; respectively. (Since STDCALL is a reserved word on the assembler, I had -; to choose a different name for the macro, hence STDECL.) -; * You will need to compile the standard AMX.C file with the macro ASM32 -; defined. On the command line, use: -; wcl386 /l=nt /dASM32 srun.c amx.c amxcore.c amxcons.c amxexec.asm -; * OP_CASETBL and OP_ICASETBL are not implemented, but they should not occur -; anyway. -; * Since the move to position-independent code, all obsolete instructions have -; been removed; you the Pawn compiler that generates position-independent code -; and that compiler does not generate obsolete instructions. -; * Not all opcodes are tested. Only those generated when using the demo -; programs are giving the impression to be working. I've flagged them "good". -; (I'm pretty sure now that all other codes are working correctly, too.) -; [I have removed these marks, because both this source code and the demo -; programs have changed significantly since the original development of this -; module --Thiadmer Riemersma] -; * On my P200 this implementation reaches >30 mill instr/sec - quite good. -; It's 5 times as fast as the ANSI C version of SRUN.EXE (supplied with the -; archive). (This was measured with the Hanoi "benchmark" [no output].) -; -; -;Copyright and license of use, please read -;----------------------------------------- -;The assembler implementation of the abstract machine for the Pawn language, -;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. -; -;Permission is hereby granted, without written agreement and without paid -;license or royalty fees, to use, copy, modify, and distribute this software -;and its documentation for any purpose, subject to the following conditions: -; -;1. The above copyright notice and this permission notice shall appear in all -; copies or substantial portions of this software. -; -;2. Modifications of this software that do not originate from me (Marc Peter) -; must be explicitly mentioned in a README file or another appropriate -; place. -; -;The use of this software as a subsystem of a larger software product is -;explicitly allowed, regardless of whether that larger product is proprietary, -;gratis or commercially available. -; -;I (Marc Peter) specifically disclaim any warranties, including, but not -;limited to, the implied warranties of merchantability and fitness for a -;particular purpose. The software is provided on an "as is" basis, -;and I have no obligation to provide maintenance, support, updates, -;enhancements or modifications. -; -;I cannot be held liable for any damage or loss of profits that results -;from the use of the software (or part thereof), or from the inability to -;use it. -; -; -;History (list of changes) -;------------------------- -; 26 august 2007 by Thiadmer Riemersma -; Minor clean-up; removed unneeded parameter. -; 31 may 2007 by Thiadmer Riemersma -; Added packed opcodes -; 30 april 2007 by Thiadmer Riemersma -; Move to position-independent code (no more relocation needed for -; branches). -; Removed cases for obsolete instructions. -; 14 december 2005 by Thiadmer Riemersma (TR) -; Addition of macro instructions, to speed up instruction decoding -; 17 february 2005 by Thiadmer Riemersma (TR) -; Addition of the BREAK opcode, removal of the older debugging opcode table. -; 6 march 2004 by Thiadmer Riemersma -; Corrected a bug in OP_FILL, where a cell preceding the array would -; be overwritten (zero'ed out). This bug was brought to my attention -; by Robert Daniels. -; 2 february 2004 by Thiadmer Riemersma (TR) -; Added checking of the return address in the RET and RETN opcodes. -; Changed handling of LINE opcode, so that the debugger can force a -; sleep. -; 22 december 2003 by Thiadmer Riemersma (TR) -; Added support for the SYMTAG and SYSCALL.D opcodes -; 3 october 2003 by Thiadmer Riemersma (TR) -; Added "non-debug" versions of various opcodes, to avoid repetitive -; checking of the "debug" flag. -; 1 July 2002 by Thiadmer Riemersma (TR) -; Modifications due to added fields in the AMX structure -; RET and RETN instructions no longer check for a 0 return address -; Stack top is now relative to the start of the data segment, to allow -; for "cloned" AMX'es -; The semantics of _CHKSTACK and _CHKMARGIN were inverted; now corrected -; New opcodes -; * OP_CALLN (but commented out, due to problems with the JIT) -; * OP_NOP -;27 August 2001 by Dario Sacca (DS) -; Corrected FRM parameter in the debug hook for the OP_LINE opcode. -;25 January 2001 by Thiadmer Riemersma (TR) -; Implement the "sleep" functionality via the OP_HALT opcode; expand the -; AMX structure for new required fields. -;21 December 2000 by Thiadmer Riemersma (TR) -; Let the AMX abort if the debug hook function returns an error code -; on the DBG_LINE code. -;19 October 2000 by Thiadmer Riemersma (TR) -; Store return value of a function in the AMX structure when calling the -; debug hook -; 9 September 2000 by Thiadmer Riemersma (TR) -; Implemented new opcodes -; * OP_SWAP_PRI -; * OP_SWAP_ALT -; * OP_PUSHADDR -;15 July 2000 By Thiadmer Riemersma (TR) -; Store address of function being called in a debug parameter -; Address for OP_CALL_PRI needs to be relocated -;15 November 1999 By Thiadmer Riemersma (TR) -; Allow the parameter for the return value to be NULL. -;30 September 1999 By Thiadmer Riemersma (TR) -; Implemented _stdcall calling convention -; 1 August 1999 By Thiadmer Riemersma (TR) -; Implemented _cdecl calling convention -;15 June 1999 By Thiadmer Riemersma (TR) -; Implemented new opcodes -; * OP_SRANGE -; * OP_JUMP_PRI -; * OP_SWITCH -; * OP_CASETBL -; Referring to the "license of use" of this file (see above), please -; note that these modifications are NOT by Marc Peter. I have marked -; the locations of the changes with a comment line starting with "TR". -;28 March 1999 -; Here are some more bug fixes. I (Mark Peter) found these when -; "designing" the code sequences for the JIT compiler. The Pawn compiler -; doesn't seem to create these opcodes, or else the bugs would have -; turned up earlier. -; * LREF_S_ALT: garbled EAX and didn't work as expected -; * in LCTRL: In the case of 'lctrl 6' no. 5 would have entered an -; infinite loop because it jumped back to it's own compare instruction -; and not to #6. -; * in SCTRL: FRM was updated, but not EBX -;----- - -.386 ; can't use .586p since TASM doesn't recognize it -.MODEL FLAT - -IFDEF @Version ; for Microsoft MASM 6.x - OPTION OLDSTRUCTS - OPTION M510 -ENDIF - -INCLUDE amxdef.asm - -IFNDEF AMX_NO_PACKED_OPC - IFNDEF AMX_TOKENTHREADING - AMX_TOKENTHREADING EQU 1 ; packed opcodes require token threading - ENDIF -ENDIF - -;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) -_PUSH MACRO v - mov [edi+ecx-4],v - sub ecx,4 - ENDM - - -;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) -_POP MACRO v - mov v,[edi+ecx] - add ecx,4 - ENDM - -NEXT MACRO - IFDEF AMX_TOKENTHREADING - mov ebp, [esi] - IFNDEF AMX_NO_PACKED_OPC - and ebp, 0ffh - ENDIF - jmp DWORD ptr [_amx_opcodelist + 4*ebp] - ELSE - IFNDEF AMX_NO_PACKED_OPC - .err ; opcode packing requires token threading - ENDIF - ; direct threading - jmp DWORD ptr [esi] - ENDIF -; ALIGN 4 - ENDM - -JUMPREL MACRO - add esi,[esi+4] - ENDM - -GETPARAM_P MACRO reg - ; ??? verify that reg != esi - mov reg,[esi] - sar reg,16 ; shift-right, keeping the sign - ENDM - -_CHKSTACK MACRO - cmp ecx,stp - jg err_stacklow - ENDM - -_CHKMARGIN MACRO - lea ebp,[ecx-16*4] ; savety margin = 16 cells - cmp hea,ebp - jg err_stack - ENDM - -_CHKHEAP MACRO - mov ebp,amx - mov ebp,[ebp+_hlw] ; MASM 6.x needs [ebp+amx_s._hlw] - cmp DWORD ptr hea,ebp - jl err_heaplow - ENDM - -_CHKDIVIDEZERO MACRO - or ebp,ebp ; check for divide by zero - jz err_divide - ENDM - -_VERIFYADDRESS MACRO adr ; used in load.i, store.i & lidx - local l1 - cmp adr,stp ; error if adr >= stp - jae err_memaccess - cmp adr,hea ; so adr=hea, ok if adr>=stk - jb err_memaccess - l1: - ENDM - - -IFDEF CDECL - CDECL_STDCALL EQU 1 -ENDIF -IFDEF STDECL - CDECL_STDCALL EQU 1 -ENDIF - -_SAVEREGS MACRO ; save the registers (that may not be - IFDEF CDECL_STDCALL ; preserved under the rules of __cdecl or - PUSHAD ; __stdcall calling conventions) - ENDIF - ENDM - -_RESTOREREGS MACRO - IFDEF CDECL_STDCALL - POPAD - ENDIF - ENDM - -_DROPARGS MACRO n ; remove function arguments from the stack - IFDEF CDECL ; (for __cdecl calling convention) - add esp,n - ENDIF - ENDM - - -.CODE - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -;cell asm_exec( AMX *amx, cell *retval, char *data ) ; -; eax edx ebx ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -IFDEF STACKARGS - - PUBLIC _amx_exec_asm - PUBLIC _amx_exec_asm@16 -_amx_exec_asm@16 LABEL BYTE -_amx_exec_asm PROC - - push ebx - mov eax,[esp+08h] - mov edx,[esp+0ch] - mov ebx,[esp+10h] - -ELSE - - PUBLIC amx_exec_asm_ -amx_exec_asm_ PROC - -ENDIF - - - push edi - push esi - push ebp - - sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - push DWORD ptr [eax+_codesize] ; store code size - push DWORD ptr [eax+_codeseg] ; store pointer to code segment - push eax ; store pointer to AMX - push edx ; store address of retval - push DWORD ptr [eax+_stp] ; store STP - push DWORD ptr [eax+_hea] ; store HEA - push DWORD ptr [eax+_frm] ; store FRM - - stk EQU [esp+36] ; define some aliases to registers - alt EQU [esp+32] ; that are stored on the stack - pri EQU [esp+28] - codesiz EQU [esp+24] - code EQU [esp+20] - amx EQU [esp+16] - retval EQU [esp+12] - stp EQU [esp+8] - hea EQU [esp+4] - frm EQU [esp] ; FRM is NOT stored in ebp, rather FRM+DAT - ; is being held in ebx. - - mov edx,code ; change the code size to an... - add codesiz,edx ; ..."end of code" address - - mov edi,ebx ; get pointer to data segment - mov edx,[eax+_alt] ; get ALT - mov esi,[eax+_cip] ; get CIP - mov ecx,[eax+_stk] ; get STK - mov ebx,[eax+_frm] ; get FRM - mov eax,[eax+_pri] ; get PRI - add ebx,edi ; relocate frame - add esi,code ; relocate code address - - NEXT ; start interpreting - - -OP_LOAD_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[edi+eax] - NEXT - - -OP_LOAD_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[edi+edx] - NEXT - - -OP_LOAD_S_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[ebx+eax] - NEXT - - -OP_LOAD_S_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[ebx+edx] - NEXT - - -OP_LOAD_I: - add esi,4 - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_LODB_I: - _VERIFYADDRESS eax - mov ebp,[esi+4] - mov eax,[edi+eax] ;subject to misalignment stalls - add esi,8 - and eax,DWORD ptr [(lodb_and-4)+ebp*4] - NEXT - - -OP_LREF_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[edi+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[edi+edx] - mov edx,[edi+edx] - NEXT - - -OP_LREF_S_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[ebx+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_S_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[ebx+edx] - mov edx,[edi+edx] - NEXT - - -OP_CONST_PRI: - mov eax,[esi+4] - add esi,8 - NEXT - - -OP_CONST_ALT: - mov edx,[esi+4] - add esi,8 - NEXT - - -OP_ADDR_PRI: - mov eax,[esi+4] - add esi,8 - add eax,frm - NEXT - - -OP_ADDR_ALT: - mov edx,[esi+4] - add esi,8 - add edx,frm - NEXT - - -OP_STOR_PRI: - mov ebp,[esi+4] - add esi,8 - mov [ebp+edi],eax - NEXT - - -OP_STOR_ALT: - mov ebp,[esi+4] - add esi,8 - mov [ebp+edi],edx - NEXT - - -OP_STOR_S_PRI: - mov ebp,[esi+4] - add esi,8 - mov [ebp+ebx],eax - NEXT - - -OP_STOR_S_ALT: - mov ebp,[esi+4] - add esi,8 - mov [ebp+ebx],edx - NEXT - - -OP_STOR_I: - add esi,4 - _VERIFYADDRESS edx - mov [edi+edx],eax - NEXT - - -OP_STRB_I: - mov ebp,[esi+4] - add esi,8 - strb_entry: - _VERIFYADDRESS edx - cmp ebp,1 - jne short strb_not1byte - mov [edi+edx],al - NEXT - strb_not1byte: - cmp ebp,4 - je short strb_4byte - mov [edi+edx],ax - NEXT - strb_4byte: - mov [edi+edx],eax - NEXT - - -OP_SREF_PRI: - mov ebp,[esi+4] - add esi,8 - mov ebp,[edi+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_ALT: - mov ebp,[esi+4] - add esi,8 - mov ebp,[edi+ebp] - mov [edi+ebp],edx - NEXT - - -OP_SREF_S_PRI: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebx+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_S_ALT: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebx+ebp] - mov [edi+ebp],edx - NEXT - - -OP_LIDX: - lea eax,[edx+4*eax] - add esi,4 - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_LIDX_B: - push ecx - mov ecx,[esi+4] - add esi,8 - shl eax,cl - pop ecx - add eax,edx - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_IDXADDR: - add esi,4 - lea eax,[edx+4*eax] - NEXT - - -OP_IDXADDR_B: - push ecx - mov ecx,[esi+4] - add esi,8 - shl eax,cl - pop ecx - add eax,edx - NEXT - - -OP_ALIGN_PRI: - mov ebp,4 - sub ebp,[esi+4] - add esi,8 - xor eax,ebp - NEXT - - -OP_ALIGN_ALT: - mov ebp,4 - sub ebp,[esi+4] - add esi,8 - xor edx,ebp - NEXT - - -OP_LCTRL: - mov ebp,[esi+4] - add esi,8 - cmp ebp,0 - jne short lctrl_1 - mov eax,code ; COD - NEXT - lctrl_1: - cmp ebp,1 - jne short lctrl_2 - mov eax,edi ; DAT - NEXT - lctrl_2: - cmp ebp,2 - jne short lctrl_3 - mov eax,hea ; 2=HEA - NEXT - lctrl_3: - cmp ebp,3 - jne short lctrl_4 - mov ebp,amx - mov eax,stp - NEXT - lctrl_4: - cmp ebp,4 - jne short lctrl_5 - mov eax,ecx ; 4=STK - NEXT - lctrl_5: - cmp ebp,5 - jne short lctrl_6 - mov eax,frm ; 5=FRM - NEXT - lctrl_6: - mov eax,esi ; 6=CIP - sub eax,code - NEXT - - -OP_SCTRL: - mov ebp,[esi+4] - add esi,8 - cmp ebp,2 - jne short sctrl_4 - mov hea,eax ; 2=HEA - NEXT - sctrl_4: - cmp ebp,4 - jne short sctrl_5 - mov ecx,eax ; 4=STK - NEXT - sctrl_5: - cmp ebp,5 - jne short sctrl_6 - mov ebx,eax ; 5=FRM - mov frm,eax - add ebx,edi ; relocate FRM - sctrl_6: - NEXT - - -OP_MOVE_PRI: - add esi,4 - mov eax,edx - NEXT - - -OP_MOVE_ALT: - add esi,4 - mov edx,eax - NEXT - - -OP_XCHG: - add esi,4 - xchg eax,edx - NEXT - - -OP_PUSH_PRI: - add esi,4 - _PUSH eax - NEXT - - -OP_PUSH_ALT: - add esi,4 - _PUSH edx - NEXT - - -OP_PICK: - mov eax,[esi+4] - add esi,8 - add eax,ecx - mov eax,[edi+eax] - NEXT - - -OP_PUSH_C: - mov ebp,[esi+4] - add esi,8 - _PUSH ebp - NEXT - - -OP_PUSH: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH_S: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_POP_PRI: - add esi,4 - _POP eax - NEXT - - -OP_POP_ALT: - add esi,4 - _POP edx - NEXT - - -OP_STACK: - mov edx,ecx - add ecx,[esi+4] - _CHKMARGIN - _CHKSTACK - add esi,8 - NEXT - - -OP_HEAP: - mov ebp,[esi+4] - mov edx,hea - add esi,8 - add hea,ebp - _CHKMARGIN - _CHKHEAP - NEXT - - -OP_PROC: - mov ebx,frm - add esi,4 - _PUSH ebx - mov ebx,edi - mov frm,ecx - add ebx,ecx - _CHKMARGIN - NEXT - - -OP_RET: - _POP ebx - _POP esi - cmp esi,code ; verify ESI>=code - jb err_memaccess - cmp esi,codesiz ; verify ESI=code - jb err_memaccess - cmp esi,codesiz ; verify ESI 0 and 0 => 1 - NEXT - - -OP_NEG: - add esi,4 - neg eax - NEXT - - -OP_INVERT: - add esi,4 - not eax - NEXT - - -OP_ADD_C: - add eax,[esi+4] - add esi,8 - NEXT - - -OP_SMUL_C: - mov ebp,[esi+4] - push edx - imul ebp - pop edx - add esi,8 - NEXT - - -OP_ZERO_PRI: - add esi,4 - sub eax,eax - NEXT - - -OP_ZERO_ALT: - add esi,4 - sub edx,edx - NEXT - - -OP_ZERO: - mov ebp,[esi+4] - add esi,8 - mov DWORD ptr [edi+ebp],0 - NEXT - - -OP_ZERO_S: - mov ebp,[esi+4] - add esi,8 - mov DWORD ptr [ebx+ebp],0 - NEXT - - -OP_SIGN_PRI: - shl eax,24 - add esi,4 - sar eax,24 - NEXT - - -OP_SIGN_ALT: - shl edx,24 - add esi,4 - sar edx,24 - NEXT - - -OP_EQ: - add esi,4 - cmp eax,edx ; PRI == ALT ? - mov eax,0 - sete al - NEXT - - -OP_NEQ: - add esi,4 - cmp eax,edx ; PRI != ALT ? - mov eax,0 - setne al - NEXT - - -OP_LESS: - add esi,4 - cmp eax,edx ; PRI < ALT ? (unsigned) - mov eax,0 - setb al - NEXT - - -OP_LEQ: - add esi,4 - cmp eax,edx ; PRI <= ALT ? (unsigned) - mov eax,0 - setbe al - NEXT - - -OP_GRTR: - add esi,4 - cmp eax,edx ; PRI > ALT ? (unsigned) - mov eax,0 - seta al - NEXT - - -OP_GEQ: - add esi,4 - cmp eax,edx ; PRI >= ALT ? (unsigned) - mov eax,0 - setae al - NEXT - - -OP_SLESS: - add esi,4 - cmp eax,edx ; PRI < ALT ? (signed) - mov eax,0 - setl al - NEXT - - -OP_SLEQ: - add esi,4 - cmp eax,edx ; PRI <= ALT ? (signed) - mov eax,0 - setle al - NEXT - - -OP_SGRTR: - add esi,4 - cmp eax,edx ; PRI > ALT ? (signed) - mov eax,0 - setg al - NEXT - - -OP_SGEQ: - add esi,4 - cmp eax,edx ; PRI >= ALT ? (signed) - mov eax,0 - setge al - NEXT - - -OP_EQ_C_PRI: - cmp eax,[esi+4] ; PRI == value ? - lea esi,[esi+8] - mov eax,0 - sete al - NEXT - - -OP_EQ_C_ALT: - xor eax,eax - cmp edx,[esi+4] ; ALT == value ? - lea esi,[esi+8] - sete al - NEXT - - -OP_INC_PRI: - add esi,4 - inc eax - NEXT - - -OP_INC_ALT: - add esi,4 - inc edx - NEXT - - -OP_INC: - mov ebp,[esi+4] - add esi,8 - inc DWORD ptr [edi+ebp] - NEXT - - -OP_INC_S: - mov ebp,[esi+4] - add esi,8 - inc DWORD ptr [ebx+ebp] - NEXT - - -OP_INC_I: - add esi,4 - inc DWORD ptr [edi+eax] - NEXT - - -OP_DEC_PRI: - add esi,4 - dec eax - NEXT - - -OP_DEC_ALT: - add esi,4 - dec edx - NEXT - - -OP_DEC: - mov ebp,[esi+4] - add esi,8 - dec DWORD ptr [edi+ebp] - NEXT - - -OP_DEC_S: - mov ebp,[esi+4] - add esi,8 - dec DWORD ptr [ebx+ebp] - NEXT - - -OP_DEC_I: - add esi,4 - sub DWORD ptr [edi+eax],1 - NEXT - - -OP_MOVS: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - mov ebp,eax - add ebp,[esi+4] - dec ebp - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx - mov ecx,[esi+4] - add esi,8 - movs_entry: - push edi - push esi - lea esi,[edi+eax] - lea edi,[edi+edx] - - push ecx - shr ecx,2 - rep movsd - pop ecx - and ecx,3 - rep movsb - - pop esi - pop edi - pop ecx - NEXT - - -OP_CMPS: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - mov ebp,eax - add ebp,[esi+4] ; size in bytes - dec ebp ; EBP = PRI + size - 1 - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx - mov ecx,[esi+4] - add esi,8 - cmps_entry: - push edi - push esi - lea esi,[edi+edx] - lea edi,[edi+eax] - - xor eax,eax - repe cmpsb - je short cmps1 - sbb eax,eax - sbb eax,0ffffffffh - cmps1: - pop esi - pop edi - pop ecx - NEXT - - -OP_FILL: - mov ebp,[esi+4] ; get byte count - add esi,8 - fill_entry: - and ebp,0fffffffch ; align to words - jz short fill_ready - _VERIFYADDRESS edx ; ALT - dec ebp ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - sub ebp,edx ; restore EBP - inc ebp - - push ecx - push edi - mov ecx,ebp ; ECX = count (in bytes) - lea edi,[edi+edx] ; EDI = physical starting address - shr ecx,2 ; ECX = count (in DWORDS) - rep stosd - pop edi - pop ecx - fill_ready: - NEXT - - -OP_HALT: - cmp DWORD ptr retval,0 - je short halt_no_retval - mov ebp,retval - mov [ebp],eax - halt_no_retval: - ; store the complete status in the AMX - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) - mov [ebp+_alt],edx - mov [ebp+_stk],ecx - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - mov ebx,[esi+4] ; EBX=parameter of the HALT opcode - add esi,8 ; skip this instruction - mov eax,esi ; EAX=CIP - sub eax,code - mov [ebp+_cip],eax - mov eax,ebx ; return the parameter of the HALT opcode - jmp _return - - -OP_BOUNDS: - mov ebp,[esi+4] - add esi,8 - cmp eax,ebp - ja err_bounds ; use unsigned comparison, so <0 is >bounds - NEXT - - -OP_SYSREQ_C: - mov eax,[esi+4] ; get function number - add esi,4 - - -OP_SYSREQ_PRI: - mov ebp,amx ; get amx into ebp - add esi,4 - - mov stk,ecx ; save STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - mov ebx,esi ; also store CIP - sub ebx,code - mov [ebp+_cip],ebx - - mov edx,eax ; 2nd param: function number - mov eax,ebp ; 1st param: amx - mov ecx,stk - lea ebx,pri ; 3rd param: addr. of retval - add ecx,edi ; 4th param: addr. of function parameters -IFDEF CDECL_STDCALL - ; save a few registers (it is not necessary to save them all - ; and EAX should *not* be saved because it will hold the return - ; value) - push ebp - push esi - push edi - ; push the parameters - push ecx - push ebx - push edx - push eax -ENDIF - call [ebp+_callback] - _DROPARGS 10h ; remove arguments from stack -IFDEF CDECL_STDCALL - pop edi ; restore saved registers - pop esi - pop ebp -ENDIF - cmp eax,AMX_ERR_NONE - jne _return ; return error code, if any - - mov eax,pri ; get retval into eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm - mov ecx,stk ; restore STK - add ebx,edi ; restore FRM - NEXT - - -OP_SYSREQ_N: - mov ebp,[esi+8] ; get # of bytes passed as parameters - mov eax,[esi+4] ; get function number - _PUSH ebp ; push 2nd parameter - add esi,12 - mov ebp,amx ; get amx into ebp - - mov stk,ecx ; save STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - mov ebx,esi ; also store CIP - sub ebx,code - mov [ebp+_cip],ebx - - mov edx,eax ; 2nd param: function number - mov eax,ebp ; 1st param: amx - mov ecx,stk - lea ebx,pri ; 3rd param: addr. of retval - add ecx,edi ; 4th param: addr. of function parameters -IFDEF CDECL_STDCALL - ; save a few registers (it is not necessary to save them all - ; and EAX should *not* be saved because it will hold the return - ; value) - push ebp - push esi - push edi - ; push the parameters - push ecx - push ebx - push edx - push eax -ENDIF - call [ebp+_callback] - _DROPARGS 10h ; remove arguments from stack -IFDEF CDECL_STDCALL - pop edi ; restore saved registers - pop esi - pop ebp -ENDIF - mov edx,alt ; restore ALT - mov ebx,frm - mov ecx,stk ; restore STK - add ebx,edi ; restore FRM - add ecx,[esi-4] ; remove "number of parameter bytes" from the stack - add ecx,4 ; also remove the extra DWORD pushed - - cmp eax,AMX_ERR_NONE - jne _return ; return error code, if any - mov eax,pri ; get retval into eax (PRI) - NEXT - - -OP_FILE: -OP_LINE: -OP_SYMBOL: -OP_SRANGE: -OP_SYMTAG: -OP_CALL_PRI: -OP_JUMP_PRI: -OP_SYSREQ_D: -OP_SYSREQ_ND: - jmp OP_INVALID - - -OP_SWITCH: - push ecx - mov ebp,esi ; EBP = CIP - add ebp,[esi+4] ; EBP = offset of the casetable - add ebp,4 ; skip the "OP_CASETBL" opcode - mov ecx,[ebp] ; ECX = number of records - mov esi,ebp ; ESI = address of first record - add esi,[ebp+4] ; preset ESI to "none-matched" case - op_switch_loop: - or ecx, ecx ; number of records == 0? - jz short op_switch_end ; yes, no more records, exit loop - add ebp,8 ; skip previous record - dec ecx ; already decrement cases to do - cmp eax,[ebp] ; PRI == case label? - jne short op_switch_loop ; no, continue loop - mov esi,ebp ; yes, get jump address and exit loop - add esi,[ebp+4] - op_switch_end: - pop ecx - NEXT - - -OP_ISWITCH: - push ecx - mov ebp,esi ; EBP = CIP - add ebp,[esi+4] ; EBP = offset of the icasetable - add ebp,4 ; skip the "OP_ICASETBL" opcode - mov ecx,[ebp] ; ECX = number of records - mov edx,[ebp+4] ; preset EDX to "none-matched" case - op_iswitch_loop: - or ecx, ecx ; number of records == 0? - jz short op_iswitch_end ; yes, no more records, exit loop - add ebp,8 ; skip previous record - dec ecx ; already decrement cases to do - cmp eax,[ebp] ; PRI == icase label? - jne short op_iswitch_loop ; no, continue loop - mov edx,[ebp+4] ; yes, get jump address and exit loop - op_iswitch_end: - pop ecx - ;load overlay - mov eax,amx - mov [eax+_ovl_index],edx -IFDEF CDECL_STDCALL - _SAVEREGS - push edx ; EDX (2nd parm)=overlay index - push eax ; EAX (1st parm)=amx structure -ENDIF - call [eax+_overlay] ; call overlay function - _DROPARGS 8 ; remove arguments from stack - _RESTOREREGS - mov esi,[eax+_codeseg] ; get new code base - mov code,esi ; save new code base in local variable - NEXT - - -OP_CASETBL: -OP_ICASETBL: - jmp OP_INVALID - - -OP_SWAP_PRI: - mov ebp,[edi+ecx] - add esi,4 - mov [edi+ecx],eax - mov eax,ebp - NEXT - - -OP_SWAP_ALT: - mov ebp,[edi+ecx] - add esi,4 - mov [edi+ecx],edx - mov edx,ebp - NEXT - - -OP_PUSH_ADR: - mov ebp,[esi+4] - add esi,8 - add ebp,frm - _PUSH ebp - NEXT - - -OP_NOP: - add esi,4 - NEXT - - -OP_BREAK: - mov ebp,amx ; get amx into ebp - add esi,4 - cmp DWORD ptr [ebp+_debug], 0 - jnz break_calldebug - NEXT ; debug hook not active, ignore - - break_calldebug: - ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) - mov [ebp+_pri],eax - mov [ebp+_alt],edx ; EAX and EDX are now free to use - mov eax,frm - mov edx,hea - mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) - mov [ebp+_hea],edx - mov [ebp+_stk],ecx - mov eax,esi - sub eax,code ; EAX = CIP (relative to start of code segment) - mov [ebp+_cip],eax - ; call the debug hook - mov eax,ebp ; 1st parm: amx -IFDEF CDECL_STDCALL - _SAVEREGS - push eax -ENDIF - call [ebp+_debug] ; call debug function - _DROPARGS 4 ; remove arguments from stack - cmp eax,AMX_ERR_NONE - je short break_noabort; continue running - mov [ebp+_error],eax ; save EAX (error code) before restoring all regs - _RESTOREREGS ; abort run, but restore stack first - mov eax,[ebp+_error] ; get error code in EAX back again - jmp _return ; return error code - break_noabort: - _RESTOREREGS - mov eax,[ebp+_pri] ; restore PRI and ALT - mov edx,[ebp+_alt] - NEXT - - -OP_PUSH2_C: - add esi,12 - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - - -OP_PUSH2: - add esi,12 - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH2_S: - add esi,12 - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_PUSH2_ADR: - add esi,12 - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH3_C: - add esi,16 - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - - -OP_PUSH3: - add esi,16 - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH3_S: - add esi,16 - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_PUSH3_ADR: - add esi,16 - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH4_C: - add esi,20 - mov ebp,[esi-16] - _PUSH ebp - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - - -OP_PUSH4: - add esi,20 - mov ebp,[esi-16] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH4_S: - add esi,20 - mov ebp,[esi-16] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_PUSH4_ADR: - add esi,20 - mov ebp,[esi-16] - add ebp,frm - _PUSH ebp - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH5_C: - add esi,24 - mov ebp,[esi-20] - _PUSH ebp - mov ebp,[esi-16] - _PUSH ebp - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - - -OP_PUSH5: - add esi,24 - mov ebp,[esi-20] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-16] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH5_S: - add esi,24 - mov ebp,[esi-20] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-16] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_PUSH5_ADR: - add esi,24 - mov ebp,[esi-20] - add ebp,frm - _PUSH ebp - mov ebp,[esi-16] - add ebp,frm - _PUSH ebp - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_LOAD_BOTH: - mov eax,[esi+4] - mov edx,[esi+8] - add esi,12 - mov eax,[edi+eax] - mov edx,[edi+edx] - NEXT - - -OP_LOAD_S_BOTH: - mov eax,[esi+4] - mov edx,[esi+8] - add esi,12 - mov eax,[ebx+eax] - mov edx,[ebx+edx] - NEXT - - -OP_CONST: - push eax - mov ebp,[esi+4] - mov eax,[esi+8] - add esi,12 - mov [ebp+edi],eax - pop eax - NEXT - - -OP_CONST_S: - push eax - mov ebp,[esi+4] - mov eax,[esi+8] - add esi,12 - mov [ebp+ebx],eax - pop eax - NEXT - - -OP_INVALID: - mov eax,AMX_ERR_INVINSTR - jmp _return - - -;----- packed opcodes -IFNDEF AMX_NO_PACKED_OPC - -OP_LOAD_P_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[edi+eax] - NEXT - -OP_LOAD_P_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[edi+edx] - NEXT - - -OP_LOAD_P_S_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[ebx+eax] - NEXT - - -OP_LOAD_P_S_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[ebx+edx] - NEXT - - -OP_LODB_P_I: - _VERIFYADDRESS eax - GETPARAM_P ebp - mov eax,[edi+eax] ;subject to misalignment stalls - add esi,4 - and eax,DWORD ptr [(lodb_and-4)+ebp*4] - NEXT - - -OP_LREF_P_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[edi+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_P_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[edi+edx] - mov edx,[edi+edx] - NEXT - - -OP_LREF_P_S_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[ebx+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_P_S_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[ebx+edx] - mov edx,[edi+edx] - NEXT - - -OP_CONST_P_PRI: - GETPARAM_P eax - add esi,4 - NEXT - - -OP_CONST_P_ALT: - GETPARAM_P edx - add esi,4 - NEXT - - -OP_ADDR_P_PRI: - GETPARAM_P eax - add esi,4 - add eax,frm - NEXT - - -OP_ADDR_P_ALT: - GETPARAM_P edx - add esi,4 - add edx,frm - NEXT - - -OP_STOR_P_PRI: - GETPARAM_P ebp - add esi,4 - mov [ebp+edi],eax - NEXT - - -OP_STOR_P_ALT: - GETPARAM_P ebp - add esi,4 - mov [ebp+edi],edx - NEXT - - -OP_STOR_P_S_PRI: - GETPARAM_P ebp - add esi,4 - mov [ebp+ebx],eax - NEXT - - -OP_STOR_P_S_ALT: - GETPARAM_P ebp - add esi,4 - mov [ebp+ebx],edx - NEXT - - -OP_STRB_P_I: - GETPARAM_P ebp - add esi,4 - jmp strb_entry - - -OP_SREF_P_PRI: - GETPARAM_P ebp - add esi,4 - mov ebp,[edi+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_P_ALT: - GETPARAM_P ebp - add esi,4 - mov ebp,[edi+ebp] - mov [edi+ebp],edx - NEXT - - -OP_SREF_P_S_PRI: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebx+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_P_S_ALT: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebx+ebp] - mov [edi+ebp],edx - NEXT - - -OP_LIDX_P_B: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - add eax,edx - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_IDXADDR_P_B: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - add eax,edx - NEXT - - -OP_ALIGN_P_PRI: - GETPARAM_P ebp - add esi,4 - neg ebp ; ebp = -param - add ebp,4 ; ebp = 4 - param - xor eax,ebp - NEXT - - -OP_ALIGN_P_ALT: - GETPARAM_P ebp - add esi,4 - neg ebp - add ebp,4 - xor edx,ebp - NEXT - - -OP_PUSH_P_C: - GETPARAM_P ebp - add esi,4 - _PUSH ebp - NEXT - - -OP_PUSH_P: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH_P_S: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_STACK_P: - GETPARAM_P ebp - add esi,4 - mov edx,ecx - add ecx,ebp - _CHKMARGIN - _CHKSTACK - NEXT - - -OP_HEAP_P: - GETPARAM_P ebp - add esi,4 - mov edx,hea - add hea,ebp - _CHKMARGIN - _CHKHEAP - NEXT - - -OP_SHL_P_C_PRI: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - NEXT - - -OP_SHL_P_C_ALT: - push ecx - GETPARAM_P ecx - add esi,4 - shl edx,cl - pop ecx - NEXT - - -OP_SHR_P_C_PRI: - push ecx - GETPARAM_P ecx - add esi,4 - shr eax,cl - pop ecx - NEXT - - -OP_SHR_P_C_ALT: - push ecx - GETPARAM_P ecx - add esi,4 - shr edx,cl - pop ecx - NEXT - - -OP_ADD_P_C: - GETPARAM_P ebp - add esi,4 - add eax,ebp - NEXT - - -OP_SMUL_P_C: - GETPARAM_P ebp - add esi,4 - push edx - imul ebp - pop edx - NEXT - - -OP_ZERO_P: - GETPARAM_P ebp - add esi,4 - mov DWORD ptr [edi+ebp],0 - NEXT - - -OP_ZERO_P_S: - GETPARAM_P ebp - add esi,4 - mov DWORD ptr [ebx+ebp],0 - NEXT - - -OP_EQ_P_C_PRI: - GETPARAM_P ebp - add esi,4 - cmp eax,ebp ; PRI == value ? - mov eax,0 - sete al - NEXT - - -OP_EQ_P_C_ALT: - GETPARAM_P ebp - add esi,4 - xor eax,eax - cmp edx,[esi+4] ; ALT == value ? - sete al - NEXT - - -OP_INC_P: - GETPARAM_P ebp - add esi,4 - inc DWORD ptr [edi+ebp] - NEXT - - -OP_INC_P_S: - GETPARAM_P ebp - add esi,4 - inc DWORD ptr [ebx+ebp] - NEXT - - -OP_DEC_P: - GETPARAM_P ebp - add esi,4 - dec DWORD ptr [edi+ebp] - NEXT - - -OP_DEC_P_S: - GETPARAM_P ebp - add esi,4 - dec DWORD ptr [ebx+ebp] - NEXT - - -OP_MOVS_P: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - GETPARAM_P ebp - add ebp,eax - dec ebp - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx ; matching POP is in movs_entry - GETPARAM_P ecx - add esi,4 - jmp movs_entry - - -OP_CMPS_P: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - GETPARAM_P ebp - add ebp,eax - dec ebp ; EBP = PRI + size - 1 - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx ; matching pop is in cmps_entry - GETPARAM_P ecx - add esi,4 - jmp cmps_entry - - -OP_FILL_P: - GETPARAM_P ebp ; get byte count - add esi,4 - jmp fill_entry - - -OP_HALT_P: - cmp DWORD ptr retval,0 - je short halt_no_retval_p - mov ebp,retval - mov [ebp],eax - halt_no_retval_p: - ; store the complete status in the AMX - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) - mov [ebp+_alt],edx - mov [ebp+_stk],ecx - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - GETPARAM_P ebx ; EBX=parameter of the HALT opcode - add esi,4 ; skip this instruction - mov eax,esi ; EAX=CIP - sub eax,code - mov [ebp+_cip],eax - mov eax,ebx ; return the parameter of the HALT opcode - jmp _return - - -OP_BOUNDS_P: - GETPARAM_P ebp - add esi,4 - cmp eax,ebp - ja err_bounds ; use unsigned comparison, so <0 is >bounds - NEXT - - -OP_PUSH_P_ADR: - GETPARAM_P ebp - add esi,4 - add ebp,frm - _PUSH ebp - NEXT - -ENDIF ; AMX_NO_PACKED_OPC - - -err_call: - mov eax,AMX_ERR_CALLBACK - jmp _return - -err_stack: - mov eax,AMX_ERR_STACKERR - jmp _return - -err_stacklow: - mov eax,AMX_ERR_STACKLOW - jmp _return - -err_memaccess: - mov eax,AMX_ERR_MEMACCESS - jmp _return - -err_bounds: - mov eax,AMX_ERR_BOUNDS - jmp _return - -err_heaplow: - mov eax,AMX_ERR_HEAPLOW - jmp _return - -err_divide: - mov eax,AMX_ERR_DIVIDE - jmp _return - - -_return: - ; save a few parameters, mostly for the "sleep"function - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT) - mov [ebp+_alt],edx ; store values in AMX structure (PRI, ALT) - - pop esi ; remove FRM from stack - - pop ecx - pop ebx - pop edx - - pop esi ; remove pointer to amx from stack - pop esi ; remove code segment pointer - pop esi ; remove code size - - add esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - pop ebp - pop esi - pop edi - -IFDEF STACKARGS - pop ebx -ENDIF - - ret - -IFDEF STACKARGS - -_amx_exec_asm ENDP - -ELSE - -amx_exec_asm_ ENDP - -ENDIF - - -.DATA - ALIGN 4 ; This is essential to avoid misalignment stalls. - -lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh - - PUBLIC _amx_opcodelist -_amx_opcodelist DD OP_INVALID - DD OP_LOAD_PRI - DD OP_LOAD_ALT - DD OP_LOAD_S_PRI - DD OP_LOAD_S_ALT - DD OP_LREF_PRI - DD OP_LREF_ALT - DD OP_LREF_S_PRI - DD OP_LREF_S_ALT - DD OP_LOAD_I - DD OP_LODB_I - DD OP_CONST_PRI - DD OP_CONST_ALT - DD OP_ADDR_PRI - DD OP_ADDR_ALT - DD OP_STOR_PRI - DD OP_STOR_ALT - DD OP_STOR_S_PRI - DD OP_STOR_S_ALT - DD OP_SREF_PRI - DD OP_SREF_ALT - DD OP_SREF_S_PRI - DD OP_SREF_S_ALT - DD OP_STOR_I - DD OP_STRB_I - DD OP_LIDX - DD OP_LIDX_B - DD OP_IDXADDR - DD OP_IDXADDR_B - DD OP_ALIGN_PRI - DD OP_ALIGN_ALT - DD OP_LCTRL - DD OP_SCTRL - DD OP_MOVE_PRI - DD OP_MOVE_ALT - DD OP_XCHG - DD OP_PUSH_PRI - DD OP_PUSH_ALT - DD OP_PICK - DD OP_PUSH_C - DD OP_PUSH - DD OP_PUSH_S - DD OP_POP_PRI - DD OP_POP_ALT - DD OP_STACK - DD OP_HEAP - DD OP_PROC - DD OP_RET - DD OP_RETN - DD OP_CALL - DD OP_CALL_PRI - DD OP_JUMP - DD OP_JREL ; obsolete - DD OP_JZER - DD OP_JNZ - DD OP_JEQ - DD OP_JNEQ - DD OP_JLESS - DD OP_JLEQ - DD OP_JGRTR - DD OP_JGEQ - DD OP_JSLESS - DD OP_JSLEQ - DD OP_JSGRTR - DD OP_JSGEQ - DD OP_SHL - DD OP_SHR - DD OP_SSHR - DD OP_SHL_C_PRI - DD OP_SHL_C_ALT - DD OP_SHR_C_PRI - DD OP_SHR_C_ALT - DD OP_SMUL - DD OP_SDIV - DD OP_SDIV_ALT - DD OP_UMUL - DD OP_UDIV - DD OP_UDIV_ALT - DD OP_ADD - DD OP_SUB - DD OP_SUB_ALT - DD OP_AND - DD OP_OR - DD OP_XOR - DD OP_NOT - DD OP_NEG - DD OP_INVERT - DD OP_ADD_C - DD OP_SMUL_C - DD OP_ZERO_PRI - DD OP_ZERO_ALT - DD OP_ZERO - DD OP_ZERO_S - DD OP_SIGN_PRI - DD OP_SIGN_ALT - DD OP_EQ - DD OP_NEQ - DD OP_LESS - DD OP_LEQ - DD OP_GRTR - DD OP_GEQ - DD OP_SLESS - DD OP_SLEQ - DD OP_SGRTR - DD OP_SGEQ - DD OP_EQ_C_PRI - DD OP_EQ_C_ALT - DD OP_INC_PRI - DD OP_INC_ALT - DD OP_INC - DD OP_INC_S - DD OP_INC_I - DD OP_DEC_PRI - DD OP_DEC_ALT - DD OP_DEC - DD OP_DEC_S - DD OP_DEC_I - DD OP_MOVS - DD OP_CMPS - DD OP_FILL - DD OP_HALT - DD OP_BOUNDS - DD OP_SYSREQ_PRI - DD OP_SYSREQ_C - DD OP_FILE ; obsolete - DD OP_LINE ; obsolete - DD OP_SYMBOL ; obsolete - DD OP_SRANGE ; obsolete - DD OP_JUMP_PRI - DD OP_SWITCH - DD OP_CASETBL - DD OP_SWAP_PRI - DD OP_SWAP_ALT - DD OP_PUSH_ADR - DD OP_NOP - DD OP_SYSREQ_N - DD OP_SYMTAG ; obsolete - DD OP_BREAK - ; macro opcodes - DD OP_PUSH2_C - DD OP_PUSH2 - DD OP_PUSH2_S - DD OP_PUSH2_ADR - DD OP_PUSH3_C - DD OP_PUSH3 - DD OP_PUSH3_S - DD OP_PUSH3_ADR - DD OP_PUSH4_C - DD OP_PUSH4 - DD OP_PUSH4_S - DD OP_PUSH4_ADR - DD OP_PUSH5_C - DD OP_PUSH5 - DD OP_PUSH5_S - DD OP_PUSH5_ADR - DD OP_LOAD_BOTH - DD OP_LOAD_S_BOTH - DD OP_CONST - DD OP_CONST_S - ; overlay opcodes - DD OP_ICALL - DD OP_IRETN - DD OP_ISWITCH - DD OP_ICASETBL - ; packed opcodes -IFNDEF AMX_NO_PACKED_OPC - DD OP_LOAD_P_PRI - DD OP_LOAD_P_ALT - DD OP_LOAD_P_S_PRI - DD OP_LOAD_P_S_ALT - DD OP_LREF_P_PRI - DD OP_LREF_P_ALT - DD OP_LREF_P_S_PRI - DD OP_LREF_P_S_ALT - DD OP_LODB_P_I - DD OP_CONST_P_PRI - DD OP_CONST_P_ALT - DD OP_ADDR_P_PRI - DD OP_ADDR_P_ALT - DD OP_STOR_P_PRI - DD OP_STOR_P_ALT - DD OP_STOR_P_S_PRI - DD OP_STOR_P_S_ALT - DD OP_SREF_P_PRI - DD OP_SREF_P_ALT - DD OP_SREF_P_S_PRI - DD OP_SREF_P_S_ALT - DD OP_STRB_P_I - DD OP_LIDX_P_B - DD OP_IDXADDR_P_B - DD OP_ALIGN_P_PRI - DD OP_ALIGN_P_ALT - DD OP_PUSH_P_C - DD OP_PUSH_P - DD OP_PUSH_P_S - DD OP_STACK_P - DD OP_HEAP_P - DD OP_SHL_P_C_PRI - DD OP_SHL_P_C_ALT - DD OP_SHR_P_C_PRI - DD OP_SHR_P_C_ALT - DD OP_ADD_P_C - DD OP_SMUL_P_C - DD OP_ZERO_P - DD OP_ZERO_P_S - DD OP_EQ_P_C_PRI - DD OP_EQ_P_C_ALT - DD OP_INC_P - DD OP_INC_P_S - DD OP_DEC_P - DD OP_DEC_P_S - DD OP_MOVS_P - DD OP_CMPS_P - DD OP_FILL_P - DD OP_HALT_P - DD OP_BOUNDS_P - DD OP_PUSH_P_ADR -ENDIF ; AMX_NO_PACKED_OPC - ; "patch" opcodes - DD OP_SYSREQ_D - DD OP_SYSREQ_ND - -END +; AMXEXEC.ASM Abstract Machine for the "Pawn" language +; + +;Some notes: +; * Please also look at the "history" log (below) for more notes. +; * This file assembles with Watcom's WASM, Borland's TASM and Microsoft's +; MASM. +; * There are two "calling convention" issues: +; o The convention with which amx_exec_asm() itself is called. The default +; calling convention is Watcom's register calling convention. Change +; this to __cdecl by setting the macro STACKARGS. +; o The convention for calling the "hook" functions (native functions and +; the debugger callback). Again, the default is Watcom's register calling +; convention. Use the macros CDECL or STDECL for __cdecl and __stdcall +; respectively. (Since STDCALL is a reserved word on the assembler, I had +; to choose a different name for the macro, hence STDECL.) +; * You will need to compile the standard AMX.C file with the macro ASM32 +; defined. On the command line, use: +; wcl386 /l=nt /dASM32 srun.c amx.c amxcore.c amxcons.c amxexec.asm +; * OP_FILE and OP_CASETBL are not implemented, but they should not occur +; anyway. +; * Not all opcodes are tested. Only those generated when using the demo +; programs are giving the impression to be working. I've flagged them "good". +; (I'm pretty sure now that all other codes are working correctly, too.) +; * On my P200 this implementation reaches >30 mill instr/sec - quite good. +; It's 5 times as fast as the ANSI C version of SRUN.EXE (supplied with the +; archive). (This was measured with the Hanoi "benchmark" [no output].) +; +; +;Copyright and license of use, please read +;----------------------------------------- +;The assembler implementation of the abstract machine for the Pawn language, +;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. +; +;Permission is hereby granted, without written agreement and without paid +;license or royalty fees, to use, copy, modify, and distribute this software +;and its documentation for any purpose, subject to the following conditions: +; +;1. The above copyright notice and this permission notice shall appear in all +; copies or substantial portions of this software. +; +;2. Modifications of this software that do not originate from me (Marc Peter) +; must be explicitly mentioned in a README file or another appropriate +; place. +; +;The use of this software as a subsystem of a larger software product is +;explicitly allowed, regardless of whether that larger product is proprietary, +;gratis or commercially available. +; +;I (Marc Peter) specifically disclaim any warranties, including, but not +;limited to, the implied warranties of merchantability and fitness for a +;particular purpose. The software is provided on an "as is" basis, +;and I have no obligation to provide maintenance, support, updates, +;enhancements or modifications. +; +;I cannot be held liable for any damage or loss of profits that results +;from the use of the software (or part thereof), or from the inability to +;use it. +; +; +;History (list of changes) +;------------------------- +; 14 december 2005 by Thiadmer Riemersma (TR) +; Addition of macro instructions, to speed up instruction decoding +; 17 february 2005 by Thiadmer Riemersma (TR) +; Addition of the BREAK opcode, removal of the older debugging opcode table. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 2 february 2004 by Thiadmer Riemersma (TR) +; Added checking of the return address in the RET and RETN opcodes. +; Changed handling of LINE opcode, so that the debugger can force a +; sleep. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added support for the SYMTAG and SYSCALL.D opcodes +; 3 october 2003 by Thiadmer Riemersma (TR) +; Added "non-debug" versions of various opcodes, to avoid repetitive +; checking of the "debug" flag. +; 1 July 2002 by Thiadmer Riemersma (TR) +; Modifications due to added fields in the AMX structure +; RET and RETN instructions no longer check for a 0 return address +; Stack top is now relative to the start of the data segment, to allow +; for "cloned" AMX'es +; The semantics of _CHKSTACK and _CHKMARGIN were inverted; now corrected +; New opcodes +; * OP_CALLN (but commented out, due to problems with the JIT) +; * OP_NOP +;27 August 2001 by Dario Sacca (DS) +; Corrected FRM parameter in the debug hook for the OP_LINE opcode. +;25 January 2001 by Thiadmer Riemersma (TR) +; Implement the "sleep" functionality via the OP_HALT opcode; expand the +; AMX structure for new required fields. +;21 December 2000 by Thiadmer Riemersma (TR) +; Let the AMX abort if the debug hook function returns an error code +; on the DBG_LINE code. +;19 October 2000 by Thiadmer Riemersma (TR) +; Store return value of a function in the AMX structure when calling the +; debug hook +; 9 September 2000 by Thiadmer Riemersma (TR) +; Implemented new opcodes +; * OP_SWAP_PRI +; * OP_SWAP_ALT +; * OP_PUSHADDR +;15 July 2000 By Thiadmer Riemersma (TR) +; Store address of function being called in a debug parameter +; Address for OP_CALL_PRI needs to be relocated +;15 November 1999 By Thiadmer Riemersma (TR) +; Allow the parameter for the return value to be NULL. +;30 September 1999 By Thiadmer Riemersma (TR) +; Implemented _stdcall calling convention +; 1 August 1999 By Thiadmer Riemersma (TR) +; Implemented _cdecl calling convention +;15 June 1999 By Thiadmer Riemersma (TR) +; Implemented new opcodes +; * OP_SRANGE +; * OP_JUMP_PRI +; * OP_SWITCH +; * OP_CASETBL +; Referring to the "license of use" of this file (see above), please +; note that these modifications are NOT by Marc Peter. I have marked +; the locations of the changes with a comment line starting with "TR". +;28 March 1999 +; Here are some more bug fixes. I (Mark Peter) found these when +; "designing" the code sequences for the JIT compiler. The Pawn compiler +; doesn't seem to create these opcodes, or else the bugs would have +; turned up earlier. +; * LREF_S_ALT: garbled EAX and didn't work as expected +; * in LCTRL: In the case of 'lctrl 6' no. 5 would have entered an +; infinite loop because it jumped back to it's own compare instruction +; and not to #6. +; * in SCTRL: FRM was updated, but not EBX +;----- + +.386 ; can't use .586p since TASM doesn't recognize it +.MODEL FLAT + +IFDEF @Version ; for Microsoft MASM 6.x + OPTION OLDSTRUCTS + OPTION M510 +ENDIF + +INCLUDE amxdef.asm + +;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +_PUSH MACRO v + mov [edi+ecx-4],v + sub ecx,4 + ENDM + + +;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +_POP MACRO v + mov v,[edi+ecx] + add ecx,4 + ENDM + +GO_ON MACRO + jmp DWORD ptr [esi] +; ALIGN 4 + ENDM + + +_CHKSTACK MACRO + cmp ecx,stp + jg err_stacklow + ENDM + +_CHKMARGIN MACRO + lea ebp,[ecx-16*4] ;savety margin = 16 cells + cmp hea,ebp + jg err_stack + ENDM + +_CHKHEAP MACRO + mov ebp,amx + mov ebp,[ebp+_hlw] ; MASM 6.x needs [ebp+amx_s._hlw] + cmp DWORD ptr hea,ebp + jl err_heaplow + ENDM + +_CHKDIVIDEZERO MACRO + or ebp,ebp ; check for divide by zero + jz err_divide + ENDM + +_VERIFYADDRESS MACRO adr ; used in load.i, store.i & lidx + local l1 + cmp adr,stp ; error if adr >= stp + jae err_memaccess + cmp adr,hea ; so adr=hea, ok if adr>=stk + jb err_memaccess + l1: + ENDM + + +IFDEF CDECL + CDECL_STDCALL EQU 1 +ENDIF +IFDEF STDECL + CDECL_STDCALL EQU 1 +ENDIF + +_SAVEREGS MACRO ; save the registers (that may not be + IFDEF CDECL_STDCALL ; preserved under the rules of __cdecl or + PUSHAD ; __stdcall calling conventions) + ENDIF + ENDM + +_RESTOREREGS MACRO + IFDEF CDECL_STDCALL + POPAD + ENDIF + ENDM + +_DROPARGS MACRO n ; remove function arguments from the stack + IFDEF CDECL ; (for __cdecl calling convention) + add esp,n + ENDIF + ENDM + + +.CODE + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell asm_exec( cell *regs, cell *retval, cell stp, cell hea ); +; eax edx ebx ecx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF STACKARGS + + PUBLIC _amx_exec_asm + PUBLIC _amx_exec_asm@16 +_amx_exec_asm@16 LABEL BYTE +_amx_exec_asm PROC + + push ebx + mov eax,[esp+08h] + mov edx,[esp+0ch] + mov ebx,[esp+10h] + mov ecx,[esp+14h] + +ELSE + + PUBLIC amx_exec_asm_ +amx_exec_asm_ PROC + +ENDIF + + + push edi + push esi + push ebp + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push DWORD ptr [eax+20h] ; store code size + push DWORD ptr [eax+1ch] ; store pointer to code segment + push DWORD ptr [eax+18h] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push DWORD ptr[eax+14h] ; store FRM + + stk EQU [esp+36] ; define some aliases to registers + alt EQU [esp+32] ; that are stored on the stack + pri EQU [esp+28] + codesiz EQU [esp+24] + code EQU [esp+20] + amx EQU [esp+16] + retval EQU [esp+12] + stp EQU [esp+8] + hea EQU [esp+4] + frm EQU [esp] ; FRM is NOT stored in ebp, rather FRM+DAT + ; is being held in ebx. + + mov edx,code ; change the code size to an... + add codesiz,edx ; ..."end of code" address + + mov edx,[eax+04h] ; get ALT + mov esi,[eax+08h] ; get CIP + mov edi,[eax+0ch] ; get pointer to data segment + mov ecx,[eax+10h] ; get STK + mov ebx,[eax+14h] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + GO_ON ; start interpreting + + +OP_LOAD_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + GO_ON + +OP_LOAD_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + GO_ON + +;good +OP_LOAD_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + GO_ON + +;good +OP_LOAD_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + GO_ON + +OP_LOAD_I: + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LODB_I: + _VERIFYADDRESS eax + mov ebp,[esi+4] + mov eax,[edi+eax] ;subject to misalignment stalls + add esi,8 + and eax,DWORD ptr [(lodb_and-4)+ebp*4] + GO_ON + +OP_LREF_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + mov edx,[edi+edx] + GO_ON + +OP_LREF_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + mov edx,[edi+edx] + GO_ON + +;good +OP_CONST_PRI: + mov eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_CONST_ALT: + mov edx,[esi+4] + add esi,8 + GO_ON + +;good +OP_ADDR_PRI: + mov eax,[esi+4] + add esi,8 + add eax,frm + GO_ON + +;good +OP_ADDR_ALT: + mov edx,[esi+4] + add esi,8 + add edx,frm + GO_ON + +OP_STOR_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],eax + GO_ON + +OP_STOR_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],edx + GO_ON + +;good +OP_STOR_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],eax + GO_ON + +;good +OP_STOR_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],edx + GO_ON + +;good +OP_STOR_I: + add esi,4 + _VERIFYADDRESS edx + mov [edi+edx],eax + GO_ON + +OP_STRB_I: + _VERIFYADDRESS edx + mov ebp,[esi+4] + add esi,8 + cmp ebp,1 + jne short strb_not1byte + mov [edi+edx],al + GO_ON + strb_not1byte: + cmp ebp,4 + je short strb_4byte + mov [edi+edx],ax + GO_ON + strb_4byte: + mov [edi+edx],eax + GO_ON + +OP_SREF_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],edx + GO_ON + +OP_SREF_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],edx + GO_ON + +;good +OP_LIDX: + lea eax,[edx+4*eax] + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LIDX_B: + push ecx + mov ecx,[esi+4] + shl eax,cl + add esi,8 + add eax,edx + pop ecx + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +;good +OP_IDXADDR: + add esi,4 + lea eax,[edx+4*eax] + GO_ON + +OP_IDXADDR_B: + push ecx + mov ecx,[esi+4] + add esi,8 + shl eax,cl + pop ecx + add eax,edx + GO_ON + +OP_ALIGN_PRI: + mov ebp,4 ; ??? one operation too many? + sub ebp,[esi+4] + add esi,8 + xor eax,ebp + GO_ON + +OP_ALIGN_ALT: + mov ebp,4 + sub ebp,[esi+4] + add esi,8 + xor edx,ebp + GO_ON + +OP_LCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,0 + jne short lctrl_1 + mov eax,code ; COD + GO_ON + lctrl_1: + cmp ebp,1 + jne short lctrl_2 + mov eax,edi ; DAT + GO_ON + lctrl_2: + cmp ebp,2 + jne short lctrl_3 + mov eax,hea ; 2=HEA + GO_ON + lctrl_3: + cmp ebp,3 + jne short lctrl_4 + mov ebp,amx + mov eax,stp + GO_ON + lctrl_4: + cmp ebp,4 + jne short lctrl_5 + mov eax,ecx ; 4=STK + GO_ON + lctrl_5: + cmp ebp,5 + jne short lctrl_6 + mov eax,frm ; 5=FRM + GO_ON + lctrl_6: + mov eax,esi ; 6=CIP + sub eax,code + GO_ON + +OP_SCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,2 + jne short sctrl_4 + mov hea,eax ; 2=HEA + GO_ON + sctrl_4: + cmp ebp,4 + jne short sctrl_5 + mov ecx,eax ; 4=STK + GO_ON + sctrl_5: + cmp ebp,5 + jne short sctrl_6 + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate FRM + sctrl_6: + GO_ON + +OP_MOVE_PRI: + add esi,4 + mov eax,edx + GO_ON + +;good +OP_MOVE_ALT: + add esi,4 + mov edx,eax + GO_ON + +OP_XCHG: + add esi,4 + xchg eax,edx + GO_ON + +;good +OP_PUSH_PRI: + add esi,4 + _PUSH eax + GO_ON + +;good +OP_PUSH_ALT: + add esi,4 + _PUSH edx + GO_ON + +OP_PUSH_R_PRI: + mov ebp,[esi+4] + add esi,8 + push_loop: + _PUSH eax + dec ebp + jnz short push_loop + GO_ON + +;good +OP_PUSH_C: + mov ebp,[esi+4] + add esi,8 + _PUSH ebp + GO_ON + +OP_PUSH: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +;good +OP_PUSH_S: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_POP_PRI: + add esi,4 + _POP eax + GO_ON + +;good +OP_POP_ALT: + add esi,4 + _POP edx + GO_ON + +;good +OP_STACK: + mov edx,ecx + add ecx,[esi+4] + _CHKMARGIN + _CHKSTACK + add esi,8 + GO_ON + +;good +OP_HEAP: + mov ebp,[esi+4] + mov edx,hea + add esi,8 + add hea,ebp + _CHKMARGIN + _CHKHEAP + GO_ON + +;good +OP_PROC: + mov ebx,frm + add esi,4 + _PUSH ebx + mov ebx,edi + mov frm,ecx + add ebx,ecx + _CHKMARGIN + GO_ON + +OP_RET: + _POP ebx + _POP esi + cmp esi,code ; verify ESI>=code + jb err_memaccess + cmp esi,codesiz ; verify ESI=code + jb err_memaccess + cmp esi,codesiz ; verify ESI 0 and 0 => 1 + GO_ON + +OP_NEG: + add esi,4 + neg eax + GO_ON + +OP_INVERT: + add esi,4 + not eax + GO_ON + +;good +OP_ADD_C: + add eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_SMUL_C: + mov ebp,[esi+4] + push edx + imul ebp + pop edx + add esi,8 + GO_ON + +;good +OP_ZERO_PRI: + add esi,4 + sub eax,eax + GO_ON + +;good +OP_ZERO_ALT: + add esi,4 + sub edx,edx + GO_ON + +OP_ZERO: + mov ebp,[esi+4] + add esi,8 + mov DWORD ptr [edi+ebp],0 + GO_ON + +OP_ZERO_S: + mov ebp,[esi+4] + add esi,8 + mov DWORD ptr [ebx+ebp],0 + GO_ON + +OP_SIGN_PRI: + shl eax,24 + add esi,4 + sar eax,24 + GO_ON + +OP_SIGN_ALT: + shl edx,24 + add esi,4 + sar edx,24 + GO_ON + +OP_EQ: + add esi,4 + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + GO_ON + +OP_NEQ: + add esi,4 + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + GO_ON + +OP_LESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + GO_ON + +OP_LEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + GO_ON + +OP_GRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + GO_ON + +OP_GEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + GO_ON + +;good +OP_SLESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + GO_ON + +OP_SLEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + GO_ON + +OP_SGRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + GO_ON + +OP_SGEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + GO_ON + +OP_EQ_C_PRI: + cmp eax,[esi+4] ; PRI == value ? + lea esi,[esi+8] + mov eax,0 + sete al + GO_ON + +OP_EQ_C_ALT: + xor eax,eax + cmp edx,[esi+4] ; ALT == value ? + lea esi,[esi+8] + sete al + GO_ON + +OP_INC_PRI: + add esi,4 + inc eax + GO_ON + +OP_INC_ALT: + add esi,4 + inc edx + GO_ON + +OP_INC: + mov ebp,[esi+4] + add esi,8 + inc DWORD ptr [edi+ebp] + GO_ON + +;good +OP_INC_S: + mov ebp,[esi+4] + add esi,8 + inc DWORD ptr [ebx+ebp] + GO_ON + +OP_INC_I: + add esi,4 + inc DWORD ptr [edi+eax] + GO_ON + +OP_DEC_PRI: + add esi,4 + dec eax + GO_ON + +OP_DEC_ALT: + add esi,4 + dec edx + GO_ON + +OP_DEC: + mov ebp,[esi+4] + add esi,8 + dec DWORD ptr [edi+ebp] + GO_ON + +OP_DEC_S: + mov ebp,[esi+4] + add esi,8 + dec DWORD ptr [ebx+ebp] + GO_ON + +OP_DEC_I: + add esi,4 + sub DWORD ptr [edi+eax],1 + GO_ON + +OP_MOVS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; PRI + size - 1 + mov ebp,edx + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx + shr ecx,2 + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + pop ecx + GO_ON + +OP_CMPS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] ; size in bytes + dec ebp ; EBP = PRI + size - 1 + _VERIFYADDRESS ebp ; PRI + size - 1 + sub ebp,eax ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax + repe cmpsb + je short cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + pop ecx + GO_ON + + +OP_FILL: + mov ebp,[esi+4] ; get byte count + add esi,8 + and ebp,0fffffffch ; align to words + jz short fill_ready + _VERIFYADDRESS edx ; ALT + dec ebp ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + sub ebp,edx ; restore EBP + inc ebp + + push ecx + push edi + mov ecx,ebp ; ECX = count (in bytes) + lea edi,[edi+edx] ; EDI = physical starting address + shr ecx,2 ; ECX = count (in DWORDS) + rep stosd + pop edi + pop ecx + fill_ready: + GO_ON + + +OP_HALT: + cmp DWORD ptr retval,0 + je short halt_no_retval + mov ebp,retval + mov [ebp],eax + halt_no_retval: + ; store the complete status in the AMX + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) + mov [ebp+_alt],edx + mov [ebp+_stk],ecx + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + mov ebx,[esi+4] ; EBX=parameter of the HALT opcode + add esi,8 ; skip this instruction + mov eax,esi ; EAX=CIP + sub eax,code + mov [ebp+_cip],eax + mov eax,ebx ; return the parameter of the HALT opcode + jmp _return + + +OP_BOUNDS: + mov ebp,[esi+4] + add esi,8 + cmp eax,ebp + ja err_bounds ; use unsigned comparison, so <0 is >bounds + GO_ON + + +OP_SYSREQ_C: + mov eax,[esi+4] ; get function number + add esi,4 + + +OP_SYSREQ_PRI: + mov ebp,amx ; get amx into ebp + add esi,4 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov ebx,esi ; also store CIP + sub ebx,code + mov [ebp+_cip],ebx + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + mov ecx,stk + lea ebx,pri ; 3rd param: addr. of retval + add ecx,edi ; 4th param: addr. of function parameters +IFDEF CDECL_STDCALL + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push ecx + push ebx + push edx + push eax +ENDIF + call [ebp+_callback] + _DROPARGS 10h ; remove arguments from stack +IFDEF CDECL_STDCALL + pop edi ; restore saved registers + pop esi + pop ebp +ENDIF + cmp eax,AMX_ERR_NONE + jne _return ; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_SYSREQ_N: + mov ebp,[esi+8] ; get # of bytes passed as parameters + mov eax,[esi+4] ; get function number + _PUSH ebp ; push 2nd parameter + add esi,12 + mov ebp,amx ; get amx into ebp + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov ebx,esi ; also store CIP + sub ebx,code + mov [ebp+_cip],ebx + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + mov ecx,stk + lea ebx,pri ; 3rd param: addr. of retval + add ecx,edi ; 4th param: addr. of function parameters +IFDEF CDECL_STDCALL + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push ecx + push ebx + push edx + push eax +ENDIF + call [ebp+_callback] + _DROPARGS 10h ; remove arguments from stack +IFDEF CDECL_STDCALL + pop edi ; restore saved registers + pop esi + pop ebp +ENDIF + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + add ecx,[esi-4] ; remove "number of parameter bytes" from the stack + add ecx,4 ; also remove the extra DWORD pushed + + cmp eax,AMX_ERR_NONE + jne _return ; return error code, if any + mov eax,pri ; get retval into eax (PRI) + GO_ON + + +OP_SYSREQ_D: + mov ebx,[esi+4] ; get function address + mov ebp,amx ; get amx into ebp + add esi,8 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov eax,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + mov edx,stk + add edx,edi ; 2nd param: addr. of function parameters +IFDEF CDECL_STDCALL + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push edx + push eax +ENDIF + call ebx ; direct call + _DROPARGS 8 ; remove arguments from stack +IFDEF CDECL_STDCALL + pop edi ; restore saved registers + pop esi + pop ebp +ENDIF + cmp [ebp+_error],AMX_ERR_NONE + jne _return ; return error code, if any + + ; function result is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_SYSREQ_ND: + mov ebp,[esi+8] ; get # of bytes passed as parameters + mov ebx,[esi+4] ; get function number + _PUSH ebp ; push 2nd parameter + add esi,12 + mov ebp,amx ; get amx into ebp + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov eax,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + mov edx,stk + add edx,edi ; 2nd param: addr. of function parameters +IFDEF CDECL_STDCALL + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push edx + push eax +ENDIF + call ebx ; direct call + _DROPARGS 8 ; remove arguments from stack +IFDEF CDECL_STDCALL + pop edi ; restore saved registers + pop esi + pop ebp +ENDIF + ; function result is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + add ecx,[esi-4] ; remove "number of parameter bytes" from the stack + add ecx,4 ; also remove the extra DWORD pushed + + cmp [ebp+_error],AMX_ERR_NONE + jne _return ; return error code, if any + GO_ON + + +OP_FILE: + jmp OP_INVALID + + +OP_LINE: + add esi,12 + GO_ON + + +OP_SYMBOL: + add esi,[esi+4] + add esi,8 ; skip "fixed" part + GO_ON + + +OP_SRANGE: + add esi,12 + GO_ON + + +OP_SYMTAG: + add esi,8 + GO_ON + + +OP_JUMP_PRI: + mov esi,eax + GO_ON + + +OP_SWITCH: + push ecx + mov ebp,[esi+4] ; get offset of the switch table + add ebp,4 ; skip the "OP_CASETBL" opcode + mov ecx,[ebp] ; ECX = number of records + mov esi,[ebp+4] ; preset ESI to "none-matched" case + op_switch_loop: + or ecx, ecx ; number of records == 0? + jz short op_switch_end ; yes, no more records, exit loop + add ebp,8 ; skip previous record + dec ecx ; already decrement cases to do + cmp eax,[ebp] ; PRI == case label? + jne short op_switch_loop ; no, continue loop + mov esi,[ebp+4] ; yes, get jump address and exit loop + op_switch_end: + pop ecx + GO_ON + + +OP_CASETBL: + jmp OP_INVALID + + +OP_SWAP_PRI: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],eax + mov eax,ebp + GO_ON + + +OP_SWAP_ALT: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],edx + mov edx,ebp + GO_ON + + +OP_PUSH_ADR: + mov ebp,[esi+4] + add esi,8 + add ebp,frm + _PUSH ebp + GO_ON + + +OP_NOP: + add esi,4 + GO_ON + + +OP_BREAK: + mov ebp,amx ; get amx into ebp + add esi,4 + cmp DWORD ptr [ebp+_debug], 0 + jnz break_calldebug + GO_ON ; debug hook not active, ignore + + break_calldebug: + ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) + mov [ebp+_pri],eax + mov [ebp+_alt],edx ; EAX and EDX are now free to use + mov eax,frm + mov edx,hea + mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) + mov [ebp+_hea],edx + mov [ebp+_stk],ecx + mov eax,esi + sub eax,code ; EAX = CIP (relative to start of code segment) + mov [ebp+_cip],eax + ; call the debug hook + mov eax,ebp ; 1st parm: amx +IFDEF CDECL_STDCALL + _SAVEREGS + push eax +ENDIF + call [ebp+_debug] ; call debug function + _DROPARGS 4 ; remove arguments from stack + cmp eax,AMX_ERR_NONE + je short break_noabort; continue running + mov [ebp+_error],eax ; save EAX (error code) before restoring all regs + _RESTOREREGS ; abort run, but restore stack first + mov eax,[ebp+_error] ; get error code in EAX back again + jmp _return ; return error code + break_noabort: + _RESTOREREGS + mov eax,[ebp+_pri] ; restore PRI and ALT + mov edx,[ebp+_alt] + GO_ON + + +OP_PUSH2_C: + add esi,12 + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH2: + add esi,12 + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH2_S: + add esi,12 + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH2_ADR: + add esi,12 + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH3_C: + add esi,16 + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH3: + add esi,16 + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH3_S: + add esi,16 + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH3_ADR: + add esi,16 + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH4_C: + add esi,20 + mov ebp,[esi-16] + _PUSH ebp + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH4: + add esi,20 + mov ebp,[esi-16] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH4_S: + add esi,20 + mov ebp,[esi-16] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH4_ADR: + add esi,20 + mov ebp,[esi-16] + add ebp,frm + _PUSH ebp + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH5_C: + add esi,24 + mov ebp,[esi-20] + _PUSH ebp + mov ebp,[esi-16] + _PUSH ebp + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH5: + add esi,24 + mov ebp,[esi-20] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-16] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH5_S: + add esi,24 + mov ebp,[esi-20] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-16] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH5_ADR: + add esi,24 + mov ebp,[esi-20] + add ebp,frm + _PUSH ebp + mov ebp,[esi-16] + add ebp,frm + _PUSH ebp + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_LOAD_BOTH: + mov eax,[esi+4] + mov edx,[esi+8] + add esi,12 + mov eax,[edi+eax] + mov edx,[edi+edx] + GO_ON + + +OP_LOAD_S_BOTH: + mov eax,[esi+4] + mov edx,[esi+8] + add esi,12 + mov eax,[ebx+eax] + mov edx,[ebx+edx] + GO_ON + + +OP_CONST: + push eax + mov ebp,[esi+4] + mov eax,[esi+8] + add esi,12 + mov [ebp+edi],eax + pop eax + GO_ON + + +OP_CONST_S: + push eax + mov ebp,[esi+4] + mov eax,[esi+8] + add esi,12 + mov [ebp+ebx],eax + pop eax + GO_ON + + +OP_INVALID: + mov eax,AMX_ERR_INVINSTR + jmp _return + +err_call: + mov eax,AMX_ERR_CALLBACK + jmp _return + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return + +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return + +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return + + +_return: + ; save a few parameters, mostly for the "sleep"function + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT) + mov [ebp+_alt],edx ; store values in AMX structure (PRI, ALT) + + pop esi ; remove FRM from stack + + pop ecx + pop ebx + pop edx + + pop esi ; remove pointer to amx from stack + pop esi ; remove code segment pointer + pop esi ; remove code size + + add esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + pop ebp + pop esi + pop edi + +IFDEF STACKARGS + pop ebx +ENDIF + + ret + +IFDEF STACKARGS + +_amx_exec_asm ENDP + +ELSE + +amx_exec_asm_ ENDP + +ENDIF + + +.DATA + ALIGN 4 ; This is essential to avoid misalignment stalls. + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + + PUBLIC _amx_opcodelist +_amx_opcodelist DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI ; obsolete + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_PRI + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE ; obsolete + DD OP_LINE ; obsolete + DD OP_SYMBOL ; obsolete + DD OP_SRANGE ; obsolete + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI + DD OP_SWAP_ALT + DD OP_PUSH_ADR + DD OP_NOP + DD OP_SYSREQ_N + DD OP_SYMTAG ; obsolete + DD OP_BREAK + ; macro opcodes + DD OP_PUSH2_C + DD OP_PUSH2 + DD OP_PUSH2_S + DD OP_PUSH2_ADR + DD OP_PUSH3_C + DD OP_PUSH3 + DD OP_PUSH3_S + DD OP_PUSH3_ADR + DD OP_PUSH4_C + DD OP_PUSH4 + DD OP_PUSH4_S + DD OP_PUSH4_ADR + DD OP_PUSH5_C + DD OP_PUSH5 + DD OP_PUSH5_S + DD OP_PUSH5_ADR + DD OP_LOAD_BOTH + DD OP_LOAD_S_BOTH + DD OP_CONST + DD OP_CONST_S + ; "patch" opcodes + DD OP_SYSREQ_D + DD OP_SYSREQ_ND + +END diff --git a/amx-deps/src/amx/amxexecn.asm b/amx-deps/src/amx/amxexecn.asm index 701ff03..6acc3cb 100644 --- a/amx-deps/src/amx/amxexecn.asm +++ b/amx-deps/src/amx/amxexecn.asm @@ -1,2536 +1,2021 @@ -; AMXEXECN.ASM Abstract Machine for the "Pawn" language -; - -;Some notes: -; * This file was adapted from AMXEXEC.ASM (for MASM/TASM/WASM). This -; version is for NASM (Netwide Assembler). NASM uses Intel syntax for -; the mnemonics, but it is not compatible with MASM. -; * The "calling convention" is __cdecl for the amx_exec_asm() itself and -; __cdecl or __stdcall for the native routines (the default is __cdecl, -; define the macro STDECL to set __stdcall). -; * The borland compiler uses different segment definitions as Microsoft -; Visual C/C++ and GNU GCC. To assemble the abstract machine with "Borland" -; segments, add the definition "BORLAND" on the command line. -; * You will need to compile the standard AMX.C file with the macro ASM32 -; defined. On the command line, use: -; nasmw -O1 -f obj -d BORLAND amxexecn.asm -; bcc32 -DASM32 srun.c amx.c amxcore.c amxcons.c amxexecn.obj -; or -; nasmw -O1 -f win32 amxexecn.asm -; or -; nasm -O1 -f elf amxexecn.asm -; gcc -o srun -DLINUX -DASM32 -I../linux srun.c amx.c amxcore.c amxcons.c amxexecn.o -; * See the notes in AMXEXEC.ASM for more information and a change log). -; -; -;Copyright and license of use, please read -;----------------------------------------- -;The assembler implementation of the abstract machine for the Pawn language, -;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. -; -;Permission is hereby granted, without written agreement and without paid -;license or royalty fees, to use, copy, modify, and distribute this software -;and its documentation for any purpose, subject to the following conditions: -; -;1. The above copyright notice and this permission notice shall appear in all -; copies or substantial portions of this software. -; -;2. Modifications of this software that do not originate from me (Marc Peter) -; must be explicitly mentioned in a README file or another appropriate -; place. -; -;The use of this software as a subsystem of a larger software product is -;explicitly allowed, regardless of whether that larger product is proprietary, -;gratis or commercially available. -; -;I (Marc Peter) specifically disclaim any warranties, including, but not -;limited to, the implied warranties of merchantability and fitness for a -;particular purpose. The software is provided on an "as is" basis, -;and I have no obligation to provide maintenance, support, updates, -;enhancements or modifications. -; -;I cannot be held liable for any damage or loss of profits that results -;from the use of the software (or part thereof), or from the inability to -;use it. -; -; -;History (list of changes) -;------------------------- -; 26 august 2007 by Thiadmer Riemersma -; Minor clean-up; removed unneeded parameters -; 31 may 2007 by Thiadmer Riemersma -; Added packed opcodes -; 30 april 2007 by Thiadmer Riemersma (TR) -; Move to position-independent code (no more relocation needed for -; branches). -; Removed cases for obsolete instructions. -; 14 december 2005 by Thiadmer Riemersma (TR) -; Addition of macro instructions, to speed up instruction decoding -; 17 february 2005 by Thiadmer Riemersma (TR) -; Addition of the BREAK opcode, removal of the older debugging opcode table. -; 6 march 2004 by Thiadmer Riemersma -; Corrected a bug in OP_FILL, where a cell preceding the array would -; be overwritten (zero'ed out). This bug was brought to my attention -; by Robert Daniels. -; 2 february 2004 by Thiadmer Riemersma (TR) -; Added checking of the return address in the RET and RETN opcodes. -; Changed handling of LINE opcode, so that the debugger can force a -; sleep. -; 22 december 2003 by Thiadmer Riemersma (TR) -; Added support for the SYMTAG and SYSCALL.D opcodes -; 3 october 2003 by Thiadmer Riemersma (TR) -; Added "non-debug" versions of various opcodes, to avoid repetitive -; checking of the "debug" flag. -; 15 September 2003 by Thiadmer Riemersma (TR) -; Minor corrections, mostly to support older versions of NASM -; 26 January 2003 by Thiadmer Riemersma (TR) -; Port to NASM -;----- - -;CPU 386 -- some older versions of NASM do not support this keyword - -; Macro to begin a code segment -%macro Start_CODE 0 - %ifdef BORLAND - segment _TEXT public align=1 class=CODE use32 - %else - segment .text - %endif -%endmacro - -; Macro to begin a data segment -%macro Start_DATA 0 - %ifdef BORLAND - segment _DATA public align=4 class=DATA use32 - %else - segment .data - %endif -%endmacro - -%include "amxdefn.asm" - -%ifndef AMX_NO_PACKED_OPC - %ifndef AMX_TOKENTHREADING - %define AMX_TOKENTHREADING 1 ; packed opcodes require token threading - %endif -%endif - - -;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) -%macro _PUSH 1 - mov [edi+ecx-4],%1 - sub ecx,4 -%endmacro - - -;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) -%macro _POP 1 - mov %1,[edi+ecx] - add ecx,4 -%endmacro - -%macro NEXT 0 - %ifdef AMX_TOKENTHREADING - mov ebp, [esi] - %ifndef AMX_NO_PACKED_OPC - and ebp, 0ffh - %endif - jmp DWORD [_amx_opcodelist + 4*ebp] - %else - %ifndef AMX_NO_PACKED_OPC - %error opcode packing requires token threading - %endif - ; direct threading - jmp DWORD [esi] - %endif -; ALIGN 4 -%endmacro - -%macro JUMPREL 0 - add esi,[esi+4] -%endmacro - -%macro GETPARAM_P 1 - ; ??? verify that %1 != esi - mov %1,[esi] - sar %1,16 ; shift-right, keeping the sign -%endmacro - -%macro _CHKSTACK 0 - cmp ecx,stp - jg near err_stacklow -%endmacro - -%macro _CHKMARGIN 0 - lea ebp,[ecx-16*4] ;savety margin = 16 cells - cmp hea,ebp - jg near err_stack -%endmacro - -%macro _CHKHEAP 0 - mov ebp,amx - mov ebp,[ebp+_hlw] - cmp DWORD hea,ebp - jl near err_heaplow -%endmacro - -%macro _CHKDIVIDEZERO 0 - or ebp,ebp ; check for divide by zero - jz near err_divide -%endmacro - -%macro _VERIFYADDRESS 1 ; used in load.i, store.i & lidx - cmp %1,stp ; error if address >= stp - jae near err_memaccess - cmp %1,hea ; so address=hea, ok if address>=stk - jb near err_memaccess - %%address_ok: -%endmacro - -%macro _SAVEREGS 0 ; save the registers (that may not be - PUSHAD ; __stdcall calling conventions) -%endmacro - -%macro _RESTOREREGS 0 - POPAD -%endmacro - -%macro _DROPARGS 1 ; remove function arguments from the stack - %ifndef STDECL ; (only for __cdecl calling convention) - add esp,%1 - %endif -%endmacro - - -Start_CODE - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -;cell asm_exec( AMX *amx, cell *retval, char *data ) ; -; ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - GLOBAL _amx_exec_asm - GLOBAL amx_exec_asm -amx_exec_asm: -_amx_exec_asm: ;PROC - - push ebx - mov eax,[esp+08h] - mov edx,[esp+0ch] - mov ebx,[esp+10h] - - push edi - push esi - push ebp - - sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - push DWORD [eax+_codesize] ; store code size - push DWORD [eax+_codeseg] ; store pointer to code segment - push eax ; store pointer to AMX - push edx ; store address of retval - push DWORD [eax+_stp] ; store STP - push DWORD [eax+_hea] ; store HEA - push DWORD [eax+_frm] ; store FRM - -%define stk [esp+36] ; define some aliases to registers -%define alt [esp+32] ; that are stored on the stack -%define pri [esp+28] -%define codesiz [esp+24] -%define code [esp+20] -%define amx [esp+16] -%define retval [esp+ 12] -%define stp [esp+8] -%define hea [esp+4] -%define frm [esp] ; FRM is NOT stored in ebp, rather FRM+DAT - ; is being held in ebx. - - mov edx,code ; change the code size to an... - add codesiz,edx ; ..."end of code" address - - mov edi,ebx ; get pointer to data segment - mov edx,[eax+_alt] ; get ALT - mov esi,[eax+_cip] ; get CIP - mov ecx,[eax+_stk] ; get STK - mov ebx,[eax+_frm] ; get FRM - mov eax,[eax+_pri] ; get PRI - add ebx,edi ; relocate frame - add esi,code ; relocate code address - - NEXT ; start interpreting - - -OP_LOAD_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[edi+eax] - NEXT - -OP_LOAD_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[edi+edx] - NEXT - - -OP_LOAD_S_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[ebx+eax] - NEXT - - -OP_LOAD_S_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[ebx+edx] - NEXT - - -OP_LOAD_I: - add esi,4 - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_LODB_I: - _VERIFYADDRESS eax - mov ebp,[esi+4] - mov eax,[edi+eax] ;subject to misalignment stalls - add esi,8 - and eax,DWORD [(lodb_and-4)+ebp*4] - NEXT - -OP_LREF_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[edi+eax] - mov eax,[edi+eax] - NEXT - -OP_LREF_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[edi+edx] - mov edx,[edi+edx] - NEXT - -OP_LREF_S_PRI: - mov eax,[esi+4] - add esi,8 - mov eax,[ebx+eax] - mov eax,[edi+eax] - NEXT - -OP_LREF_S_ALT: - mov edx,[esi+4] - add esi,8 - mov edx,[ebx+edx] - mov edx,[edi+edx] - NEXT - - -OP_CONST_PRI: - mov eax,[esi+4] - add esi,8 - NEXT - - -OP_CONST_ALT: - mov edx,[esi+4] - add esi,8 - NEXT - - -OP_ADDR_PRI: - mov eax,[esi+4] - add esi,8 - add eax,frm - NEXT - - -OP_ADDR_ALT: - mov edx,[esi+4] - add esi,8 - add edx,frm - NEXT - -OP_STOR_PRI: - mov ebp,[esi+4] - add esi,8 - mov [ebp+edi],eax - NEXT - -OP_STOR_ALT: - mov ebp,[esi+4] - add esi,8 - mov [ebp+edi],edx - NEXT - - -OP_STOR_S_PRI: - mov ebp,[esi+4] - add esi,8 - mov [ebp+ebx],eax - NEXT - - -OP_STOR_S_ALT: - mov ebp,[esi+4] - add esi,8 - mov [ebp+ebx],edx - NEXT - - -OP_STOR_I: - add esi,4 - _VERIFYADDRESS edx - mov [edi+edx],eax - NEXT - -OP_STRB_I: - mov ebp,[esi+4] - add esi,8 - strb_entry: - _VERIFYADDRESS edx - cmp ebp,1 - jne short strb_not1byte - mov [edi+edx],al - NEXT - strb_not1byte: - cmp ebp,4 - je short strb_4byte - mov [edi+edx],ax - NEXT - strb_4byte: - mov [edi+edx],eax - NEXT - -OP_SREF_PRI: - mov ebp,[esi+4] - add esi,8 - mov ebp,[edi+ebp] - mov [edi+ebp],eax - NEXT - -OP_SREF_ALT: - mov ebp,[esi+4] - add esi,8 - mov ebp,[edi+ebp] - mov [edi+ebp],edx - NEXT - -OP_SREF_S_PRI: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebx+ebp] - mov [edi+ebp],eax - NEXT - -OP_SREF_S_ALT: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebx+ebp] - mov [edi+ebp],edx - NEXT - - -OP_LIDX: - lea eax,[edx+4*eax] - add esi,4 - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - -OP_LIDX_B: - push ecx - mov ecx,[esi+4] - shl eax,cl - add esi,8 - add eax,edx - pop ecx - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_IDXADDR: - add esi,4 - lea eax,[edx+4*eax] - NEXT - -OP_IDXADDR_B: - push ecx - mov ecx,[esi+4] - add esi,8 - shl eax,cl - pop ecx - add eax,edx - NEXT - -OP_ALIGN_PRI: - mov ebp,4 ; ??? one operation too many? - sub ebp,[esi+4] - add esi,8 - xor eax,ebp - NEXT - -OP_ALIGN_ALT: - mov ebp,4 - sub ebp,[esi+4] - add esi,8 - xor edx,ebp - NEXT - -OP_LCTRL: - mov ebp,[esi+4] - add esi,8 - cmp ebp,0 - jne short lctrl_1 - mov eax,code ; COD - NEXT - lctrl_1: - cmp ebp,1 - jne short lctrl_2 - mov eax,edi ; DAT - NEXT - lctrl_2: - cmp ebp,2 - jne short lctrl_3 - mov eax,hea ; 2=HEA - NEXT - lctrl_3: - cmp ebp,3 - jne short lctrl_4 - mov ebp,amx - mov eax,stp - NEXT - lctrl_4: - cmp ebp,4 - jne short lctrl_5 - mov eax,ecx ; 4=STK - NEXT - lctrl_5: - cmp ebp,5 - jne short lctrl_6 - mov eax,frm ; 5=FRM - NEXT - lctrl_6: - mov eax,esi ; 6=CIP - sub eax,code - NEXT - -OP_SCTRL: - mov ebp,[esi+4] - add esi,8 - cmp ebp,2 - jne short sctrl_4 - mov hea,eax ; 2=HEA - NEXT - sctrl_4: - cmp ebp,4 - jne short sctrl_5 - mov ecx,eax ; 4=STK - NEXT - sctrl_5: - cmp ebp,5 - jne short sctrl_6 - mov ebx,eax ; 5=FRM - mov frm,eax - add ebx,edi ; relocate FRM - sctrl_6: - NEXT - -OP_MOVE_PRI: - add esi,4 - mov eax,edx - NEXT - - -OP_MOVE_ALT: - add esi,4 - mov edx,eax - NEXT - -OP_XCHG: - add esi,4 - xchg eax,edx - NEXT - - -OP_PUSH_PRI: - add esi,4 - _PUSH eax - NEXT - - -OP_PUSH_ALT: - add esi,4 - _PUSH edx - NEXT - - -OP_PICK: - mov eax,[esi+4] - add esi,8 - add eax,ecx - mov eax,[edi+eax] - NEXT - - -OP_PUSH_C: - mov ebp,[esi+4] - add esi,8 - _PUSH ebp - NEXT - -OP_PUSH: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH_S: - mov ebp,[esi+4] - add esi,8 - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - -OP_POP_PRI: - add esi,4 - _POP eax - NEXT - - -OP_POP_ALT: - add esi,4 - _POP edx - NEXT - - -OP_STACK: - mov edx,ecx - add ecx,[esi+4] - _CHKMARGIN - _CHKSTACK - add esi,8 - NEXT - - -OP_HEAP: - mov ebp,[esi+4] - mov edx,hea - add esi,8 - add hea,ebp - _CHKMARGIN - _CHKHEAP - NEXT - - -OP_PROC: - mov ebx,frm - add esi,4 - _PUSH ebx - mov ebx,edi - mov frm,ecx - add ebx,ecx - _CHKMARGIN - NEXT - -OP_RET: - _POP ebx - _POP esi - cmp esi,code ; verify ESI>=code - jb near err_memaccess - cmp esi,codesiz ; verify ESI=code - jb near err_memaccess - cmp esi,codesiz ; verify ESI 0 and 0 => 1 - NEXT - -OP_NEG: - add esi,4 - neg eax - NEXT - -OP_INVERT: - add esi,4 - not eax - NEXT - - -OP_ADD_C: - add eax,[esi+4] - add esi,8 - NEXT - - -OP_SMUL_C: - mov ebp,[esi+4] - push edx - imul ebp - pop edx - add esi,8 - NEXT - - -OP_ZERO_PRI: - add esi,4 - sub eax,eax - NEXT - - -OP_ZERO_ALT: - add esi,4 - sub edx,edx - NEXT - -OP_ZERO: - mov ebp,[esi+4] - add esi,8 - mov DWORD [edi+ebp],0 - NEXT - -OP_ZERO_S: - mov ebp,[esi+4] - add esi,8 - mov DWORD [ebx+ebp],0 - NEXT - -OP_SIGN_PRI: - shl eax,24 - add esi,4 - sar eax,24 - NEXT - -OP_SIGN_ALT: - shl edx,24 - add esi,4 - sar edx,24 - NEXT - -OP_EQ: - add esi,4 - cmp eax,edx ; PRI == ALT ? - mov eax,0 - sete al - NEXT - -OP_NEQ: - add esi,4 - cmp eax,edx ; PRI != ALT ? - mov eax,0 - setne al - NEXT - -OP_LESS: - add esi,4 - cmp eax,edx ; PRI < ALT ? (unsigned) - mov eax,0 - setb al - NEXT - -OP_LEQ: - add esi,4 - cmp eax,edx ; PRI <= ALT ? (unsigned) - mov eax,0 - setbe al - NEXT - -OP_GRTR: - add esi,4 - cmp eax,edx ; PRI > ALT ? (unsigned) - mov eax,0 - seta al - NEXT - -OP_GEQ: - add esi,4 - cmp eax,edx ; PRI >= ALT ? (unsigned) - mov eax,0 - setae al - NEXT - - -OP_SLESS: - add esi,4 - cmp eax,edx ; PRI < ALT ? (signed) - mov eax,0 - setl al - NEXT - -OP_SLEQ: - add esi,4 - cmp eax,edx ; PRI <= ALT ? (signed) - mov eax,0 - setle al - NEXT - -OP_SGRTR: - add esi,4 - cmp eax,edx ; PRI > ALT ? (signed) - mov eax,0 - setg al - NEXT - -OP_SGEQ: - add esi,4 - cmp eax,edx ; PRI >= ALT ? (signed) - mov eax,0 - setge al - NEXT - -OP_EQ_C_PRI: - cmp eax,[esi+4] ; PRI == value ? - lea esi,[esi+8] - mov eax,0 - sete al - NEXT - -OP_EQ_C_ALT: - xor eax,eax - cmp edx,[esi+4] ; ALT == value ? - lea esi,[esi+8] - sete al - NEXT - -OP_INC_PRI: - add esi,4 - inc eax - NEXT - -OP_INC_ALT: - add esi,4 - inc edx - NEXT - -OP_INC: - mov ebp,[esi+4] - add esi,8 - inc DWORD [edi+ebp] - NEXT - - -OP_INC_S: - mov ebp,[esi+4] - add esi,8 - inc DWORD [ebx+ebp] - NEXT - -OP_INC_I: - add esi,4 - inc DWORD [edi+eax] - NEXT - -OP_DEC_PRI: - add esi,4 - dec eax - NEXT - -OP_DEC_ALT: - add esi,4 - dec edx - NEXT - -OP_DEC: - mov ebp,[esi+4] - add esi,8 - dec DWORD [edi+ebp] - NEXT - -OP_DEC_S: - mov ebp,[esi+4] - add esi,8 - dec DWORD [ebx+ebp] - NEXT - -OP_DEC_I: - add esi,4 - sub DWORD [edi+eax],1 - NEXT - -OP_MOVS: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - mov ebp,eax - add ebp,[esi+4] - dec ebp - _VERIFYADDRESS ebp ; PRI + size - 1 - mov ebp,edx - add ebp,[esi+4] - dec ebp - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx - mov ecx,[esi+4] - add esi,8 - movs_entry: - push edi - push esi - lea esi,[edi+eax] - lea edi,[edi+edx] - - push ecx - shr ecx,2 - rep movsd - pop ecx - and ecx,3 - rep movsb - - pop esi - pop edi - pop ecx - NEXT - -OP_CMPS: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - mov ebp,eax - add ebp,[esi+4] ; size in bytes - dec ebp ; EBP = PRI + size - 1 - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx - mov ecx,[esi+4] - add esi,8 - cmps_entry: - push edi - push esi - lea esi,[edi+edx] - lea edi,[edi+eax] - - xor eax,eax - repe cmpsb - je short cmps1 - sbb eax,eax - sbb eax,0ffffffffh - cmps1: - pop esi - pop edi - pop ecx - NEXT - - -OP_FILL: - mov ebp,[esi+4] ; get byte count - add esi,8 - fill_entry: - and ebp,0fffffffch ; align to words - jz short fill_ready - _VERIFYADDRESS edx ; ALT - dec ebp ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - sub ebp,edx ; restore EBP - inc ebp - - push ecx - push edi - mov ecx,ebp ; ECX = count (in bytes) - lea edi,[edi+edx] ; EDI = physical starting address - shr ecx,2 ; ECX = count (in DWORDS) - rep stosd - pop edi - pop ecx - fill_ready: - NEXT - - -OP_HALT: - cmp DWORD retval,0 - je short halt_no_retval - mov ebp,retval - mov [ebp],eax - halt_no_retval: - ; store the complete status in the AMX - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) - mov [ebp+_alt],edx - mov [ebp+_stk],ecx - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - mov ebx,[esi+4] ; EBX=parameter of the HALT opcode - add esi,8 ; skip this instruction - mov eax,esi ; EAX=CIP - sub eax,code - mov [ebp+_cip],eax - mov eax,ebx ; return the parameter of the HALT opcode - jmp _return - - -OP_BOUNDS: - mov ebp,[esi+4] - add esi,8 - cmp eax,ebp - ja near err_bounds ; use unsigned comparison, so <0 is >bounds - NEXT - - -OP_SYSREQ_C: - mov eax,[esi+4] ; get function number - add esi,4 - - -OP_SYSREQ_PRI: - mov ebp,amx ; get amx into ebp - add esi,4 - - mov stk,ecx ; save STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - mov ebx,esi ; also store CIP - sub ebx,code - mov [ebp+_cip],ebx - - mov edx,eax ; 2nd param: function number - mov eax,ebp ; 1st param: amx - mov ecx,stk - lea ebx,pri ; 3rd param: addr. of retval - add ecx,edi ; 4th param: addr. of function parameters - ; save a few registers (it is not necessary to save them all - ; and EAX should *not* be saved because it will hold the return - ; value) - push ebp - push esi - push edi - ; push the parameters - push ecx - push ebx - push edx - push eax - call [ebp+_callback] - _DROPARGS 10h ; remove arguments from stack - pop edi ; restore saved registers - pop esi - pop ebp - cmp eax,AMX_ERR_NONE - jne near _return ; return error code, if any - - mov eax,pri ; get retval into eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm - mov ecx,stk ; restore STK - add ebx,edi ; restore FRM - NEXT - - -OP_SYSREQ_N: - mov ebp,[esi+8] ; get # of bytes passed as parameters - mov eax,[esi+4] ; get function number - _PUSH ebp ; push 2nd parameter - add esi,12 - mov ebp,amx ; get amx into ebp - - mov stk,ecx ; save STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - mov ebx,esi ; also store CIP - sub ebx,code - mov [ebp+_cip],ebx - - mov edx,eax ; 2nd param: function number - mov eax,ebp ; 1st param: amx - mov ecx,stk - lea ebx,pri ; 3rd param: addr. of retval - add ecx,edi ; 4th param: addr. of function parameters - ; save a few registers (it is not necessary to save them all - ; and EAX should *not* be saved because it will hold the return - ; value) - push ebp - push esi - push edi - ; push the parameters - push ecx - push ebx - push edx - push eax - call [ebp+_callback] - _DROPARGS 10h ; remove arguments from stack - pop edi ; restore saved registers - pop esi - pop ebp - - mov edx,alt ; restore ALT - mov ebx,frm - mov ecx,stk ; restore STK - add ebx,edi ; restore FRM - add ecx,[esi-4] ; remove "number of parameter bytes" from the stack - add ecx,4 ; also remove the extra DWORD pushed - - cmp eax,AMX_ERR_NONE - jne near _return ; return error code, if any - mov eax,pri ; get retval into eax (PRI) - NEXT - - -OP_SYSREQ_D: -OP_SYSREQ_ND: -OP_FILE: -OP_LINE: -OP_SYMBOL: -OP_SRANGE: -OP_SYMTAG: -OP_JUMP_PRI: -OP_CALL_PRI: - jmp OP_INVALID - - -OP_SWITCH: - push ecx - mov ebp,esi ; EBP = CIP - add ebp,[esi+4] ; EBP = offset of the switch table - add ebp,4 ; skip the "OP_CASETBL" opcode - mov ecx,[ebp] ; ECX = number of records - mov esi,ebp ; ESI = address of first record - add esi,[ebp+4] ; preset ESI to "none-matched" case - op_switch_loop: - or ecx, ecx ; number of records == 0? - jz short op_switch_end ; yes, no more records, exit loop - add ebp,8 ; skip previous record - dec ecx ; already decrement cases to do - cmp eax,[ebp] ; PRI == case label? - jne short op_switch_loop ; no, continue loop - mov esi,ebp ; yes, get jump address and exit loop - add esi,[ebp+4] - op_switch_end: - pop ecx - NEXT - - -OP_ISWITCH: - push ecx - mov ebp,esi ; EBP = CIP - add ebp,[esi+4] ; EBP = offset of the icasetable - add ebp,4 ; skip the "OP_ICASETBL" opcode - mov ecx,[ebp] ; ECX = number of records - mov edx,[ebp+4] ; preset EDX to "none-matched" case - op_iswitch_loop: - or ecx, ecx ; number of records == 0? - jz short op_iswitch_end ; yes, no more records, exit loop - add ebp,8 ; skip previous record - dec ecx ; already decrement cases to do - cmp eax,[ebp] ; PRI == icase label? - jne short op_iswitch_loop ; no, continue loop - mov edx,[ebp+4] ; yes, get jump address and exit loop - op_iswitch_end: - pop ecx - ;load overlay - mov eax,amx - mov [eax+_ovl_index],edx -%ifdef CDECL_STDCALL - _SAVEREGS - push edx ; EDX (2nd parm)=overlay index - push eax ; EAX (1st parm)=amx structure -%endif - call [eax+_overlay] ; call overlay function - _DROPARGS 8 ; remove arguments from stack - _RESTOREREGS - mov esi,[eax+_codeseg] ; get new code base - mov code,esi ; save new code base in local variable - NEXT - - -OP_CASETBL: -OP_ICASETBL: - jmp OP_INVALID - - -OP_SWAP_PRI: - mov ebp,[edi+ecx] - add esi,4 - mov [edi+ecx],eax - mov eax,ebp - NEXT - - -OP_SWAP_ALT: - mov ebp,[edi+ecx] - add esi,4 - mov [edi+ecx],edx - mov edx,ebp - NEXT - - -OP_PUSH_ADR: - mov ebp,[esi+4] - add esi,8 - add ebp,frm - _PUSH ebp - NEXT - - -OP_NOP: - add esi,4 - NEXT - - -OP_BREAK: - mov ebp,amx ; get amx into ebp - add esi,4 - cmp DWORD [ebp+_debug], 0 - jnz break_calldebug - NEXT ; debug hook not active, ignore - - break_calldebug: - ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) - mov [ebp+_pri],eax - mov [ebp+_alt],edx ; EAX and EDX are now free to use - mov eax,frm - mov edx,hea - mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) - mov [ebp+_hea],edx - mov [ebp+_stk],ecx - mov eax,esi - sub eax,code ; EAX = CIP (relative to start of code segment) - mov [ebp+_cip],eax - ; call the debug hook - mov eax,ebp ; 1st parm: amx - _SAVEREGS - push eax - call [ebp+_debug] ; call debug function - _DROPARGS 4 ; remove arguments from stack - cmp eax,AMX_ERR_NONE - je short break_noabort; continue running - mov [ebp+_error],eax ; save EAX (error code) before restoring all regs - _RESTOREREGS ; abort run, but restore stack first - mov eax,[ebp+_error] ; get error code in EAX back again - jmp _return ; return error code - break_noabort: - _RESTOREREGS - mov eax,[ebp+_pri] ; restore PRI and ALT - mov edx,[ebp+_alt] - NEXT - - -OP_PUSH2_C: - add esi,12 - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - -OP_PUSH2: - add esi,12 - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - -OP_PUSH2_S: - add esi,12 - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - -OP_PUSH2_ADR: - add esi,12 - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH3_C: - add esi,16 - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - -OP_PUSH3: - add esi,16 - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - -OP_PUSH3_S: - add esi,16 - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - -OP_PUSH3_ADR: - add esi,16 - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH4_C: - add esi,20 - mov ebp,[esi-16] - _PUSH ebp - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - -OP_PUSH4: - add esi,20 - mov ebp,[esi-16] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - -OP_PUSH4_S: - add esi,20 - mov ebp,[esi-16] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - -OP_PUSH4_ADR: - add esi,20 - mov ebp,[esi-16] - add ebp,frm - _PUSH ebp - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_PUSH5_C: - add esi,24 - mov ebp,[esi-20] - _PUSH ebp - mov ebp,[esi-16] - _PUSH ebp - mov ebp,[esi-12] - _PUSH ebp - mov ebp,[esi-8] - _PUSH ebp - mov ebp,[esi-4] - _PUSH ebp - NEXT - -OP_PUSH5: - add esi,24 - mov ebp,[esi-20] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-16] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+edi] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - -OP_PUSH5_S: - add esi,24 - mov ebp,[esi-20] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-16] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-12] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-8] - mov ebp,[ebp+ebx] - _PUSH ebp - mov ebp,[esi-4] - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - -OP_PUSH5_ADR: - add esi,24 - mov ebp,[esi-20] - add ebp,frm - _PUSH ebp - mov ebp,[esi-16] - add ebp,frm - _PUSH ebp - mov ebp,[esi-12] - add ebp,frm - _PUSH ebp - mov ebp,[esi-8] - add ebp,frm - _PUSH ebp - mov ebp,[esi-4] - add ebp,frm - _PUSH ebp - NEXT - - -OP_LOAD_BOTH: - mov eax,[esi+4] - mov edx,[esi+8] - add esi,12 - mov eax,[edi+eax] - mov edx,[edi+edx] - NEXT - - -OP_LOAD_S_BOTH: - mov eax,[esi+4] - mov edx,[esi+8] - add esi,12 - mov eax,[ebx+eax] - mov edx,[ebx+edx] - NEXT - - -OP_CONST: - push eax - mov ebp,[esi+4] - mov eax,[esi+8] - add esi,12 - mov [ebp+edi],eax - pop eax - NEXT - - -OP_CONST_S: - push eax - mov ebp,[esi+4] - mov eax,[esi+8] - add esi,12 - mov [ebp+ebx],eax - pop eax - NEXT - - -OP_INVALID: - mov eax,AMX_ERR_INVINSTR - jmp _return - - -;----- packed opcodes -%ifndef AMX_NO_PACKED_OPC - -OP_LOAD_P_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[edi+eax] - NEXT - -OP_LOAD_P_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[edi+edx] - NEXT - - -OP_LOAD_P_S_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[ebx+eax] - NEXT - - -OP_LOAD_P_S_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[ebx+edx] - NEXT - - -OP_LODB_P_I: - _VERIFYADDRESS eax - GETPARAM_P ebp - mov eax,[edi+eax] ;subject to misalignment stalls - add esi,4 - and eax,DWORD [(lodb_and-4)+ebp*4] - NEXT - - -OP_LREF_P_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[edi+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_P_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[edi+edx] - mov edx,[edi+edx] - NEXT - - -OP_LREF_P_S_PRI: - GETPARAM_P eax - add esi,4 - mov eax,[ebx+eax] - mov eax,[edi+eax] - NEXT - - -OP_LREF_P_S_ALT: - GETPARAM_P edx - add esi,4 - mov edx,[ebx+edx] - mov edx,[edi+edx] - NEXT - - -OP_CONST_P_PRI: - GETPARAM_P eax - add esi,4 - NEXT - - -OP_CONST_P_ALT: - GETPARAM_P edx - add esi,4 - NEXT - - -OP_ADDR_P_PRI: - GETPARAM_P eax - add esi,4 - add eax,frm - NEXT - - -OP_ADDR_P_ALT: - GETPARAM_P edx - add esi,4 - add edx,frm - NEXT - - -OP_STOR_P_PRI: - GETPARAM_P ebp - add esi,4 - mov [ebp+edi],eax - NEXT - - -OP_STOR_P_ALT: - GETPARAM_P ebp - add esi,4 - mov [ebp+edi],edx - NEXT - - -OP_STOR_P_S_PRI: - GETPARAM_P ebp - add esi,4 - mov [ebp+ebx],eax - NEXT - - -OP_STOR_P_S_ALT: - GETPARAM_P ebp - add esi,4 - mov [ebp+ebx],edx - NEXT - - -OP_STRB_P_I: - GETPARAM_P ebp - add esi,4 - jmp near strb_entry - - -OP_SREF_P_PRI: - GETPARAM_P ebp - add esi,4 - mov ebp,[edi+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_P_ALT: - GETPARAM_P ebp - add esi,4 - mov ebp,[edi+ebp] - mov [edi+ebp],edx - NEXT - - -OP_SREF_P_S_PRI: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebx+ebp] - mov [edi+ebp],eax - NEXT - - -OP_SREF_P_S_ALT: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebx+ebp] - mov [edi+ebp],edx - NEXT - - -OP_LIDX_P_B: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - add eax,edx - _VERIFYADDRESS eax - mov eax,[edi+eax] - NEXT - - -OP_IDXADDR_P_B: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - add eax,edx - NEXT - - -OP_ALIGN_P_PRI: - GETPARAM_P ebp - add esi,4 - neg ebp ; ebp = -param - add ebp,4 ; ebp = 4 - param - xor eax,ebp - NEXT - - -OP_ALIGN_P_ALT: - GETPARAM_P ebp - add esi,4 - neg ebp - add ebp,4 - xor edx,ebp - NEXT - - -OP_PUSH_P_C: - GETPARAM_P ebp - add esi,4 - _PUSH ebp - NEXT - - -OP_PUSH_P: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebp+edi] - _PUSH ebp - NEXT - - -OP_PUSH_P_S: - GETPARAM_P ebp - add esi,4 - mov ebp,[ebp+ebx] - _PUSH ebp - NEXT - - -OP_STACK_P: - GETPARAM_P ebp - add esi,4 - mov edx,ecx - add ecx,ebp - _CHKMARGIN - _CHKSTACK - NEXT - - -OP_HEAP_P: - GETPARAM_P ebp - add esi,4 - mov edx,hea - add hea,ebp - _CHKMARGIN - _CHKHEAP - NEXT - - -OP_SHL_P_C_PRI: - push ecx - GETPARAM_P ecx - add esi,4 - shl eax,cl - pop ecx - NEXT - - -OP_SHL_P_C_ALT: - push ecx - GETPARAM_P ecx - add esi,4 - shl edx,cl - pop ecx - NEXT - - -OP_SHR_P_C_PRI: - push ecx - GETPARAM_P ecx - add esi,4 - shr eax,cl - pop ecx - NEXT - - -OP_SHR_P_C_ALT: - push ecx - GETPARAM_P ecx - add esi,4 - shr edx,cl - pop ecx - NEXT - - -OP_ADD_P_C: - GETPARAM_P ebp - add esi,4 - add eax,ebp - NEXT - - -OP_SMUL_P_C: - GETPARAM_P ebp - add esi,4 - push edx - imul ebp - pop edx - NEXT - - -OP_ZERO_P: - GETPARAM_P ebp - add esi,4 - mov DWORD [edi+ebp],0 - NEXT - - -OP_ZERO_P_S: - GETPARAM_P ebp - add esi,4 - mov DWORD [ebx+ebp],0 - NEXT - - -OP_EQ_P_C_PRI: - GETPARAM_P ebp - add esi,4 - cmp eax,ebp ; PRI == value ? - mov eax,0 - sete al - NEXT - - -OP_EQ_P_C_ALT: - GETPARAM_P ebp - add esi,4 - xor eax,eax - cmp edx,[esi+4] ; ALT == value ? - sete al - NEXT - - -OP_INC_P: - GETPARAM_P ebp - add esi,4 - inc DWORD [edi+ebp] - NEXT - - -OP_INC_P_S: - GETPARAM_P ebp - add esi,4 - inc DWORD [ebx+ebp] - NEXT - - -OP_DEC_P: - GETPARAM_P ebp - add esi,4 - dec DWORD [edi+ebp] - NEXT - - -OP_DEC_P_S: - GETPARAM_P ebp - add esi,4 - dec DWORD [ebx+ebp] - NEXT - - -OP_MOVS_P: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - GETPARAM_P ebp - add ebp,eax - dec ebp - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx ; matching POP is in movs_entry - GETPARAM_P ecx - add esi,4 - jmp near movs_entry - - -OP_CMPS_P: - _VERIFYADDRESS eax ; PRI - _VERIFYADDRESS edx ; ALT - GETPARAM_P ebp - add ebp,eax - dec ebp ; EBP = PRI + size - 1 - _VERIFYADDRESS ebp ; PRI + size - 1 - sub ebp,eax ; EBP = size - 1 - add ebp,edx ; EBP = ALT + size - 1 - _VERIFYADDRESS ebp ; ALT + size - 1 - - push ecx ; matching pop is in cmps_entry - GETPARAM_P ecx - add esi,4 - jmp near cmps_entry - - -OP_FILL_P: - GETPARAM_P ebp ; get byte count - add esi,4 - jmp near fill_entry - - -OP_HALT_P: - cmp DWORD retval,0 - je short halt_no_retval_p - mov ebp,retval - mov [ebp],eax - halt_no_retval_p: - ; store the complete status in the AMX - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) - mov [ebp+_alt],edx - mov [ebp+_stk],ecx - mov ecx,hea - mov ebx,frm - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - GETPARAM_P ebx ; EBX=parameter of the HALT opcode - add esi,4 ; skip this instruction - mov eax,esi ; EAX=CIP - sub eax,code - mov [ebp+_cip],eax - mov eax,ebx ; return the parameter of the HALT opcode - jmp _return - - -OP_BOUNDS_P: - GETPARAM_P ebp - add esi,4 - cmp eax,ebp - ja err_bounds ; use unsigned comparison, so <0 is >bounds - NEXT - - -OP_PUSH_P_ADR: - GETPARAM_P ebp - add esi,4 - add ebp,frm - _PUSH ebp - NEXT - -%endif ; AMX_NO_PACKED_OPC - - -err_call: - mov eax,AMX_ERR_CALLBACK - jmp _return - -err_stack: - mov eax,AMX_ERR_STACKERR - jmp _return - -err_stacklow: - mov eax,AMX_ERR_STACKLOW - jmp _return - -err_memaccess: - mov eax,AMX_ERR_MEMACCESS - jmp _return - -err_bounds: - mov eax,AMX_ERR_BOUNDS - jmp _return - -err_heaplow: - mov eax,AMX_ERR_HEAPLOW - jmp _return - -err_divide: - mov eax,AMX_ERR_DIVIDE - jmp _return - - -_return: - ; save a few parameters, mostly for the "sleep"function - mov ebp,amx ; get amx into ebp - mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT) - mov [ebp+_alt],edx ; store values in AMX structure (PRI, ALT) - - pop esi ; remove FRM from stack - - pop ecx - pop ebx - pop edx - - pop esi ; remove pointer to amx from stack - pop esi ; remove code segment pointer - pop esi ; remove code size - - add esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - pop ebp - pop esi - pop edi - pop ebx - ret - -; _amx_exec_asm ENDP - - -Start_DATA - ALIGN 4 ; This is essential to avoid misalignment stalls. - -lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh - - GLOBAL amx_opcodelist - GLOBAL _amx_opcodelist -amx_opcodelist: -_amx_opcodelist DD OP_INVALID - DD OP_LOAD_PRI - DD OP_LOAD_ALT - DD OP_LOAD_S_PRI - DD OP_LOAD_S_ALT - DD OP_LREF_PRI - DD OP_LREF_ALT - DD OP_LREF_S_PRI - DD OP_LREF_S_ALT - DD OP_LOAD_I - DD OP_LODB_I - DD OP_CONST_PRI - DD OP_CONST_ALT - DD OP_ADDR_PRI - DD OP_ADDR_ALT - DD OP_STOR_PRI - DD OP_STOR_ALT - DD OP_STOR_S_PRI - DD OP_STOR_S_ALT - DD OP_SREF_PRI - DD OP_SREF_ALT - DD OP_SREF_S_PRI - DD OP_SREF_S_ALT - DD OP_STOR_I - DD OP_STRB_I - DD OP_LIDX - DD OP_LIDX_B - DD OP_IDXADDR - DD OP_IDXADDR_B - DD OP_ALIGN_PRI - DD OP_ALIGN_ALT - DD OP_LCTRL - DD OP_SCTRL - DD OP_MOVE_PRI - DD OP_MOVE_ALT - DD OP_XCHG - DD OP_PUSH_PRI - DD OP_PUSH_ALT - DD OP_PICK - DD OP_PUSH_C - DD OP_PUSH - DD OP_PUSH_S - DD OP_POP_PRI - DD OP_POP_ALT - DD OP_STACK - DD OP_HEAP - DD OP_PROC - DD OP_RET - DD OP_RETN - DD OP_CALL - DD OP_CALL_PRI - DD OP_JUMP - DD OP_JREL - DD OP_JZER - DD OP_JNZ - DD OP_JEQ - DD OP_JNEQ - DD OP_JLESS - DD OP_JLEQ - DD OP_JGRTR - DD OP_JGEQ - DD OP_JSLESS - DD OP_JSLEQ - DD OP_JSGRTR - DD OP_JSGEQ - DD OP_SHL - DD OP_SHR - DD OP_SSHR - DD OP_SHL_C_PRI - DD OP_SHL_C_ALT - DD OP_SHR_C_PRI - DD OP_SHR_C_ALT - DD OP_SMUL - DD OP_SDIV - DD OP_SDIV_ALT - DD OP_UMUL - DD OP_UDIV - DD OP_UDIV_ALT - DD OP_ADD - DD OP_SUB - DD OP_SUB_ALT - DD OP_AND - DD OP_OR - DD OP_XOR - DD OP_NOT - DD OP_NEG - DD OP_INVERT - DD OP_ADD_C - DD OP_SMUL_C - DD OP_ZERO_PRI - DD OP_ZERO_ALT - DD OP_ZERO - DD OP_ZERO_S - DD OP_SIGN_PRI - DD OP_SIGN_ALT - DD OP_EQ - DD OP_NEQ - DD OP_LESS - DD OP_LEQ - DD OP_GRTR - DD OP_GEQ - DD OP_SLESS - DD OP_SLEQ - DD OP_SGRTR - DD OP_SGEQ - DD OP_EQ_C_PRI - DD OP_EQ_C_ALT - DD OP_INC_PRI - DD OP_INC_ALT - DD OP_INC - DD OP_INC_S - DD OP_INC_I - DD OP_DEC_PRI - DD OP_DEC_ALT - DD OP_DEC - DD OP_DEC_S - DD OP_DEC_I - DD OP_MOVS - DD OP_CMPS - DD OP_FILL - DD OP_HALT - DD OP_BOUNDS - DD OP_SYSREQ_PRI - DD OP_SYSREQ_C - DD OP_FILE ; obsolete - DD OP_LINE ; obsolete - DD OP_SYMBOL ; obsolete - DD OP_SRANGE ; obsolete - DD OP_JUMP_PRI - DD OP_SWITCH - DD OP_CASETBL - DD OP_SWAP_PRI - DD OP_SWAP_ALT - DD OP_PUSH_ADR - DD OP_NOP - DD OP_SYSREQ_N - DD OP_SYMTAG ; obsolete - DD OP_BREAK - ; macro opcodes - DD OP_PUSH2_C - DD OP_PUSH2 - DD OP_PUSH2_S - DD OP_PUSH2_ADR - DD OP_PUSH3_C - DD OP_PUSH3 - DD OP_PUSH3_S - DD OP_PUSH3_ADR - DD OP_PUSH4_C - DD OP_PUSH4 - DD OP_PUSH4_S - DD OP_PUSH4_ADR - DD OP_PUSH5_C - DD OP_PUSH5 - DD OP_PUSH5_S - DD OP_PUSH5_ADR - DD OP_LOAD_BOTH - DD OP_LOAD_S_BOTH - DD OP_CONST - DD OP_CONST_S - ; overlay opcodes - DD OP_ICALL - DD OP_IRETN - DD OP_ISWITCH - DD OP_ICASETBL - ; packed opcodes -%ifndef AMX_NO_PACKED_OPC - DD OP_LOAD_P_PRI - DD OP_LOAD_P_ALT - DD OP_LOAD_P_S_PRI - DD OP_LOAD_P_S_ALT - DD OP_LREF_P_PRI - DD OP_LREF_P_ALT - DD OP_LREF_P_S_PRI - DD OP_LREF_P_S_ALT - DD OP_LODB_P_I - DD OP_CONST_P_PRI - DD OP_CONST_P_ALT - DD OP_ADDR_P_PRI - DD OP_ADDR_P_ALT - DD OP_STOR_P_PRI - DD OP_STOR_P_ALT - DD OP_STOR_P_S_PRI - DD OP_STOR_P_S_ALT - DD OP_SREF_P_PRI - DD OP_SREF_P_ALT - DD OP_SREF_P_S_PRI - DD OP_SREF_P_S_ALT - DD OP_STRB_P_I - DD OP_LIDX_P_B - DD OP_IDXADDR_P_B - DD OP_ALIGN_P_PRI - DD OP_ALIGN_P_ALT - DD OP_PUSH_P_C - DD OP_PUSH_P - DD OP_PUSH_P_S - DD OP_STACK_P - DD OP_HEAP_P - DD OP_SHL_P_C_PRI - DD OP_SHL_P_C_ALT - DD OP_SHR_P_C_PRI - DD OP_SHR_P_C_ALT - DD OP_ADD_P_C - DD OP_SMUL_P_C - DD OP_ZERO_P - DD OP_ZERO_P_S - DD OP_EQ_P_C_PRI - DD OP_EQ_P_C_ALT - DD OP_INC_P - DD OP_INC_P_S - DD OP_DEC_P - DD OP_DEC_P_S - DD OP_MOVS_P - DD OP_CMPS_P - DD OP_FILL_P - DD OP_HALT_P - DD OP_BOUNDS_P - DD OP_PUSH_P_ADR -%endif ; AMX_NO_PACKED_OPC - ; "patch" opcodes - DD OP_SYSREQ_D - DD OP_SYSREQ_ND - +; AMXEXECN.ASM Abstract Machine for the "Pawn" language +; + +;Some notes: +; * This file was adapted from AMXEXEC.ASM (for MASM/TASM/WASM). This +; version is for NASM (Netwide Assembler). NASM uses Intel syntax for +; the mnemonics, but it is not compatible with MASM. +; * The "calling convention" is __cdecl for the amx_exec_asm() itself and +; __cdecl or __stdcall for the native routines (the default is __cdecl, +; define the macro STDECL to set __stdcall). +; * The borland compiler uses different segment definitions as Microsoft +; Visual C/C++ and GNU GCC. To assemble the abstract machine with "Borland" +; segments, add the definition "BORLAND" on the command line. +; * You will need to compile the standard AMX.C file with the macro ASM32 +; defined. On the command line, use: +; nasmw -O1 -f obj -d BORLAND amxexecn.asm +; bcc32 -DASM32 srun.c amx.c amxcore.c amxcons.c amxexecn.obj +; or +; nasmw -O1 -f win32 amxexecn.asm +; or +; nasm -O1 -f elf amxexecn.asm +; gcc -o srun -DLINUX -DASM32 -I../linux srun.c amx.c amxcore.c amxcons.c amxexecn.o +; * See the notes in AMXEXEC.ASM for more information and a change log). +; +; +;Copyright and license of use, please read +;----------------------------------------- +;The assembler implementation of the abstract machine for the Pawn language, +;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. +; +;Permission is hereby granted, without written agreement and without paid +;license or royalty fees, to use, copy, modify, and distribute this software +;and its documentation for any purpose, subject to the following conditions: +; +;1. The above copyright notice and this permission notice shall appear in all +; copies or substantial portions of this software. +; +;2. Modifications of this software that do not originate from me (Marc Peter) +; must be explicitly mentioned in a README file or another appropriate +; place. +; +;The use of this software as a subsystem of a larger software product is +;explicitly allowed, regardless of whether that larger product is proprietary, +;gratis or commercially available. +; +;I (Marc Peter) specifically disclaim any warranties, including, but not +;limited to, the implied warranties of merchantability and fitness for a +;particular purpose. The software is provided on an "as is" basis, +;and I have no obligation to provide maintenance, support, updates, +;enhancements or modifications. +; +;I cannot be held liable for any damage or loss of profits that results +;from the use of the software (or part thereof), or from the inability to +;use it. +; +; +;History (list of changes) +;------------------------- +; 14 december 2005 by Thiadmer Riemersma (TR) +; Addition of macro instructions, to speed up instruction decoding +; 17 february 2005 by Thiadmer Riemersma (TR) +; Addition of the BREAK opcode, removal of the older debugging opcode table. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 2 february 2004 by Thiadmer Riemersma (TR) +; Added checking of the return address in the RET and RETN opcodes. +; Changed handling of LINE opcode, so that the debugger can force a +; sleep. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added support for the SYMTAG and SYSCALL.D opcodes +; 3 october 2003 by Thiadmer Riemersma (TR) +; Added "non-debug" versions of various opcodes, to avoid repetitive +; checking of the "debug" flag. +; 15 September 2003 by Thiadmer Riemersma (TR) +; Minor corrections, mostly to support older versions of NASM +; 26 January 2003 by Thiadmer Riemersma (TR) +; Port to NASM +;----- + +;CPU 386 -- some older versions of NASM do not support this keyword + +; Macro to begin a code segment +%macro Start_CODE 0 + %ifdef BORLAND + segment _TEXT public align=1 class=CODE use32 + %else + segment .text + %endif +%endmacro + +; Macro to begin a data segment +%macro Start_DATA 0 + %ifdef BORLAND + segment _DATA public align=4 class=DATA use32 + %else + segment .data + %endif +%endmacro + +%include "amxdefn.asm" + +;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +%macro _PUSH 1 + mov [edi+ecx-4],%1 + sub ecx,4 +%endmacro + + +;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +%macro _POP 1 + mov %1,[edi+ecx] + add ecx,4 +%endmacro + +%macro GO_ON 0 + jmp DWORD [esi] +; ALIGN 4 +%endmacro + + +%macro _CHKSTACK 0 + cmp ecx,stp + jg near err_stacklow +%endmacro + +%macro _CHKMARGIN 0 + lea ebp,[ecx-16*4] ;savety margin = 16 cells + cmp hea,ebp + jg near err_stack +%endmacro + +%macro _CHKHEAP 0 + mov ebp,amx + mov ebp,[ebp+_hlw] + cmp DWORD hea,ebp + jl near err_heaplow +%endmacro + +%macro _CHKDIVIDEZERO 0 + or ebp,ebp ; check for divide by zero + jz near err_divide +%endmacro + +%macro _VERIFYADDRESS 1 ; used in load.i, store.i & lidx + cmp %1,stp ; error if address >= stp + jae near err_memaccess + cmp %1,hea ; so address=hea, ok if address>=stk + jb near err_memaccess + %%address_ok: +%endmacro + +%macro _SAVEREGS 0 ; save the registers (that may not be + PUSHAD ; __stdcall calling conventions) +%endmacro + +%macro _RESTOREREGS 0 + POPAD +%endmacro + +%macro _DROPARGS 1 ; remove function arguments from the stack + %ifndef STDECL ; (only for __cdecl calling convention) + add esp,%1 + %endif +%endmacro + + +Start_CODE + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell asm_exec( cell *regs, cell *retval, cell stp, cell hea ); +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + GLOBAL _amx_exec_asm + GLOBAL amx_exec_asm +amx_exec_asm: +_amx_exec_asm: ;PROC + + push ebx + mov eax,[esp+08h] + mov edx,[esp+0ch] + mov ebx,[esp+10h] + mov ecx,[esp+14h] + + push edi + push esi + push ebp + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push DWORD [eax+20h] ; store code size + push DWORD [eax+1ch] ; store pointer to code segment + push DWORD [eax+18h] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push DWORD [eax+14h] ; store FRM + +%define stk [esp+36] ; define some aliases to registers +%define alt [esp+32] ; that are stored on the stack +%define pri [esp+28] +%define codesiz [esp+24] +%define code [esp+20] +%define amx [esp+16] +%define retval [esp+12] +%define stp [esp+8] +%define hea [esp+4] +%define frm [esp] ; FRM is NOT stored in ebp, rather FRM+DAT + ; is being held in ebx. + + mov edx,code ; change the code size to an... + add codesiz,edx ; ..."end of code" address + + mov edx,[eax+04h] ; get ALT + mov esi,[eax+08h] ; get CIP + mov edi,[eax+0ch] ; get pointer to data segment + mov ecx,[eax+10h] ; get STK + mov ebx,[eax+14h] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + GO_ON ; start interpreting + + +OP_LOAD_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + GO_ON + +OP_LOAD_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + GO_ON + +;good +OP_LOAD_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + GO_ON + +;good +OP_LOAD_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + GO_ON + +OP_LOAD_I: + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LODB_I: + _VERIFYADDRESS eax + mov ebp,[esi+4] + mov eax,[edi+eax] ;subject to misalignment stalls + add esi,8 + and eax,DWORD [(lodb_and-4)+ebp*4] + GO_ON + +OP_LREF_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + mov edx,[edi+edx] + GO_ON + +OP_LREF_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + mov edx,[edi+edx] + GO_ON + +;good +OP_CONST_PRI: + mov eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_CONST_ALT: + mov edx,[esi+4] + add esi,8 + GO_ON + +;good +OP_ADDR_PRI: + mov eax,[esi+4] + add esi,8 + add eax,frm + GO_ON + +;good +OP_ADDR_ALT: + mov edx,[esi+4] + add esi,8 + add edx,frm + GO_ON + +OP_STOR_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],eax + GO_ON + +OP_STOR_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],edx + GO_ON + +;good +OP_STOR_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],eax + GO_ON + +;good +OP_STOR_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],edx + GO_ON + +;good +OP_STOR_I: + add esi,4 + _VERIFYADDRESS edx + mov [edi+edx],eax + GO_ON + +OP_STRB_I: + _VERIFYADDRESS edx + mov ebp,[esi+4] + add esi,8 + cmp ebp,1 + jne short strb_not1byte + mov [edi+edx],al + GO_ON + strb_not1byte: + cmp ebp,4 + je short strb_4byte + mov [edi+edx],ax + GO_ON + strb_4byte: + mov [edi+edx],eax + GO_ON + +OP_SREF_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],edx + GO_ON + +OP_SREF_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],edx + GO_ON + +;good +OP_LIDX: + lea eax,[edx+4*eax] + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LIDX_B: + push ecx + mov ecx,[esi+4] + shl eax,cl + add esi,8 + add eax,edx + pop ecx + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +;good +OP_IDXADDR: + add esi,4 + lea eax,[edx+4*eax] + GO_ON + +OP_IDXADDR_B: + push ecx + mov ecx,[esi+4] + add esi,8 + shl eax,cl + pop ecx + add eax,edx + GO_ON + +OP_ALIGN_PRI: + mov ebp,4 ; ??? one operation too many? + sub ebp,[esi+4] + add esi,8 + xor eax,ebp + GO_ON + +OP_ALIGN_ALT: + mov ebp,4 + sub ebp,[esi+4] + add esi,8 + xor edx,ebp + GO_ON + +OP_LCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,0 + jne short lctrl_1 + mov eax,code ; COD + GO_ON + lctrl_1: + cmp ebp,1 + jne short lctrl_2 + mov eax,edi ; DAT + GO_ON + lctrl_2: + cmp ebp,2 + jne short lctrl_3 + mov eax,hea ; 2=HEA + GO_ON + lctrl_3: + cmp ebp,3 + jne short lctrl_4 + mov ebp,amx + mov eax,stp + GO_ON + lctrl_4: + cmp ebp,4 + jne short lctrl_5 + mov eax,ecx ; 4=STK + GO_ON + lctrl_5: + cmp ebp,5 + jne short lctrl_6 + mov eax,frm ; 5=FRM + GO_ON + lctrl_6: + mov eax,esi ; 6=CIP + sub eax,code + GO_ON + +OP_SCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,2 + jne short sctrl_4 + mov hea,eax ; 2=HEA + GO_ON + sctrl_4: + cmp ebp,4 + jne short sctrl_5 + mov ecx,eax ; 4=STK + GO_ON + sctrl_5: + cmp ebp,5 + jne short sctrl_6 + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate FRM + sctrl_6: + GO_ON + +OP_MOVE_PRI: + add esi,4 + mov eax,edx + GO_ON + +;good +OP_MOVE_ALT: + add esi,4 + mov edx,eax + GO_ON + +OP_XCHG: + add esi,4 + xchg eax,edx + GO_ON + +;good +OP_PUSH_PRI: + add esi,4 + _PUSH eax + GO_ON + +;good +OP_PUSH_ALT: + add esi,4 + _PUSH edx + GO_ON + +OP_PUSH_R_PRI: + mov ebp,[esi+4] + add esi,8 + push_loop: + _PUSH eax + dec ebp + jnz short push_loop + GO_ON + +;good +OP_PUSH_C: + mov ebp,[esi+4] + add esi,8 + _PUSH ebp + GO_ON + +OP_PUSH: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +;good +OP_PUSH_S: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_POP_PRI: + add esi,4 + _POP eax + GO_ON + +;good +OP_POP_ALT: + add esi,4 + _POP edx + GO_ON + +;good +OP_STACK: + mov edx,ecx + add ecx,[esi+4] + _CHKMARGIN + _CHKSTACK + add esi,8 + GO_ON + +;good +OP_HEAP: + mov ebp,[esi+4] + mov edx,hea + add esi,8 + add hea,ebp + _CHKMARGIN + _CHKHEAP + GO_ON + +;good +OP_PROC: + mov ebx,frm + add esi,4 + _PUSH ebx + mov ebx,edi + mov frm,ecx + add ebx,ecx + _CHKMARGIN + GO_ON + +OP_RET: + _POP ebx + _POP esi + cmp esi,code ; verify ESI>=code + jb err_memaccess + cmp esi,codesiz ; verify ESI=code + jb err_memaccess + cmp esi,codesiz ; verify ESI 0 and 0 => 1 + GO_ON + +OP_NEG: + add esi,4 + neg eax + GO_ON + +OP_INVERT: + add esi,4 + not eax + GO_ON + +;good +OP_ADD_C: + add eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_SMUL_C: + mov ebp,[esi+4] + push edx + imul ebp + pop edx + add esi,8 + GO_ON + +;good +OP_ZERO_PRI: + add esi,4 + sub eax,eax + GO_ON + +;good +OP_ZERO_ALT: + add esi,4 + sub edx,edx + GO_ON + +OP_ZERO: + mov ebp,[esi+4] + add esi,8 + mov DWORD [edi+ebp],0 + GO_ON + +OP_ZERO_S: + mov ebp,[esi+4] + add esi,8 + mov DWORD [ebx+ebp],0 + GO_ON + +OP_SIGN_PRI: + shl eax,24 + add esi,4 + sar eax,24 + GO_ON + +OP_SIGN_ALT: + shl edx,24 + add esi,4 + sar edx,24 + GO_ON + +OP_EQ: + add esi,4 + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + GO_ON + +OP_NEQ: + add esi,4 + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + GO_ON + +OP_LESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + GO_ON + +OP_LEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + GO_ON + +OP_GRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + GO_ON + +OP_GEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + GO_ON + +;good +OP_SLESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + GO_ON + +OP_SLEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + GO_ON + +OP_SGRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + GO_ON + +OP_SGEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + GO_ON + +OP_EQ_C_PRI: + cmp eax,[esi+4] ; PRI == value ? + lea esi,[esi+8] + mov eax,0 + sete al + GO_ON + +OP_EQ_C_ALT: + xor eax,eax + cmp edx,[esi+4] ; ALT == value ? + lea esi,[esi+8] + sete al + GO_ON + +OP_INC_PRI: + add esi,4 + inc eax + GO_ON + +OP_INC_ALT: + add esi,4 + inc edx + GO_ON + +OP_INC: + mov ebp,[esi+4] + add esi,8 + inc DWORD [edi+ebp] + GO_ON + +;good +OP_INC_S: + mov ebp,[esi+4] + add esi,8 + inc DWORD [ebx+ebp] + GO_ON + +OP_INC_I: + add esi,4 + inc DWORD [edi+eax] + GO_ON + +OP_DEC_PRI: + add esi,4 + dec eax + GO_ON + +OP_DEC_ALT: + add esi,4 + dec edx + GO_ON + +OP_DEC: + mov ebp,[esi+4] + add esi,8 + dec DWORD [edi+ebp] + GO_ON + +OP_DEC_S: + mov ebp,[esi+4] + add esi,8 + dec DWORD [ebx+ebp] + GO_ON + +OP_DEC_I: + add esi,4 + sub DWORD [edi+eax],1 + GO_ON + +OP_MOVS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; PRI + size - 1 + mov ebp,edx + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx + shr ecx,2 + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + pop ecx + GO_ON + +OP_CMPS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] ; size in bytes + dec ebp ; EBP = PRI + size - 1 + _VERIFYADDRESS ebp ; PRI + size - 1 + sub ebp,eax ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax + repe cmpsb + je short cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + pop ecx + GO_ON + + +OP_FILL: + mov ebp,[esi+4] ; get byte count + add esi,8 + and ebp,0fffffffch ; align to words + jz short fill_ready + _VERIFYADDRESS edx ; ALT + dec ebp ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + sub ebp,edx ; restore EBP + inc ebp + + push ecx + push edi + mov ecx,ebp ; ECX = count (in bytes) + lea edi,[edi+edx] ; EDI = physical starting address + shr ecx,2 ; ECX = count (in DWORDS) + rep stosd + pop edi + pop ecx + fill_ready: + GO_ON + + +OP_HALT: + cmp DWORD retval,0 + je short halt_no_retval + mov ebp,retval + mov [ebp],eax + halt_no_retval: + ; store the complete status in the AMX + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) + mov [ebp+_alt],edx + mov [ebp+_stk],ecx + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + mov ebx,[esi+4] ; EBX=parameter of the HALT opcode + add esi,8 ; skip this instruction + mov eax,esi ; EAX=CIP + sub eax,code + mov [ebp+_cip],eax + mov eax,ebx ; return the parameter of the HALT opcode + jmp _return + + +OP_BOUNDS: + mov ebp,[esi+4] + add esi,8 + cmp eax,ebp + ja near err_bounds ; use unsigned comparison, so <0 is >bounds + GO_ON + + +OP_SYSREQ_C: + mov eax,[esi+4] ; get function number + add esi,4 + + +OP_SYSREQ_PRI: + mov ebp,amx ; get amx into ebp + add esi,4 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov ebx,esi ; also store CIP + sub ebx,code + mov [ebp+_cip],ebx + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + mov ecx,stk + lea ebx,pri ; 3rd param: addr. of retval + add ecx,edi ; 4th param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push ecx + push ebx + push edx + push eax + call [ebp+_callback] + _DROPARGS 10h ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + cmp eax,AMX_ERR_NONE + jne near _return ; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_SYSREQ_N: + mov ebp,[esi+8] ; get # of bytes passed as parameters + mov eax,[esi+4] ; get function number + _PUSH ebp ; push 2nd parameter + add esi,12 + mov ebp,amx ; get amx into ebp + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov ebx,esi ; also store CIP + sub ebx,code + mov [ebp+_cip],ebx + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + mov ecx,stk + lea ebx,pri ; 3rd param: addr. of retval + add ecx,edi ; 4th param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push ecx + push ebx + push edx + push eax + call [ebp+_callback] + _DROPARGS 10h ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + add ecx,[esi-4] ; remove "number of parameter bytes" from the stack + add ecx,4 ; also remove the extra DWORD pushed + + cmp eax,AMX_ERR_NONE + jne near _return ; return error code, if any + mov eax,pri ; get retval into eax (PRI) + GO_ON + + +OP_SYSREQ_D: + mov ebx,[esi+4] ; get function address + mov ebp,amx ; get amx into ebp + add esi,8 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov eax,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + mov edx,stk + add edx,edi ; 2nd param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push edx + push eax + call ebx ; direct call + _DROPARGS 8 ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + cmp DWORD [ebp+_error],AMX_ERR_NONE + jne near _return ; return error code, if any + + ; function result is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_SYSREQ_ND: + mov ebp,[esi+8] ; get # of bytes passed as parameters + mov ebx,[esi+4] ; get function number + _PUSH ebp ; push 2nd parameter + add esi,12 + mov ebp,amx ; get amx into ebp + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov eax,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + mov edx,stk + add edx,edi ; 2nd param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push edx + push eax + call ebx ; direct call + _DROPARGS 8 ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + + ; function result is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + add ecx,[esi-4] ; remove "number of parameter bytes" from the stack + add ecx,4 ; also remove the extra DWORD pushed + + cmp DWORD [ebp+_error],AMX_ERR_NONE + jne near _return ; return error code, if any + GO_ON + + +OP_FILE: + jmp OP_INVALID + + +OP_LINE: + add esi,12 + GO_ON + + +OP_SYMBOL: + add esi,[esi+4] + add esi,8 ; skip "fixed" part + GO_ON + + +OP_SRANGE: + add esi,12 + GO_ON + + +OP_SYMTAG: + add esi,8 + GO_ON + + +OP_JUMP_PRI: + mov esi,eax + GO_ON + + +OP_SWITCH: + push ecx + mov ebp,[esi+4] ; get offset of the switch table + add ebp,4 ; skip the "OP_CASETBL" opcode + mov ecx,[ebp] ; ECX = number of records + mov esi,[ebp+4] ; preset ESI to "none-matched" case + op_switch_loop: + or ecx, ecx ; number of records == 0? + jz short op_switch_end ; yes, no more records, exit loop + add ebp,8 ; skip previous record + dec ecx ; already decrement cases to do + cmp eax,[ebp] ; PRI == case label? + jne short op_switch_loop ; no, continue loop + mov esi,[ebp+4] ; yes, get jump address and exit loop + op_switch_end: + pop ecx + GO_ON + + +OP_CASETBL: + jmp OP_INVALID + + +OP_SWAP_PRI: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],eax + mov eax,ebp + GO_ON + + +OP_SWAP_ALT: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],edx + mov edx,ebp + GO_ON + + +OP_PUSH_ADR: + mov ebp,[esi+4] + add esi,8 + add ebp,frm + _PUSH ebp + GO_ON + + +OP_NOP: + add esi,4 + GO_ON + + +OP_BREAK: + mov ebp,amx ; get amx into ebp + add esi,4 + cmp DWORD [ebp+_debug], 0 + jnz break_calldebug + GO_ON ; debug hook not active, ignore + + break_calldebug: + ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) + mov [ebp+_pri],eax + mov [ebp+_alt],edx ; EAX and EDX are now free to use + mov eax,frm + mov edx,hea + mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) + mov [ebp+_hea],edx + mov [ebp+_stk],ecx + mov eax,esi + sub eax,code ; EAX = CIP (relative to start of code segment) + mov [ebp+_cip],eax + ; call the debug hook + mov eax,ebp ; 1st parm: amx + _SAVEREGS + push eax + call [ebp+_debug] ; call debug function + _DROPARGS 4 ; remove arguments from stack + cmp eax,AMX_ERR_NONE + je short break_noabort; continue running + mov [ebp+_error],eax ; save EAX (error code) before restoring all regs + _RESTOREREGS ; abort run, but restore stack first + mov eax,[ebp+_error] ; get error code in EAX back again + jmp _return ; return error code + break_noabort: + _RESTOREREGS + mov eax,[ebp+_pri] ; restore PRI and ALT + mov edx,[ebp+_alt] + GO_ON + + +OP_PUSH2_C: + add esi,12 + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH2: + add esi,12 + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH2_S: + add esi,12 + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH2_ADR: + add esi,12 + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH3_C: + add esi,16 + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH3: + add esi,16 + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH3_S: + add esi,16 + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH3_ADR: + add esi,16 + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH4_C: + add esi,20 + mov ebp,[esi-16] + _PUSH ebp + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH4: + add esi,20 + mov ebp,[esi-16] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH4_S: + add esi,20 + mov ebp,[esi-16] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH4_ADR: + add esi,20 + mov ebp,[esi-16] + add ebp,frm + _PUSH ebp + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_PUSH5_C: + add esi,24 + mov ebp,[esi-20] + _PUSH ebp + mov ebp,[esi-16] + _PUSH ebp + mov ebp,[esi-12] + _PUSH ebp + mov ebp,[esi-8] + _PUSH ebp + mov ebp,[esi-4] + _PUSH ebp + GO_ON + +OP_PUSH5: + add esi,24 + mov ebp,[esi-20] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-16] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+edi] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +OP_PUSH5_S: + add esi,24 + mov ebp,[esi-20] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-16] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-12] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-8] + mov ebp,[ebp+ebx] + _PUSH ebp + mov ebp,[esi-4] + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_PUSH5_ADR: + add esi,24 + mov ebp,[esi-20] + add ebp,frm + _PUSH ebp + mov ebp,[esi-16] + add ebp,frm + _PUSH ebp + mov ebp,[esi-12] + add ebp,frm + _PUSH ebp + mov ebp,[esi-8] + add ebp,frm + _PUSH ebp + mov ebp,[esi-4] + add ebp,frm + _PUSH ebp + GO_ON + + +OP_LOAD_BOTH: + mov eax,[esi+4] + mov edx,[esi+8] + add esi,12 + mov eax,[edi+eax] + mov edx,[edi+edx] + GO_ON + + +OP_LOAD_S_BOTH: + mov eax,[esi+4] + mov edx,[esi+8] + add esi,12 + mov eax,[ebx+eax] + mov edx,[ebx+edx] + GO_ON + + +OP_CONST: + push eax + mov ebp,[esi+4] + mov eax,[esi+8] + add esi,12 + mov [ebp+edi],eax + pop eax + GO_ON + + +OP_CONST_S: + push eax + mov ebp,[esi+4] + mov eax,[esi+8] + add esi,12 + mov [ebp+ebx],eax + pop eax + GO_ON + + +OP_INVALID: + mov eax,AMX_ERR_INVINSTR + jmp _return + +err_call: + mov eax,AMX_ERR_CALLBACK + jmp _return + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return + +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return + +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return + + +_return: + ; save a few parameters, mostly for the "sleep"function + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT) + mov [ebp+_alt],edx ; store values in AMX structure (PRI, ALT) + + pop esi ; remove FRM from stack + + pop ecx + pop ebx + pop edx + + pop esi ; remove pointer to amx from stack + pop esi ; remove code segment pointer + pop esi ; remove code size + + add esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + pop ebp + pop esi + pop edi + pop ebx + ret + +; _amx_exec_asm ENDP + + +Start_DATA + ALIGN 4 ; This is essential to avoid misalignment stalls. + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + + GLOBAL amx_opcodelist + GLOBAL _amx_opcodelist +amx_opcodelist: +_amx_opcodelist DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI ; obsolete + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_PRI + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE ; obsolete + DD OP_LINE ; obsolete + DD OP_SYMBOL ; obsolete + DD OP_SRANGE ; obsolete + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI + DD OP_SWAP_ALT + DD OP_PUSH_ADR + DD OP_NOP + DD OP_SYSREQ_N + DD OP_SYMTAG ; obsolete + DD OP_BREAK + ; macro opcodes + DD OP_PUSH2_C + DD OP_PUSH2 + DD OP_PUSH2_S + DD OP_PUSH2_ADR + DD OP_PUSH3_C + DD OP_PUSH3 + DD OP_PUSH3_S + DD OP_PUSH3_ADR + DD OP_PUSH4_C + DD OP_PUSH4 + DD OP_PUSH4_S + DD OP_PUSH4_ADR + DD OP_PUSH5_C + DD OP_PUSH5 + DD OP_PUSH5_S + DD OP_PUSH5_ADR + DD OP_LOAD_BOTH + DD OP_LOAD_S_BOTH + DD OP_CONST + DD OP_CONST_S + ; "patch" opcodes + DD OP_SYSREQ_D + DD OP_SYSREQ_ND + diff --git a/amx-deps/src/amx/amxfile.c b/amx-deps/src/amx/amxfile.c index 5dc4a77..d356a5d 100644 --- a/amx-deps/src/amx/amxfile.c +++ b/amx-deps/src/amx/amxfile.c @@ -1,1034 +1,1228 @@ -/* Text file I/O module for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2003-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxfile.c 3931 2008-03-04 17:02:34Z thiadmer $ - */ -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include -#include -#include -#include -#include -#if defined __WIN32__ -#include -#else -#include -#endif -#include -#include "osdefs.h" -#if defined __WIN32__ || defined __MSDOS__ - #include -#endif -#if defined __WIN32__ || defined _Windows - #include -#endif -#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS - #include -#else - #include -#endif -#include "amx.h" - -#include "fpattern.c" - -#if !defined AMXFILE_VAR - #define AMXFILE_VAR "AMXFILE" -#elif AMXFILE_VAR=="" - #undef AMXFILE_VAR -#endif - -#if !defined sizearray - #define sizearray(a) (sizeof(a)/sizeof((a)[0])) -#endif - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _tchmod chmod -# define _tcscat strcat -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _tcsncpy strncpy -# define _tcspbrk strpbrk -# define _tcsrchr strrchr -# define _tfopen fopen -# define _tfputs fputs -# define _tgetenv getenv -# define _tremove remove -# define _trename rename -# define _tstat _stat -# define _tutime _utime -#endif - -TCHAR* getScriptFilePath(AMX *amx, TCHAR *dest, const TCHAR *filename, size_t destsize); - -#if !defined UNUSED_PARAM - #define UNUSED_PARAM(p) ((void)(p)) -#endif - -enum filemode { - io_read, /* file must exist */ - io_write, /* creates a new file */ - io_readwrite, /* file must exist */ - io_append, /* file must exist, opened for writing only and seek to the end */ -}; - -enum seek_whence { - seek_start, - seek_current, - seek_end, -}; - - -/* This function only stores unpacked strings. UTF-8 is used for - * Unicode, and packed strings can only store 7-bit and 8-bit - * character sets (ASCII, Latin-1). - */ -static size_t fgets_cell(FILE *fp,cell *string,size_t max,int utf8mode) -{ - size_t index; - fpos_t pos; - cell c; - int follow,lastcr; - cell lowmark; - - assert(sizeof(cell)>=4); - assert(fp!=NULL); - assert(string!=NULL); - if (max==0) - return 0; - - /* get the position, in case we have to back up */ - fgetpos(fp, &pos); - - index=0; - follow=0; - lowmark=0; - lastcr=0; - for ( ;; ) { - assert(index0 && (c & 0xc0)==0x80) { - /* leader code is active, combine with earlier code */ - string[index]=(string[index] << 6) | ((unsigned char)c & 0x3f); - if (--follow==0) { - /* encoding a character in more bytes than is strictly needed, - * is not really valid UTF-8; we are strict here to increase - * the chance of heuristic dectection of non-UTF-8 text - * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) - */ - if (string[index]=0xd800 && string[index]<=0xdfff - || string[index]==0xfffe || string[index]==0xffff) - utf8mode=0; - index++; - } /* if */ - } else if (follow==0 && (c & 0x80)==0x80) { - /* UTF-8 leader code */ - if ((c & 0xe0)==0xc0) { - /* 110xxxxx 10xxxxxx */ - follow=1; - lowmark=0x80; - string[index]=c & 0x1f; - } else if ((c & 0xf0)==0xe0) { - /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ - follow=2; - lowmark=0x800; - string[index]=c & 0x0f; - } else if ((c & 0xf8)==0xf0) { - /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - follow=3; - lowmark=0x10000; - string[index]=c & 0x07; - } else if ((c & 0xfc)==0xf8) { - /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - follow=4; - lowmark=0x200000; - string[index]=c & 0x03; - } else if ((c & 0xfe)==0xfc) { - /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ - follow=5; - lowmark=0x4000000; - string[index]=c & 0x01; - } else { - /* this is invalid UTF-8 */ - utf8mode=0; - } /* if */ - } else if (follow==0 && (c & 0x80)==0x00) { - /* 0xxxxxxx (US-ASCII) */ - string[index++]=c; - if (c==__T('\n')) - break; /* read newline, done */ - } else { - /* this is invalid UTF-8 */ - utf8mode=0; - } /* if */ - if (!utf8mode) { - /* UTF-8 mode was switched just off, which means that non-conforming - * UTF-8 codes were found, which means in turn that the string is - * probably not intended as UTF-8; start over again - */ - index=0; - fsetpos(fp, &pos); - } /* if */ - } else { - string[index++]=c; - if (c==__T('\n')) { - break; /* read newline, done */ - } else if (lastcr) { - ungetc(c,fp); /* carriage return was read, no newline follows */ - break; - } /* if */ - lastcr=(c==__T('\r')); - } /* if */ - } /* for */ - assert(index=4); - assert(fp!=NULL); - assert(string!=NULL); - - while (*string!=0) { - if (utf8mode) { - cell c=*string; - if (c<0x80) { - /* 0xxxxxxx */ - fputc((unsigned char)c,fp); - } else if (c<0x800) { - /* 110xxxxx 10xxxxxx */ - fputc((unsigned char)((c>>6) & 0x1f | 0xc0),fp); - fputc((unsigned char)(c & 0x3f | 0x80),fp); - } else if (c<0x10000) { - /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ - fputc((unsigned char)((c>>12) & 0x0f | 0xe0),fp); - fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); - fputc((unsigned char)(c & 0x3f | 0x80),fp); - } else if (c<0x200000) { - /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - fputc((unsigned char)((c>>18) & 0x07 | 0xf0),fp); - fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); - fputc((unsigned char)(c & 0x3f | 0x80),fp); - } else if (c<0x4000000) { - /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - fputc((unsigned char)((c>>24) & 0x03 | 0xf8),fp); - fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); - fputc((unsigned char)(c & 0x3f | 0x80),fp); - } else { - /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ - fputc((unsigned char)((c>>30) & 0x01 | 0xfc),fp); - fputc((unsigned char)((c>>24) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); - fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); - fputc((unsigned char)(c & 0x3f | 0x80),fp); - } /* if */ - } else { - /* not UTF-8 mode */ - fputc((unsigned char)*string,fp); - } /* if */ - string++; - count++; - } /* while */ - return count; -} - -static size_t fgets_char(FILE *fp, char *string, size_t max) -{ - size_t index; - int c,lastcr; - - index=0; - lastcr=0; - for ( ;; ) { - assert(index=size) /* +1 because directory separator is appended */ - return NULL; - _tcscpy(dest,prefix); - /* append a directory separator (if not already present) */ - len=_tcslen(dest); - if (len==0) - return NULL; /* empty start directory is not allowed */ - if (dest[len-1]!=__T(DIRSEP_CHAR) && dest[len-1]!=__T('/') && len+1=size) - return NULL; - _tcscat(dest,src); - - /* change forward slashes into proper directory separators */ - #if DIRSEP_CHAR!='/' - while ((ptr=_tcschr(dest,__T('/')))!=NULL) - *ptr=__T(DIRSEP_CHAR); - #endif - return dest; - - #else - if (_tcslen(src)>=size) - return NULL; - _tcscpy(dest,src); - /* change forward slashes into proper directory separators */ - #if DIRSEP_CHAR!='/' - while ((ptr=_tcschr(dest,__T('/')))!=NULL) - *ptr=__T(DIRSEP_CHAR); - #endif - return dest; - #endif -} - -/* File: fopen(const name[], filemode: mode) */ -static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) -{ - TCHAR *attrib,*altattrib; - TCHAR *name,fullname[_MAX_PATH]; - FILE *f = NULL; - - altattrib=NULL; - switch (params[2] & 0x7fff) { - case io_read: - attrib=__T("rb"); - break; - case io_write: - attrib=__T("wb"); - break; - case io_readwrite: - attrib=__T("r+b"); - altattrib=__T("w+b"); - break; - case io_append: - attrib=__T("ab"); - break; - default: - return 0; - } /* switch */ - - /* get the filename */ - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) { - f=_tfopen(fullname,attrib); - if (f==NULL && altattrib!=NULL) - f=_tfopen(fullname,altattrib); - } /* if */ - return (cell)f; -} - -/* fclose(File: handle) */ -static cell AMX_NATIVE_CALL n_fclose(AMX *amx, const cell *params) -{ - UNUSED_PARAM(amx); - if(!params[1]) - return 0; - return fclose((FILE*)params[1]) == 0; -} - -/* fwrite(File: handle, const string[]) */ -static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params) -{ - int r = 0; - cell *cptr; - char *str; - int len; - - if(!params[1]) - return 0; - - amx_GetAddr(amx,params[2],&cptr); - amx_StrLen(cptr,&len); - if (len==0) - return 0; - - if ((ucell)*cptr>UNPACKEDMAX) { - /* the string is packed, write it as an ASCII/ANSI string */ - if ((str=(char*)alloca(len + 1))!=NULL) { - amx_GetString(str,cptr,0,len); - r=fputs(str,(FILE*)params[1]); - } /* if */ - } else { - /* the string is unpacked, write it as UTF-8 */ - r=fputs_cell((FILE*)params[1],cptr,1); - } /* if */ - return r; -} - -/* fread(File: handle, string[], size=sizeof string, bool:pack=false) */ -static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) -{ - int chars,max; - char *str; - cell *cptr; - - if(!params[1]) - return 0; - - max=(int)params[3]; - if (max<=0) - return 0; - if (params[4]) - max*=sizeof(cell); - - amx_GetAddr(amx,params[2],&cptr); - str=(char *)alloca(max); - if (str==NULL || cptr==NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - if (params[4]) { - /* store as packed string, read an ASCII/ANSI string */ - chars=fgets_char((FILE*)params[1],str,max); - assert(chars0) - // params[2]=-params[2]; - break; - default: - return 0; - } /* switch */ - UNUSED_PARAM(amx); - return lseek(fileno((FILE*)params[1]),params[2],whence); -} - -/* bool: fremove(const name[]) */ -static cell AMX_NATIVE_CALL n_fremove(AMX *amx, const cell *params) -{ - int r=1; - TCHAR *name,fullname[_MAX_PATH]; - - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) - r=_tremove(fullname); - return r==0; -} - -/* bool: frename(const oldname[], const newname[]) */ -static cell AMX_NATIVE_CALL n_frename(AMX *amx, const cell *params) -{ - int r=1; - TCHAR *name,oldname[_MAX_PATH],newname[_MAX_PATH]; - - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,oldname,name,sizearray(oldname))!=NULL) { - amx_StrParam(amx,params[2],name); - if (name!=NULL && getScriptFilePath(amx,newname,name,sizearray(newname))!=NULL) - r=_trename(oldname,newname); - } /* if */ - return r==0; -} - -/* flength(File: handle) */ -static cell AMX_NATIVE_CALL n_flength(AMX *amx, const cell *params) -{ - long l,c; - int fn; - if(!params[1]) - return 0; - fn=fileno((FILE*)params[1]); - c=lseek(fn,0,SEEK_CUR); /* save the current position */ - l=lseek(fn,0,SEEK_END); /* return the file position at its end */ - lseek(fn,c,SEEK_SET); /* restore the file pointer */ - UNUSED_PARAM(amx); - return l; -} - -static int match_optcopy(TCHAR *out,int outlen,const TCHAR *in,int skip) -{ - if (out==NULL || skip!=0 || outlen<=0) - return 0; - _tcsncpy(out,in,outlen); - out[outlen-1]='\0'; - return 1; -} - -static int matchfiles(TCHAR *path,int skip,TCHAR *out,int outlen) -{ - int count=0; - TCHAR *basename; - #if DIRSEP_CHAR!='/' - TCHAR *ptr; - #endif - #if defined __WIN32__ - HANDLE hfind; - WIN32_FIND_DATA fd; - #else - /* assume LINUX, FreeBSD, OpenBSD, or some other variant */ - DIR *dir; - struct dirent *entry; - TCHAR dirname[_MAX_PATH]; - #endif - - basename=_tcsrchr(path,'/'); - if(!basename) - basename = _tcsrchr(path, '\\'); - basename=(basename==NULL) ? path : basename+1; - /* - #if DIRSEP_CHAR!='/' - ptr=_tcsrchr(basename,DIRSEP_CHAR); - basename=(ptr==NULL) ? basename : ptr+1; - #endif - */ - - #if defined __WIN32__ - if(*basename == 0) { - /* checking for a directory */ - *(basename-1) = 0; - basename = _tcsrchr(path, '/'); - if(!basename) - basename = _tcsrchr(path, '\\'); - basename = (basename == NULL) ? path : basename+1; - } - if ((hfind=FindFirstFile(path,&fd))!=INVALID_HANDLE_VALUE) { - do { - if (fpattern_match(basename,fd.cFileName,-1,FALSE)) { - count++; - if (match_optcopy(out,outlen,fd.cFileName,skip--)) - break; - } /* if */ - } while (FindNextFile(hfind,&fd)); - FindClose(hfind); - } /* if */ - #else - /* copy directory part only (zero-terminate) */ - if (basename==path) { - strcpy(dirname,"."); - } else { - strncpy(dirname,path,(int)(basename-path)); - dirname[(int)(basename-path)]=__T('\0'); - } /* if */ - if ((dir=opendir(dirname))!=NULL) { - while ((entry=readdir(dir))!=NULL) { - if (fpattern_match(basename,entry->d_name,-1,TRUE)) { - count++; - if (match_optcopy(out,outlen,entry->d_name,skip--)) - break; - } /* if */ - } /* while */ - closedir(dir); - } /* if */ - #endif - return count; -} - -/* fexist(const pattern[]) */ -static cell AMX_NATIVE_CALL n_fexist(AMX *amx, const cell *params) -{ - int r=0; - TCHAR *name,fullname[_MAX_PATH]; - - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) - r=matchfiles(fullname,0,NULL,0); - return r; -} - -/* bool: fmatch(filename[], const pattern[], index=0, maxlength=sizeof filename) */ -static cell AMX_NATIVE_CALL n_fmatch(AMX *amx, const cell *params) -{ - TCHAR *name,fullname[_MAX_PATH]=""; - cell *cptr; - - amx_StrParam(amx,params[2],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) { - if (!matchfiles(fullname,params[3],fullname,sizearray(fullname))) { - fullname[0]='\0'; - } else { - /* copy the string into the destination */ - amx_GetAddr(amx,params[1],&cptr); - amx_SetString(cptr,fullname,1,0,params[4]); - } /* if */ - } /* if */ - return fullname[0]!='\0'; -} - -/* bool: fstat(const name[], &size = 0, ×tamp = 0, &attrib = 0, &inode = 0) */ -static cell AMX_NATIVE_CALL n_fstat(AMX *amx, const cell *params) -{ - #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) - #define _stat(n,b) stat(n,b) - #endif - TCHAR *name,fullname[_MAX_PATH]=""; - cell *cptr; - int result=0; - - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) { - struct stat stbuf; - if (_tstat(name, &stbuf) == 0) { - amx_GetAddr(amx,params[2],&cptr); - *cptr=stbuf.st_size; - amx_GetAddr(amx,params[3],&cptr); - *cptr=stbuf.st_mtime; - amx_GetAddr(amx,params[4],&cptr); - *cptr=stbuf.st_mode; /* mode/protection bits */ - amx_GetAddr(amx,params[5],&cptr); - *cptr=stbuf.st_ino; /* inode number, unique id for a file */ - result=1; - } /* if */ - } /* if */ - return result; -} - -/* bool: fattrib(const name[], timestamp=0, attrib=0x0f) */ -static cell AMX_NATIVE_CALL n_fattrib(AMX *amx, const cell *params) -{ - #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) - #define _utime(n,t) utime(n,t) - #endif - TCHAR *name,fullname[_MAX_PATH]=""; - int result=0; - int mode=S_IREAD | S_IWRITE; - unsigned long timestamp=time(NULL); - - if (params[2] != 0) - timestamp=(unsigned long)params[2]; - if (params[3] != 0x0f) - mode=(int)params[3]; - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL) { - struct utimbuf times; - times.actime=timestamp; - times.modtime=timestamp; - if (_tchmod(name,mode)==0 && _tutime(name,×)==0) - result=1; - } /* if */ - return result; -} - -/* CRC32 functions are adapted from source code from www.networkdls.com - * The table generation routines are replaced by a hard-coded table, which - * can be stored in Flash ROM. - */ -const unsigned long ulCRCTable[256] = // CRC Lookup Table Array -{ - 0x00000000Lu, 0x77073096Lu, 0xee0e612cLu, 0x990951baLu, - 0x076dc419Lu, 0x706af48fLu, 0xe963a535Lu, 0x9e6495a3Lu, - 0x0edb8832Lu, 0x79dcb8a4Lu, 0xe0d5e91eLu, 0x97d2d988Lu, - 0x09b64c2bLu, 0x7eb17cbdLu, 0xe7b82d07Lu, 0x90bf1d91Lu, - 0x1db71064Lu, 0x6ab020f2Lu, 0xf3b97148Lu, 0x84be41deLu, - 0x1adad47dLu, 0x6ddde4ebLu, 0xf4d4b551Lu, 0x83d385c7Lu, - 0x136c9856Lu, 0x646ba8c0Lu, 0xfd62f97aLu, 0x8a65c9ecLu, - 0x14015c4fLu, 0x63066cd9Lu, 0xfa0f3d63Lu, 0x8d080df5Lu, - 0x3b6e20c8Lu, 0x4c69105eLu, 0xd56041e4Lu, 0xa2677172Lu, - 0x3c03e4d1Lu, 0x4b04d447Lu, 0xd20d85fdLu, 0xa50ab56bLu, - 0x35b5a8faLu, 0x42b2986cLu, 0xdbbbc9d6Lu, 0xacbcf940Lu, - 0x32d86ce3Lu, 0x45df5c75Lu, 0xdcd60dcfLu, 0xabd13d59Lu, - 0x26d930acLu, 0x51de003aLu, 0xc8d75180Lu, 0xbfd06116Lu, - 0x21b4f4b5Lu, 0x56b3c423Lu, 0xcfba9599Lu, 0xb8bda50fLu, - 0x2802b89eLu, 0x5f058808Lu, 0xc60cd9b2Lu, 0xb10be924Lu, - 0x2f6f7c87Lu, 0x58684c11Lu, 0xc1611dabLu, 0xb6662d3dLu, - 0x76dc4190Lu, 0x01db7106Lu, 0x98d220bcLu, 0xefd5102aLu, - 0x71b18589Lu, 0x06b6b51fLu, 0x9fbfe4a5Lu, 0xe8b8d433Lu, - 0x7807c9a2Lu, 0x0f00f934Lu, 0x9609a88eLu, 0xe10e9818Lu, - 0x7f6a0dbbLu, 0x086d3d2dLu, 0x91646c97Lu, 0xe6635c01Lu, - 0x6b6b51f4Lu, 0x1c6c6162Lu, 0x856530d8Lu, 0xf262004eLu, - 0x6c0695edLu, 0x1b01a57bLu, 0x8208f4c1Lu, 0xf50fc457Lu, - 0x65b0d9c6Lu, 0x12b7e950Lu, 0x8bbeb8eaLu, 0xfcb9887cLu, - 0x62dd1ddfLu, 0x15da2d49Lu, 0x8cd37cf3Lu, 0xfbd44c65Lu, - 0x4db26158Lu, 0x3ab551ceLu, 0xa3bc0074Lu, 0xd4bb30e2Lu, - 0x4adfa541Lu, 0x3dd895d7Lu, 0xa4d1c46dLu, 0xd3d6f4fbLu, - 0x4369e96aLu, 0x346ed9fcLu, 0xad678846Lu, 0xda60b8d0Lu, - 0x44042d73Lu, 0x33031de5Lu, 0xaa0a4c5fLu, 0xdd0d7cc9Lu, - 0x5005713cLu, 0x270241aaLu, 0xbe0b1010Lu, 0xc90c2086Lu, - 0x5768b525Lu, 0x206f85b3Lu, 0xb966d409Lu, 0xce61e49fLu, - 0x5edef90eLu, 0x29d9c998Lu, 0xb0d09822Lu, 0xc7d7a8b4Lu, - 0x59b33d17Lu, 0x2eb40d81Lu, 0xb7bd5c3bLu, 0xc0ba6cadLu, - 0xedb88320Lu, 0x9abfb3b6Lu, 0x03b6e20cLu, 0x74b1d29aLu, - 0xead54739Lu, 0x9dd277afLu, 0x04db2615Lu, 0x73dc1683Lu, - 0xe3630b12Lu, 0x94643b84Lu, 0x0d6d6a3eLu, 0x7a6a5aa8Lu, - 0xe40ecf0bLu, 0x9309ff9dLu, 0x0a00ae27Lu, 0x7d079eb1Lu, - 0xf00f9344Lu, 0x8708a3d2Lu, 0x1e01f268Lu, 0x6906c2feLu, - 0xf762575dLu, 0x806567cbLu, 0x196c3671Lu, 0x6e6b06e7Lu, - 0xfed41b76Lu, 0x89d32be0Lu, 0x10da7a5aLu, 0x67dd4accLu, - 0xf9b9df6fLu, 0x8ebeeff9Lu, 0x17b7be43Lu, 0x60b08ed5Lu, - 0xd6d6a3e8Lu, 0xa1d1937eLu, 0x38d8c2c4Lu, 0x4fdff252Lu, - 0xd1bb67f1Lu, 0xa6bc5767Lu, 0x3fb506ddLu, 0x48b2364bLu, - 0xd80d2bdaLu, 0xaf0a1b4cLu, 0x36034af6Lu, 0x41047a60Lu, - 0xdf60efc3Lu, 0xa867df55Lu, 0x316e8eefLu, 0x4669be79Lu, - 0xcb61b38cLu, 0xbc66831aLu, 0x256fd2a0Lu, 0x5268e236Lu, - 0xcc0c7795Lu, 0xbb0b4703Lu, 0x220216b9Lu, 0x5505262fLu, - 0xc5ba3bbeLu, 0xb2bd0b28Lu, 0x2bb45a92Lu, 0x5cb36a04Lu, - 0xc2d7ffa7Lu, 0xb5d0cf31Lu, 0x2cd99e8bLu, 0x5bdeae1dLu, - 0x9b64c2b0Lu, 0xec63f226Lu, 0x756aa39cLu, 0x026d930aLu, - 0x9c0906a9Lu, 0xeb0e363fLu, 0x72076785Lu, 0x05005713Lu, - 0x95bf4a82Lu, 0xe2b87a14Lu, 0x7bb12baeLu, 0x0cb61b38Lu, - 0x92d28e9bLu, 0xe5d5be0dLu, 0x7cdcefb7Lu, 0x0bdbdf21Lu, - 0x86d3d2d4Lu, 0xf1d4e242Lu, 0x68ddb3f8Lu, 0x1fda836eLu, - 0x81be16cdLu, 0xf6b9265bLu, 0x6fb077e1Lu, 0x18b74777Lu, - 0x88085ae6Lu, 0xff0f6a70Lu, 0x66063bcaLu, 0x11010b5cLu, - 0x8f659effLu, 0xf862ae69Lu, 0x616bffd3Lu, 0x166ccf45Lu, - 0xa00ae278Lu, 0xd70dd2eeLu, 0x4e048354Lu, 0x3903b3c2Lu, - 0xa7672661Lu, 0xd06016f7Lu, 0x4969474dLu, 0x3e6e77dbLu, - 0xaed16a4aLu, 0xd9d65adcLu, 0x40df0b66Lu, 0x37d83bf0Lu, - 0xa9bcae53Lu, 0xdebb9ec5Lu, 0x47b2cf7fLu, 0x30b5ffe9Lu, - 0xbdbdf21cLu, 0xcabac28aLu, 0x53b39330Lu, 0x24b4a3a6Lu, - 0xbad03605Lu, 0xcdd70693Lu, 0x54de5729Lu, 0x23d967bfLu, - 0xb3667a2eLu, 0xc4614ab8Lu, 0x5d681b02Lu, 0x2a6f2b94Lu, - 0xb40bbe37Lu, 0xc30c8ea1Lu, 0x5a05df1bLu, 0x2d02ef8dLu -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// This function uses the ulCRCTable lookup table to generate a CRC for xData - -static unsigned long PartialCRC(unsigned long ulCRC, unsigned char *sBuf, unsigned long lBufSz) -{ - while(lBufSz--) - ulCRC = (ulCRC >> 8) ^ ulCRCTable[(ulCRC & 0xFF) ^ *sBuf++]; - - return ulCRC; -} - -/* filecrc(const name[]) */ -static cell AMX_NATIVE_CALL n_filecrc(AMX *amx, const cell *params) -{ - TCHAR *name,fullname[_MAX_PATH]=""; - FILE *fp; - unsigned char buffer[256]; - unsigned long ulCRC = 0xffffffff; - int numread; - - amx_StrParam(amx,params[1],name); - if (name!=NULL && getScriptFilePath(amx,fullname,name,sizearray(fullname))!=NULL - && (fp=_tfopen(fullname,"rb"))!=NULL) - { - do { - numread=fread(buffer,sizeof(unsigned char),sizeof buffer,fp); - PartialCRC(ulCRC,buffer,numread); - } while(numread==sizeof buffer); - fclose(fp); - } /* if */ - return(ulCRC ^ 0xffffffff); -} - - -#if defined __cplusplus - extern "C" -#endif -AMX_NATIVE_INFO file_Natives[] = { - { "fopen", n_fopen }, - { "fclose", n_fclose }, - { "fwrite", n_fwrite }, - { "fread", n_fread }, - { "fputchar", n_fputchar }, - { "fgetchar", n_fgetchar }, - { "fblockwrite", n_fblockwrite }, - { "fblockread", n_fblockread }, - { "ftemp", n_ftemp }, - { "fseek", n_fseek }, - { "flength", n_flength }, - { "fremove", n_fremove }, - { "frename", n_frename }, - { "fexist", n_fexist }, - { "fmatch", n_fmatch }, - { "fstat", n_fstat }, - { "fattrib", n_fattrib }, - { "filecrc", n_filecrc }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_FileInit(AMX *amx) -{ - return amx_Register(amx, file_Natives, -1); -} - -int AMXEXPORT amx_FileCleanup(AMX *amx) -{ - UNUSED_PARAM(amx); - return AMX_ERR_NONE; -} +/* Text file I/O module for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2003-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxfile.c 5588 2016-10-25 11:13:28Z $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "osdefs.h" +#if (defined __linux || defined __linux__) && !defined __LINUX__ + #define __LINUX__ +#endif +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if defined __BORLANDC__ + #include +#endif +#if defined __BORLANDC__ || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ + #include +#else + #include +#endif +#if defined __WIN32__ || defined __MSDOS__ + #include +#endif +#if defined __WATCOMC__ || defined _MSC_VER + #include +#endif +#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ + #include +#else + #include +#endif +#if defined __GNUC__ || defined __clang__ + #include +#endif +#include "amx.h" +#if defined __WIN32__ || defined _Windows + #include +#endif + +#include "fpattern.c" + +#if !defined AMXFILE_VAR + #define AMXFILE_VAR "AMXFILE" +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#if defined _UNICODE + #include +#elif !defined __T + typedef char TCHAR; + #define __T(string) string + #define _tchmod chmod + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsdup strdup + #define _tcslen strlen + #define _tcsncpy strncpy + #define _tcsnicmp strnicmp + #define _tcspbrk strpbrk + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tfopen fopen + #define _tfputs fputs + #define _tgetenv getenv + #define _tremove remove + #define _trename rename + #if defined __BORLANDC__ || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ + #define _tmkdir mkdir + #define _trmdir rmdir + #define _tstat stat + #define _tutime utime + #else + #define _tmkdir _mkdir + #define _trmdir _rmdir + #define _tstat _stat + #define _tstat64 _stat64 + #define _tutime _utime + #endif + #if defined __APPLE__ + #define t_stat stat + #elif defined __WIN32__ + #if defined __WATCOMC__ + #define t_stat __stat + #else + #define t_stat _stat + #endif + #else + #define t_stat stat + #endif +#endif +#if !defined t_stat + #if defined _tstat + #define t_stat _tstat + #else + #define t_stat stat + #endif +#endif +#if !defined S_ISDIR + #define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) +#endif + +#include "minIni.c" + +enum filemode { + io_read, /* file must exist */ + io_write, /* creates a new file */ + io_readwrite, /* file must exist */ + io_append, /* file must exist, opened for writing only and seek to the end */ +}; + +enum seek_whence { + seek_start, + seek_current, + seek_end, +}; + + +/* This function only stores unpacked strings. UTF-8 is used for + * Unicode, and packed strings can only store 7-bit and 8-bit + * character sets (ASCII, Latin-1). + */ +static size_t fgets_cell(FILE *fp,cell *string,size_t max,int utf8mode) +{ + size_t index; + fpos_t pos; + cell c; + int follow,lastcr; + cell lowmark; + + assert(sizeof(cell)>=4); + assert(fp!=NULL); + assert(string!=NULL); + if (max==0) + return 0; + + /* get the position, in case we have to back up */ + fgetpos(fp, &pos); + + index=0; + follow=0; + lowmark=0; + lastcr=0; + for ( ;; ) { + assert(index0 && (c & 0xc0)==0x80) { + /* leader code is active, combine with earlier code */ + string[index]=(string[index] << 6) | ((unsigned char)c & 0x3f); + if (--follow==0) { + /* encoding a character in more bytes than is strictly needed, + * is not really valid UTF-8; we are strict here to increase + * the chance of heuristic dectection of non-UTF-8 text + * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) + */ + if (string[index]=0xd800 && string[index]<=0xdfff + || string[index]==0xfffe || string[index]==0xffff) + utf8mode=0; + index++; + } /* if */ + } else if (follow==0 && (c & 0x80)==0x80) { + /* UTF-8 leader code */ + if ((c & 0xe0)==0xc0) { + /* 110xxxxx 10xxxxxx */ + follow=1; + lowmark=0x80; + string[index]=c & 0x1f; + } else if ((c & 0xf0)==0xe0) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + follow=2; + lowmark=0x800; + string[index]=c & 0x0f; + } else if ((c & 0xf8)==0xf0) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=3; + lowmark=0x10000; + string[index]=c & 0x07; + } else if ((c & 0xfc)==0xf8) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=4; + lowmark=0x200000; + string[index]=c & 0x03; + } else if ((c & 0xfe)==0xfc) { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + follow=5; + lowmark=0x4000000; + string[index]=c & 0x01; + } else { + /* this is invalid UTF-8 */ + utf8mode=0; + } /* if */ + } else if (follow==0 && (c & 0x80)==0x00) { + /* 0xxxxxxx (US-ASCII) */ + string[index++]=c; + if (c==__T('\n')) + break; /* read newline, done */ + } else { + /* this is invalid UTF-8 */ + utf8mode=0; + } /* if */ + if (!utf8mode) { + /* UTF-8 mode was switched just off, which means that non-conforming + * UTF-8 codes were found, which means in turn that the string is + * probably not intended as UTF-8; start over again + */ + index=0; + fsetpos(fp, &pos); + } /* if */ + } else { + string[index++]=c; + if (c==__T('\n')) { + break; /* read newline, done */ + } else if (lastcr) { + ungetc(c,fp); /* carriage return was read, no newline follows */ + break; + } /* if */ + lastcr=(c==__T('\r')); + } /* if */ + } /* for */ + assert(index=4); + assert(fp!=NULL); + assert(string!=NULL); + + while (*string!=0) { + if (utf8mode) { + cell c=*string; + if (c<0x80) { + /* 0xxxxxxx */ + fputc((unsigned char)c,fp); + } else if (c<0x800) { + /* 110xxxxx 10xxxxxx */ + fputc((unsigned char)((c>>6) & 0x1f | 0xc0),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x10000) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + fputc((unsigned char)((c>>12) & 0x0f | 0xe0),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x200000) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + fputc((unsigned char)((c>>18) & 0x07 | 0xf0),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x4000000) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + fputc((unsigned char)((c>>24) & 0x03 | 0xf8),fp); + fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + fputc((unsigned char)((c>>30) & 0x01 | 0xfc),fp); + fputc((unsigned char)((c>>24) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } /* if */ + } else { + /* not UTF-8 mode */ + fputc((unsigned char)*string,fp); + } /* if */ + string++; + count++; + } /* while */ + return count; +} + +static size_t fgets_char(FILE *fp, char *string, size_t max) +{ + size_t index; + int c,lastcr; + + index=0; + lastcr=0; + for ( ;; ) { + assert(index=size) /* +1 because directory separator is appended */ + return NULL; + _tcscpy(dest,prefix); + /* append a directory separator (if not already present) */ + len=_tcslen(dest); + if (len==0) + return NULL; /* empty start directory is not allowed */ + if (dest[len-1]!=__T(DIRSEP_CHAR) && dest[len-1]!=__T('/') && len+1=size) + return NULL; + _tcscat(dest,src); + + /* change forward slashes into proper directory separators */ + #if DIRSEP_CHAR!='/' + while ((ptr=_tcschr(dest,__T('/')))!=NULL) + *ptr=__T(DIRSEP_CHAR); + #endif + return dest; + + #else + if (_tcslen(src)>=size) + return NULL; + _tcscpy(dest,src); + /* change forward slashes into proper directory separators */ + #if DIRSEP_CHAR!='/' + while ((ptr=_tcschr(dest,__T('/')))!=NULL) + *ptr=__T(DIRSEP_CHAR); + #endif + return dest; + #endif +} + +/* File: fopen(const name[], filemode: mode) */ +static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) +{ + TCHAR *attrib,*altattrib; + TCHAR *name,fullname[_MAX_PATH]; + FILE *f = NULL; + + (void)amx; + altattrib=NULL; + switch (params[2] & 0x7fff) { + case io_read: + attrib=__T("rb"); + break; + case io_write: + attrib=__T("wb"); + break; + case io_readwrite: + attrib=__T("r+b"); + altattrib=__T("w+b"); + break; + case io_append: + attrib=__T("ab"); + break; + default: + return 0; + } /* switch */ + + /* get the filename */ + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + f=_tfopen(fullname,attrib); + if (f==NULL && altattrib!=NULL) + f=_tfopen(fullname,altattrib); + } /* if */ + return (cell)f; +} + +/* fclose(File: handle) */ +static cell AMX_NATIVE_CALL n_fclose(AMX *amx, const cell *params) +{ + (void)amx; + return fclose((FILE*)params[1]) == 0; +} + +/* fwrite(File: handle, const string[]) */ +static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params) +{ + size_t r = 0; + cell *cptr; + char *str; + int len; + + (void)amx; + cptr=amx_Address(amx,params[2]); + amx_StrLen(cptr,&len); + if (len==0) + return 0; + + if ((ucell)*cptr>UNPACKEDMAX) { + /* the string is packed, write it as an ASCII/ANSI string */ + if ((str=(char*)alloca(len + 1))!=NULL) { + amx_GetString(str,cptr,0,len); + r=fputs(str,(FILE*)params[1]); + } /* if */ + } else { + /* the string is unpacked, write it as UTF-8 */ + r=fputs_cell((FILE*)params[1],cptr,1); + } /* if */ + return (cell)r; +} + +/* fread(File: handle, string[], size=sizeof string, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) +{ + size_t chars; + int max; + char *str; + cell *cptr; + + max=(int)params[3]; + if (max<=0) + return 0; + if (params[4]) + max*=sizeof(cell); + + cptr=amx_Address(amx,params[2]); + str=(char *)alloca(max); + if (str==NULL || cptr==NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + if (params[4]) { + /* store as packed string, read an ASCII/ANSI string */ + chars=fgets_char((FILE*)params[1],str,max); + assert((int)chars0) + // params[2]=-params[2]; + break; + default: + return 0; + } /* switch */ + return lseek(fileno((FILE*)params[1]),params[2],whence); +} + +/* bool: fremove(const name[]) */ +static cell AMX_NATIVE_CALL n_fremove(AMX *amx, const cell *params) +{ + int r=1; + TCHAR *name,fullname[_MAX_PATH]; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + /* if this is a directory, try _trmdir() */ + #if defined _WIN64 + struct __stat64 stbuf; + _tstat64(fullname, &stbuf); + #else + struct t_stat stbuf; + _tstat(fullname, &stbuf); + #endif + if (S_ISDIR(stbuf.st_mode)) + r=_trmdir(fullname); + else + r=_tremove(fullname); + } /* if */ + return r==0; +} + +/* bool: fcopy(const source[], const target[]) */ +static cell AMX_NATIVE_CALL n_fcopy(AMX *amx, const cell *params) +{ + int r=1; + TCHAR *name,oldname[_MAX_PATH],newname[_MAX_PATH]; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(oldname,name,sizearray(oldname))!=NULL) { + amx_StrParam(amx,params[2],name); + if (name!=NULL && completename(newname,name,sizearray(newname))!=NULL) { + #if defined __WIN32__ + r= CopyFile(oldname,newname,FALSE)==FALSE; + #else + TCHAR cmd[2*_MAX_PATH + 10]; + sprintf(cmd,"cp %s %s",oldname,newname); + r= system(cmd)<0; + #endif + } /* if */ + } /* if */ + return r==0; +} + +/* bool: frename(const oldname[], const newname[]) */ +static cell AMX_NATIVE_CALL n_frename(AMX *amx, const cell *params) +{ + int r=1; + TCHAR *name,oldname[_MAX_PATH],newname[_MAX_PATH]; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(oldname,name,sizearray(oldname))!=NULL) { + amx_StrParam(amx,params[2],name); + if (name!=NULL && completename(newname,name,sizearray(newname))!=NULL) + r=_trename(oldname,newname); + } /* if */ + return r==0; +} + +/* flength(File: handle) */ +static cell AMX_NATIVE_CALL n_flength(AMX *amx, const cell *params) +{ + long l,c; + int fn=fileno((FILE*)params[1]); + c=lseek(fn,0,SEEK_CUR); /* save the current position */ + l=lseek(fn,0,SEEK_END); /* return the file position at its end */ + lseek(fn,c,SEEK_SET); /* restore the file pointer */ + (void)amx; + return l; +} + +static int match_optcopy(TCHAR *out,int outlen,const TCHAR *in,int skip) +{ + if (out==NULL || skip!=0 || outlen<=0) + return 0; + _tcsncpy(out,in,outlen); + out[outlen-1]='\0'; + return 1; +} + +static int matchfiles(const TCHAR *path,int skip,TCHAR *out,int outlen) +{ + int count=0; + const TCHAR *basename; + #if DIRSEP_CHAR!='/' + TCHAR *ptr; + #endif + #if defined __WIN32__ + HANDLE hfind; + WIN32_FIND_DATA fd; + #else + /* assume LINUX, FreeBSD, OpenBSD, or some other variant */ + DIR *dir; + struct dirent *entry; + TCHAR dirname[_MAX_PATH]; + #endif + + basename=_tcsrchr(path,DIRSEP_CHAR); + basename=(basename==NULL) ? path : basename+1; + #if DIRSEP_CHAR!='/' + ptr=_tcsrchr(basename,DIRSEP_CHAR); + basename=(ptr==NULL) ? basename : ptr+1; + #endif + + #if defined __WIN32__ + if ((hfind=FindFirstFile(path,&fd))!=INVALID_HANDLE_VALUE) { + do { + if (fpattern_match(basename,fd.cFileName,-1,FALSE)) { + count++; + if (match_optcopy(out,outlen,fd.cFileName,skip--)) + break; + } /* if */ + } while (FindNextFile(hfind,&fd)); + FindClose(hfind); + } /* if */ + #else + /* copy directory part only (zero-terminate) */ + if (basename==path) { + _tcscpy(dirname,"."); + } else { + _tcsncpy(dirname,path,(int)(basename-path)); + dirname[(int)(basename-path)]=__T('\0'); + } /* if */ + if ((dir=opendir(dirname))!=NULL) { + while ((entry=readdir(dir))!=NULL) { + if (fpattern_match(basename,entry->d_name,-1,TRUE)) { + count++; + if (match_optcopy(out,outlen,entry->d_name,skip--)) + break; + } /* if */ + } /* while */ + closedir(dir); + } /* if */ + #endif + return count; +} + +/* fexist(const pattern[]) */ +static cell AMX_NATIVE_CALL n_fexist(AMX *amx, const cell *params) +{ + int r=0; + TCHAR *name,fullname[_MAX_PATH]; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) + r=matchfiles(fullname,0,NULL,0); + return r; +} + +/* bool: fmatch(filename[], const pattern[], index=0, maxlength=sizeof filename) */ +static cell AMX_NATIVE_CALL n_fmatch(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + cell *cptr; + + (void)amx; + amx_StrParam(amx,params[2],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + if (!matchfiles(fullname,params[3],fullname,sizearray(fullname))) { + fullname[0]='\0'; + } else { + /* copy the string into the destination */ + cptr=amx_Address(amx,params[1]); + amx_SetString(cptr,fullname,1,0,params[4]); + } /* if */ + } /* if */ + return fullname[0]!='\0'; +} + +/* bool: fstat(const name[], &size = 0, ×tamp = 0, &attrib = 0, &inode = 0) */ +static cell AMX_NATIVE_CALL n_fstat(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + cell *cptr; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + int err; + #if defined _WIN64 + struct __stat64 stbuf; + err = _tstat64(fullname, &stbuf); + #else + struct t_stat stbuf; + err = _tstat(fullname, &stbuf); + #endif + if (err == 0) { + cptr=amx_Address(amx,params[2]); + *cptr=(cell)stbuf.st_size; + cptr=amx_Address(amx,params[3]); + *cptr=(cell)stbuf.st_mtime; + cptr=amx_Address(amx,params[4]); + *cptr=stbuf.st_mode; /* mode/protection bits */ + cptr=amx_Address(amx,params[5]); + *cptr=stbuf.st_ino; /* inode number, unique id for a file */ + result=1; + } /* if */ + } /* if */ + return result; +} + +/* bool: fattrib(const name[], timestamp=0, attrib=0x0f) */ +static cell AMX_NATIVE_CALL n_fattrib(AMX *amx, const cell *params) +{ + #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) || defined __BORLANDC__ + #define _utime(n,t) utime(n,t) + #define _utimbuf utimbuf + #endif + TCHAR *name,fullname[_MAX_PATH]=""; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + result=1; + if (params[2]!=0) { + struct _utimbuf times; + times.actime=(unsigned long)params[2]; + times.modtime=(unsigned long)params[2]; + result=result && (_tutime(name,×)==0); + } /* if */ + if (params[3]!=0x0f) + result=result && (_tchmod(name,(int)params[3])==0); + } /* if */ + return result; +} + +/* bool: fcreatedir(const name[]) */ +static cell AMX_NATIVE_CALL n_fcreatedir(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + int r=1; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + #if defined __WIN32__ || defined __DOS__ + r=_tmkdir(fullname); + #else + r=_tmkdir(fullname,0755); + #endif + } /* if */ + return r==0; +} + +/* CRC32 functions are adapted from source code from www.networkdls.com + * The table generation routines are replaced by a hard-coded table, which + * can be stored in Flash ROM. + */ +const unsigned long ulCRCTable[256] = // CRC Lookup Table Array +{ + 0x00000000Lu, 0x77073096Lu, 0xee0e612cLu, 0x990951baLu, + 0x076dc419Lu, 0x706af48fLu, 0xe963a535Lu, 0x9e6495a3Lu, + 0x0edb8832Lu, 0x79dcb8a4Lu, 0xe0d5e91eLu, 0x97d2d988Lu, + 0x09b64c2bLu, 0x7eb17cbdLu, 0xe7b82d07Lu, 0x90bf1d91Lu, + 0x1db71064Lu, 0x6ab020f2Lu, 0xf3b97148Lu, 0x84be41deLu, + 0x1adad47dLu, 0x6ddde4ebLu, 0xf4d4b551Lu, 0x83d385c7Lu, + 0x136c9856Lu, 0x646ba8c0Lu, 0xfd62f97aLu, 0x8a65c9ecLu, + 0x14015c4fLu, 0x63066cd9Lu, 0xfa0f3d63Lu, 0x8d080df5Lu, + 0x3b6e20c8Lu, 0x4c69105eLu, 0xd56041e4Lu, 0xa2677172Lu, + 0x3c03e4d1Lu, 0x4b04d447Lu, 0xd20d85fdLu, 0xa50ab56bLu, + 0x35b5a8faLu, 0x42b2986cLu, 0xdbbbc9d6Lu, 0xacbcf940Lu, + 0x32d86ce3Lu, 0x45df5c75Lu, 0xdcd60dcfLu, 0xabd13d59Lu, + 0x26d930acLu, 0x51de003aLu, 0xc8d75180Lu, 0xbfd06116Lu, + 0x21b4f4b5Lu, 0x56b3c423Lu, 0xcfba9599Lu, 0xb8bda50fLu, + 0x2802b89eLu, 0x5f058808Lu, 0xc60cd9b2Lu, 0xb10be924Lu, + 0x2f6f7c87Lu, 0x58684c11Lu, 0xc1611dabLu, 0xb6662d3dLu, + 0x76dc4190Lu, 0x01db7106Lu, 0x98d220bcLu, 0xefd5102aLu, + 0x71b18589Lu, 0x06b6b51fLu, 0x9fbfe4a5Lu, 0xe8b8d433Lu, + 0x7807c9a2Lu, 0x0f00f934Lu, 0x9609a88eLu, 0xe10e9818Lu, + 0x7f6a0dbbLu, 0x086d3d2dLu, 0x91646c97Lu, 0xe6635c01Lu, + 0x6b6b51f4Lu, 0x1c6c6162Lu, 0x856530d8Lu, 0xf262004eLu, + 0x6c0695edLu, 0x1b01a57bLu, 0x8208f4c1Lu, 0xf50fc457Lu, + 0x65b0d9c6Lu, 0x12b7e950Lu, 0x8bbeb8eaLu, 0xfcb9887cLu, + 0x62dd1ddfLu, 0x15da2d49Lu, 0x8cd37cf3Lu, 0xfbd44c65Lu, + 0x4db26158Lu, 0x3ab551ceLu, 0xa3bc0074Lu, 0xd4bb30e2Lu, + 0x4adfa541Lu, 0x3dd895d7Lu, 0xa4d1c46dLu, 0xd3d6f4fbLu, + 0x4369e96aLu, 0x346ed9fcLu, 0xad678846Lu, 0xda60b8d0Lu, + 0x44042d73Lu, 0x33031de5Lu, 0xaa0a4c5fLu, 0xdd0d7cc9Lu, + 0x5005713cLu, 0x270241aaLu, 0xbe0b1010Lu, 0xc90c2086Lu, + 0x5768b525Lu, 0x206f85b3Lu, 0xb966d409Lu, 0xce61e49fLu, + 0x5edef90eLu, 0x29d9c998Lu, 0xb0d09822Lu, 0xc7d7a8b4Lu, + 0x59b33d17Lu, 0x2eb40d81Lu, 0xb7bd5c3bLu, 0xc0ba6cadLu, + 0xedb88320Lu, 0x9abfb3b6Lu, 0x03b6e20cLu, 0x74b1d29aLu, + 0xead54739Lu, 0x9dd277afLu, 0x04db2615Lu, 0x73dc1683Lu, + 0xe3630b12Lu, 0x94643b84Lu, 0x0d6d6a3eLu, 0x7a6a5aa8Lu, + 0xe40ecf0bLu, 0x9309ff9dLu, 0x0a00ae27Lu, 0x7d079eb1Lu, + 0xf00f9344Lu, 0x8708a3d2Lu, 0x1e01f268Lu, 0x6906c2feLu, + 0xf762575dLu, 0x806567cbLu, 0x196c3671Lu, 0x6e6b06e7Lu, + 0xfed41b76Lu, 0x89d32be0Lu, 0x10da7a5aLu, 0x67dd4accLu, + 0xf9b9df6fLu, 0x8ebeeff9Lu, 0x17b7be43Lu, 0x60b08ed5Lu, + 0xd6d6a3e8Lu, 0xa1d1937eLu, 0x38d8c2c4Lu, 0x4fdff252Lu, + 0xd1bb67f1Lu, 0xa6bc5767Lu, 0x3fb506ddLu, 0x48b2364bLu, + 0xd80d2bdaLu, 0xaf0a1b4cLu, 0x36034af6Lu, 0x41047a60Lu, + 0xdf60efc3Lu, 0xa867df55Lu, 0x316e8eefLu, 0x4669be79Lu, + 0xcb61b38cLu, 0xbc66831aLu, 0x256fd2a0Lu, 0x5268e236Lu, + 0xcc0c7795Lu, 0xbb0b4703Lu, 0x220216b9Lu, 0x5505262fLu, + 0xc5ba3bbeLu, 0xb2bd0b28Lu, 0x2bb45a92Lu, 0x5cb36a04Lu, + 0xc2d7ffa7Lu, 0xb5d0cf31Lu, 0x2cd99e8bLu, 0x5bdeae1dLu, + 0x9b64c2b0Lu, 0xec63f226Lu, 0x756aa39cLu, 0x026d930aLu, + 0x9c0906a9Lu, 0xeb0e363fLu, 0x72076785Lu, 0x05005713Lu, + 0x95bf4a82Lu, 0xe2b87a14Lu, 0x7bb12baeLu, 0x0cb61b38Lu, + 0x92d28e9bLu, 0xe5d5be0dLu, 0x7cdcefb7Lu, 0x0bdbdf21Lu, + 0x86d3d2d4Lu, 0xf1d4e242Lu, 0x68ddb3f8Lu, 0x1fda836eLu, + 0x81be16cdLu, 0xf6b9265bLu, 0x6fb077e1Lu, 0x18b74777Lu, + 0x88085ae6Lu, 0xff0f6a70Lu, 0x66063bcaLu, 0x11010b5cLu, + 0x8f659effLu, 0xf862ae69Lu, 0x616bffd3Lu, 0x166ccf45Lu, + 0xa00ae278Lu, 0xd70dd2eeLu, 0x4e048354Lu, 0x3903b3c2Lu, + 0xa7672661Lu, 0xd06016f7Lu, 0x4969474dLu, 0x3e6e77dbLu, + 0xaed16a4aLu, 0xd9d65adcLu, 0x40df0b66Lu, 0x37d83bf0Lu, + 0xa9bcae53Lu, 0xdebb9ec5Lu, 0x47b2cf7fLu, 0x30b5ffe9Lu, + 0xbdbdf21cLu, 0xcabac28aLu, 0x53b39330Lu, 0x24b4a3a6Lu, + 0xbad03605Lu, 0xcdd70693Lu, 0x54de5729Lu, 0x23d967bfLu, + 0xb3667a2eLu, 0xc4614ab8Lu, 0x5d681b02Lu, 0x2a6f2b94Lu, + 0xb40bbe37Lu, 0xc30c8ea1Lu, 0x5a05df1bLu, 0x2d02ef8dLu +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This function uses the ulCRCTable lookup table to generate a CRC for xData + +static unsigned long PartialCRC(unsigned long ulCRC, unsigned char *sBuf, unsigned long lBufSz) +{ + while(lBufSz--) + ulCRC = (ulCRC >> 8) ^ ulCRCTable[(ulCRC & 0xFF) ^ *sBuf++]; + + return ulCRC; +} + +/* filecrc(const name[]) */ +static cell AMX_NATIVE_CALL n_filecrc(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + FILE *fp; + unsigned char buffer[256]; + unsigned long ulCRC = 0xffffffff; + size_t numread; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL + && (fp=_tfopen(fullname,"rb"))!=NULL) + { + do { + numread=fread(buffer,sizeof(unsigned char),sizeof buffer,fp); + ulCRC=PartialCRC(ulCRC,buffer,(unsigned long)numread); + } while(numread==sizeof buffer); + fclose(fp); + } /* if */ + return(ulCRC ^ 0xffffffff); +} + + +const TCHAR default_ini_name[] = "config.ini"; + +/* readcfg(const filename[]="", const section[]="", const key[], value[], size=sizeof value, const defvalue[]="", bool:packed=false) */ +static cell AMX_NATIVE_CALL n_readcfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key,*defvalue; + TCHAR *buffer; + int size,result=0; + cell *cptr; + + size=(int)params[5]; + if (size<=0) + return 0; + if (params[7]) + size*=sizeof(cell); + + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + amx_StrParam(amx,params[6],defvalue); + + cptr=amx_Address(amx,params[4]); + buffer=(char *)alloca(size); + if (buffer==NULL || cptr==NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + result=ini_gets(section,key,defvalue,buffer,size,fullname); + amx_SetString(cptr,buffer,params[7],0,size); + } /* if */ + return result; +} + +/* readcfgvalue(const filename[]="", const section[]="", const key[], defvalue=0) */ +static cell AMX_NATIVE_CALL n_readcfgvalue(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + long result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + result=ini_getl(section,key,(long)params[4],fullname); + } /* if */ + return result; +} + +/* writecfg(const filename[]="", const section[]="", const key[], const value[]) */ +static cell AMX_NATIVE_CALL n_writecfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key,*value; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + amx_StrParam(amx,params[4],value); + result=ini_puts(section,key,value,fullname); + } /* if */ + return result; +} + +/* writecfgvalue(const filename[]="", const section[]="", const key[], value) */ +static cell AMX_NATIVE_CALL n_writecfgvalue(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + result=ini_putl(section,key,(long)params[4],fullname); + } /* if */ + return result; +} + +/* deletecfg(const filename[]="", const section[]="", const key[]="") */ +static cell AMX_NATIVE_CALL n_deletecfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + if (section!=NULL && *section=='\0') + section=NULL; + amx_StrParam(amx,params[3],key); + if (key!=NULL && *key=='\0') + key=NULL; + result=ini_puts(section,key,NULL,fullname); + } /* if */ + return result; +} + +#if defined __cplusplus + extern "C" +#endif +AMX_NATIVE_INFO file_Natives[] = { + { "fopen", n_fopen }, + { "fclose", n_fclose }, + { "fwrite", n_fwrite }, + { "fread", n_fread }, + { "fputchar", n_fputchar }, + { "fgetchar", n_fgetchar }, + { "fblockwrite", n_fblockwrite }, + { "fblockread", n_fblockread }, + { "ftemp", n_ftemp }, + { "fseek", n_fseek }, + { "flength", n_flength }, + { "fremove", n_fremove }, + { "fcopy", n_fcopy }, + { "frename", n_frename }, + { "fexist", n_fexist }, + { "fmatch", n_fmatch }, + { "fstat", n_fstat }, + { "fattrib", n_fattrib }, + { "filecrc", n_filecrc }, + { "fcreatedir", n_fcreatedir }, + { "readcfg", n_readcfg }, + { "readcfgvalue", n_readcfgvalue }, + { "writecfg", n_writecfg }, + { "writecfgvalue",n_writecfgvalue }, + { "deletecfg", n_deletecfg }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_FileInit(AMX *amx) +{ + return amx_Register(amx, file_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_FileCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxfile.def b/amx-deps/src/amx/amxfile.def index 7d2a74a..3811e15 100644 --- a/amx-deps/src/amx/amxfile.def +++ b/amx-deps/src/amx/amxfile.def @@ -1,6 +1,6 @@ -NAME amxFile -DESCRIPTION 'File I/O support library' - -EXPORTS - amx_FileInit - amx_FileCleanup +NAME amxFile +DESCRIPTION 'File I/O support library' + +EXPORTS + amx_FileInit + amx_FileCleanup diff --git a/amx-deps/src/amx/float.c b/amx-deps/src/amx/amxfloat.c similarity index 90% rename from amx-deps/src/amx/float.c rename to amx-deps/src/amx/amxfloat.c index 3bb9061..0ae2e7e 100644 --- a/amx-deps/src/amx/float.c +++ b/amx-deps/src/amx/amxfloat.c @@ -1,362 +1,380 @@ -/* Float arithmetic for the Pawn Abstract Machine - * - * Copyright (c) Artran, Inc. 1999 - * Written by Greg Garner (gmg@artran.com) - * This file may be freely used. No warranties of any kind. - * - * CHANGES - - * 2002-08-27: Basic conversion of source from C++ to C by Adam D. Moss - * - * 2003-08-29: Removal of the dynamic memory allocation and replacing two - * type conversion functions by macros, by Thiadmer Riemersma - * 2003-09-22: Moved the type conversion macros to AMX.H, and simplifications - * of some routines, by Thiadmer Riemersma - * 2003-11-24: A few more native functions (geometry), plus minor modifications, - * mostly to be compatible with dynamically loadable extension - * modules, by Thiadmer Riemersma - * 2004-01-09: Adaptions for 64-bit cells (using "double precision"), by - * Thiadmer Riemersma - */ -#include /* for atof() */ -#include /* for NULL */ -#include -#include -#include "amx.h" - -/* - #if defined __BORLANDC__ - #pragma resource "amxFloat.res" - #endif -*/ - -#if PAWN_CELL_SIZE==32 - #define REAL float -#elif PAWN_CELL_SIZE==64 - #define REAL double -#else - #error Unsupported cell size -#endif - -#define PI 3.1415926535897932384626433832795 - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_float(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = long value to convert to a float - */ - REAL fValue; - - (void)amx; - /* Convert to a float. Calls the compilers long to float conversion. */ - fValue = (REAL) params[1]; - - /* Return the cell. */ - return amx_ftoc(fValue); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_strfloat(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = virtual string address to convert to a float - */ - char szSource[60]; - cell *pString; - REAL fNum; - int nLen; - - (void)amx; - /* They should have sent us 1 cell. */ - assert(params[0]/sizeof(cell)==1); - - /* Get the real address of the string. */ - amx_GetAddr(amx,params[1],&pString); - - /* Find out how long the string is in characters. */ - amx_StrLen(pString, &nLen); - if (nLen == 0 || nLen >= sizeof szSource) - return 0; - - /* Now convert the Pawn string into a C type null terminated string */ - amx_GetString(szSource, pString, 0, sizeof szSource); - - /* Now convert this to a float. */ - fNum = (REAL)atof(szSource); - - return amx_ftoc(fNum); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatmul(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 - * params[2] = float operand 2 - */ - REAL fRes = amx_ctof(params[1]) * amx_ctof(params[2]); - (void)amx; - return amx_ftoc(fRes); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatdiv(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float dividend (top) - * params[2] = float divisor (bottom) - */ - REAL fRes = amx_ctof(params[1]) / amx_ctof(params[2]); - (void)amx; - return amx_ftoc(fRes); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatadd(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 - * params[2] = float operand 2 - */ - REAL fRes = amx_ctof(params[1]) + amx_ctof(params[2]); - (void)amx; - return amx_ftoc(fRes); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatsub(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 - * params[2] = float operand 2 - */ - REAL fRes = amx_ctof(params[1]) - amx_ctof(params[2]); - (void)amx; - return amx_ftoc(fRes); -} - -/******************************************************************/ -/* Return fractional part of float */ -static cell AMX_NATIVE_CALL n_floatfract(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand - */ - REAL fA = amx_ctof(params[1]); - fA = fA - (REAL)(floor((double)fA)); - (void)amx; - return amx_ftoc(fA); -} - -/******************************************************************/ -/* Return integer part of float, rounded */ -static cell AMX_NATIVE_CALL n_floatround(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand - * params[2] = Type of rounding (long) - */ - REAL fA = amx_ctof(params[1]); - - (void)amx; - switch (params[2]) - { - case 1: /* round downwards (truncate) */ - fA = (REAL)(floor((double)fA)); - break; - case 2: /* round upwards */ - fA = (REAL)(ceil((double)fA)); - break; - case 3: /* round towards zero */ - if ( fA>=0.0 ) - fA = (REAL)(floor((double)fA)); - else - fA = (REAL)(ceil((double)fA)); - break; - default: /* standard, round to nearest */ - fA = (REAL)(floor((double)fA+.5)); - break; - } - - return (long)fA; -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatcmp(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 - * params[2] = float operand 2 - */ - REAL fA, fB; - - (void)amx; - fA = amx_ctof(params[1]); - fB = amx_ctof(params[2]); - if (fA == fB) - return 0; - else if (fA>fB) - return 1; - else - return -1; - -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatsqroot(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand - */ - REAL fA = amx_ctof(params[1]); - fA = (REAL)sqrt(fA); - if (fA < 0) - return amx_RaiseError(amx, AMX_ERR_DOMAIN); - return amx_ftoc(fA); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatpower(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 (base) - * params[2] = float operand 2 (exponent) - */ - REAL fA = amx_ctof(params[1]); - REAL fB = amx_ctof(params[2]); - fA = (REAL)pow(fA, fB); - (void)amx; - return amx_ftoc(fA); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatlog(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 (value) - * params[2] = float operand 2 (base) - */ - REAL fValue = amx_ctof(params[1]); - REAL fBase = amx_ctof(params[2]); - (void)amx; - if (fValue <= 0.0 || fBase <= 0) - return amx_RaiseError(amx, AMX_ERR_DOMAIN); - if (fBase == 10.0) // ??? epsilon - fValue = (REAL)log10(fValue); - else - fValue = (REAL)(log(fValue) / log(fBase)); - return amx_ftoc(fValue); -} - -static REAL ToRadians(REAL angle, int radix) -{ - switch (radix) - { - case 1: /* degrees, sexagesimal system (technically: degrees/minutes/seconds) */ - return (REAL)(angle * PI / 180.0); - case 2: /* grades, centesimal system */ - return (REAL)(angle * PI / 200.0); - default: /* assume already radian */ - return angle; - } /* switch */ -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatsin(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 (angle) - * params[2] = float operand 2 (radix) - */ - REAL fA = amx_ctof(params[1]); - fA = ToRadians(fA, params[2]); - fA = sin(fA); - (void)amx; - return amx_ftoc(fA); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatcos(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 (angle) - * params[2] = float operand 2 (radix) - */ - REAL fA = amx_ctof(params[1]); - fA = ToRadians(fA, params[2]); - fA = cos(fA); - (void)amx; - return amx_ftoc(fA); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floattan(AMX *amx,const cell *params) -{ - /* - * params[0] = number of bytes - * params[1] = float operand 1 (angle) - * params[2] = float operand 2 (radix) - */ - REAL fA = amx_ctof(params[1]); - fA = ToRadians(fA, params[2]); - fA = tan(fA); - (void)amx; - return amx_ftoc(fA); -} - -/******************************************************************/ -static cell AMX_NATIVE_CALL n_floatabs(AMX *amx,const cell *params) -{ - REAL fA = amx_ctof(params[1]); - fA = (fA >= 0) ? fA : -fA; - (void)amx; - return amx_ftoc(fA); -} - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO float_Natives[] = { - { "float", n_float }, - { "strfloat", n_strfloat }, - { "floatmul", n_floatmul }, - { "floatdiv", n_floatdiv }, - { "floatadd", n_floatadd }, - { "floatsub", n_floatsub }, - { "floatfract", n_floatfract }, - { "floatround", n_floatround }, - { "floatcmp", n_floatcmp }, - { "floatsqroot", n_floatsqroot}, - { "floatpower", n_floatpower }, - { "floatlog", n_floatlog }, - { "floatsin", n_floatsin }, - { "floatcos", n_floatcos }, - { "floattan", n_floattan }, - { "floatabs", n_floatabs }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_FloatInit(AMX *amx) -{ - return amx_Register(amx,float_Natives,-1); -} - -int AMXEXPORT amx_FloatCleanup(AMX *amx) -{ - (void)amx; - return AMX_ERR_NONE; -} +/* Float arithmetic for the Pawn Abstract Machine + * + * Copyright (c) Artran, Inc. 1999 + * Written by Greg Garner (gmg@artran.com) + * This file may be freely used. No warranties of any kind. + * + * CHANGES - + * 2002-08-27: Basic conversion of source from C++ to C by Adam D. Moss + * + * 2003-08-29: Removal of the dynamic memory allocation and replacing two + * type conversion functions by macros, by Thiadmer Riemersma + * 2003-09-22: Moved the type conversion macros to AMX.H, and simplifications + * of some routines, by Thiadmer Riemersma + * 2003-11-24: A few more native functions (geometry), plus minor modifications, + * mostly to be compatible with dynamically loadable extension + * modules, by Thiadmer Riemersma + * 2004-01-09: Adaptions for 64-bit cells (using "double precision"), by + * Thiadmer Riemersma + */ + +#include /* for atof() */ +#include /* for NULL */ +#include +#include +#include "amx.h" + +/* + #if defined __BORLANDC__ + #pragma resource "amxFloat.res" + #endif +*/ + +#if PAWN_CELL_SIZE==32 + #define REAL float +#elif PAWN_CELL_SIZE==64 + #define REAL double +#else + #error Unsupported cell size +#endif + +#define PI 3.1415926535897932384626433832795 + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_float(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = integer value to convert to a float + */ + REAL fValue; + + (void)amx; + /* Convert to a float. Calls the compilers long to float conversion. */ + fValue = (REAL) params[1]; + + /* Return the cell. */ + return amx_ftoc(fValue); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_strfloat(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = virtual string address to convert to a float + */ + char szSource[60]; + cell *pString; + REAL fNum; + int nLen; + + (void)amx; + /* They should have sent us 1 cell. */ + assert(params[0]/sizeof(cell)==1); + + /* Get the real address of the string. */ + pString=amx_Address(amx,params[1]); + + /* Find out how long the string is in characters. */ + amx_StrLen(pString, &nLen); + if (nLen == 0 || nLen >= sizeof szSource) + return 0; + + /* Now convert the Pawn string into a C type null terminated string */ + amx_GetString(szSource, pString, 0, sizeof szSource); + + /* Now convert this to a float. */ + fNum = (REAL)atof(szSource); + + return amx_ftoc(fNum); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatmul(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 + * params[2] = float operand 2 + */ + REAL fRes = amx_ctof(params[1]) * amx_ctof(params[2]); + (void)amx; + return amx_ftoc(fRes); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatdiv(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float dividend (top) + * params[2] = float divisor (bottom) + */ + REAL fRes = amx_ctof(params[1]) / amx_ctof(params[2]); + (void)amx; + return amx_ftoc(fRes); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatadd(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 + * params[2] = float operand 2 + */ + REAL fRes = amx_ctof(params[1]) + amx_ctof(params[2]); + (void)amx; + return amx_ftoc(fRes); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatsub(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 + * params[2] = float operand 2 + */ + REAL fRes = amx_ctof(params[1]) - amx_ctof(params[2]); + (void)amx; + return amx_ftoc(fRes); +} + +/******************************************************************/ +/* Return fractional part of float */ +static cell AMX_NATIVE_CALL n_floatfract(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand + */ + REAL fA = amx_ctof(params[1]); + fA = fA - (REAL)(floor((double)fA)); + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +/* Return integer part of float, rounded */ +static cell AMX_NATIVE_CALL n_floatround(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand + * params[2] = Type of rounding (integer) + */ + REAL fA = amx_ctof(params[1]); + + (void)amx; + switch (params[2]) + { + case 1: /* round downwards */ + fA = (REAL)(floor((double)fA)); + break; + case 2: /* round upwards */ + fA = (REAL)(ceil((double)fA)); + break; + case 3: /* round towards zero (truncate) */ + if ( fA>=0.0 ) + fA = (REAL)(floor((double)fA)); + else + fA = (REAL)(ceil((double)fA)); + break; + default: /* standard, round to nearest */ + fA = (REAL)(floor((double)fA+.5)); + break; + } + + return (cell)fA; +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatcmp(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 + * params[2] = float operand 2 + */ + REAL fA, fB; + + (void)amx; + fA = amx_ctof(params[1]); + fB = amx_ctof(params[2]); + if (fA == fB) + return 0; + else if (fA>fB) + return 1; + else + return -1; + +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatsqroot(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand + */ + REAL fA = amx_ctof(params[1]); + fA = (REAL)sqrt(fA); + if (fA < 0) + return amx_RaiseError(amx, AMX_ERR_DOMAIN); + return amx_ftoc(fA); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatpower(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 (base) + * params[2] = float operand 2 (exponent) + */ + REAL fA = amx_ctof(params[1]); + REAL fB = amx_ctof(params[2]); + fA = (REAL)pow(fA, fB); + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatlog(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 (value) + * params[2] = float operand 2 (base) + */ + REAL fValue = amx_ctof(params[1]); + REAL fBase = amx_ctof(params[2]); + (void)amx; + if (fValue <= 0.0 || fBase <= 0) + return amx_RaiseError(amx, AMX_ERR_DOMAIN); + if (fBase == 10.0) // ??? epsilon + fValue = (REAL)log10(fValue); + else + fValue = (REAL)(log(fValue) / log(fBase)); + return amx_ftoc(fValue); +} + +static REAL ToRadians(REAL angle, int radix) +{ + switch (radix) + { + case 1: /* degrees, sexagesimal system (technically: degrees/minutes/seconds) */ + return (REAL)(angle * PI / 180.0); + case 2: /* grades, centesimal system */ + return (REAL)(angle * PI / 200.0); + default: /* assume already radian */ + return angle; + } /* switch */ +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatsin(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 (angle) + * params[2] = float operand 2 (radix) + */ + REAL fA = amx_ctof(params[1]); + fA = ToRadians(fA, params[2]); + fA = (float)sin(fA); + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatcos(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 (angle) + * params[2] = float operand 2 (radix) + */ + REAL fA = amx_ctof(params[1]); + fA = ToRadians(fA, params[2]); + fA = (float)cos(fA); + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floattan(AMX *amx,const cell *params) +{ + /* + * params[0] = number of bytes + * params[1] = float operand 1 (angle) + * params[2] = float operand 2 (radix) + */ + REAL fA = amx_ctof(params[1]); + fA = ToRadians(fA, params[2]); + fA = (float)tan(fA); + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +static cell AMX_NATIVE_CALL n_floatabs(AMX *amx,const cell *params) +{ + REAL fA = amx_ctof(params[1]); + fA = (fA >= 0) ? fA : -fA; + (void)amx; + return amx_ftoc(fA); +} + +/******************************************************************/ +/* return the integer part of a real value, truncated + * Return integer part of float, truncated (same as floatround + * with mode 3) + */ +static cell AMX_NATIVE_CALL n_floatint(AMX *amx,const cell *params) +{ + REAL fA = amx_ctof(params[1]); + if ( fA>=0.0 ) + fA = (REAL)(floor((double)fA)); + else + fA = (REAL)(ceil((double)fA)); + (void)amx; + return (cell)fA; +} + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO float_Natives[] = { + { "float", n_float }, + { "strfloat", n_strfloat }, + { "floatmul", n_floatmul }, + { "floatdiv", n_floatdiv }, + { "floatadd", n_floatadd }, + { "floatsub", n_floatsub }, + { "floatfract", n_floatfract }, + { "floatround", n_floatround }, + { "floatcmp", n_floatcmp }, + { "floatsqroot", n_floatsqroot}, + { "floatpower", n_floatpower }, + { "floatlog", n_floatlog }, + { "floatsin", n_floatsin }, + { "floatcos", n_floatcos }, + { "floattan", n_floattan }, + { "floatabs", n_floatabs }, + { "floatint", n_floatint }, // also add user-defined operator "=" + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_FloatInit(AMX *amx) +{ + return amx_Register(amx,float_Natives,-1); +} + +int AMXEXPORT AMXAPI amx_FloatCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxgc.c b/amx-deps/src/amx/amxgc.c index 835937e..e1dc1f0 100644 --- a/amx-deps/src/amx/amxgc.c +++ b/amx-deps/src/amx/amxgc.c @@ -1,307 +1,308 @@ -/* Simple garbage collector for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxgc.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include /* for malloc()/free() */ -#include /* for memset() */ -#include "amx.h" -#include "amxgc.h" - -typedef struct tagGCPAIR { - cell value; - int count; -} GCPAIR; - -typedef struct tagGCINFO { - GCPAIR *table; - GC_FREE callback; - int exponent; - int flags; - int count; -} GCINFO; - -#define SHIFT1 (sizeof(cell)*4) -#define MASK1 (~(((cell)-1) << SHIFT1)) -#define FOLD1(p) ( ((p) & MASK1) ^ (((p) >> SHIFT1) & MASK1) ) - /* call FOLD1(c) if the table exponent < SHIFT1 */ -#define SHIFT2 (sizeof(cell)*2) -#define MASK2 (~(((cell)-1) << SHIFT2)) -#define FOLD2(p) ( ((p) & MASK2) ^ (((p) >> SHIFT2) & MASK2) ) - /* call FOLD2(c) if the table size < MASK2 */ -#define SHIFT3 (sizeof(cell)) -#define MASK3 (~(((cell)-1)<> SHIFT3) & MASK3) ) - /* call FOLD3(c) if the table size < MASK3 */ -#define MASK(exp) (~(((cell)-1) << (exp))) - -static unsigned increments[17] = { 1, 1, 1, 3, 5, 7, 17, 31, 67, 127, 257, - 509, 1021, 2053, 4099, 8191, 16381 }; - -static unsigned char inverse[256] = { - 255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240, - 239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224, - 223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208, - 207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192, - 191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176, - 175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160, - 159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144, - 143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128, - 127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112, - 111,110,109,108,107,106,105,104,103,102,101,100, 99, 98, 97, 96, - 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, - 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, - 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, - 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -}; - -static GCINFO SharedGC; - - -int gc_setcallback(GC_FREE callback) -{ - SharedGC.callback=callback; - return GC_ERR_NONE; -} - -int gc_settable(int exponent, int flags) -{ - if (exponent==0) { - gc_clean(); /* delete all "live" objects first */ - if (SharedGC.table!=NULL) { - free(SharedGC.table); - SharedGC.table=NULL; - } /* if */ - SharedGC.exponent=0; - SharedGC.flags=0; - SharedGC.count=0; - } else { - int size,oldsize; - GCPAIR *table,*oldtable; - - if (exponent<7 || (1L<INT_MAX) - return GC_ERR_PARAMS; - size=(1<size) - return GC_ERR_PARAMS; - /* allocate the new table */ - table=malloc(size*sizeof(*table)); - if (table==NULL) - return GC_ERR_MEMORY; - /* save the statistics of the old table */ - oldtable=SharedGC.table; - oldsize=(1<=(1<0); - index=(index+incr) & mask; - if (incridx>0) - incr=increments[--incridx]; - } /* while */ - - if (t!=0) { - assert(t==value); - assert(SharedGC.table[index].value==value); - return GC_ERR_DUPLICATE; - } /* if */ - - SharedGC.table[index].value=value; - assert(SharedGC.table[index].count==0); - - return GC_ERR_NONE; -} - -static void scansection(cell *start,size_t size) -{ - int index,incr,incridx,incridx_org,mask; - cell v,t; - unsigned char *minorbyte; - - assert(SharedGC.table!=NULL); - assert((size % sizeof(cell))==0); - assert(start!=NULL); - size/=sizeof(cell); /* from number of bytes to number of cells */ - - incridx_org= (SharedGC.exponent0) { - v=*start; - /* first "fold" the value, to make maximum use of all bits */ - if (SharedGC.exponent0); - index=(index+incr) & mask; - if (incridx>0) - incr=increments[--incridx]; - } /* while */ - - /* if found, mark it */ - if (t!=0) { - assert(t==*start); - assert(SharedGC.table[index].value==*start); - SharedGC.table[index].count+=1; - } /* if */ - - size--; - start++; - } /* while */ -} - -int gc_scan(AMX *amx) -{ - AMX_HEADER *hdr; - unsigned char *data; - - if (amx==NULL) - return GC_ERR_PARAMS; - if (SharedGC.table==NULL) - return GC_ERR_INIT; - - hdr=(AMX_HEADER*)amx->base; - - /* scan data segment */ - data=amx->data ? amx->data : amx->base+(int)hdr->dat; - scansection((cell *)data, hdr->hea - hdr->dat); - /* scan stack */ - scansection((cell *)(data + amx->hlw), amx->hea - amx->hlw); - /* scan heap */ - scansection((cell *)(data + amx->stk), amx->stp - amx->stk); - - return GC_ERR_NONE; -} - -int gc_clean(void) -{ - int size; - GCPAIR *item; - - if (SharedGC.table==NULL) - return GC_ERR_INIT; - if (SharedGC.callback==NULL) - return GC_ERR_CALLBACK; - - size=(1<0) { - if (item->value!=0) { - if (item->count==0) { - SharedGC.callback(item->value); - item->value=0; - } /* if */ - item->count=0; - } /* if */ - size--; - item++; - } /* while */ - return GC_ERR_NONE; -} +/* Simple garbage collector for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxgc.c 3660 2006-11-05 13:05:09Z thiadmer $ + */ + +#include +#include +#include /* for malloc()/free() */ +#include /* for memset() */ +#include "amx.h" +#include "amxgc.h" + +typedef struct tagGCPAIR { + cell value; + int count; +} GCPAIR; + +typedef struct tagGCINFO { + GCPAIR *table; + GC_FREE callback; + int exponent; + int flags; + int count; +} GCINFO; + +#define SHIFT1 (sizeof(cell)*4) +#define MASK1 (~(((cell)-1) << SHIFT1)) +#define FOLD1(p) ( ((p) & MASK1) ^ (((p) >> SHIFT1) & MASK1) ) + /* call FOLD1(c) if the table exponent < SHIFT1 */ +#define SHIFT2 (sizeof(cell)*2) +#define MASK2 (~(((cell)-1) << SHIFT2)) +#define FOLD2(p) ( ((p) & MASK2) ^ (((p) >> SHIFT2) & MASK2) ) + /* call FOLD2(c) if the table size < MASK2 */ +#define SHIFT3 (sizeof(cell)) +#define MASK3 (~(((cell)-1)<> SHIFT3) & MASK3) ) + /* call FOLD3(c) if the table size < MASK3 */ +#define MASK(exp) (~(((cell)-1) << (exp))) + +static unsigned increments[17] = { 1, 1, 1, 3, 5, 7, 17, 31, 67, 127, 257, + 509, 1021, 2053, 4099, 8191, 16381 }; + +static unsigned char inverse[256] = { + 255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240, + 239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224, + 223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208, + 207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192, + 191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176, + 175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160, + 159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144, + 143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128, + 127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112, + 111,110,109,108,107,106,105,104,103,102,101,100, 99, 98, 97, 96, + 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, + 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +static GCINFO SharedGC; + + +int gc_setcallback(GC_FREE callback) +{ + SharedGC.callback=callback; + return GC_ERR_NONE; +} + +int gc_settable(int exponent, int flags) +{ + if (exponent==0) { + gc_clean(); /* delete all "live" objects first */ + if (SharedGC.table!=NULL) { + free(SharedGC.table); + SharedGC.table=NULL; + } /* if */ + SharedGC.exponent=0; + SharedGC.flags=0; + SharedGC.count=0; + } else { + int size,oldsize; + GCPAIR *table,*oldtable; + + if (exponent<7 || (1L<INT_MAX) + return GC_ERR_PARAMS; + size=(1<size) + return GC_ERR_PARAMS; + /* allocate the new table */ + table=malloc(size*sizeof(*table)); + if (table==NULL) + return GC_ERR_MEMORY; + /* save the statistics of the old table */ + oldtable=SharedGC.table; + oldsize=(1<=(1<0); + index=(index+incr) & mask; + if (incridx>0) + incr=increments[--incridx]; + } /* while */ + + if (t!=0) { + assert(t==value); + assert(SharedGC.table[index].value==value); + return GC_ERR_DUPLICATE; + } /* if */ + + SharedGC.table[index].value=value; + assert(SharedGC.table[index].count==0); + + return GC_ERR_NONE; +} + +static void scansection(cell *start,size_t size) +{ + int index,incr,incridx,incridx_org,mask; + cell v,t; + unsigned char *minorbyte; + + assert(SharedGC.table!=NULL); + assert((size % sizeof(cell))==0); + assert(start!=NULL); + size/=sizeof(cell); /* from number of bytes to number of cells */ + + incridx_org= (SharedGC.exponent0) { + v=*start; + /* first "fold" the value, to make maximum use of all bits */ + if (SharedGC.exponent0); + index=(index+incr) & mask; + if (incridx>0) + incr=increments[--incridx]; + } /* while */ + + /* if found, mark it */ + if (t!=0) { + assert(t==*start); + assert(SharedGC.table[index].value==*start); + SharedGC.table[index].count+=1; + } /* if */ + + size--; + start++; + } /* while */ +} + +int gc_scan(AMX *amx) +{ + AMX_HEADER *hdr; + unsigned char *data; + + if (amx==NULL) + return GC_ERR_PARAMS; + if (SharedGC.table==NULL) + return GC_ERR_INIT; + + hdr=(AMX_HEADER*)amx->base; + + /* scan data segment */ + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + scansection((cell *)data, hdr->hea - hdr->dat); + /* scan stack */ + scansection((cell *)(data + amx->hlw), amx->hea - amx->hlw); + /* scan heap */ + scansection((cell *)(data + amx->stk), amx->stp - amx->stk); + + return GC_ERR_NONE; +} + +int gc_clean(void) +{ + int size; + GCPAIR *item; + + if (SharedGC.table==NULL) + return GC_ERR_INIT; + if (SharedGC.callback==NULL) + return GC_ERR_CALLBACK; + + size=(1<0) { + if (item->value!=0) { + if (item->count==0) { + SharedGC.callback(item->value); + item->value=0; + } /* if */ + item->count=0; + } /* if */ + size--; + item++; + } /* while */ + return GC_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxgc.h b/amx-deps/src/amx/amxgc.h index 85d13a3..7f90f16 100644 --- a/amx-deps/src/amx/amxgc.h +++ b/amx-deps/src/amx/amxgc.h @@ -1,54 +1,54 @@ -/* Simple garbage collector for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxgc.h 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#ifndef AMXGC_H -#define AMXGC_H - -typedef void _FAR (* GC_FREE)(cell unreferenced); -enum { - GC_ERR_NONE, - GC_ERR_CALLBACK, /* no callback, or invalid callback */ - GC_ERR_INIT, /* garbage collector not initialized (no table size) */ - GC_ERR_MEMORY, /* insufficient memory to set/resize the table */ - GC_ERR_PARAMS, /* parameter error */ - GC_ERR_TABLEFULL, /* domain error, expression result does not fit in range */ - GC_ERR_DUPLICATE, /* item is already in the table */ -}; - -/* flags */ -#define GC_AUTOGROW 1 /* gc_mark() may grow the hash table when it fills up */ - -int gc_setcallback(GC_FREE callback); - -int gc_settable(int exponent,int flags); -int gc_tablestat(int *exponent,int *percentage); - /* Upon return, "exponent" will hold the values passed to gc_settable(); - * "percentage" is the level (in percent) that the hash table is filled - * up. Either parameter may be set to NULL. - */ - -int gc_mark(cell value); -int gc_scan(AMX *amx); -int gc_clean(void); - -#endif /* AMXGC_H */ +/* Simple garbage collector for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxgc.h 3612 2006-07-22 09:59:46Z thiadmer $ + */ + +#ifndef AMXGC_H +#define AMXGC_H + +typedef void _FAR (* GC_FREE)(cell unreferenced); +enum { + GC_ERR_NONE, + GC_ERR_CALLBACK, /* no callback, or invalid callback */ + GC_ERR_INIT, /* garbage collector not initialized (no table size) */ + GC_ERR_MEMORY, /* insufficient memory to set/resize the table */ + GC_ERR_PARAMS, /* parameter error */ + GC_ERR_TABLEFULL, /* domain error, expression result does not fit in range */ + GC_ERR_DUPLICATE, /* item is already in the table */ +}; + +/* flags */ +#define GC_AUTOGROW 1 /* gc_mark() may grow the hash table when it fills up */ + +int gc_setcallback(GC_FREE callback); + +int gc_settable(int exponent,int flags); +int gc_tablestat(int *exponent,int *percentage); + /* Upon return, "exponent" will hold the values passed to gc_settable(); + * "percentage" is the level (in percent) that the hash table is filled + * up. Either parameter may be set to NULL. + */ + +int gc_mark(cell value); +int gc_scan(AMX *amx); +int gc_clean(void); + +#endif /* AMXGC_H */ diff --git a/amx-deps/src/amx/amxjitr.asm b/amx-deps/src/amx/amxjitr.asm index d4d864e..311cf0e 100644 --- a/amx-deps/src/amx/amxjitr.asm +++ b/amx-deps/src/amx/amxjitr.asm @@ -1,2225 +1,2229 @@ -; ASMJITR.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" -; scripting language -; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES - -; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" -; (27 disks, no output, DOS4/GW under Win95) with this implementation of the -; JIT compiler. - -; NOTE 1: -; There is only one pass implemented in this version. This means there is no -; way of knowing the size of the compiled code before it has actually been com- -; piled. So the only chance the caller has, is to count the number of opcodes -; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to -; obtain a size value big enough to hold the entire code (and data, including -; the stack and heap, after adding their sizes). Afterwards it can realloc() -; this memory block to the actually needed smaller size. - -; NOTE 2: -; The compiler destroys the opcode addresses of the given source by storing the -; respective compiled code's addresses there for the final address relocation -; step. - -; NOTE 3: -; During execution of the compiled code with amx_exec_jit() the x86 processor's -; stack is switched into the data section of the abstract machine. This means -; that there should always be enough memory left between HEA and STK to provide -; stack space for occurring interrupts! (see the STACKRESERVE variable) - -; NOTE 4: -; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I -; instructions, I have to tell that they don't work as expected in a JIT -; compiled program, because there is no easy way of transforming AMX code -; addresses and JIT translated ones. This might be fixed in a future version. - -; NX ("No eXecute") and XD (eXecution Denied) bits -; (by Thiadmer Riemersma) -; -; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit -; processors) and Intel came with the same design, but calling it differently. -; The purpose is to make "buffer overrun" security holes impossible, or at least -; very, very difficult, by marking the stack and the heap as memory regions -; such that an attempt to execute processor instructions will cause a processor -; exception (of course, a buffer overrun that is not explictly handled will then -; crash the application --instead of executing the rogue code). -; -; For JIT compilers, this has the impact that you are not allowed to execute the -; code that the JIT has generated. To do that, you must adjust the attributes -; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to -; allocate a memory block with the appropriate fags; on Linux (with a recent -; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the -; function VirtualProtect() to change the page attributes of an existing memory -; block, but there are caveats in its use: if the block spans multiple pages, -; these pages must be consecutive, and if there are blocks of memory in a page -; unrelated to the JIT, their page attributes will change too. -; -; The JIT compiler itself requires only read-write access (this is the default -; for a memory block that you allocate). The execution of the JIT-compiled code -; requires full access to the memory block: read, write and execute. It needs -; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the -; first lookup (this is an optimization, look up the address of the native -; function only once). For processors that do not support the NX/XD bit, -; execution of code is implicitly supported if read access is supported. -; -; During compilation, the JIT compiler requires write-access to its own code -; segment: the JIT-compiler patches P-code parameters into its own code segment -; during compilation. This is handled in the support code for amx_InitJIT. -; -; -; CALLING CONVENTIONS -; (by Thiadmer Riemersma) -; -; This version is the JIT that uses the "register calling convention" (which is -; particular for Watcom C/C++) both for the calling convention for the _asm_runJIT -; routine itself as for the callback functions. See the other files amxJit*.asm -; for implementations with other calling conventions. - -; Revision History -; ---------------- -; 26 august 2007 by Thiadmer Riemersma -; Minor clean-up; removed unneeded parameter. -; 28 july 2005 -; Bug fix for the switch table, in the situation where only the default -; case was present. Bug found by Bailopan. -; 17 february 2005 by Thiadmer Riemersma (TR) -; Addition of the BREAK opcode, removal of the older debugging opcode -; table. There should now be some debug support (if enabled during the -; build of the JIT compiler), but not enough to run a debugger: the JIT -; compiler does not keep a list that relates the code addresses of the -; P-code versus the native code. -; 6 march 2004 by Thiadmer Riemersma -; Corrected a bug in OP_FILL, where a cell preceding the array would -; be overwritten (zero'ed out). This bug was brought to my attention -; by Robert Daniels. -; 22 december 2003 by Thiadmer Riemersma (TR) -; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; -; SYMTAG is a no-op) -; 14 October 2002 by Thiadmer Riemersma (TR) -; Corrected the amx_s structure. The _hlw field was missing, which caused -; errors for arguments to native functions that were passed by reference. -; 2002/08/05 TR -; * store the status of the abstract machine in the AMX structure upon -; return, so that the machine can be restarted (OP_SLEEP) -; * added OP_NOP (for alignment, it is ignored by the JIT) -; * make sure the JIT does not crash when we NULL is passed for the -; return value -; 2000/03/03 MP -; * added SRANGE as a no-op, so debugging info doesn't upset the JIT -; compiler anymore -; * added note about LCTRL, SCTRL and CALL.I -; * removed some rather unnecessary pops in the epilog of amx_exec_asm -; * changed the template for CALL into a DB byte sequence (tasm 4.1 -; didn't like the immediate value) -; 1999/12/07 MP -; * fixed crash caused by JIT compiler not saving registers -; 1999/08/06 MP - design change: closer to the "iron" with native stack -; * The JIT compiler now generates relocatable code for case tables by -; setting FORCERELOCATABLE = 1. -; * removed all debug hook code -; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ -; pushes/pops are possible -; * removed the run-time functions for the CALL, CALL_I and RET op-codes, -; they are now inline -; * All these changes gained around 80% performance increase for the -; hanoi bench. -; 1999/08/05 MP -; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address -; was stored for the LINE byte code (i.e. SWITCH would jump to totally -; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and -; OP_SCTRL (for the no-op case). -; 1999/08/04 MP -; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. -; arrays have not been tested.) -; * hacked relocation code to support absoulute addresses for CASETBL -; (This assumes that no generated address will be greater than -; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) -; * The run-time function for SWITCH uses a (hopefully) faster algorithm -; to compute the destination address: It searches backwards now. -; 1999/07/08 MP - initial revision - -.386 -.MODEL FLAT - -IFDEF @Version ; for Microsoft MASM 6.x - OPTION OLDSTRUCTS - OPTION M510 -ENDIF - -; -; Support for the BREAK opcode (callback to the debugger): 0 = no, all other -; values = yes. Beware that the compiled code runs slower when this is enabled, -; and that debug support is still fairly minimal. -; -DEBUGSUPPORT = 0 - -; -; If this is set to 1 the JIT generates relocatable code for case tables, too. -; If set to 0, a faster variant for switch (using absolute addresses) is -; generated. I consider setting it to 0 a bad idea. -; -FORCERELOCATABLE = 1 - -; -; Determines how much memory should be reserved for occurring interrupts. -; If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes -; for interrupts that occur in real mode and are promoted to protected mode. -; This value _MUST_ be greater than 64 (for AMX needs) and should be at least -; 128 (to serve interrupts). -; -STACKRESERVE = 256 - -; -; This variable controls the generation of memory range checks at run-time. -; You should set this to 0, only when you are sure that there are no range -; violations in your Pawn programs and you really need those 5% speed gain. -; -DORUNTIMECHECKS = 1 - -IFNDEF JIT - JIT = 1 -ENDIF -INCLUDE amxdef.asm - -; -; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) -; -_PUSH MACRO v - push DWORD ptr v - ENDM - -; -; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) -; -_POP MACRO v - pop DWORD ptr v - ENDM - -; -; For determining the biggest native code section generated for ONE Pawn -; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) -; -MAXCODESIZE = 0 - -; -; This is the work horse of the whole JIT: It actually copies the code. -; -GO_ON MACRO from, to, opsize ;opsize may be 4, 8 or 12 (default=4) - mov esi,OFFSET from ;get source address of JIT code - CODESIZE = to-from - mov ecx,CODESIZE ;get number of bytes to copy - ;all the following could (should?) be in a sub-routine... - ;determining the maximum size of a single code snippet - IF MAXCODESIZE LT CODESIZE - MAXCODESIZE = CODESIZE - ENDIF - mov [ebx],edi ;store address for jump-correction - IFB ;add operand size to the source ptr - add ebx,4 - ELSE - add ebx,opsize - ENDIF - rep movsb - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ;go on with the next opcode - ENDM - -; -; Modify the argument of an x86 instruction with the Pawn opcode's parameter -; before copying the code. -; -putval MACRO where - mov eax,[ebx+4] - mov DWORD ptr [where],eax - ENDM - -; -; Add an entry to the table of addresses which have to be relocated after the -; code compilation is done. -; -RELOC MACRO adr, dest - mov ebp,[reloc_num] - IFB - mov eax,[ebx+4] - ELSE - lea eax,[dest] - ENDIF - mov [edx+ebp],eax ; write absolute destination - lea eax,[edi+adr] - mov [edx+ebp+4],eax ; write address of jump operand - add DWORD ptr [reloc_num],8 - ENDM - - -.CODE - - - PUBLIC asm_runJIT_ - PUBLIC amx_exec_jit_ - PUBLIC getMaxCodeSize_ - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -; void asm_runJIT( AMX *amxh, JumpAddressArray *jumps, void *dest ) ; -; eax edx ebx ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; asm_runJIT() assumes that the code of this module is allready browsed and -; relocated for the JIT compiler. It also assumes that both the jumps array and -; the dest memory block are large enough to hold all the data it has to write -; to them, as well as that the prefix (header) has already been copied to dest. - -asm_runJIT_ PROC - push ebp - push ecx - push edi - push esi - - mov [amxhead],eax ; save pointer to AMX_HEADER struct - mov ecx,[eax+_cod] ; get offset of start of code - mov eax,[eax+_dat] ; offset of start of data = end of code - mov edi,ecx - add ecx,[amxhead] ; compute the real pointer - add eax,[amxhead] ; dito - add edi,ebx ; get write pointer into EDI - mov [compiled_code],ebx - mov [end_code],eax ; Store end-of-code address, so JIT - ; compiler knows when to stop. - mov DWORD ptr [reloc_num],0 ; init the index into the jumps array - - mov ebx,ecx - jmp DWORD ptr [ecx] ; start compiling - - ; The compiler will jump back here when code generation is complete. - -code_gen_done: ; Now copy the data section. - mov ebp,[amxhead] ; get source AMX_HEADER start address - add edi,3 ; DAT follows directly after COD - and edi,0fffffffch ; align it on a DWORD boundary - push edi ; save data start pointer - mov esi,[end_code] ; get start of data segment - mov ecx,[ebp]._h_hea - sub ecx,[ebp]._dat ; compute length of array to copy - rep movsb ; copy the data - - ; Now adjust the register values in the compiled AMX_HEADER. - ; COD stays the same since the size of AMX_HEADER doesn't change in - ; compiled mode. - mov ebx,[compiled_code] ; get compiled AMX's header address - pop esi ; recall data start pointer - sub esi,ebx ; DAT = size of code + size of prefix - mov [ebx]._dat,esi ; write corrected DAT register - - ;HEA and STP are already relative to DAT, so we don't need to fix them. - - ; Now the calls/jumps in the compiled code have to be relocated. - sub ecx,ecx ; reset offset into relocation table - cmp ecx,[reloc_num] - jae reloc_code_done ; if there's nothing to fix, skip this part - reloc_code_loop: - mov eax,[edx+ecx] ; get destination address - mov edi,[edx+ecx+4] ; determine where to write the relocated value - add ecx,8 ; set pointer to next entry in relocation table - add edi,4 ; base address from where the offset is taken -IF FORCERELOCATABLE EQ 0 - ;MP: hack to suport absolute addresses for the CASETBL instruction - test eax,80000000h ; check whether it is an absolute address - pushf - and eax,7fffffffh ; clear the flag bit for absolute addresses - popf - mov eax,[eax] ; translate into compiled absolute address - jne write_reloc ; leave out the subtraction if absolute -ELSE - mov eax,[eax] ; translate into compiled absolute address -ENDIF - sub eax,edi ; make a relative offset - write_reloc: - mov [edi-4],eax ; write the relocated address - cmp ecx,[reloc_num] - jb reloc_code_loop - -reloc_code_done: - ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) - add ebp,[ebp]._cod ; make all addresses relative to COD, not base - mov eax,[ebx]._h_cip - add eax,ebp ; get absolute source CIP - mov eax,[eax] ; translate CIP to compiled address - sub eax,ebx ; make it relative to base - sub eax,[ebx]._cod ; and now relative to COD - mov [ebx]._h_cip,eax; store relocated CIP - mov edi,[ebx]._publics - sub esi,esi - mov ecx,[ebx]._natives - sub ecx,edi ; ECX = _natives - _publics = public table size - mov si,[ebx]._defsize - or ecx,ecx - jz reloc_done ; If there are no publics, we are done. - reloc_publics_loop: - mov eax,[ebx+edi] ; get public function offset - add eax,ebp ; make it a source address - mov eax,[eax] ; translate to compiled address - sub eax,ebx ; make it an offset relative to base - sub eax,[ebx]._cod ; and now relative to COD - mov [ebx+edi],eax ; write corrected address back - add edi,esi ; step to next public function entry - sub ecx,esi - ja reloc_publics_loop - -reloc_done: - mov eax,0 - pop esi - pop edi - pop ecx - pop ebp - ret - -OP_LOAD_PRI: -;nop; - putval j_load_pri+2 - GO_ON j_load_pri, OP_LOAD_ALT, 8 - - j_load_pri: - mov eax,[edi+12345678h] - -OP_LOAD_ALT: -;nop; - putval j_load_alt+2 - GO_ON j_load_alt, OP_LOAD_S_PRI, 8 - - j_load_alt: - mov edx,[edi+12345678h] - -;good -OP_LOAD_S_PRI: -;nop; - putval j_load_s_pri+2 - GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 - - j_load_s_pri: - mov eax,[ebx+12345678h] - -;good -OP_LOAD_S_ALT: -;nop; - putval j_load_s_alt+2 - GO_ON j_load_s_alt, OP_LOAD_I, 8 - - j_load_s_alt: - mov edx,[ebx+12345678h] - -OP_LOAD_I: -;nop; - GO_ON j_load_i, OP_LODB_I - - j_load_i: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -OP_LODB_I: -;nop; - mov eax,[ebx+4] - mov eax,DWORD ptr [(lodb_and-4)+eax*4] - mov DWORD ptr [j_lodb_i_sm+1],eax ;modify AND instruction - GO_ON j_lodb_i, OP_LREF_PRI, 8 - - j_lodb_i: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_eax] -ENDIF - mov eax,[edi+eax] ;subject to misalignment stalls - j_lodb_i_sm: - and eax,12345678h - -OP_LREF_PRI: -;nop; - putval j_lref_pri+2 - GO_ON j_lref_pri, OP_LREF_ALT, 8 - - j_lref_pri: - mov eax,[edi+12345678h] - mov eax,[edi+eax] - -OP_LREF_ALT: -;nop; - putval j_lref_alt+2 - GO_ON j_lref_alt, OP_LREF_S_PRI, 8 - - j_lref_alt: - mov edx,[edi+12345678h] - mov edx,[edi+edx] - -OP_LREF_S_PRI: -;nop; - putval j_lref_s_pri+2 - GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 - - j_lref_s_pri: - mov eax,[ebx+12345678h] - mov eax,[edi+eax] - -OP_LREF_S_ALT: -;nop; - putval j_lref_s_alt+2 - GO_ON j_lref_s_alt, OP_CONST_PRI, 8 - - j_lref_s_alt: - mov edx,[ebx+12345678h] - mov edx,[edi+edx] - -;good -OP_CONST_PRI: -;nop; - putval j_const_pri+1 - GO_ON j_const_pri, OP_CONST_ALT, 8 - - j_const_pri: - mov eax,12345678h - -;good -OP_CONST_ALT: -;nop; - putval j_const_alt+1 - GO_ON j_const_alt, OP_ADDR_PRI, 8 - - j_const_alt: - mov edx,12345678h - -;good -OP_ADDR_PRI: -;nop; - putval j_addr_pri+1 - GO_ON j_addr_pri, OP_ADDR_ALT, 8 - - j_addr_pri: - mov eax,12345678h - add eax,frm - -;good -OP_ADDR_ALT: -;nop; - putval j_addr_alt+1 - GO_ON j_addr_alt, OP_STOR_PRI, 8 - - j_addr_alt: - mov edx,12345678h - add edx,frm - -OP_STOR_PRI: -;nop; - putval j_stor_pri+2 - GO_ON j_stor_pri, OP_STOR_ALT, 8 - - j_stor_pri: - mov [edi+12345678h],eax - -OP_STOR_ALT: -;nop; - putval j_stor_alt+2 - GO_ON j_stor_alt, OP_STOR_S_PRI, 8 - - j_stor_alt: - mov [edi+12345678h],edx - -;good -OP_STOR_S_PRI: -;nop; - putval j_stor_s_pri+2 - GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 - - j_stor_s_pri: - mov [ebx+12345678h],eax - -;good -OP_STOR_S_ALT: -;nop; - putval j_stor_s_alt+2 - GO_ON j_stor_s_alt, OP_STOR_I, 8 - - j_stor_s_alt: - mov [ebx+12345678h],edx - -;good -OP_STOR_I: -;nop; - GO_ON j_stor_i, OP_STRB_I - - j_stor_i: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_edx] -ENDIF - mov [edi+edx],eax - -OP_STRB_I: -;nop; - mov eax,[ebx+4] - cmp eax,1 - jne strb_not1byte - GO_ON j_strb_i_1b, strb_not1byte, 8 - j_strb_i_1b: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_edx] -ENDIF - mov [edi+edx],al - - strb_not1byte: - cmp eax,4 - je strb_4byte - GO_ON j_strb_i_2b, strb_4byte, 8 - j_strb_i_2b: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_edx] -ENDIF - mov [edi+edx],ax - - strb_4byte: - GO_ON j_strb_i_4b, OP_SREF_PRI, 8 - j_strb_i_4b: -IF DORUNTIMECHECKS NE 0 - call [verify_adr_edx] -ENDIF - mov [edi+edx],eax - -OP_SREF_PRI: -;nop; - putval j_sref_pri+2 - GO_ON j_sref_pri, OP_SREF_ALT, 8 - - j_sref_pri: - mov ebp,[edi+12345678h] - mov [edi+ebp],eax - -OP_SREF_ALT: -;nop; - putval j_sref_alt+2 - GO_ON j_sref_alt, OP_SREF_S_PRI, 8 - - j_sref_alt: - mov ebp,[edi+12345678h] - mov [edi+ebp],edx - -OP_SREF_S_PRI: -;nop; - putval j_sref_s_pri+2 - GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 - - j_sref_s_pri: - mov ebp,[ebx+12345678h] - mov [edi+ebp],eax - -OP_SREF_S_ALT: -;nop; - putval j_sref_s_alt+2 - GO_ON j_sref_s_alt, OP_LIDX, 8 - - j_sref_s_alt: - mov ebp,[ebx+12345678h] - mov [edi+ebp],edx - -;good -OP_LIDX: -;nop; - GO_ON j_lidx, OP_LIDX_B - - j_lidx: - lea eax,[edx+4*eax] -IF DORUNTIMECHECKS NE 0 - call [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -OP_LIDX_B: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_lidx_b+2],al - GO_ON j_lidx_b, OP_IDXADDR, 8 - - j_lidx_b: - shl eax,12h - add eax,edx -IF DORUNTIMECHECKS NE 0 - call [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -;good -OP_IDXADDR: -;nop; - GO_ON j_idxaddr, OP_IDXADDR_B - - j_idxaddr: - lea eax,[edx+4*eax] - -OP_IDXADDR_B: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_idxaddr_b+2],al - GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 - - j_idxaddr_b: - shl eax,12h - add eax,edx - -OP_ALIGN_PRI: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov DWORD ptr [j_align_pri+1],eax - GO_ON j_align_pri, OP_ALIGN_ALT, 8 - - j_align_pri: - xor eax,12345678h - -OP_ALIGN_ALT: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov DWORD ptr [j_align_alt+1],eax - GO_ON j_align_alt, OP_LCTRL, 8 - - j_align_alt: - xor edx,12345678h - -OP_LCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,0 - jne lctrl_1 - GO_ON j_lctrl_0, lctrl_1, 8 - j_lctrl_0: - mov eax,code ; 1=COD - lctrl_1: - cmp eax,1 - jne lctrl_2 - GO_ON j_lctrl_1, lctrl_2, 8 - j_lctrl_1: - mov eax,edi ; 1=DAT - lctrl_2: - cmp eax,2 - jne lctrl_3 - GO_ON j_lctrl_2, lctrl_3, 8 - j_lctrl_2: - mov eax,hea ; 2=HEA - lctrl_3: - cmp eax,3 - jne lctrl_4 - GO_ON j_lctrl_3, lctrl_4, 8 - j_lctrl_3: - mov ebp,amx - mov eax,[ebp+_stp] - lctrl_4: - cmp eax,4 - jne lctrl_5 - GO_ON j_lctrl_4, lctrl_5, 8 - j_lctrl_4: - mov eax,esp ; 4=STK - sub eax,edi - lctrl_5: - cmp eax,5 - jne lctrl_6 - GO_ON j_lctrl_5, lctrl_6, 8 - j_lctrl_5: - mov eax,frm ; 5=FRM - lctrl_6: - mov DWORD ptr [j_lctrl_6+1],edi - GO_ON j_lctrl_6, OP_SCTRL, 8 - j_lctrl_6: - mov eax,12345678h ; 6=CIP - - -OP_SCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,2 - jne sctrl_4 - GO_ON j_sctrl_2, sctrl_4, 8 - j_sctrl_2: - mov hea,eax ; 2=HEA - sctrl_4: - cmp eax,4 - jne sctrl_5 - GO_ON j_sctrl_4, sctrl_5, 8 - j_sctrl_4: - ;mov esp,eax ; 4=STK - ;add esp,edi ; relocate stack - lea esp,[eax+edi] - sctrl_5: - cmp eax,5 - jne sctrl_ignore - GO_ON j_sctrl_5, sctrl_ignore, 8 - j_sctrl_5: - mov ebx,eax ; 5=FRM - mov frm,eax - add ebx,edi ; relocate frame - sctrl_ignore: - mov [ebx],edi - add ebx,8 - jmp DWORD ptr [ebx] - -OP_MOVE_PRI: -;nop; - GO_ON j_move_pri, OP_MOVE_ALT - - j_move_pri: - mov eax,edx - -;good -OP_MOVE_ALT: -;nop; - GO_ON j_move_alt, OP_XCHG - - j_move_alt: - mov edx,eax - -OP_XCHG: -;nop; - GO_ON j_xchg, OP_PUSH_PRI - - j_xchg: ;one might use pushes/pops for pre-586's - xchg eax,edx - -;good -OP_PUSH_PRI: -;nop; - GO_ON j_push_pri, OP_PUSH_ALT - - j_push_pri: - _PUSH eax - -;good -OP_PUSH_ALT: -;nop; - GO_ON j_push_alt, OP_PICK - - j_push_alt: - _PUSH edx - -OP_PICK: -;nop; - putval j_pick+2 - GO_ON j_pick, OP_PUSH_C, 8 - - j_pick: - mov eax,[esp+12345678h] - -;good -OP_PUSH_C: -;nop; - putval j_push_c+1 - GO_ON j_push_c, OP_PUSH, 8 - - j_push_c: - _PUSH 12345678h - -OP_PUSH: -;nop; - putval j_push+2 - GO_ON j_push, OP_PUSH_S, 8 - - j_push: - _PUSH [edi+12345678h] - -;good -OP_PUSH_S: -;nop; - putval j_push_s+2 - GO_ON j_push_s, OP_POP_PRI, 8 - - j_push_s: - _PUSH [ebx+12345678h] - -OP_POP_PRI: -;nop; - GO_ON j_pop_pri, OP_POP_ALT - - j_pop_pri: - _POP eax - -;good -OP_POP_ALT: -;nop; - GO_ON j_pop_alt, OP_STACK - - j_pop_alt: - _POP edx - -;good -OP_STACK: -;nop; - putval j_stack+4 - GO_ON j_stack, OP_HEAP, 8 - - j_stack: - mov edx,esp - add esp,12345678h - sub edx,edi -IF DORUNTIMECHECKS NE 0 - call [chk_marginstack] -ENDIF - -;good -OP_HEAP: -;nop; - putval j_heap_call-4 - GO_ON j_heap, OP_PROC, 8 - - j_heap: - mov edx,hea - add DWORD ptr hea,12345678h - j_heap_call: -IF DORUNTIMECHECKS NE 0 - call [chk_marginheap] -ENDIF -;good -OP_PROC: -;nop; - GO_ON j_proc, OP_RET - - j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK - _PUSH frm ; push old frame (for RET/RETN) - mov frm,esp ; get new frame - mov ebx,esp ; already relocated - sub frm,edi ; relocate frame - -OP_RET: -;nop; - GO_ON j_ret, OP_RETN - - j_ret: - _POP ebx ; pop frame - mov frm,ebx - add ebx,edi - ret - ;call [jit_ret] - -;good -OP_RETN: -;nop; - GO_ON j_retn, OP_CALL - - j_retn: - jmp [jit_retn] - -;good -OP_CALL: -;nop; - RELOC 1 - GO_ON j_call, OP_CALL_I, 8 - - j_call: - ;call 12345678h ; tasm chokes on this out of a sudden - db 0e8h, 0, 0, 0, 0 - -OP_CALL_I: -;nop; - GO_ON j_call_i, OP_JUMP - - j_call_i: - call eax - -;good -OP_JUMP: -;nop; - RELOC 1 - GO_ON j_jump, OP_JREL, 8 - - j_jump: - DB 0e9h - DD 12345678h - -OP_JREL: -;nop; - mov eax,[ebx+4] - ; create an absolute address from the relative one - RELOC 1, eax+ebx+8 - GO_ON j_jump, OP_JREL, 8 - -;good -OP_JZER: -;nop; - RELOC 4 - GO_ON j_jzer, OP_JNZ, 8 - - j_jzer: - or eax,eax - DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) - -;good -OP_JNZ: -;nop; - RELOC 4 - GO_ON j_jnz, OP_JEQ, 8 - - j_jnz: - or eax,eax - DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 - -;good -OP_JEQ: -;nop; - RELOC 4 - GO_ON j_jeq, OP_JNEQ, 8 - - j_jeq: - cmp eax,edx - DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) - -OP_JNEQ: -;nop; - RELOC 4 - GO_ON j_jneq, OP_JLESS, 8 - - j_jneq: - cmp eax,edx - DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) - -OP_JLESS: -;nop; - RELOC 4 - GO_ON j_jless, OP_JLEQ, 8 - - j_jless: - cmp eax,edx - DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) - -OP_JLEQ: -;nop; - RELOC 4 - GO_ON j_jleq, OP_JGRTR, 8 - - j_jleq: - cmp eax,edx - DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) - -OP_JGRTR: -;nop; - RELOC 4 - GO_ON j_jgrtr, OP_JGEQ, 8 - - j_jgrtr: - cmp eax,edx - DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) - -OP_JGEQ: -;nop; - RELOC 4 - GO_ON j_jgeq, OP_JSLESS, 8 - - j_jgeq: - cmp eax,edx - DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) - -OP_JSLESS: -;nop; - RELOC 4 - GO_ON j_jsless, OP_JSLEQ, 8 - - j_jsless: - cmp eax,edx - DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 - -;good -OP_JSLEQ: -;nop; - RELOC 4 - GO_ON j_jsleq, OP_JSGRTR, 8 - - j_jsleq: - cmp eax,edx - DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 - -OP_JSGRTR: -;nop; - RELOC 4 - GO_ON j_jsgrtr, OP_JSGEQ, 8 - - j_jsgrtr: - cmp eax,edx - DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 - -OP_JSGEQ: -;nop; - RELOC 4 - GO_ON j_jsgeq, OP_SHL, 8 - - j_jsgeq: - cmp eax,edx - DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 - -OP_SHL: -;nop; - GO_ON j_shl, OP_SHR - j_shl: - mov ecx,edx ; TODO: save ECX if used as special register - shl eax,cl - -OP_SHR: -;nop; - GO_ON j_shr, OP_SSHR - j_shr: - mov ecx,edx ; TODO: save ECX if used as special register - shr eax,cl - -OP_SSHR: -;nop; - GO_ON j_sshr, OP_SHL_C_PRI - j_sshr: - mov ecx,edx ; TODO: save ECX if used as special register - sar eax,cl - -OP_SHL_C_PRI: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shl_c_pri+2],al - GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 - j_shl_c_pri: - shl eax,12h - -OP_SHL_C_ALT: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shl_c_alt+2],al - GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 - j_shl_c_alt: - shl edx,12h - -OP_SHR_C_PRI: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shr_c_pri+2],al - GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 - j_shr_c_pri: - shr eax,12h - -OP_SHR_C_ALT: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shr_c_alt+2],al - GO_ON j_shr_c_alt, OP_SMUL, 8 - j_shr_c_alt: - shr edx,12h - -OP_SMUL: -;nop; - GO_ON j_smul, OP_SDIV - j_smul: - push edx - imul edx - pop edx - -;good -OP_SDIV: -;nop; - GO_ON j_sdiv, OP_SDIV_ALT - j_sdiv: - call [jit_sdiv] - -OP_SDIV_ALT: -;nop; - GO_ON j_sdiv_alt, OP_UMUL - j_sdiv_alt: - xchg eax,edx - call [jit_sdiv] - -OP_UMUL: -;nop; - GO_ON j_umul, OP_UDIV - j_umul: - push edx - mul edx - pop edx - -OP_UDIV: -;nop; - GO_ON j_udiv, OP_UDIV_ALT - j_udiv: - mov ebp,edx - sub edx,edx - call [chk_dividezero] - div ebp - -OP_UDIV_ALT: -;nop; - GO_ON j_udiv_alt, OP_ADD - j_udiv_alt: - mov ebp,eax - mov eax,edx - sub edx,edx - call [chk_dividezero] - div ebp - -;good -OP_ADD: -;nop; - GO_ON j_add, OP_SUB - j_add: - add eax,edx - -;good -OP_SUB: -;nop; - GO_ON j_sub, OP_SUB_ALT - j_sub: - sub eax,edx - -;good -OP_SUB_ALT: -;nop; - GO_ON j_sub_alt, OP_AND - j_sub_alt: - neg eax - add eax,edx - -OP_AND: -;nop; - GO_ON j_and, OP_OR - j_and: - and eax,edx - -OP_OR: -;nop; - GO_ON j_or, OP_XOR - j_or: - or eax,edx - -OP_XOR: -;nop; - GO_ON j_xor, OP_NOT - j_xor: - xor eax,edx - -OP_NOT: -;nop; - GO_ON j_not, OP_NEG - j_not: - neg eax ; sets CF iff EAX != 0 - sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) - inc eax ; -1 => 0 and 0 => 1 - -OP_NEG: -;nop; - GO_ON j_neg, OP_INVERT - j_neg: - neg eax - -OP_INVERT: -;nop; - GO_ON j_invert, OP_ADD_C - j_invert: - not eax - -;good -OP_ADD_C: -;nop; - putval j_add_c+1 - GO_ON j_add_c, OP_SMUL_C, 8 - j_add_c: - add eax,12345678h - -;good -OP_SMUL_C: -;nop; - putval j_smul_c+3 - GO_ON j_smul_c, OP_ZERO_PRI, 8 - j_smul_c: - push edx - imul eax,12345678h - pop edx - -;good -OP_ZERO_PRI: -;nop; - GO_ON j_zero_pri, OP_ZERO_ALT - j_zero_pri: - sub eax,eax - -;good -OP_ZERO_ALT: -;nop; - GO_ON j_zero_alt, OP_ZERO - j_zero_alt: - sub edx,edx - -OP_ZERO: -;nop; - putval j_zero+2 - GO_ON j_zero, OP_ZERO_S, 8 - j_zero: - mov DWORD ptr [edi+12345678h],0 - -OP_ZERO_S: -;nop; - putval j_zero_s+2 - GO_ON j_zero_s, OP_SIGN_PRI, 8 - j_zero_s: - mov DWORD ptr [ebx+12345678h],0 - -OP_SIGN_PRI: -;nop; - GO_ON j_sign_pri, OP_SIGN_ALT - j_sign_pri: - shl eax,24 - sar eax,24 - -OP_SIGN_ALT: -;nop; - GO_ON j_sign_alt, OP_EQ - j_sign_alt: - shl edx,24 - sar edx,24 - -OP_EQ: -;nop; - GO_ON j_eq, OP_NEQ - j_eq: - cmp eax,edx ; PRI == ALT ? - mov eax,0 - sete al - -OP_NEQ: -;nop; - GO_ON j_neq, OP_LESS - j_neq: - cmp eax,edx ; PRI != ALT ? - mov eax,0 - setne al - -OP_LESS: -;nop; - GO_ON j_less, OP_LEQ - j_less: - cmp eax,edx ; PRI < ALT ? (unsigned) - mov eax,0 - setb al - -OP_LEQ: -;nop; - GO_ON j_leq, OP_GRTR - j_leq: - cmp eax,edx ; PRI <= ALT ? (unsigned) - mov eax,0 - setbe al - -OP_GRTR: -;nop; - GO_ON j_grtr, OP_GEQ - j_grtr: - cmp eax,edx ; PRI > ALT ? (unsigned) - mov eax,0 - seta al - -OP_GEQ: -;nop; - GO_ON j_geq, OP_SLESS - j_geq: - cmp eax,edx ; PRI >= ALT ? (unsigned) - mov eax,0 - setae al - -;good -OP_SLESS: -;nop; - GO_ON j_sless, OP_SLEQ - j_sless: - cmp eax,edx ; PRI < ALT ? (signed) - mov eax,0 - setl al - -OP_SLEQ: -;nop; - GO_ON j_sleq, OP_SGRTR - j_sleq: - cmp eax,edx ; PRI <= ALT ? (signed) - mov eax,0 - setle al - -OP_SGRTR: -;nop; - GO_ON j_sgrtr, OP_SGEQ - j_sgrtr: - cmp eax,edx ; PRI > ALT ? (signed) - mov eax,0 - setg al - -OP_SGEQ: -;nop; - GO_ON j_sgeq, OP_EQ_C_PRI - j_sgeq: - cmp eax,edx ; PRI >= ALT ? (signed) - mov eax,0 - setge al - -OP_EQ_C_PRI: -;nop; - putval j_eq_c_pri+1 - GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 - j_eq_c_pri: - cmp eax,12345678h ; PRI == value ? - mov eax,0 - sete al - -OP_EQ_C_ALT: -;nop; - putval j_eq_c_alt+4 - GO_ON j_eq_c_alt, OP_INC_PRI, 8 - j_eq_c_alt: - sub eax,eax - cmp edx,12345678h ; ALT == value ? - sete al - -OP_INC_PRI: -;nop; - GO_ON j_inc_pri, OP_INC_ALT - j_inc_pri: - inc eax - -OP_INC_ALT: -;nop; - GO_ON j_inc_alt, OP_INC - j_inc_alt: - inc edx - -OP_INC: -;nop; - putval j_inc+2 - GO_ON j_inc, OP_INC_S, 8 - j_inc: - inc DWORD ptr [edi+12345678h] - -;good -OP_INC_S: -;nop; - putval j_inc_s+2 - GO_ON j_inc_s, OP_INC_I, 8 - j_inc_s: - inc DWORD ptr [ebx+12345678h] - -OP_INC_I: -;nop; - GO_ON j_inc_i, OP_DEC_PRI - j_inc_i: - inc DWORD ptr [edi+eax] - -OP_DEC_PRI: -;nop; - GO_ON j_dec_pri, OP_DEC_ALT - j_dec_pri: - dec eax - -OP_DEC_ALT: -;nop; - GO_ON j_dec_alt, OP_DEC - j_dec_alt: - dec edx - -OP_DEC: -;nop; - putval j_dec+2 - GO_ON j_dec, OP_DEC_S, 8 - j_dec: - dec DWORD ptr [edi+12345678h] - -OP_DEC_S: -;nop; - putval j_dec_s+2 - GO_ON j_dec_s, OP_DEC_I, 8 - j_dec_s: - dec DWORD ptr [ebx+12345678h] - -OP_DEC_I: -;nop; - GO_ON j_dec_i, OP_MOVS - j_dec_i: - dec DWORD ptr [edi+eax] - -OP_MOVS: -;nop; - putval j_movs+1 - GO_ON j_movs, OP_CMPS, 8 - j_movs: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_movs] - -OP_CMPS: -;nop; - putval j_cmps+1 - GO_ON j_cmps, OP_FILL, 8 - j_cmps: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_cmps] - -OP_FILL: -;nop; - putval j_fill+1 - GO_ON j_fill, OP_HALT, 8 - j_fill: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_fill] - -;good -OP_HALT: -;nop; - putval j_halt_sm+1 - GO_ON j_halt, OP_BOUNDS, 8 - j_halt: - cmp DWORD PTR retval,0 - je j_halt_no_value - mov ebp,retval - mov [ebp],eax - j_halt_no_value: - j_halt_sm: - mov eax,12345678h - jmp [jit_return] - -;good -OP_BOUNDS: -;nop; - putval j_bounds+1 - GO_ON j_bounds, OP_SYSREQ_C, 8 - j_bounds: - mov ebp,12345678h - call [jit_bounds] - -;good -OP_SYSREQ_C: -;nop; - putval j_sysreq_c+1 - GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 - j_sysreq_c: - mov eax,12345678h ; get function number - j_sysreq: - call [jit_sysreq] - -OP_SYSREQ_PRI: -;nop; - GO_ON j_sysreq, OP_SYSREQ_PRI - -OP_FILE: ;opcode is simply ignored -;nop; - mov eax,[ebx+4] ;get size - mov [ebx],edi - lea ebx,[ebx+eax+8] ;move on to next opcode - cmp ebx,DWORD ptr [end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ;go on with the next opcode - -OP_LINE: ;ignored -;nop; - mov [ebx],edi ; no line number support: ignore opcode - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - -OP_SYMBOL: ;ignored - mov [ebx],edi - mov eax,[ebx+4] ; get size - lea ebx,[ebx+eax+8] ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -OP_SRANGE: ;ignored - mov [ebx],edi ; store relocated address - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -;not tested -OP_JUMP_PRI: - GO_ON j_jump_pri, OP_SWITCH - - j_jump_pri: ; MP: This opcode makes sense only in con- - jmp [eax] ; junction with a possibility to get the - ; address of a code location... - - -;good -OP_SWITCH: - lea eax,[edi+6] ; The case table will be copied directly - neg eax ; after the run-time call to [jit_switch]. - and eax,3 ; We should align this table on a DWORD - mov ecx,eax ; boundary. - mov al,90h ; 90h = opcode of x86 NOP instruction - rep stosb ; Write the right number of NOPs. - mov [ebx],edi ; store address of SWITCH for relocation step - mov esi,OFFSET j_switch - mov ecx,6 - rep movsb ; copy the call instruction - mov esi,[ebx+4] ; get address of CASETBL instruction - add ebx,8 ; set instruction pointer to next opcode - add esi,4 ; point esi to first entry: (count, default adr) - mov ecx,[esi] ; get number of cases (excluding default) - inc ecx - mov ebp,[reloc_num] - j_case_loop: - mov eax,[esi] ; get case value - stosd ; write it - mov eax,[esi+4] ; get destination address -IF FORCERELOCATABLE EQ 0 - or eax,80000000h ; add flag for "absolute address" -ENDIF - mov [edx+ebp],eax ; write dest. adr. into relocation table - mov eax,[esi+4] ; get destination address (again) - add esi,8 ; set ESI to next case - mov [edx+ebp+4],edi ; write adr. to patch into relocation table - add ebp,8 ; promote relocation pointer - stosd ; write dest. adr. - dec ecx - jnz j_case_loop - mov DWORD ptr [reloc_num],ebp ; write back updated reloc_num - - jmp [ebx] ; GO_ON to next op-code - - j_switch: - call [jit_switch] - -;good -OP_CASETBL: ; compiles to nothing, SWITCH does all the work - mov eax,[ebx+4] ; get count of cases - lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer - jmp [ebx] ; GO_ON with next op-code - - -OP_SWAP_PRI: ; TR - GO_ON j_swap_pri, OP_SWAP_ALT - - j_swap_pri: - _POP ebp - _PUSH eax - mov eax,ebp - - -OP_SWAP_ALT: ; TR - GO_ON j_swap_alt, OP_PUSHADDR - - j_swap_alt: - _POP ebp - _PUSH edx - mov edx,ebp - - -OP_PUSHADDR: ; TR - putval j_pushaddr+1 - GO_ON j_pushaddr, OP_NOP, 8 - - j_pushaddr: - mov ebp,12345678h ;get address (offset from frame) - add ebp,frm - _PUSH ebp - - -OP_NOP: ; TR - GO_ON j_nop, OP_SYSREQ_D - j_nop: ; code alignment is ignored by the JIT - - -OP_SYSREQ_D: -;nop; - putval j_sysreq_d+1 - GO_ON j_sysreq_d, OP_SYMTAG, 8 - j_sysreq_d: - mov ebx,12345678h ; get function address - call [jit_sysreq_d] - - -OP_SYMTAG: ;ignored (TR) - mov [ebx],edi ; store relocated address - add ebx,8 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -OP_BREAK: -IF DEBUGSUPPORT EQ 0 - mov [ebx],edi ; no debugger number support: ignore opcode - add ebx,4 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode -ELSE - GO_ON j_break, OP_INVALID - j_break: - mov ebp,amx - cmp DWORD [ebp+_debug], 0 - je $+4 ; jump around the "call" statement - call [jit_break] -ENDIF - -OP_INVALID: ; break from the compiler with an error code - mov eax,AMX_ERR_INVINSTR - pop esi - pop edi - pop ecx - pop ebp - ret - - -asm_runJIT_ ENDP - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -;cell asm_exec_jit( AMX *amx, cell *retval, char *data ) ; -; eax edx ebx ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -amx_exec_jit_ PROC - push edi - push esi - push ebp - - sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - push DWORD ptr [eax+_codeseg] ; store pointer to code segment - push eax ; store pointer to AMX - push edx ; store address of retval - push DWORD ptr [eax+_stp] ; store STP - push DWORD ptr [eax+_hea] ; store HEA - push DWORD ptr [eax+_frm] ; store FRM - - stk equ [esi+32] ; define some aliases to registers that will - alt equ [esi+28] ; be stored on the stack when the code is - pri equ [esi+24] ; actually beeing executed - code equ [esi+20] - amx equ [esi+16] - retval equ [esi+12] - stp equ [esi+8] - hea equ [esi+4] - frm equ [esi] ; FRM is NOT stored in ebp, rather FRM+DAT is being - ; held in ebx. - - mov edi,ebx ; get pointer to data segment - mov edx,[eax+_alt] ; get ALT - mov ecx,[eax+_cip] ; get CIP (N.B. different from ASM interpreter) - mov esi,[eax+_stk] ; get STK (N.B. different from ASM interpreter) - mov ebx,[eax+_frm] ; get FRM - mov eax,[eax+_pri] ; get PRI - add ebx,edi ; relocate frame - - add esi,edi ; ESP will contain DAT+STK - xchg esp,esi ; switch to AMX stack - - add stp,edi ; make STP absolute address for run-time checks - - _POP ebp ; AMX pseudo-return address, ignored - ; Call compiled code via CALL NEAR
- call ecx - -return_to_caller: - cmp DWORD PTR retval,0 - je return_to_caller_no_value - mov ebp,retval - mov [ebp],eax ; provide return value - - return_to_caller_no_value: - mov eax,AMX_ERR_NONE - jmp _return - -_return_popstack: - add esp,4 ; Correct ESP, because we just come from a - ; runtime error checking routine. -_return: - ; store machine state - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... - mov ecx,hea ; ... HEA, ... - mov [ebp+_hea],ecx - mov ecx,ebx ; ... and FRM - sub ecx,edi ; (correct FRM) - mov [ebp+_frm],ecx - mov [ebp+_pri],eax ; also store PRI, ... - mov [ebp+_alt],edx ; ... and ALT - - ; return - sub stp,edi ; make STP relative to DAT again - xchg esp,esi ; switch back to caller's stack - - add esp,4*9 ; remove temporary data - - pop ebp - pop esi - pop edi - - ret - - -err_stack: - mov eax,AMX_ERR_STACKERR - jmp _return_popstack - -err_stacklow: - mov eax,AMX_ERR_STACKLOW - jmp _return_popstack - -_CHKMARGIN_STACK: ; some run-time check routines - cmp esp,stp - lea ebp,[esp-STACKRESERVE] - jg err_stacklow - sub ebp,edi - cmp hea,ebp - jg err_stack - ret - -err_heaplow: - mov eax,AMX_ERR_HEAPLOW - jmp _return_popstack - -_CHKMARGIN_HEAP: - cmp esp,stp - jg err_stacklow - mov ebp,amx - mov ebp,[ebp+_hlw] - cmp DWORD ptr hea,ebp - jl err_heaplow - ret - -err_memaccess: - mov eax,AMX_ERR_MEMACCESS - jmp _return_popstack - -_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx - cmp eax,stp - jae err_memaccess - cmp eax,hea - jb veax1 - lea ebp,[eax+edi] - cmp ebp,esp - jb err_memaccess - veax1: - ret - -_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx - cmp edx,stp - jae err_memaccess - cmp edx,hea - jb vedx1 - lea ebp,[edx+edi] - cmp ebp,esp - jb err_memaccess - vedx1: - ret - -JIT_OP_SDIV: - mov ebp,edx - xor edx,eax ; Check signs of the operands. - cdq - js sdiv_fiddle ; If the signs of the operands are different - ; we'll have to fiddle around to achieve - ; proper rounding towards minus infinity. - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp ; default behavior is right in the other cases - ret - - sdiv_fiddle: - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp - or edx,edx - jz sdiv_goon ; If there's no remainder the result is correct - add edx,ebp ; else fix the result values. - dec eax ; Amazing, how simple this is... - sdiv_goon: - ret - - ALIGN 4 - -JIT_OP_RETN: - _POP ebx ; pop frame - _POP ecx ; get return address - - mov frm,ebx - _POP ebp - - add ebx,edi - add esp,ebp ; remove data from stack - - jmp ecx - - -JIT_OP_MOVS: ;length of block to copy is already in ECX - push edi - push esi - lea esi,[edi+eax] - lea edi,[edi+edx] - - push ecx ; I hope the blocks to copy are properly - shr ecx,2 ; aligned, so I don't do anything about that. - rep movsd - pop ecx - and ecx,3 - rep movsb - - pop esi - pop edi - ret - -JIT_OP_CMPS: ;length of block to compare is already in ECX - push edi - push esi - lea esi,[edi+edx] - lea edi,[edi+eax] - - xor eax,eax ; This is surely not the fastest way to do this - repe cmpsb ; but the most simple one. - je cmps1 - sbb eax,eax - sbb eax,0ffffffffh - cmps1: - pop esi - pop edi - ret - - -JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX - push edi - lea edi,[edi+edx] - - shr ecx,2 ;length in 32-bit cells - rep stosd ;the value to use is already in EAX - - pop edi - ret - -JIT_OP_BOUNDS: - cmp eax,0 - jl err_bounds - cmp eax,ebp - jg err_bounds - ret -err_bounds: - mov eax,AMX_ERR_BOUNDS - jmp _return_popstack - -_CHKDIVIDEZERO: - or ebp,ebp ; check for divide by zero - jz err_divide - ret -err_divide: - mov eax,AMX_ERR_DIVIDE - jmp _return_popstack - -JIT_OP_SYSREQ: - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - mov edx,eax ; 2nd param: function number - mov eax,ebp ; 1st param: amx - lea ebx,pri ; 3rd param: addr. of retval - lea ecx,[esp+4] ; 4th param: parameter array - xchg esp,esi ; switch to caller stack - call [ebp+_callback] - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov eax,pri ; get retval into eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_SYSREQ_D: ; (TR) - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov eax,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],eax ; eax & ecx are invalid by now - - mov eax,ebp ; 1st param: amx - lea edx,[esp+4] ; 2nd param: parameter array - xchg esp,esi ; switch to caller stack - call ebx ; direct call - xchg esp,esi ; switch back to AMX stack - mov ebp,amx ; get amx into EBP - cmp [ebp+_error],AMX_ERR_NONE - jne _return_popstack; return error code, if any - - ; return value is in eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_BREAK: -IF DEBUGSUPPORT EQ 0 -ELSE - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - - mov [ebp+_pri],eax ; store values in AMX structure: PRI, - mov [ebp+_alt],edx ; ALT, - mov [ebp+_stk],ecx ; STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - ;??? storing CIP is not very useful, because the code changed (during JIT compile) - - mov eax,ebp ; 1st param: amx - xchg esp,esi ; switch to caller stack - call [ebp+_debug] - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov ebp,amx ; get amx into EBP - mov eax,[ebp+_pri] ; restore values - mov edx,[ebp+_alt] ; ALT, - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame -ENDIF - ret - - -JIT_OP_SWITCH: - pop ebp ; pop return address = table address - mov ecx,[ebp] ; ECX = number of records - lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case - jecxz op_switch_jump ; special case: no cases at all - op_switch_loop: - cmp eax,[ebp-8] ; PRI == case label? - je op_switch_jump ; found, jump - sub ebp,8 ; position to preceding case - loop op_switch_loop ; check next case, or fall through - op_switch_jump: -IF FORCERELOCATABLE EQ 0 - jmp [ebp-4] ; jump to the case instructions -ELSE - add ebp,[ebp-4] ; add offset to make absolute adddress - jmp ebp -ENDIF - -amx_exec_jit_ ENDP - -; -; The caller of asm_runJIT() can determine the maximum size of the compiled -; code by multiplying the result of this function by the number of opcodes in -; Pawn module. -; -; unsigned long getMaxCodeSize_(); -; -getMaxCodeSize_ PROC - - mov eax,MAXCODESIZE - ret - -getMaxCodeSize_ ENDP - - -IFNDEF @Version - ; Microsoft MASM 6.x gives the error message "Register assumed to - ; ERROR" when I put the code lables in the data segment. I cannot find - ; a proper way around it. -.DATA -ENDIF - ALIGN 4 ; This is essential to avoid misalignment stalls. - -end_code DD 0 ; pointer to the end of the source code - -compiled_code DD 0 ; pointer to compiled code (including preamble) - -amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) - -reloc_num DD 0 ; counts the addresses in the relocation table (jumps) - -lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh - -; -; A list of the "run-time-library" functions that are called via indirect calls. -; So these calls don't have to be relocated. This gives also the possibility to -; replace some of these with shorter/faster non-debug or non-checking versions, -; without changing the compiled code. Instead this table could be changed... -; -verify_adr_eax DD _VERIFYADDRESS_eax -verify_adr_edx DD _VERIFYADDRESS_edx -chk_marginstack DD _CHKMARGIN_STACK -chk_marginheap DD _CHKMARGIN_HEAP -chk_dividezero DD _CHKDIVIDEZERO -jit_return DD _return -jit_retn DD JIT_OP_RETN -jit_sdiv DD JIT_OP_SDIV -jit_movs DD JIT_OP_MOVS -jit_cmps DD JIT_OP_CMPS -jit_fill DD JIT_OP_FILL -jit_bounds DD JIT_OP_BOUNDS -jit_sysreq DD JIT_OP_SYSREQ -jit_sysreq_d DD JIT_OP_SYSREQ_D -jit_break DD JIT_OP_BREAK -jit_switch DD JIT_OP_SWITCH - -; -; The table for the browser/relocator function. -; - PUBLIC _amx_opcodelist_jit -_amx_opcodelist_jit: - DD OP_INVALID - DD OP_LOAD_PRI - DD OP_LOAD_ALT - DD OP_LOAD_S_PRI - DD OP_LOAD_S_ALT - DD OP_LREF_PRI - DD OP_LREF_ALT - DD OP_LREF_S_PRI - DD OP_LREF_S_ALT - DD OP_LOAD_I - DD OP_LODB_I - DD OP_CONST_PRI - DD OP_CONST_ALT - DD OP_ADDR_PRI - DD OP_ADDR_ALT - DD OP_STOR_PRI - DD OP_STOR_ALT - DD OP_STOR_S_PRI - DD OP_STOR_S_ALT - DD OP_SREF_PRI - DD OP_SREF_ALT - DD OP_SREF_S_PRI - DD OP_SREF_S_ALT - DD OP_STOR_I - DD OP_STRB_I - DD OP_LIDX - DD OP_LIDX_B - DD OP_IDXADDR - DD OP_IDXADDR_B - DD OP_ALIGN_PRI - DD OP_ALIGN_ALT - DD OP_LCTRL - DD OP_SCTRL - DD OP_MOVE_PRI - DD OP_MOVE_ALT - DD OP_XCHG - DD OP_PUSH_PRI - DD OP_PUSH_ALT - DD OP_PICK - DD OP_PUSH_C - DD OP_PUSH - DD OP_PUSH_S - DD OP_POP_PRI - DD OP_POP_ALT - DD OP_STACK - DD OP_HEAP - DD OP_PROC - DD OP_RET - DD OP_RETN - DD OP_CALL - DD OP_CALL_I - DD OP_JUMP - DD OP_JREL - DD OP_JZER - DD OP_JNZ - DD OP_JEQ - DD OP_JNEQ - DD OP_JLESS - DD OP_JLEQ - DD OP_JGRTR - DD OP_JGEQ - DD OP_JSLESS - DD OP_JSLEQ - DD OP_JSGRTR - DD OP_JSGEQ - DD OP_SHL - DD OP_SHR - DD OP_SSHR - DD OP_SHL_C_PRI - DD OP_SHL_C_ALT - DD OP_SHR_C_PRI - DD OP_SHR_C_ALT - DD OP_SMUL - DD OP_SDIV - DD OP_SDIV_ALT - DD OP_UMUL - DD OP_UDIV - DD OP_UDIV_ALT - DD OP_ADD - DD OP_SUB - DD OP_SUB_ALT - DD OP_AND - DD OP_OR - DD OP_XOR - DD OP_NOT - DD OP_NEG - DD OP_INVERT - DD OP_ADD_C - DD OP_SMUL_C - DD OP_ZERO_PRI - DD OP_ZERO_ALT - DD OP_ZERO - DD OP_ZERO_S - DD OP_SIGN_PRI - DD OP_SIGN_ALT - DD OP_EQ - DD OP_NEQ - DD OP_LESS - DD OP_LEQ - DD OP_GRTR - DD OP_GEQ - DD OP_SLESS - DD OP_SLEQ - DD OP_SGRTR - DD OP_SGEQ - DD OP_EQ_C_PRI - DD OP_EQ_C_ALT - DD OP_INC_PRI - DD OP_INC_ALT - DD OP_INC - DD OP_INC_S - DD OP_INC_I - DD OP_DEC_PRI - DD OP_DEC_ALT - DD OP_DEC - DD OP_DEC_S - DD OP_DEC_I - DD OP_MOVS - DD OP_CMPS - DD OP_FILL - DD OP_HALT - DD OP_BOUNDS - DD OP_SYSREQ_PRI - DD OP_SYSREQ_C - DD OP_FILE - DD OP_LINE - DD OP_SYMBOL - DD OP_SRANGE - DD OP_JUMP_PRI - DD OP_SWITCH - DD OP_CASETBL - DD OP_SWAP_PRI ; TR - DD OP_SWAP_ALT ; TR - DD OP_PUSHADDR ; TR - DD OP_NOP ; TR - DD OP_SYSREQ_D ; TR - DD OP_SYMTAG ; TR - DD OP_BREAK ; TR - -END +; ASMJITR.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" +; scripting language +; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES + +; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" +; (27 disks, no output, DOS4/GW under Win95) with this implementation of the +; JIT compiler. + +; NOTE 1: +; There is only one pass implemented in this version. This means there is no +; way of knowing the size of the compiled code before it has actually been com- +; piled. So the only chance the caller has, is to count the number of opcodes +; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to +; obtain a size value big enough to hold the entire code (and data, including +; the stack and heap, after adding their sizes). Afterwards it can realloc() +; this memory block to the actually needed smaller size. + +; NOTE 2: +; The compiler destroys the opcode addresses of the given source by storing the +; respective compiled code's addresses there for the final address relocation +; step. + +; NOTE 3: +; During execution of the compiled code with amx_exec_jit() the x86 processor's +; stack is switched into the data section of the abstract machine. This means +; that there should always be enough memory left between HEA and STK to provide +; stack space for occurring interrupts! (see the STACKRESERVE variable) + +; NOTE 4: +; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I +; instructions, I have to tell that they don't work as expected in a JIT +; compiled program, because there is no easy way of transforming AMX code +; addresses and JIT translated ones. This might be fixed in a future version. + +; NX ("No eXecute") and XD (eXecution Denied) bits +; (by Thiadmer Riemersma) +; +; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit +; processors) and Intel came with the same design, but calling it differently. +; The purpose is to make "buffer overrun" security holes impossible, or at least +; very, very difficult, by marking the stack and the heap as memory regions +; such that an attempt to execute processor instructions will cause a processor +; exception (of course, a buffer overrun that is not explictly handled will then +; crash the application --instead of executing the rogue code). +; +; For JIT compilers, this has the impact that you are not allowed to execute the +; code that the JIT has generated. To do that, you must adjust the attributes +; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to +; allocate a memory block with the appropriate fags; on Linux (with a recent +; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the +; function VirtualProtect() to change the page attributes of an existing memory +; block, but there are caveats in its use: if the block spans multiple pages, +; these pages must be consecutive, and if there are blocks of memory in a page +; unrelated to the JIT, their page attributes will change too. +; +; The JIT compiler itself requires only read-write access (this is the default +; for a memory block that you allocate). The execution of the JIT-compiled code +; requires full access to the memory block: read, write and execute. It needs +; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the +; first lookup (this is an optimization, look up the address of the native +; function only once). For processors that do not support the NX/XD bit, +; execution of code is implicitly supported if read access is supported. +; +; During compilation, the JIT compiler requires write-access to its own code +; segment: the JIT-compiler patches P-code parameters into its own code segment +; during compilation. This is handled in the support code for amx_InitJIT. +; +; +; CALLING CONVENTIONS +; (by Thiadmer Riemersma) +; +; This version is the JIT that uses the "register calling convention" (which is +; particular for Watcom C/C++) both for the calling convention for the _asm_runJIT +; routine itself as for the callback functions. See the other files amxJit*.asm +; for implementations with other calling conventions. + +; Revision History +; ---------------- +; 28 july 2005 +; Bug fix for the switch table, in the situation where only the default +; case was present. Bug found by Bailopan. +; 17 february 2005 by Thiadmer Riemersma (TR) +; Addition of the BREAK opcode, removal of the older debugging opcode +; table. There should now be some debug support (if enabled during the +; build of the JIT compiler), but not enough to run a debugger: the JIT +; compiler does not keep a list that relates the code addresses of the +; P-code versus the native code. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; +; SYMTAG is a no-op) +; 14 October 2002 by Thiadmer Riemersma (TR) +; Corrected the amx_s structure. The _hlw field was missing, which caused +; errors for arguments to native functions that were passed by reference. +; 2002/08/05 TR +; * store the status of the abstract machine in the AMX structure upon +; return, so that the machine can be restarted (OP_SLEEP) +; * added OP_NOP (for alignment, it is ignored by the JIT) +; * make sure the JIT does not crash when we NULL is passed for the +; return value +; 2000/03/03 MP +; * added SRANGE as a no-op, so debugging info doesn't upset the JIT +; compiler anymore +; * added note about LCTRL, SCTRL and CALL.I +; * removed some rather unnecessary pops in the epilog of amx_exec_asm +; * changed the template for CALL into a DB byte sequence (tasm 4.1 +; didn't like the immediate value) +; 1999/12/07 MP +; * fixed crash caused by JIT compiler not saving registers +; 1999/08/06 MP - design change: closer to the "iron" with native stack +; * The JIT compiler now generates relocatable code for case tables by +; setting FORCERELOCATABLE = 1. +; * removed all debug hook code +; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ +; pushes/pops are possible +; * removed the run-time functions for the CALL, CALL_I and RET op-codes, +; they are now inline +; * All these changes gained around 80% performance increase for the +; hanoi bench. +; 1999/08/05 MP +; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address +; was stored for the LINE byte code (i.e. SWITCH would jump to totally +; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and +; OP_SCTRL (for the no-op case). +; 1999/08/04 MP +; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. +; arrays have not been tested.) +; * hacked relocation code to support absoulute addresses for CASETBL +; (This assumes that no generated address will be greater than +; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) +; * The run-time function for SWITCH uses a (hopefully) faster algorithm +; to compute the destination address: It searches backwards now. +; 1999/07/08 MP - initial revision + +.386 +.MODEL FLAT + +IFDEF @Version ; for Microsoft MASM 6.x + OPTION OLDSTRUCTS + OPTION M510 +ENDIF + +; +; Support for the BREAK opcode (callback to the debugger): 0 = no, all other +; values = yes. Beware that the compiled code runs slower when this is enabled, +; and that debug support is still fairly minimal. +; +DEBUGSUPPORT = 0 + +; +; If this is set to 1 the JIT generates relocatable code for case tables, too. +; If set to 0, a faster variant for switch (using absolute addresses) is +; generated. I consider setting it to 0 a bad idea. +; +FORCERELOCATABLE = 1 + +; +; Determines how much memory should be reserved for occurring interrupts. +; If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes +; for interrupts that occur in real mode and are promoted to protected mode. +; This value _MUST_ be greater than 64 (for AMX needs) and should be at least +; 128 (to serve interrupts). +; +STACKRESERVE = 256 + +; +; This variable controls the generation of memory range checks at run-time. +; You should set this to 0, only when you are sure that there are no range +; violations in your Pawn programs and you really need those 5% speed gain. +; +DORUNTIMECHECKS = 1 + +IFNDEF JIT + JIT = 1 +ENDIF +INCLUDE amxdef.asm + +; +; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +; +_PUSH MACRO v + push DWORD ptr v + ENDM + +; +; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +; +_POP MACRO v + pop DWORD ptr v + ENDM + +; +; For determining the biggest native code section generated for ONE Pawn +; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) +; +MAXCODESIZE = 0 + +; +; This is the work horse of the whole JIT: It actually copies the code. +; +GO_ON MACRO from, to, opsize ;opsize may be 4, 8 or 12 (default=4) + mov esi,OFFSET from ;get source address of JIT code + CODESIZE = to-from + mov ecx,CODESIZE ;get number of bytes to copy + ;all the following could (should?) be in a sub-routine... + ;determining the maximum size of a single code snippet + IF MAXCODESIZE LT CODESIZE + MAXCODESIZE = CODESIZE + ENDIF + mov [ebx],edi ;store address for jump-correction + IFB ;add operand size to the source ptr + add ebx,4 + ELSE + add ebx,opsize + ENDIF + rep movsb + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ;go on with the next opcode + ENDM + +; +; Modify the argument of an x86 instruction with the Pawn opcode's parameter +; before copying the code. +; +putval MACRO where + mov eax,[ebx+4] + mov DWORD ptr [where],eax + ENDM + +; +; Add an entry to the table of addresses which have to be relocated after the +; code compilation is done. +; +RELOC MACRO adr, dest + mov ebp,[reloc_num] + IFB + mov eax,[ebx+4] + ELSE + lea eax,[dest] + ENDIF + mov [edx+ebp],eax ; write absolute destination + lea eax,[edi+adr] + mov [edx+ebp+4],eax ; write address of jump operand + add DWORD ptr [reloc_num],8 + ENDM + + +.CODE + + + PUBLIC asm_runJIT_ + PUBLIC amx_exec_jit_ + PUBLIC getMaxCodeSize_ + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; void asm_runJIT( AMX_HEADER *amxh, JumpAddressArray *jumps, void *dest ) ; +; eax edx ebx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; asm_runJIT() assumes that the code of this module is allready browsed and +; relocated for the JIT compiler. It also assumes that both the jumps array and +; the dest memory block are large enough to hold all the data it has to write +; to them, as well as that the prefix (header) has already been copied to dest. + +asm_runJIT_ PROC + push ebp + push ecx + push edi + push esi + + mov [amxhead],eax ; save pointer to AMX_HEADER struct + mov ecx,[eax+_cod] ; get offset of start of code + mov eax,[eax+_dat] ; offset of start of data = end of code + mov edi,ecx + add ecx,[amxhead] ; compute the real pointer + add eax,[amxhead] ; dito + add edi,ebx ; get write pointer into EDI + mov [compiled_code],ebx + mov [end_code],eax ; Store end-of-code address, so JIT + ; compiler knows when to stop. + mov DWORD ptr [reloc_num],0 ; init the index into the jumps array + + mov ebx,ecx + jmp DWORD ptr [ecx] ; start compiling + + ; The compiler will jump back here when code generation is complete. + +code_gen_done: ; Now copy the data section. + mov ebp,[amxhead] ; get source AMX_HEADER start address + add edi,3 ; DAT follows directly after COD + and edi,0fffffffch ; align it on a DWORD boundary + push edi ; save data start pointer + mov esi,[end_code] ; get start of data segment + mov ecx,[ebp]._h_hea + sub ecx,[ebp]._dat ; compute length of array to copy + rep movsb ; copy the data + + ; Now adjust the register values in the compiled AMX_HEADER. + ; COD stays the same since the size of AMX_HEADER doesn't change in + ; compiled mode. + mov ebx,[compiled_code] ; get compiled AMX's header address + pop esi ; recall data start pointer + sub esi,ebx ; DAT = size of code + size of prefix + mov [ebx]._dat,esi ; write corrected DAT register + + ;HEA and STP are already relative to DAT, so we don't need to fix them. + + ; Now the calls/jumps in the compiled code have to be relocated. + sub ecx,ecx ; reset offset into relocation table + cmp ecx,[reloc_num] + jae reloc_code_done ; if there's nothing to fix, skip this part + reloc_code_loop: + mov eax,[edx+ecx] ; get destination address + mov edi,[edx+ecx+4] ; determine where to write the relocated value + add ecx,8 ; set pointer to next entry in relocation table + add edi,4 ; base address from where the offset is taken +IF FORCERELOCATABLE EQ 0 + ;MP: hack to suport absolute addresses for the CASETBL instruction + test eax,80000000h ; check whether it is an absolute address + pushf + and eax,7fffffffh ; clear the flag bit for absolute addresses + popf + mov eax,[eax] ; translate into compiled absolute address + jne write_reloc ; leave out the subtraction if absolute +ELSE + mov eax,[eax] ; translate into compiled absolute address +ENDIF + sub eax,edi ; make a relative offset + write_reloc: + mov [edi-4],eax ; write the relocated address + cmp ecx,[reloc_num] + jb reloc_code_loop + +reloc_code_done: + ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) + add ebp,[ebp]._cod ; make all addresses relative to COD, not base + mov eax,[ebx]._h_cip + add eax,ebp ; get absolute source CIP + mov eax,[eax] ; translate CIP to compiled address + sub eax,ebx ; make it relative to base + sub eax,[ebx]._cod ; and now relative to COD + mov [ebx]._h_cip,eax; store relocated CIP + mov edi,[ebx]._publics + sub esi,esi + mov ecx,[ebx]._natives + sub ecx,edi ; ECX = _natives - _publics = public table size + mov si,[ebx]._defsize + or ecx,ecx + jz reloc_done ; If there are no publics, we are done. + reloc_publics_loop: + mov eax,[ebx+edi] ; get public function offset + add eax,ebp ; make it a source address + mov eax,[eax] ; translate to compiled address + sub eax,ebx ; make it an offset relative to base + sub eax,[ebx]._cod ; and now relative to COD + mov [ebx+edi],eax ; write corrected address back + add edi,esi ; step to next public function entry + sub ecx,esi + ja reloc_publics_loop + +reloc_done: + mov eax,0 + pop esi + pop edi + pop ecx + pop ebp + ret + +OP_LOAD_PRI: +;nop; + putval j_load_pri+2 + GO_ON j_load_pri, OP_LOAD_ALT, 8 + + j_load_pri: + mov eax,[edi+12345678h] + +OP_LOAD_ALT: +;nop; + putval j_load_alt+2 + GO_ON j_load_alt, OP_LOAD_S_PRI, 8 + + j_load_alt: + mov edx,[edi+12345678h] + +;good +OP_LOAD_S_PRI: +;nop; + putval j_load_s_pri+2 + GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 + + j_load_s_pri: + mov eax,[ebx+12345678h] + +;good +OP_LOAD_S_ALT: +;nop; + putval j_load_s_alt+2 + GO_ON j_load_s_alt, OP_LOAD_I, 8 + + j_load_s_alt: + mov edx,[ebx+12345678h] + +OP_LOAD_I: +;nop; + GO_ON j_load_i, OP_LODB_I + + j_load_i: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +OP_LODB_I: +;nop; + mov eax,[ebx+4] + mov eax,DWORD ptr [(lodb_and-4)+eax*4] + mov DWORD ptr [j_lodb_i_sm+1],eax ;modify AND instruction + GO_ON j_lodb_i, OP_LREF_PRI, 8 + + j_lodb_i: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_eax] +ENDIF + mov eax,[edi+eax] ;subject to misalignment stalls + j_lodb_i_sm: + and eax,12345678h + +OP_LREF_PRI: +;nop; + putval j_lref_pri+2 + GO_ON j_lref_pri, OP_LREF_ALT, 8 + + j_lref_pri: + mov eax,[edi+12345678h] + mov eax,[edi+eax] + +OP_LREF_ALT: +;nop; + putval j_lref_alt+2 + GO_ON j_lref_alt, OP_LREF_S_PRI, 8 + + j_lref_alt: + mov edx,[edi+12345678h] + mov edx,[edi+edx] + +OP_LREF_S_PRI: +;nop; + putval j_lref_s_pri+2 + GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 + + j_lref_s_pri: + mov eax,[ebx+12345678h] + mov eax,[edi+eax] + +OP_LREF_S_ALT: +;nop; + putval j_lref_s_alt+2 + GO_ON j_lref_s_alt, OP_CONST_PRI, 8 + + j_lref_s_alt: + mov edx,[ebx+12345678h] + mov edx,[edi+edx] + +;good +OP_CONST_PRI: +;nop; + putval j_const_pri+1 + GO_ON j_const_pri, OP_CONST_ALT, 8 + + j_const_pri: + mov eax,12345678h + +;good +OP_CONST_ALT: +;nop; + putval j_const_alt+1 + GO_ON j_const_alt, OP_ADDR_PRI, 8 + + j_const_alt: + mov edx,12345678h + +;good +OP_ADDR_PRI: +;nop; + putval j_addr_pri+1 + GO_ON j_addr_pri, OP_ADDR_ALT, 8 + + j_addr_pri: + mov eax,12345678h + add eax,frm + +;good +OP_ADDR_ALT: +;nop; + putval j_addr_alt+1 + GO_ON j_addr_alt, OP_STOR_PRI, 8 + + j_addr_alt: + mov edx,12345678h + add edx,frm + +OP_STOR_PRI: +;nop; + putval j_stor_pri+2 + GO_ON j_stor_pri, OP_STOR_ALT, 8 + + j_stor_pri: + mov [edi+12345678h],eax + +OP_STOR_ALT: +;nop; + putval j_stor_alt+2 + GO_ON j_stor_alt, OP_STOR_S_PRI, 8 + + j_stor_alt: + mov [edi+12345678h],edx + +;good +OP_STOR_S_PRI: +;nop; + putval j_stor_s_pri+2 + GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 + + j_stor_s_pri: + mov [ebx+12345678h],eax + +;good +OP_STOR_S_ALT: +;nop; + putval j_stor_s_alt+2 + GO_ON j_stor_s_alt, OP_STOR_I, 8 + + j_stor_s_alt: + mov [ebx+12345678h],edx + +;good +OP_STOR_I: +;nop; + GO_ON j_stor_i, OP_STRB_I + + j_stor_i: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_edx] +ENDIF + mov [edi+edx],eax + +OP_STRB_I: +;nop; + mov eax,[ebx+4] + cmp eax,1 + jne strb_not1byte + GO_ON j_strb_i_1b, strb_not1byte, 8 + j_strb_i_1b: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_edx] +ENDIF + mov [edi+edx],al + + strb_not1byte: + cmp eax,4 + je strb_4byte + GO_ON j_strb_i_2b, strb_4byte, 8 + j_strb_i_2b: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_edx] +ENDIF + mov [edi+edx],ax + + strb_4byte: + GO_ON j_strb_i_4b, OP_SREF_PRI, 8 + j_strb_i_4b: +IF DORUNTIMECHECKS NE 0 + call [verify_adr_edx] +ENDIF + mov [edi+edx],eax + +OP_SREF_PRI: +;nop; + putval j_sref_pri+2 + GO_ON j_sref_pri, OP_SREF_ALT, 8 + + j_sref_pri: + mov ebp,[edi+12345678h] + mov [edi+ebp],eax + +OP_SREF_ALT: +;nop; + putval j_sref_alt+2 + GO_ON j_sref_alt, OP_SREF_S_PRI, 8 + + j_sref_alt: + mov ebp,[edi+12345678h] + mov [edi+ebp],edx + +OP_SREF_S_PRI: +;nop; + putval j_sref_s_pri+2 + GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 + + j_sref_s_pri: + mov ebp,[ebx+12345678h] + mov [edi+ebp],eax + +OP_SREF_S_ALT: +;nop; + putval j_sref_s_alt+2 + GO_ON j_sref_s_alt, OP_LIDX, 8 + + j_sref_s_alt: + mov ebp,[ebx+12345678h] + mov [edi+ebp],edx + +;good +OP_LIDX: +;nop; + GO_ON j_lidx, OP_LIDX_B + + j_lidx: + lea eax,[edx+4*eax] +IF DORUNTIMECHECKS NE 0 + call [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +OP_LIDX_B: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_lidx_b+2],al + GO_ON j_lidx_b, OP_IDXADDR, 8 + + j_lidx_b: + shl eax,12h + add eax,edx +IF DORUNTIMECHECKS NE 0 + call [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +;good +OP_IDXADDR: +;nop; + GO_ON j_idxaddr, OP_IDXADDR_B + + j_idxaddr: + lea eax,[edx+4*eax] + +OP_IDXADDR_B: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_idxaddr_b+2],al + GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 + + j_idxaddr_b: + shl eax,12h + add eax,edx + +OP_ALIGN_PRI: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov DWORD ptr [j_align_pri+1],eax + GO_ON j_align_pri, OP_ALIGN_ALT, 8 + + j_align_pri: + xor eax,12345678h + +OP_ALIGN_ALT: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov DWORD ptr [j_align_alt+1],eax + GO_ON j_align_alt, OP_LCTRL, 8 + + j_align_alt: + xor edx,12345678h + +OP_LCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,0 + jne lctrl_1 + GO_ON j_lctrl_0, lctrl_1, 8 + j_lctrl_0: + mov eax,code ; 1=COD + lctrl_1: + cmp eax,1 + jne lctrl_2 + GO_ON j_lctrl_1, lctrl_2, 8 + j_lctrl_1: + mov eax,edi ; 1=DAT + lctrl_2: + cmp eax,2 + jne lctrl_3 + GO_ON j_lctrl_2, lctrl_3, 8 + j_lctrl_2: + mov eax,hea ; 2=HEA + lctrl_3: + cmp eax,3 + jne lctrl_4 + GO_ON j_lctrl_3, lctrl_4, 8 + j_lctrl_3: + mov ebp,amx + mov eax,[ebp+_stp] + lctrl_4: + cmp eax,4 + jne lctrl_5 + GO_ON j_lctrl_4, lctrl_5, 8 + j_lctrl_4: + mov eax,esp ; 4=STK + sub eax,edi + lctrl_5: + cmp eax,5 + jne lctrl_6 + GO_ON j_lctrl_5, lctrl_6, 8 + j_lctrl_5: + mov eax,frm ; 5=FRM + lctrl_6: + mov DWORD ptr [j_lctrl_6+1],edi + GO_ON j_lctrl_6, OP_SCTRL, 8 + j_lctrl_6: + mov eax,12345678h ; 6=CIP + + +OP_SCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,2 + jne sctrl_4 + GO_ON j_sctrl_2, sctrl_4, 8 + j_sctrl_2: + mov hea,eax ; 2=HEA + sctrl_4: + cmp eax,4 + jne sctrl_5 + GO_ON j_sctrl_4, sctrl_5, 8 + j_sctrl_4: + ;mov esp,eax ; 4=STK + ;add esp,edi ; relocate stack + lea esp,[eax+edi] + sctrl_5: + cmp eax,5 + jne sctrl_ignore + GO_ON j_sctrl_5, sctrl_ignore, 8 + j_sctrl_5: + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate frame + sctrl_ignore: + mov [ebx],edi + add ebx,8 + jmp DWORD ptr [ebx] + +OP_MOVE_PRI: +;nop; + GO_ON j_move_pri, OP_MOVE_ALT + + j_move_pri: + mov eax,edx + +;good +OP_MOVE_ALT: +;nop; + GO_ON j_move_alt, OP_XCHG + + j_move_alt: + mov edx,eax + +OP_XCHG: +;nop; + GO_ON j_xchg, OP_PUSH_PRI + + j_xchg: ;one might use pushes/pops for pre-586's + xchg eax,edx + +;good +OP_PUSH_PRI: +;nop; + GO_ON j_push_pri, OP_PUSH_ALT + + j_push_pri: + _PUSH eax + +;good +OP_PUSH_ALT: +;nop; + GO_ON j_push_alt, OP_PUSH_R_PRI + + j_push_alt: + _PUSH edx + +OP_PUSH_R_PRI: +;nop; + putval j_push_r_pri+1 + GO_ON j_push_r_pri, OP_PUSH_C, 8 + + j_push_r_pri: + mov ecx,12345678h + j_push_loop: + _PUSH eax + loop j_push_loop + ;dec ecx + ;jnz j_push_loop + +;good +OP_PUSH_C: +;nop; + putval j_push_c+1 + GO_ON j_push_c, OP_PUSH, 8 + + j_push_c: + _PUSH 12345678h + +OP_PUSH: +;nop; + putval j_push+2 + GO_ON j_push, OP_PUSH_S, 8 + + j_push: + _PUSH [edi+12345678h] + +;good +OP_PUSH_S: +;nop; + putval j_push_s+2 + GO_ON j_push_s, OP_POP_PRI, 8 + + j_push_s: + _PUSH [ebx+12345678h] + +OP_POP_PRI: +;nop; + GO_ON j_pop_pri, OP_POP_ALT + + j_pop_pri: + _POP eax + +;good +OP_POP_ALT: +;nop; + GO_ON j_pop_alt, OP_STACK + + j_pop_alt: + _POP edx + +;good +OP_STACK: +;nop; + putval j_stack+4 + GO_ON j_stack, OP_HEAP, 8 + + j_stack: + mov edx,esp + add esp,12345678h + sub edx,edi +IF DORUNTIMECHECKS NE 0 + call [chk_marginstack] +ENDIF + +;good +OP_HEAP: +;nop; + putval j_heap_call-4 + GO_ON j_heap, OP_PROC, 8 + + j_heap: + mov edx,hea + add DWORD ptr hea,12345678h + j_heap_call: +IF DORUNTIMECHECKS NE 0 + call [chk_marginheap] +ENDIF +;good +OP_PROC: +;nop; + GO_ON j_proc, OP_RET + + j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK + _PUSH frm ; push old frame (for RET/RETN) + mov frm,esp ; get new frame + mov ebx,esp ; already relocated + sub frm,edi ; relocate frame + +OP_RET: +;nop; + GO_ON j_ret, OP_RETN + + j_ret: + _POP ebx ; pop frame + mov frm,ebx + add ebx,edi + ret + ;call [jit_ret] + +;good +OP_RETN: +;nop; + GO_ON j_retn, OP_CALL + + j_retn: + jmp [jit_retn] + +;good +OP_CALL: +;nop; + RELOC 1 + GO_ON j_call, OP_CALL_I, 8 + + j_call: + ;call 12345678h ; tasm chokes on this out of a sudden + db 0e8h, 0, 0, 0, 0 + +OP_CALL_I: +;nop; + GO_ON j_call_i, OP_JUMP + + j_call_i: + call eax + +;good +OP_JUMP: +;nop; + RELOC 1 + GO_ON j_jump, OP_JREL, 8 + + j_jump: + DB 0e9h + DD 12345678h + +OP_JREL: +;nop; + mov eax,[ebx+4] + ; create an absolute address from the relative one + RELOC 1, eax+ebx+8 + GO_ON j_jump, OP_JREL, 8 + +;good +OP_JZER: +;nop; + RELOC 4 + GO_ON j_jzer, OP_JNZ, 8 + + j_jzer: + or eax,eax + DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) + +;good +OP_JNZ: +;nop; + RELOC 4 + GO_ON j_jnz, OP_JEQ, 8 + + j_jnz: + or eax,eax + DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 + +;good +OP_JEQ: +;nop; + RELOC 4 + GO_ON j_jeq, OP_JNEQ, 8 + + j_jeq: + cmp eax,edx + DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) + +OP_JNEQ: +;nop; + RELOC 4 + GO_ON j_jneq, OP_JLESS, 8 + + j_jneq: + cmp eax,edx + DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) + +OP_JLESS: +;nop; + RELOC 4 + GO_ON j_jless, OP_JLEQ, 8 + + j_jless: + cmp eax,edx + DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) + +OP_JLEQ: +;nop; + RELOC 4 + GO_ON j_jleq, OP_JGRTR, 8 + + j_jleq: + cmp eax,edx + DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) + +OP_JGRTR: +;nop; + RELOC 4 + GO_ON j_jgrtr, OP_JGEQ, 8 + + j_jgrtr: + cmp eax,edx + DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) + +OP_JGEQ: +;nop; + RELOC 4 + GO_ON j_jgeq, OP_JSLESS, 8 + + j_jgeq: + cmp eax,edx + DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) + +OP_JSLESS: +;nop; + RELOC 4 + GO_ON j_jsless, OP_JSLEQ, 8 + + j_jsless: + cmp eax,edx + DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 + +;good +OP_JSLEQ: +;nop; + RELOC 4 + GO_ON j_jsleq, OP_JSGRTR, 8 + + j_jsleq: + cmp eax,edx + DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 + +OP_JSGRTR: +;nop; + RELOC 4 + GO_ON j_jsgrtr, OP_JSGEQ, 8 + + j_jsgrtr: + cmp eax,edx + DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 + +OP_JSGEQ: +;nop; + RELOC 4 + GO_ON j_jsgeq, OP_SHL, 8 + + j_jsgeq: + cmp eax,edx + DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 + +OP_SHL: +;nop; + GO_ON j_shl, OP_SHR + j_shl: + mov ecx,edx ; TODO: save ECX if used as special register + shl eax,cl + +OP_SHR: +;nop; + GO_ON j_shr, OP_SSHR + j_shr: + mov ecx,edx ; TODO: save ECX if used as special register + shr eax,cl + +OP_SSHR: +;nop; + GO_ON j_sshr, OP_SHL_C_PRI + j_sshr: + mov ecx,edx ; TODO: save ECX if used as special register + sar eax,cl + +OP_SHL_C_PRI: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shl_c_pri+2],al + GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 + j_shl_c_pri: + shl eax,12h + +OP_SHL_C_ALT: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shl_c_alt+2],al + GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 + j_shl_c_alt: + shl edx,12h + +OP_SHR_C_PRI: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shr_c_pri+2],al + GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 + j_shr_c_pri: + shr eax,12h + +OP_SHR_C_ALT: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shr_c_alt+2],al + GO_ON j_shr_c_alt, OP_SMUL, 8 + j_shr_c_alt: + shr edx,12h + +OP_SMUL: +;nop; + GO_ON j_smul, OP_SDIV + j_smul: + push edx + imul edx + pop edx + +;good +OP_SDIV: +;nop; + GO_ON j_sdiv, OP_SDIV_ALT + j_sdiv: + call [jit_sdiv] + +OP_SDIV_ALT: +;nop; + GO_ON j_sdiv_alt, OP_UMUL + j_sdiv_alt: + xchg eax,edx + call [jit_sdiv] + +OP_UMUL: +;nop; + GO_ON j_umul, OP_UDIV + j_umul: + push edx + mul edx + pop edx + +OP_UDIV: +;nop; + GO_ON j_udiv, OP_UDIV_ALT + j_udiv: + mov ebp,edx + sub edx,edx + call [chk_dividezero] + div ebp + +OP_UDIV_ALT: +;nop; + GO_ON j_udiv_alt, OP_ADD + j_udiv_alt: + mov ebp,eax + mov eax,edx + sub edx,edx + call [chk_dividezero] + div ebp + +;good +OP_ADD: +;nop; + GO_ON j_add, OP_SUB + j_add: + add eax,edx + +;good +OP_SUB: +;nop; + GO_ON j_sub, OP_SUB_ALT + j_sub: + sub eax,edx + +;good +OP_SUB_ALT: +;nop; + GO_ON j_sub_alt, OP_AND + j_sub_alt: + neg eax + add eax,edx + +OP_AND: +;nop; + GO_ON j_and, OP_OR + j_and: + and eax,edx + +OP_OR: +;nop; + GO_ON j_or, OP_XOR + j_or: + or eax,edx + +OP_XOR: +;nop; + GO_ON j_xor, OP_NOT + j_xor: + xor eax,edx + +OP_NOT: +;nop; + GO_ON j_not, OP_NEG + j_not: + neg eax ; sets CF iff EAX != 0 + sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) + inc eax ; -1 => 0 and 0 => 1 + +OP_NEG: +;nop; + GO_ON j_neg, OP_INVERT + j_neg: + neg eax + +OP_INVERT: +;nop; + GO_ON j_invert, OP_ADD_C + j_invert: + not eax + +;good +OP_ADD_C: +;nop; + putval j_add_c+1 + GO_ON j_add_c, OP_SMUL_C, 8 + j_add_c: + add eax,12345678h + +;good +OP_SMUL_C: +;nop; + putval j_smul_c+3 + GO_ON j_smul_c, OP_ZERO_PRI, 8 + j_smul_c: + push edx + imul eax,12345678h + pop edx + +;good +OP_ZERO_PRI: +;nop; + GO_ON j_zero_pri, OP_ZERO_ALT + j_zero_pri: + sub eax,eax + +;good +OP_ZERO_ALT: +;nop; + GO_ON j_zero_alt, OP_ZERO + j_zero_alt: + sub edx,edx + +OP_ZERO: +;nop; + putval j_zero+2 + GO_ON j_zero, OP_ZERO_S, 8 + j_zero: + mov DWORD ptr [edi+12345678h],0 + +OP_ZERO_S: +;nop; + putval j_zero_s+2 + GO_ON j_zero_s, OP_SIGN_PRI, 8 + j_zero_s: + mov DWORD ptr [ebx+12345678h],0 + +OP_SIGN_PRI: +;nop; + GO_ON j_sign_pri, OP_SIGN_ALT + j_sign_pri: + shl eax,24 + sar eax,24 + +OP_SIGN_ALT: +;nop; + GO_ON j_sign_alt, OP_EQ + j_sign_alt: + shl edx,24 + sar edx,24 + +OP_EQ: +;nop; + GO_ON j_eq, OP_NEQ + j_eq: + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + +OP_NEQ: +;nop; + GO_ON j_neq, OP_LESS + j_neq: + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + +OP_LESS: +;nop; + GO_ON j_less, OP_LEQ + j_less: + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + +OP_LEQ: +;nop; + GO_ON j_leq, OP_GRTR + j_leq: + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + +OP_GRTR: +;nop; + GO_ON j_grtr, OP_GEQ + j_grtr: + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + +OP_GEQ: +;nop; + GO_ON j_geq, OP_SLESS + j_geq: + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + +;good +OP_SLESS: +;nop; + GO_ON j_sless, OP_SLEQ + j_sless: + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + +OP_SLEQ: +;nop; + GO_ON j_sleq, OP_SGRTR + j_sleq: + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + +OP_SGRTR: +;nop; + GO_ON j_sgrtr, OP_SGEQ + j_sgrtr: + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + +OP_SGEQ: +;nop; + GO_ON j_sgeq, OP_EQ_C_PRI + j_sgeq: + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + +OP_EQ_C_PRI: +;nop; + putval j_eq_c_pri+1 + GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 + j_eq_c_pri: + cmp eax,12345678h ; PRI == value ? + mov eax,0 + sete al + +OP_EQ_C_ALT: +;nop; + putval j_eq_c_alt+4 + GO_ON j_eq_c_alt, OP_INC_PRI, 8 + j_eq_c_alt: + sub eax,eax + cmp edx,12345678h ; ALT == value ? + sete al + +OP_INC_PRI: +;nop; + GO_ON j_inc_pri, OP_INC_ALT + j_inc_pri: + inc eax + +OP_INC_ALT: +;nop; + GO_ON j_inc_alt, OP_INC + j_inc_alt: + inc edx + +OP_INC: +;nop; + putval j_inc+2 + GO_ON j_inc, OP_INC_S, 8 + j_inc: + inc DWORD ptr [edi+12345678h] + +;good +OP_INC_S: +;nop; + putval j_inc_s+2 + GO_ON j_inc_s, OP_INC_I, 8 + j_inc_s: + inc DWORD ptr [ebx+12345678h] + +OP_INC_I: +;nop; + GO_ON j_inc_i, OP_DEC_PRI + j_inc_i: + inc DWORD ptr [edi+eax] + +OP_DEC_PRI: +;nop; + GO_ON j_dec_pri, OP_DEC_ALT + j_dec_pri: + dec eax + +OP_DEC_ALT: +;nop; + GO_ON j_dec_alt, OP_DEC + j_dec_alt: + dec edx + +OP_DEC: +;nop; + putval j_dec+2 + GO_ON j_dec, OP_DEC_S, 8 + j_dec: + dec DWORD ptr [edi+12345678h] + +OP_DEC_S: +;nop; + putval j_dec_s+2 + GO_ON j_dec_s, OP_DEC_I, 8 + j_dec_s: + dec DWORD ptr [ebx+12345678h] + +OP_DEC_I: +;nop; + GO_ON j_dec_i, OP_MOVS + j_dec_i: + dec DWORD ptr [edi+eax] + +OP_MOVS: +;nop; + putval j_movs+1 + GO_ON j_movs, OP_CMPS, 8 + j_movs: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_movs] + +OP_CMPS: +;nop; + putval j_cmps+1 + GO_ON j_cmps, OP_FILL, 8 + j_cmps: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_cmps] + +OP_FILL: +;nop; + putval j_fill+1 + GO_ON j_fill, OP_HALT, 8 + j_fill: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_fill] + +;good +OP_HALT: +;nop; + putval j_halt_sm+1 + GO_ON j_halt, OP_BOUNDS, 8 + j_halt: + cmp DWORD PTR retval,0 + je j_halt_no_value + mov ebp,retval + mov [ebp],eax + j_halt_no_value: + j_halt_sm: + mov eax,12345678h + jmp [jit_return] + +;good +OP_BOUNDS: +;nop; + putval j_bounds+1 + GO_ON j_bounds, OP_SYSREQ_C, 8 + j_bounds: + mov ebp,12345678h + call [jit_bounds] + +;good +OP_SYSREQ_C: +;nop; + putval j_sysreq_c+1 + GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 + j_sysreq_c: + mov eax,12345678h ; get function number + j_sysreq: + call [jit_sysreq] + +OP_SYSREQ_PRI: +;nop; + GO_ON j_sysreq, OP_SYSREQ_PRI + +OP_FILE: ;opcode is simply ignored +;nop; + mov eax,[ebx+4] ;get size + mov [ebx],edi + lea ebx,[ebx+eax+8] ;move on to next opcode + cmp ebx,DWORD ptr [end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ;go on with the next opcode + +OP_LINE: ;ignored +;nop; + mov [ebx],edi ; no line number support: ignore opcode + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + +OP_SYMBOL: ;ignored + mov [ebx],edi + mov eax,[ebx+4] ; get size + lea ebx,[ebx+eax+8] ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +OP_SRANGE: ;ignored + mov [ebx],edi ; store relocated address + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +;not tested +OP_JUMP_PRI: + GO_ON j_jump_pri, OP_SWITCH + + j_jump_pri: ; MP: This opcode makes sense only in con- + jmp [eax] ; junction with a possibility to get the + ; address of a code location... + + +;good +OP_SWITCH: + lea eax,[edi+6] ; The case table will be copied directly + neg eax ; after the run-time call to [jit_switch]. + and eax,3 ; We should align this table on a DWORD + mov ecx,eax ; boundary. + mov al,90h ; 90h = opcode of x86 NOP instruction + rep stosb ; Write the right number of NOPs. + mov [ebx],edi ; store address of SWITCH for relocation step + mov esi,OFFSET j_switch + mov ecx,6 + rep movsb ; copy the call instruction + mov esi,[ebx+4] ; get address of CASETBL instruction + add ebx,8 ; set instruction pointer to next opcode + add esi,4 ; point esi to first entry: (count, default adr) + mov ecx,[esi] ; get number of cases (excluding default) + inc ecx + mov ebp,[reloc_num] + j_case_loop: + mov eax,[esi] ; get case value + stosd ; write it + mov eax,[esi+4] ; get destination address +IF FORCERELOCATABLE EQ 0 + or eax,80000000h ; add flag for "absolute address" +ENDIF + mov [edx+ebp],eax ; write dest. adr. into relocation table + mov eax,[esi+4] ; get destination address (again) + add esi,8 ; set ESI to next case + mov [edx+ebp+4],edi ; write adr. to patch into relocation table + add ebp,8 ; promote relocation pointer + stosd ; write dest. adr. + dec ecx + jnz j_case_loop + mov DWORD ptr [reloc_num],ebp ; write back updated reloc_num + + jmp [ebx] ; GO_ON to next op-code + + j_switch: + call [jit_switch] + +;good +OP_CASETBL: ; compiles to nothing, SWITCH does all the work + mov eax,[ebx+4] ; get count of cases + lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer + jmp [ebx] ; GO_ON with next op-code + + +OP_SWAP_PRI: ; TR + GO_ON j_swap_pri, OP_SWAP_ALT + + j_swap_pri: + _POP ebp + _PUSH eax + mov eax,ebp + + +OP_SWAP_ALT: ; TR + GO_ON j_swap_alt, OP_PUSHADDR + + j_swap_alt: + _POP ebp + _PUSH edx + mov edx,ebp + + +OP_PUSHADDR: ; TR + putval j_pushaddr+1 + GO_ON j_pushaddr, OP_NOP, 8 + + j_pushaddr: + mov ebp,12345678h ;get address (offset from frame) + add ebp,frm + _PUSH ebp + + +OP_NOP: ; TR + GO_ON j_nop, OP_SYSREQ_D + j_nop: ; code alignment is ignored by the JIT + + +OP_SYSREQ_D: +;nop; + putval j_sysreq_d+1 + GO_ON j_sysreq_d, OP_SYMTAG, 8 + j_sysreq_d: + mov ebx,12345678h ; get function address + call [jit_sysreq_d] + + +OP_SYMTAG: ;ignored (TR) + mov [ebx],edi ; store relocated address + add ebx,8 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +OP_BREAK: +IF DEBUGSUPPORT EQ 0 + mov [ebx],edi ; no debugger number support: ignore opcode + add ebx,4 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode +ELSE + GO_ON j_break, OP_INVALID + j_break: + mov ebp,amx + cmp DWORD [ebp+_debug], 0 + je $+4 ; jump around the "call" statement + call [jit_break] +ENDIF + +OP_INVALID: ; break from the compiler with an error code + mov eax,AMX_ERR_INVINSTR + pop esi + pop edi + pop ecx + pop ebp + ret + + +asm_runJIT_ ENDP + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell amx_exec( cell *regs, cell *retval, cell stp, cell hea ); +; eax edx ebx ecx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +amx_exec_jit_ PROC +;TODO: clean up this mess, make it use __cdecl..... + push edi + push esi + push ebp + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push DWORD ptr [eax+28] ; store pointer to code segment + push DWORD ptr [eax+24] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push DWORD ptr[eax+20]; store FRM + + stk equ [esi+32] ; define some aliases to registers that will + alt equ [esi+28] ; be stored on the stack when the code is + pri equ [esi+24] ; actually beeing executed + code equ [esi+20] + amx equ [esi+16] + retval equ [esi+12] + stp equ [esi+8] + hea equ [esi+4] + frm equ [esi] ; FRM is NOT stored in ebp, rather FRM+DAT is being + ; held in ebx. + + mov edx,[eax+4] ; get ALT + mov ecx,[eax+8] ; get CIP + mov edi,[eax+12] ; get pointer to data segment + mov esi,[eax+16] ; get STK + mov ebx,[eax+20] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + add esi,edi ; ESP will contain DAT+STK + xchg esp,esi ; switch to AMX stack + + add stp,edi ; make STP absolute address for run-time checks + + _POP ebp ; AMX pseudo-return address, ignored + ; Call compiled code via CALL NEAR
+ call ecx + +return_to_caller: + cmp DWORD PTR retval,0 + je return_to_caller_no_value + mov ebp,retval + mov [ebp],eax ; provide return value + + return_to_caller_no_value: + mov eax,AMX_ERR_NONE + jmp _return + +_return_popstack: + add esp,4 ; Correct ESP, because we just come from a + ; runtime error checking routine. +_return: + ; store machine state + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... + mov ecx,hea ; ... HEA, ... + mov [ebp+_hea],ecx + mov ecx,ebx ; ... and FRM + sub ecx,edi ; (correct FRM) + mov [ebp+_frm],ecx + mov [ebp+_pri],eax ; also store PRI, ... + mov [ebp+_alt],edx ; ... and ALT + + ; return + sub stp,edi ; make STP relative to DAT again + xchg esp,esi ; switch back to caller's stack + + add esp,4*9 ; remove temporary data + + pop ebp + pop esi + pop edi + + ret + + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return_popstack + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return_popstack + +_CHKMARGIN_STACK: ; some run-time check routines + cmp esp,stp + lea ebp,[esp-STACKRESERVE] + jg err_stacklow + sub ebp,edi + cmp hea,ebp + jg err_stack + ret + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return_popstack + +_CHKMARGIN_HEAP: + cmp esp,stp + jg err_stacklow + mov ebp,amx + mov ebp,[ebp+_hlw] + cmp DWORD ptr hea,ebp + jl err_heaplow + ret + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return_popstack + +_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx + cmp eax,stp + jae err_memaccess + cmp eax,hea + jb veax1 + lea ebp,[eax+edi] + cmp ebp,esp + jb err_memaccess + veax1: + ret + +_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx + cmp edx,stp + jae err_memaccess + cmp edx,hea + jb vedx1 + lea ebp,[edx+edi] + cmp ebp,esp + jb err_memaccess + vedx1: + ret + +JIT_OP_SDIV: + mov ebp,edx + xor edx,eax ; Check signs of the operands. + cdq + js sdiv_fiddle ; If the signs of the operands are different + ; we'll have to fiddle around to achieve + ; proper rounding towards minus infinity. + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp ; default behavior is right in the other cases + ret + + sdiv_fiddle: + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp + or edx,edx + jz sdiv_goon ; If there's no remainder the result is correct + add edx,ebp ; else fix the result values. + dec eax ; Amazing, how simple this is... + sdiv_goon: + ret + + ALIGN 4 + +JIT_OP_RETN: + _POP ebx ; pop frame + _POP ecx ; get return address + + mov frm,ebx + _POP ebp + + add ebx,edi + add esp,ebp ; remove data from stack + + jmp ecx + + +JIT_OP_MOVS: ;length of block to copy is already in ECX + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx ; I hope the blocks to copy are properly + shr ecx,2 ; aligned, so I don't do anything about that. + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + ret + +JIT_OP_CMPS: ;length of block to compare is already in ECX + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax ; This is surely not the fastest way to do this + repe cmpsb ; but the most simple one. + je cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + ret + + +JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX + push edi + lea edi,[edi+edx] + + shr ecx,2 ;length in 32-bit cells + rep stosd ;the value to use is already in EAX + + pop edi + ret + +JIT_OP_BOUNDS: + cmp eax,0 + jl err_bounds + cmp eax,ebp + jg err_bounds + ret +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return_popstack + +_CHKDIVIDEZERO: + or ebp,ebp ; check for divide by zero + jz err_divide + ret +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return_popstack + +JIT_OP_SYSREQ: + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + lea ebx,pri ; 3rd param: addr. of retval + lea ecx,[esp+4] ; 4th param: parameter array + xchg esp,esi ; switch to caller stack + call [ebp+_callback] + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_SYSREQ_D: ; (TR) + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov eax,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + lea edx,[esp+4] ; 2nd param: parameter array + xchg esp,esi ; switch to caller stack + call ebx ; direct call + xchg esp,esi ; switch back to AMX stack + mov ebp,amx ; get amx into EBP + cmp [ebp+_error],AMX_ERR_NONE + jne _return_popstack; return error code, if any + + ; return value is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_BREAK: +IF DEBUGSUPPORT EQ 0 +ELSE + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + + mov [ebp+_pri],eax ; store values in AMX structure: PRI, + mov [ebp+_alt],edx ; ALT, + mov [ebp+_stk],ecx ; STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + ;??? storing CIP is not very useful, because the code changed (during JIT compile) + + mov eax,ebp ; 1st param: amx + xchg esp,esi ; switch to caller stack + call [ebp+_debug] + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov ebp,amx ; get amx into EBP + mov eax,[ebp+_pri] ; restore values + mov edx,[ebp+_alt] ; ALT, + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame +ENDIF + ret + + +JIT_OP_SWITCH: + pop ebp ; pop return address = table address + mov ecx,[ebp] ; ECX = number of records + lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case + jecxz op_switch_jump ; special case: no cases at all + op_switch_loop: + cmp eax,[ebp-8] ; PRI == case label? + je op_switch_jump ; found, jump + sub ebp,8 ; position to preceding case + loop op_switch_loop ; check next case, or fall through + op_switch_jump: +IF FORCERELOCATABLE EQ 0 + jmp [ebp-4] ; jump to the case instructions +ELSE + add ebp,[ebp-4] ; add offset to make absolute adddress + jmp ebp +ENDIF + +amx_exec_jit_ ENDP + +; +; The caller of asm_runJIT() can determine the maximum size of the compiled +; code by multiplying the result of this function by the number of opcodes in +; Pawn module. +; +; unsigned long getMaxCodeSize_(); +; +getMaxCodeSize_ PROC + + mov eax,MAXCODESIZE + ret + +getMaxCodeSize_ ENDP + + +IFNDEF @Version + ; Microsoft MASM 6.x gives the error message "Register assumed to + ; ERROR" when I put the code lables in the data segment. I cannot find + ; a proper way around it. +.DATA +ENDIF + ALIGN 4 ; This is essential to avoid misalignment stalls. + +end_code DD 0 ; pointer to the end of the source code + +compiled_code DD 0 ; pointer to compiled code (including preamble) + +amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) + +reloc_num DD 0 ; counts the addresses in the relocation table (jumps) + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + +; +; A list of the "run-time-library" functions that are called via indirect calls. +; So these calls don't have to be relocated. This gives also the possibility to +; replace some of these with shorter/faster non-debug or non-checking versions, +; without changing the compiled code. Instead this table could be changed... +; +verify_adr_eax DD _VERIFYADDRESS_eax +verify_adr_edx DD _VERIFYADDRESS_edx +chk_marginstack DD _CHKMARGIN_STACK +chk_marginheap DD _CHKMARGIN_HEAP +chk_dividezero DD _CHKDIVIDEZERO +jit_return DD _return +jit_retn DD JIT_OP_RETN +jit_sdiv DD JIT_OP_SDIV +jit_movs DD JIT_OP_MOVS +jit_cmps DD JIT_OP_CMPS +jit_fill DD JIT_OP_FILL +jit_bounds DD JIT_OP_BOUNDS +jit_sysreq DD JIT_OP_SYSREQ +jit_sysreq_d DD JIT_OP_SYSREQ_D +jit_break DD JIT_OP_BREAK +jit_switch DD JIT_OP_SWITCH + +; +; The table for the browser/relocator function. +; + PUBLIC _amx_opcodelist_jit +_amx_opcodelist_jit: + DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_I + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE + DD OP_LINE + DD OP_SYMBOL + DD OP_SRANGE + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI ; TR + DD OP_SWAP_ALT ; TR + DD OP_PUSHADDR ; TR + DD OP_NOP ; TR + DD OP_SYSREQ_D ; TR + DD OP_SYMTAG ; TR + DD OP_BREAK ; TR + +END diff --git a/amx-deps/src/amx/amxjits.asm b/amx-deps/src/amx/amxjits.asm index 08071cd..bcb39b6 100644 --- a/amx-deps/src/amx/amxjits.asm +++ b/amx-deps/src/amx/amxjits.asm @@ -1,2257 +1,2261 @@ -; ASMJITS.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" -; scripting language -; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES - -; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" -; (27 disks, no output, DOS4/GW under Win95) with this implementation of the -; JIT compiler. - -; NOTE 1: -; There is only one pass implemented in this version. This means there is no -; way of knowing the size of the compiled code before it has actually been com- -; piled. So the only chance the caller has, is to count the number of opcodes -; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to -; obtain a size value big enough to hold the entire code (and data, including -; the stack and heap, after adding their sizes). Afterwards it can realloc() -; this memory block to the actually needed smaller size. - -; NOTE 2: -; The compiler destroys the opcode addresses of the given source by storing the -; respective compiled code's addresses there for the final address relocation -; step. - -; NOTE 3: -; During execution of the compiled code with amx_exec_jit() the x86 processor's -; stack is switched into the data section of the abstract machine. This means -; that there should always be enough memory left between HEA and STK to provide -; stack space for occurring interrupts! (see the STACKRESERVE variable) - -; NOTE 4: -; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I -; instructions, I have to tell that they don't work as expected in a JIT -; compiled program, because there is no easy way of transforming AMX code -; addresses and JIT translated ones. This might be fixed in a future version. - -; NX ("No eXecute") and XD (eXecution Denied) bits -; (by Thiadmer Riemersma) -; -; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit -; processors) and Intel came with the same design, but calling it differently. -; The purpose is to make "buffer overrun" security holes impossible, or at least -; very, very difficult, by marking the stack and the heap as memory regions -; such that an attempt to execute processor instructions will cause a processor -; exception (of course, a buffer overrun that is not explictly handled will then -; crash the application --instead of executing the rogue code). -; -; For JIT compilers, this has the impact that you are not allowed to execute the -; code that the JIT has generated. To do that, you must adjust the attributes -; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to -; allocate a memory block with the appropriate fags; on Linux (with a recent -; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the -; function VirtualProtect() to change the page attributes of an existing memory -; block, but there are caveats in its use: if the block spans multiple pages, -; these pages must be consecutive, and if there are blocks of memory in a page -; unrelated to the JIT, their page attributes will change too. -; -; The JIT compiler itself requires only read-write access (this is the default -; for a memory block that you allocate). The execution of the JIT-compiled code -; requires full access to the memory block: read, write and execute. It needs -; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the -; first lookup (this is an optimization, look up the address of the native -; function only once). For processors that do not support the NX/XD bit, -; execution of code is implicitly supported if read access is supported. -; -; During compilation, the JIT compiler requires write-access to its own code -; segment: the JIT-compiler patches P-code parameters into its own code segment -; during compilation. This is handled in the support code for amx_InitJIT. -; -; -; CALLING CONVENTIONS -; (by Thiadmer Riemersma) -; -; This version is the JIT that uses the "stack calling convention". In the -; original implementation, this meant __cdecl; both for the calling convention -; for the _asm_runJIT routine itself as for the callback functions. -; The current release supports __stdcall for the callback functions; to -; use it, you need to assemble the file with STDECL defined (Since STDCALL is -; a reserved word on the assembler, I had to choose a different name for the -; macro, hence STDECL.) - -; Revision History -; ---------------- -; 26 august 2007 by Thiadmer Riemersma -; Minor clean-up; removed unneeded parameter. -; 17 february 2005 by Thiadmer Riemersma (TR) -; Addition of the BREAK opcode, removal of the older debugging opcode -; table. There should now be some debug support (if enabled during the -; build of the JIT compiler), but not enough to run a debugger: the JIT -; compiler does not keep a list that relates the code addresses of the -; P-code versus the native code. -; 6 march 2004 by Thiadmer Riemersma -; Corrected a bug in OP_FILL, where a cell preceding the array would -; be overwritten (zero'ed out). This bug was brought to my attention -; by Robert Daniels. -; 22 december 2003 by Thiadmer Riemersma (TR) -; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; -; SYMTAG is a no-op). -; Support __stdcall calling convention for for the native function "hook" -; function (the __cdecl calling convention is also still supported). -; 14 October 2002 by Thiadmer Riemersma (TR) -; Corrected the amx_s structure. The _hlw field was missing, which caused -; errors for arguments to native functions that were passed by reference. -; 2002/08/05 TR -; * store the status of the abstract machine in the AMX structure upon -; return, so that the machine can be restarted (OP_SLEEP) -; * added OP_NOP (for alignment, it is ignored by the JIT) -; * make sure the JIT does not crash when we NULL is passed for the -; return value -; 2000/03/03 MP -; * _amx_opcodelist is equipped with an underscore, again 8-P -; * added SRANGE as a no-op, so debugging info doesn't upset the JIT -; compiler anymore -; * added note about LCTRL, SCTRL and CALL.I -; 2000/03/02 MP -; * made JIT support __cdecl calling conventions -; * removed some rather unnecessary pops in the epilog of amx_exec_asm -; * changed the template for CALL into a DB byte sequence (tasm 4.1 -; didn't like the immediate value) -; 1999/12/07 MP -; * fixed crash caused by JIT compiler not saving registers -; 1999/08/06 MP - design change: closer to the "iron" with native stack -; * The JIT compiler now generates relocatable code for case tables by -; setting FORCERELOCATABLE = 1. -; * removed all debug hook code -; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ -; pushes/pops are possible -; * removed the run-time functions for the CALL, CALL_I and RET op-codes, -; they are now inline -; * All these changes gained around 80% performance increase for the -; hanoi bench. -; 1999/08/05 MP -; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address -; was stored for the LINE byte code (i.e. SWITCH would jump to totally -; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and -; OP_SCTRL (for the no-op case). -; 1999/08/04 MP -; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. -; arrays have not been tested.) -; * hacked relocation code to support absoulute addresses for CASETBL -; (This assumes that no generated address will be greater than -; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) -; * The run-time function for SWITCH uses a (hopefully) faster algorithm -; to compute the destination address: It searches backwards now. -; 1999/07/08 MP - initial revision - -.386 -.MODEL FLAT - -IFDEF @Version ; for Microsoft MASM 6.x - OPTION OLDSTRUCTS - OPTION M510 -ENDIF - -; -; Support for the BREAK opcode (callback to the debugger): 0 = no, all other -; values = yes. Beware that the compiled code runs slower when this is enabled, -; and that debug support is still fairly minimal. -; -DEBUGSUPPORT = 0 - -; -; If this is set to 1 the JIT generates relocatable code for case tables, too. -; If set to 0, a faster variant for switch (using absolute addresses) is -; generated. I consider setting it to 0 a bad idea. -; -FORCERELOCATABLE = 1 - -; -; Determines how much memory should be reserved for occurring interrupts. -; (If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes -; for interrupts that occur in real mode and are promoted to protected mode.) -; This value _MUST_ be greater than 64 (for AMX needs) and should be at least -; 128 (to serve interrupts). -; -STACKRESERVE = 256 - -; -; This variable controls the generation of memory range checks at run-time. -; You should set this to 0, only when you are sure that there are no range -; violations in your Pawn programs and you really need those 5% speed gain. -; -DORUNTIMECHECKS = 1 - -IFNDEF JIT - JIT = 1 -ENDIF -INCLUDE amxdef.asm - -; -; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) -; -_PUSH MACRO v - push DWORD ptr v - ENDM - -; -; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) -; -_POP MACRO v - pop DWORD ptr v - ENDM - -; -; For determining the biggest native code section generated for ONE Pawn -; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) -; -MAXCODESIZE = 0 - -; -; This is the work horse of the whole JIT: It actually copies the code. -; -GO_ON MACRO from, to, opsize ;opsize may be 4, 8 or 12 (default=4) - mov esi,OFFSET from ;get source address of JIT code - CODESIZE = to-from - mov ecx,CODESIZE ;get number of bytes to copy - ;all the following could (should?) be in a sub-routine... - ;determining the maximum size of a single code snippet - IF MAXCODESIZE LT CODESIZE - MAXCODESIZE = CODESIZE - ENDIF - mov [ebx],edi ;store address for jump-correction - IFB ;add operand size to the source ptr - add ebx,4 - ELSE - add ebx,opsize - ENDIF - rep movsb - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ;go on with the next opcode - ENDM - -; -; Modify the argument of an x86 instruction with the Pawn opcode's parameter -; before copying the code. -; -putval MACRO where - mov eax,[ebx+4] - mov DWORD ptr [where],eax - ENDM - -; -; Add an entry to the table of addresses which have to be relocated after the -; code compilation is done. -; -RELOC MACRO adr, dest - mov ebp,[reloc_num] - IFB - mov eax,[ebx+4] - ELSE - lea eax,[dest] - ENDIF - mov [edx+ebp],eax ; write absolute destination - lea eax,[edi+adr] - mov [edx+ebp+4],eax ; write address of jump operand - add DWORD ptr [reloc_num],8 - ENDM - -_DROPARGS MACRO n ; (TR) remove function arguments from the stack - IFNDEF STDECL ; (for __cdecl calling convention only) - add esp,n - ENDIF - ENDM - - -.CODE - - - PUBLIC _asm_runJIT - PUBLIC _amx_exec_jit - PUBLIC _getMaxCodeSize - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -; void asm_runJIT( AMX *amxh, JumpAddressArray *jumps, void *dest ) ; -; ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; asm_runJIT() assumes that the code of this module is allready browsed and -; relocated for the JIT compiler. It also assumes that both the jumps array and -; the dest memory block are large enough to hold all the data it has to write -; to them, as well as that the prefix (header) has already been copied to dest. - -_asm_runJIT PROC - push ebp - push ebx - push edi - push esi - - mov eax,[esp+20] ; get amxh - mov edx,[esp+24] ; get jumps array - mov ebx,[esp+28] ; get destination - - mov [amxhead],eax ; save pointer to AMX_HEADER struct - mov ecx,[eax+_cod] ; get offset of start of code - mov eax,[eax+_dat] ; offset of start of data = end of code - mov edi,ecx - add ecx,[amxhead] ; compute the real pointer - add eax,[amxhead] ; dito - add edi,ebx ; get write pointer into EDI - mov [compiled_code],ebx - mov [end_code],eax ; Store end-of-code address, so JIT - ; compiler knows when to stop. - mov DWORD ptr [reloc_num],0 ; init the index into the jumps array - - mov ebx,ecx - jmp DWORD ptr [ecx] ; start compiling - - ; The compiler will jump back here when code generation is complete. - -code_gen_done: ; Now copy the data section. - mov ebp,[amxhead] ; get source AMX_HEADER start address - add edi,3 ; DAT follows directly after COD - and edi,0fffffffch ; align it on a DWORD boundary - push edi ; save data start pointer - mov esi,[end_code] ; get start of data segment - mov ecx,[ebp]._h_hea - sub ecx,[ebp]._dat ; compute length of array to copy - rep movsb ; copy the data - - ; Now adjust the register values in the compiled AMX_HEADER. - ; COD stays the same since the size of AMX_HEADER doesn't change in - ; compiled mode. - mov ebx,[compiled_code] ; get compiled AMX's header address - pop esi ; recall data start pointer - sub esi,ebx ; DAT = size of code + size of prefix - mov [ebx]._dat,esi ; write corrected DAT register - - ;HEA and STP are already relative to DAT, so we don't need to fix them. - - ; Now the calls/jumps in the compiled code have to be relocated. - sub ecx,ecx ; reset offset into relocation table - cmp ecx,[reloc_num] - jae reloc_code_done ; if there's nothing to fix, skip this part - reloc_code_loop: - mov eax,[edx+ecx] ; get destination address - mov edi,[edx+ecx+4] ; determine where to write the relocated value - add ecx,8 ; set pointer to next entry in relocation table - add edi,4 ; base address from where the offset is taken -IF FORCERELOCATABLE EQ 0 - ;MP: hack to suport absolute addresses for the CASETBL instruction - test eax,80000000h ; check whether it is an absolute address - pushf - and eax,7fffffffh ; clear the flag bit for absolute addresses - popf - mov eax,[eax] ; translate into compiled absolute address - jne write_reloc ; leave out the subtraction if absolute -ELSE - mov eax,[eax] ; translate into compiled absolute address -ENDIF - sub eax,edi ; make a relative offset - write_reloc: - mov [edi-4],eax ; write the relocated address - cmp ecx,[reloc_num] - jb reloc_code_loop - -reloc_code_done: - ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) - add ebp,[ebp]._cod ; make all addresses relative to COD, not base - mov eax,[ebx]._h_cip - add eax,ebp ; get absolute source CIP - mov eax,[eax] ; translate CIP to compiled address - sub eax,ebx ; make it relative to base - sub eax,[ebx]._cod ; and now relative to COD - mov [ebx]._h_cip,eax; store relocated CIP - mov edi,[ebx]._publics - sub esi,esi - mov ecx,[ebx]._natives - sub ecx,edi ; ECX = _natives - _publics = public table size - mov si,[ebx]._defsize - or ecx,ecx - jz reloc_done ; If there are no publics, we are done. - reloc_publics_loop: - mov eax,[ebx+edi] ; get public function offset - add eax,ebp ; make it a source address - mov eax,[eax] ; translate to compiled address - sub eax,ebx ; make it an offset relative to base - sub eax,[ebx]._cod ; and now relative to COD - mov [ebx+edi],eax ; write corrected address back - add edi,esi ; step to next public function entry - sub ecx,esi - ja reloc_publics_loop - -reloc_done: - mov eax,0 - pop esi - pop edi - pop ebx - pop ebp - ret - -OP_LOAD_PRI: -;nop; - putval j_load_pri+2 - GO_ON j_load_pri, OP_LOAD_ALT, 8 - - j_load_pri: - mov eax,[edi+12345678h] - -OP_LOAD_ALT: -;nop; - putval j_load_alt+2 - GO_ON j_load_alt, OP_LOAD_S_PRI, 8 - - j_load_alt: - mov edx,[edi+12345678h] - -;good -OP_LOAD_S_PRI: -;nop; - putval j_load_s_pri+2 - GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 - - j_load_s_pri: - mov eax,[ebx+12345678h] - -;good -OP_LOAD_S_ALT: -;nop; - putval j_load_s_alt+2 - GO_ON j_load_s_alt, OP_LOAD_I, 8 - - j_load_s_alt: - mov edx,[ebx+12345678h] - -OP_LOAD_I: -;nop; - GO_ON j_load_i, OP_LODB_I - - j_load_i: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -OP_LODB_I: -;nop; - mov eax,[ebx+4] - mov eax,DWORD ptr [(lodb_and-4)+eax*4] - mov DWORD ptr [j_lodb_i_sm+1],eax ;modify AND instruction - GO_ON j_lodb_i, OP_LREF_PRI, 8 - - j_lodb_i: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_eax] -ENDIF - mov eax,[edi+eax] ;subject to misalignment stalls - j_lodb_i_sm: - and eax,12345678h - -OP_LREF_PRI: -;nop; - putval j_lref_pri+2 - GO_ON j_lref_pri, OP_LREF_ALT, 8 - - j_lref_pri: - mov eax,[edi+12345678h] - mov eax,[edi+eax] - -OP_LREF_ALT: -;nop; - putval j_lref_alt+2 - GO_ON j_lref_alt, OP_LREF_S_PRI, 8 - - j_lref_alt: - mov edx,[edi+12345678h] - mov edx,[edi+edx] - -OP_LREF_S_PRI: -;nop; - putval j_lref_s_pri+2 - GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 - - j_lref_s_pri: - mov eax,[ebx+12345678h] - mov eax,[edi+eax] - -OP_LREF_S_ALT: -;nop; - putval j_lref_s_alt+2 - GO_ON j_lref_s_alt, OP_CONST_PRI, 8 - - j_lref_s_alt: - mov edx,[ebx+12345678h] - mov edx,[edi+edx] - -;good -OP_CONST_PRI: -;nop; - putval j_const_pri+1 - GO_ON j_const_pri, OP_CONST_ALT, 8 - - j_const_pri: - mov eax,12345678h - -;good -OP_CONST_ALT: -;nop; - putval j_const_alt+1 - GO_ON j_const_alt, OP_ADDR_PRI, 8 - - j_const_alt: - mov edx,12345678h - -;good -OP_ADDR_PRI: -;nop; - putval j_addr_pri+1 - GO_ON j_addr_pri, OP_ADDR_ALT, 8 - - j_addr_pri: - mov eax,12345678h - add eax,frm - -;good -OP_ADDR_ALT: -;nop; - putval j_addr_alt+1 - GO_ON j_addr_alt, OP_STOR_PRI, 8 - - j_addr_alt: - mov edx,12345678h - add edx,frm - -OP_STOR_PRI: -;nop; - putval j_stor_pri+2 - GO_ON j_stor_pri, OP_STOR_ALT, 8 - - j_stor_pri: - mov [edi+12345678h],eax - -OP_STOR_ALT: -;nop; - putval j_stor_alt+2 - GO_ON j_stor_alt, OP_STOR_S_PRI, 8 - - j_stor_alt: - mov [edi+12345678h],edx - -;good -OP_STOR_S_PRI: -;nop; - putval j_stor_s_pri+2 - GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 - - j_stor_s_pri: - mov [ebx+12345678h],eax - -;good -OP_STOR_S_ALT: -;nop; - putval j_stor_s_alt+2 - GO_ON j_stor_s_alt, OP_STOR_I, 8 - - j_stor_s_alt: - mov [ebx+12345678h],edx - -;good -OP_STOR_I: -;nop; - GO_ON j_stor_i, OP_STRB_I - - j_stor_i: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_edx] -ENDIF - mov [edi+edx],eax - -OP_STRB_I: -;nop; - mov eax,[ebx+4] - cmp eax,1 - jne strb_not1byte - GO_ON j_strb_i_1b, strb_not1byte, 8 - j_strb_i_1b: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_edx] -ENDIF - mov [edi+edx],al - - strb_not1byte: - cmp eax,4 - je strb_4byte - GO_ON j_strb_i_2b, strb_4byte, 8 - j_strb_i_2b: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_edx] -ENDIF - mov [edi+edx],ax - - strb_4byte: - GO_ON j_strb_i_4b, OP_SREF_PRI, 8 - j_strb_i_4b: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_edx] -ENDIF - mov [edi+edx],eax - -OP_SREF_PRI: -;nop; - putval j_sref_pri+2 - GO_ON j_sref_pri, OP_SREF_ALT, 8 - - j_sref_pri: - mov ebp,[edi+12345678h] - mov [edi+ebp],eax - -OP_SREF_ALT: -;nop; - putval j_sref_alt+2 - GO_ON j_sref_alt, OP_SREF_S_PRI, 8 - - j_sref_alt: - mov ebp,[edi+12345678h] - mov [edi+ebp],edx - -OP_SREF_S_PRI: -;nop; - putval j_sref_s_pri+2 - GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 - - j_sref_s_pri: - mov ebp,[ebx+12345678h] - mov [edi+ebp],eax - -OP_SREF_S_ALT: -;nop; - putval j_sref_s_alt+2 - GO_ON j_sref_s_alt, OP_LIDX, 8 - - j_sref_s_alt: - mov ebp,[ebx+12345678h] - mov [edi+ebp],edx - -;good -OP_LIDX: -;nop; - GO_ON j_lidx, OP_LIDX_B - - j_lidx: - lea eax,[edx+4*eax] -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -OP_LIDX_B: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_lidx_b+2],al - GO_ON j_lidx_b, OP_IDXADDR, 8 - - j_lidx_b: - shl eax,12h - add eax,edx -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [verify_adr_eax] -ENDIF - mov eax,[edi+eax] - -;good -OP_IDXADDR: -;nop; - GO_ON j_idxaddr, OP_IDXADDR_B - - j_idxaddr: - lea eax,[edx+4*eax] - -OP_IDXADDR_B: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_idxaddr_b+2],al - GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 - - j_idxaddr_b: - shl eax,12h - add eax,edx - -OP_ALIGN_PRI: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov DWORD ptr [j_align_pri+1],eax - GO_ON j_align_pri, OP_ALIGN_ALT, 8 - - j_align_pri: - xor eax,12345678h - -OP_ALIGN_ALT: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov DWORD ptr [j_align_alt+1],eax - GO_ON j_align_alt, OP_LCTRL, 8 - - j_align_alt: - xor edx,12345678h - -OP_LCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,0 - jne lctrl_1 - GO_ON j_lctrl_0, lctrl_1, 8 - j_lctrl_0: - mov eax,code ; 1=COD - lctrl_1: - cmp eax,1 - jne lctrl_2 - GO_ON j_lctrl_1, lctrl_2, 8 - j_lctrl_1: - mov eax,edi ; 1=DAT - lctrl_2: - cmp eax,2 - jne lctrl_3 - GO_ON j_lctrl_2, lctrl_3, 8 - j_lctrl_2: - mov eax,hea ; 2=HEA - lctrl_3: - cmp eax,3 - jne lctrl_4 - GO_ON j_lctrl_3, lctrl_4, 8 - j_lctrl_3: - mov ebp,amx - mov eax,[ebp+_stp] - lctrl_4: - cmp eax,4 - jne lctrl_5 - GO_ON j_lctrl_4, lctrl_5, 8 - j_lctrl_4: - mov eax,esp ; 4=STK - sub eax,edi - lctrl_5: - cmp eax,5 - jne lctrl_6 - GO_ON j_lctrl_5, lctrl_6, 8 - j_lctrl_5: - mov eax,frm ; 5=FRM - lctrl_6: - mov DWORD ptr [j_lctrl_6+1],edi - GO_ON j_lctrl_6, OP_SCTRL, 8 - j_lctrl_6: - mov eax,12345678h ; 6=CIP - - -OP_SCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,2 - jne sctrl_4 - GO_ON j_sctrl_2, sctrl_4, 8 - j_sctrl_2: - mov hea,eax ; 2=HEA - sctrl_4: - cmp eax,4 - jne sctrl_5 - GO_ON j_sctrl_4, sctrl_5, 8 - j_sctrl_4: - ;mov esp,eax ; 4=STK - ;add esp,edi ; relocate stack - lea esp,[eax+edi] - sctrl_5: - cmp eax,5 - jne sctrl_ignore - GO_ON j_sctrl_5, sctrl_ignore, 8 - j_sctrl_5: - mov ebx,eax ; 5=FRM - mov frm,eax - add ebx,edi ; relocate frame - sctrl_ignore: - mov [ebx],edi - add ebx,8 - jmp DWORD ptr [ebx] - -OP_MOVE_PRI: -;nop; - GO_ON j_move_pri, OP_MOVE_ALT - - j_move_pri: - mov eax,edx - -;good -OP_MOVE_ALT: -;nop; - GO_ON j_move_alt, OP_XCHG - - j_move_alt: - mov edx,eax - -OP_XCHG: -;nop; - GO_ON j_xchg, OP_PUSH_PRI - - j_xchg: ;one might use pushes/pops for pre-586's - xchg eax,edx - -;good -OP_PUSH_PRI: -;nop; - GO_ON j_push_pri, OP_PUSH_ALT - - j_push_pri: - _PUSH eax - -;good -OP_PUSH_ALT: -;nop; - GO_ON j_push_alt, OP_PICK - - j_push_alt: - _PUSH edx - -OP_PICK: -;nop; - putval j_pick+2 - GO_ON j_pick, OP_PUSH_C, 8 - - j_pick: - mov eax,[esp+12345678h] - -;good -OP_PUSH_C: -;nop; - putval j_push_c+1 - GO_ON j_push_c, OP_PUSH, 8 - - j_push_c: - _PUSH 12345678h - -OP_PUSH: -;nop; - putval j_push+2 - GO_ON j_push, OP_PUSH_S, 8 - - j_push: - _PUSH [edi+12345678h] - -;good -OP_PUSH_S: -;nop; - putval j_push_s+2 - GO_ON j_push_s, OP_POP_PRI, 8 - - j_push_s: - _PUSH [ebx+12345678h] - -OP_POP_PRI: -;nop; - GO_ON j_pop_pri, OP_POP_ALT - - j_pop_pri: - _POP eax - -;good -OP_POP_ALT: -;nop; - GO_ON j_pop_alt, OP_STACK - - j_pop_alt: - _POP edx - -;good -OP_STACK: -;nop; - putval j_stack+4 - GO_ON j_stack, OP_HEAP, 8 - - j_stack: - mov edx,esp - add esp,12345678h - sub edx,edi -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [chk_marginstack] -ENDIF - -;good -OP_HEAP: -;nop; - putval j_heap_call-4 - GO_ON j_heap, OP_PROC, 8 - - j_heap: - mov edx,hea - add DWORD ptr hea,12345678h - j_heap_call: -IF DORUNTIMECHECKS NE 0 - call DWORD ptr [chk_marginheap] -ENDIF -;good -OP_PROC: -;nop; - GO_ON j_proc, OP_RET - - j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK - _PUSH frm ; push old frame (for RET/RETN) - mov frm,esp ; get new frame - mov ebx,esp ; already relocated - sub frm,edi ; relocate frame - -OP_RET: -;nop; - GO_ON j_ret, OP_RETN - - j_ret: - _POP ebx ; pop frame - mov frm,ebx - add ebx,edi - ret - ;call [jit_ret] - -;good -OP_RETN: -;nop; - GO_ON j_retn, OP_CALL - - j_retn: - jmp DWORD ptr [jit_retn] - -;good -OP_CALL: -;nop; - RELOC 1 - GO_ON j_call, OP_CALL_I, 8 - - j_call: - ;call 12345678h ; tasm chokes on this out of a sudden - db 0e8h, 0, 0, 0, 0 - -OP_CALL_I: -;nop; - GO_ON j_call_i, OP_JUMP - - j_call_i: - call eax - -;good -OP_JUMP: -;nop; - RELOC 1 - GO_ON j_jump, OP_JREL, 8 - - j_jump: - DB 0e9h - DD 12345678h - -OP_JREL: -;nop; - mov eax,[ebx+4] - ; create an absolute address from the relative one - RELOC 1, eax+ebx+8 - GO_ON j_jump, OP_JREL, 8 - -;good -OP_JZER: -;nop; - RELOC 4 - GO_ON j_jzer, OP_JNZ, 8 - - j_jzer: - or eax,eax - DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) - -;good -OP_JNZ: -;nop; - RELOC 4 - GO_ON j_jnz, OP_JEQ, 8 - - j_jnz: - or eax,eax - DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 - -;good -OP_JEQ: -;nop; - RELOC 4 - GO_ON j_jeq, OP_JNEQ, 8 - - j_jeq: - cmp eax,edx - DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) - -OP_JNEQ: -;nop; - RELOC 4 - GO_ON j_jneq, OP_JLESS, 8 - - j_jneq: - cmp eax,edx - DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) - -OP_JLESS: -;nop; - RELOC 4 - GO_ON j_jless, OP_JLEQ, 8 - - j_jless: - cmp eax,edx - DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) - -OP_JLEQ: -;nop; - RELOC 4 - GO_ON j_jleq, OP_JGRTR, 8 - - j_jleq: - cmp eax,edx - DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) - -OP_JGRTR: -;nop; - RELOC 4 - GO_ON j_jgrtr, OP_JGEQ, 8 - - j_jgrtr: - cmp eax,edx - DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) - -OP_JGEQ: -;nop; - RELOC 4 - GO_ON j_jgeq, OP_JSLESS, 8 - - j_jgeq: - cmp eax,edx - DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) - -OP_JSLESS: -;nop; - RELOC 4 - GO_ON j_jsless, OP_JSLEQ, 8 - - j_jsless: - cmp eax,edx - DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 - -;good -OP_JSLEQ: -;nop; - RELOC 4 - GO_ON j_jsleq, OP_JSGRTR, 8 - - j_jsleq: - cmp eax,edx - DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 - -OP_JSGRTR: -;nop; - RELOC 4 - GO_ON j_jsgrtr, OP_JSGEQ, 8 - - j_jsgrtr: - cmp eax,edx - DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 - -OP_JSGEQ: -;nop; - RELOC 4 - GO_ON j_jsgeq, OP_SHL, 8 - - j_jsgeq: - cmp eax,edx - DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 - -OP_SHL: -;nop; - GO_ON j_shl, OP_SHR - j_shl: - mov ecx,edx ; TODO: save ECX if used as special register - shl eax,cl - -OP_SHR: -;nop; - GO_ON j_shr, OP_SSHR - j_shr: - mov ecx,edx ; TODO: save ECX if used as special register - shr eax,cl - -OP_SSHR: -;nop; - GO_ON j_sshr, OP_SHL_C_PRI - j_sshr: - mov ecx,edx ; TODO: save ECX if used as special register - sar eax,cl - -OP_SHL_C_PRI: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shl_c_pri+2],al - GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 - j_shl_c_pri: - shl eax,12h - -OP_SHL_C_ALT: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shl_c_alt+2],al - GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 - j_shl_c_alt: - shl edx,12h - -OP_SHR_C_PRI: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shr_c_pri+2],al - GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 - j_shr_c_pri: - shr eax,12h - -OP_SHR_C_ALT: -;nop; - mov al,[ebx+4] - mov BYTE ptr [j_shr_c_alt+2],al - GO_ON j_shr_c_alt, OP_SMUL, 8 - j_shr_c_alt: - shr edx,12h - -OP_SMUL: -;nop; - GO_ON j_smul, OP_SDIV - j_smul: - push edx - imul edx - pop edx - -;good -OP_SDIV: -;nop; - GO_ON j_sdiv, OP_SDIV_ALT - j_sdiv: - call DWORD ptr [jit_sdiv] - -OP_SDIV_ALT: -;nop; - GO_ON j_sdiv_alt, OP_UMUL - j_sdiv_alt: - xchg eax,edx - call DWORD ptr [jit_sdiv] - -OP_UMUL: -;nop; - GO_ON j_umul, OP_UDIV - j_umul: - push edx - mul edx - pop edx - -OP_UDIV: -;nop; - GO_ON j_udiv, OP_UDIV_ALT - j_udiv: - mov ebp,edx - sub edx,edx - call DWORD ptr [chk_dividezero] - div ebp - -OP_UDIV_ALT: -;nop; - GO_ON j_udiv_alt, OP_ADD - j_udiv_alt: - mov ebp,eax - mov eax,edx - sub edx,edx - call DWORD ptr [chk_dividezero] - div ebp - -;good -OP_ADD: -;nop; - GO_ON j_add, OP_SUB - j_add: - add eax,edx - -;good -OP_SUB: -;nop; - GO_ON j_sub, OP_SUB_ALT - j_sub: - sub eax,edx - -;good -OP_SUB_ALT: -;nop; - GO_ON j_sub_alt, OP_AND - j_sub_alt: - neg eax - add eax,edx - -OP_AND: -;nop; - GO_ON j_and, OP_OR - j_and: - and eax,edx - -OP_OR: -;nop; - GO_ON j_or, OP_XOR - j_or: - or eax,edx - -OP_XOR: -;nop; - GO_ON j_xor, OP_NOT - j_xor: - xor eax,edx - -OP_NOT: -;nop; - GO_ON j_not, OP_NEG - j_not: - neg eax ; sets CF iff EAX != 0 - sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) - inc eax ; -1 => 0 and 0 => 1 - -OP_NEG: -;nop; - GO_ON j_neg, OP_INVERT - j_neg: - neg eax - -OP_INVERT: -;nop; - GO_ON j_invert, OP_ADD_C - j_invert: - not eax - -;good -OP_ADD_C: -;nop; - putval j_add_c+1 - GO_ON j_add_c, OP_SMUL_C, 8 - j_add_c: - add eax,12345678h - -;good -OP_SMUL_C: -;nop; - putval j_smul_c+3 - GO_ON j_smul_c, OP_ZERO_PRI, 8 - j_smul_c: - push edx - imul eax,12345678h - pop edx - -;good -OP_ZERO_PRI: -;nop; - GO_ON j_zero_pri, OP_ZERO_ALT - j_zero_pri: - sub eax,eax - -;good -OP_ZERO_ALT: -;nop; - GO_ON j_zero_alt, OP_ZERO - j_zero_alt: - sub edx,edx - -OP_ZERO: -;nop; - putval j_zero+2 - GO_ON j_zero, OP_ZERO_S, 8 - j_zero: - mov DWORD ptr [edi+12345678h],0 - -OP_ZERO_S: -;nop; - putval j_zero_s+2 - GO_ON j_zero_s, OP_SIGN_PRI, 8 - j_zero_s: - mov DWORD ptr [ebx+12345678h],0 - -OP_SIGN_PRI: -;nop; - GO_ON j_sign_pri, OP_SIGN_ALT - j_sign_pri: - shl eax,24 - sar eax,24 - -OP_SIGN_ALT: -;nop; - GO_ON j_sign_alt, OP_EQ - j_sign_alt: - shl edx,24 - sar edx,24 - -OP_EQ: -;nop; - GO_ON j_eq, OP_NEQ - j_eq: - cmp eax,edx ; PRI == ALT ? - mov eax,0 - sete al - -OP_NEQ: -;nop; - GO_ON j_neq, OP_LESS - j_neq: - cmp eax,edx ; PRI != ALT ? - mov eax,0 - setne al - -OP_LESS: -;nop; - GO_ON j_less, OP_LEQ - j_less: - cmp eax,edx ; PRI < ALT ? (unsigned) - mov eax,0 - setb al - -OP_LEQ: -;nop; - GO_ON j_leq, OP_GRTR - j_leq: - cmp eax,edx ; PRI <= ALT ? (unsigned) - mov eax,0 - setbe al - -OP_GRTR: -;nop; - GO_ON j_grtr, OP_GEQ - j_grtr: - cmp eax,edx ; PRI > ALT ? (unsigned) - mov eax,0 - seta al - -OP_GEQ: -;nop; - GO_ON j_geq, OP_SLESS - j_geq: - cmp eax,edx ; PRI >= ALT ? (unsigned) - mov eax,0 - setae al - -;good -OP_SLESS: -;nop; - GO_ON j_sless, OP_SLEQ - j_sless: - cmp eax,edx ; PRI < ALT ? (signed) - mov eax,0 - setl al - -OP_SLEQ: -;nop; - GO_ON j_sleq, OP_SGRTR - j_sleq: - cmp eax,edx ; PRI <= ALT ? (signed) - mov eax,0 - setle al - -OP_SGRTR: -;nop; - GO_ON j_sgrtr, OP_SGEQ - j_sgrtr: - cmp eax,edx ; PRI > ALT ? (signed) - mov eax,0 - setg al - -OP_SGEQ: -;nop; - GO_ON j_sgeq, OP_EQ_C_PRI - j_sgeq: - cmp eax,edx ; PRI >= ALT ? (signed) - mov eax,0 - setge al - -OP_EQ_C_PRI: -;nop; - putval j_eq_c_pri+1 - GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 - j_eq_c_pri: - cmp eax,12345678h ; PRI == value ? - mov eax,0 - sete al - -OP_EQ_C_ALT: -;nop; - putval j_eq_c_alt+4 - GO_ON j_eq_c_alt, OP_INC_PRI, 8 - j_eq_c_alt: - sub eax,eax - cmp edx,12345678h ; ALT == value ? - sete al - -OP_INC_PRI: -;nop; - GO_ON j_inc_pri, OP_INC_ALT - j_inc_pri: - inc eax - -OP_INC_ALT: -;nop; - GO_ON j_inc_alt, OP_INC - j_inc_alt: - inc edx - -OP_INC: -;nop; - putval j_inc+2 - GO_ON j_inc, OP_INC_S, 8 - j_inc: - inc DWORD ptr [edi+12345678h] - -;good -OP_INC_S: -;nop; - putval j_inc_s+2 - GO_ON j_inc_s, OP_INC_I, 8 - j_inc_s: - inc DWORD ptr [ebx+12345678h] - -OP_INC_I: -;nop; - GO_ON j_inc_i, OP_DEC_PRI - j_inc_i: - inc DWORD ptr [edi+eax] - -OP_DEC_PRI: -;nop; - GO_ON j_dec_pri, OP_DEC_ALT - j_dec_pri: - dec eax - -OP_DEC_ALT: -;nop; - GO_ON j_dec_alt, OP_DEC - j_dec_alt: - dec edx - -OP_DEC: -;nop; - putval j_dec+2 - GO_ON j_dec, OP_DEC_S, 8 - j_dec: - dec DWORD ptr [edi+12345678h] - -OP_DEC_S: -;nop; - putval j_dec_s+2 - GO_ON j_dec_s, OP_DEC_I, 8 - j_dec_s: - dec DWORD ptr [ebx+12345678h] - -OP_DEC_I: -;nop; - GO_ON j_dec_i, OP_MOVS - j_dec_i: - dec DWORD ptr [edi+eax] - -OP_MOVS: -;nop; - putval j_movs+1 - GO_ON j_movs, OP_CMPS, 8 - j_movs: - mov ecx,12345678h ;TODO: save ECX if used as special register - call DWORD ptr [jit_movs] - -OP_CMPS: -;nop; - putval j_cmps+1 - GO_ON j_cmps, OP_FILL, 8 - j_cmps: - mov ecx,12345678h ;TODO: save ECX if used as special register - call DWORD ptr [jit_cmps] - -OP_FILL: -;nop; - putval j_fill+1 - GO_ON j_fill, OP_HALT, 8 - j_fill: - mov ecx,12345678h ;TODO: save ECX if used as special register - call DWORD ptr [jit_fill] - -;good -OP_HALT: -;nop; - putval j_halt_sm+1 - GO_ON j_halt, OP_BOUNDS, 8 - j_halt: - cmp DWORD PTR retval,0 - je j_halt_no_value - mov ebp,retval - mov [ebp],eax - j_halt_no_value: - j_halt_sm: - mov eax,12345678h - jmp DWORD ptr [jit_return] - -;good -OP_BOUNDS: -;nop; - putval j_bounds+1 - GO_ON j_bounds, OP_SYSREQ_C, 8 - j_bounds: - mov ebp,12345678h - call DWORD ptr [jit_bounds] - -;good -OP_SYSREQ_C: -;nop; - putval j_sysreq_c+1 - GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 - j_sysreq_c: - mov eax,12345678h ; get function number - j_sysreq: - call DWORD ptr [jit_sysreq] - -OP_SYSREQ_PRI: -;nop; - GO_ON j_sysreq, OP_SYSREQ_PRI - -OP_FILE: ;opcode is simply ignored -;nop; - mov eax,[ebx+4] ;get size - mov [ebx],edi - lea ebx,[ebx+eax+8] ;move on to next opcode - cmp ebx,DWORD ptr [end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ;go on with the next opcode - -OP_LINE: ;ignored -;nop; - mov [ebx],edi ; no line number support: ignore opcode - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - -OP_SYMBOL: ;ignored - mov [ebx],edi - mov eax,[ebx+4] ; get size - lea ebx,[ebx+eax+8] ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -OP_SRANGE: ;ignored - mov [ebx],edi ; store relocated address - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -;not tested -OP_JUMP_PRI: - GO_ON j_jump_pri, OP_SWITCH - - j_jump_pri: ; MP: This opcode makes sense only in con- - jmp [eax] ; junction with a possibility to get the - ; address of a code location... - - -;good -OP_SWITCH: - lea eax,[edi+6] ; The case table will be copied directly - neg eax ; after the run-time call to [jit_switch]. - and eax,3 ; We should align this table on a DWORD - mov ecx,eax ; boundary. - mov al,90h ; 90h = opcode of x86 NOP instruction - rep stosb ; Write the right number of NOPs. - mov [ebx],edi ; store address of SWITCH for relocation step - mov esi,OFFSET j_switch - mov ecx,6 - rep movsb ; copy the call instruction - mov esi,[ebx+4] ; get address of CASETBL instruction - add ebx,8 ; set instruction pointer to next opcode - add esi,4 ; point esi to first entry: (count, default adr) - mov ecx,[esi] ; get number of cases (excluding default) - inc ecx - mov ebp,[reloc_num] - j_case_loop: - mov eax,[esi] ; get case value - stosd ; write it - mov eax,[esi+4] ; get destination address -IF FORCERELOCATABLE EQ 0 - or eax,80000000h ; add flag for "absolute address" -ENDIF - mov [edx+ebp],eax ; write dest. adr. into relocation table - mov eax,[esi+4] ; get destination address (again) - add esi,8 ; set ESI to next case - mov [edx+ebp+4],edi ; write adr. to patch into relocation table - add ebp,8 ; promote relocation pointer - stosd ; write dest. adr. - dec ecx - jnz j_case_loop - mov DWORD ptr [reloc_num],ebp ; write back updated reloc_num - - jmp [ebx] ; GO_ON to next op-code - - j_switch: - call DWORD ptr [jit_switch] - -;good -OP_CASETBL: ; compiles to nothing, SWITCH does all the work - mov eax,[ebx+4] ; get count of cases - lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer - jmp [ebx] ; GO_ON with next op-code - - -OP_SWAP_PRI: ; TR - GO_ON j_swap_pri, OP_SWAP_ALT - - j_swap_pri: - _POP ebp - _PUSH eax - mov eax,ebp - - -OP_SWAP_ALT: ; TR - GO_ON j_swap_alt, OP_PUSHADDR - - j_swap_alt: - _POP ebp - _PUSH edx - mov edx,ebp - - -OP_PUSHADDR: ; TR - putval j_pushaddr+1 - GO_ON j_pushaddr, OP_NOP, 8 - - j_pushaddr: - mov ebp,12345678h ;get address (offset from frame) - add ebp,frm - _PUSH ebp - - -OP_NOP: ; TR - GO_ON j_nop, OP_SYSREQ_D - j_nop: ; code alignment is ignored by the JIT - - -OP_SYSREQ_D: -;nop; - putval j_sysreq_d+1 - GO_ON j_sysreq_d, OP_SYMTAG, 8 - j_sysreq_d: - mov ebx,12345678h ; get function address - call DWORD ptr [jit_sysreq_d] - - -OP_SYMTAG: ;ignored (TR) - mov [ebx],edi ; store relocated address - add ebx,8 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode - - -OP_BREAK: -IF DEBUGSUPPORT EQ 0 - mov [ebx],edi ; no line number support: ignore opcode - add ebx,4 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD ptr [ebx] ; go on with the next opcode -ELSE - GO_ON j_break, OP_INVALID - j_break: - mov ebp,amx - cmp DWORD [ebp+_debug], 0 - je $+4 ; jump around the "call" statement - call [jit_break] -ENDIF - -OP_INVALID: ; break from the compiler with an error code - mov eax,AMX_ERR_INVINSTR - pop esi - pop edi - pop ecx - pop ebp - ret - - -_asm_runJIT ENDP - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -;cell asm_exec_jit( AMX *amx, cell *retval, char *data ) ; -; ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -_amx_exec_jit PROC - - push edi - push esi - push ebp - push ebx ; due to __cdecl - - ; __cdecl overhead - mov eax, [esp+20] ; get address of AMX structure - mov edx, [esp+24] ; get address of retval - mov ebx, [esp+28] ; get pointer to data section - - sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - push DWORD ptr [eax+_codeseg] ; store pointer to code segment - push eax ; store pointer to AMX - push edx ; store address of retval - push DWORD ptr [eax+_stp] ; store STP - push DWORD ptr [eax+_hea] ; store HEA - push DWORD ptr [eax+_frm] ; store FRM - - stk equ [esi+32] ; define some aliases to registers that will - alt equ [esi+28] ; be stored on the stack when the code is - pri equ [esi+24] ; actually beeing executed - code equ [esi+20] - amx equ [esi+16] - retval equ [esi+12] - stp equ [esi+8] - hea equ [esi+4] - frm equ [esi] ; FRM is NOT stored in ebp, FRM+DAT is being held - ; in ebx instead. - - mov edi,ebx ; get pointer to data segment - mov edx,[eax+_alt] ; get ALT - mov ecx,[eax+_cip] ; get CIP (N.B. different from ASM interpreter) - mov esi,[eax+_stk] ; get STK (N.B. different from ASM interpreter) - mov ebx,[eax+_frm] ; get FRM - mov eax,[eax+_pri] ; get PRI - add ebx,edi ; relocate frame - - add esi,edi ; ESP will contain DAT+STK - xchg esp,esi ; switch to AMX stack - - add stp,edi ; make STP absolute address for run-time checks - - _POP ebp ; AMX pseudo-return address, ignored - ; Call compiled code via CALL NEAR
- call ecx - -return_to_caller: - cmp DWORD PTR retval,0 - je return_to_caller_no_value - mov ebp,retval - mov [ebp],eax ; provide return value - - return_to_caller_no_value: - mov eax,AMX_ERR_NONE - jmp _return - -_return_popstack: - add esp,4 ; Correct ESP, because we just come from a - ; runtime error checking routine. -_return: - ; store machine state - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... - mov ecx,hea ; ... HEA, ... - mov [ebp+_hea],ecx - mov ecx,ebx ; ... and FRM - sub ecx,edi ; (correct FRM) - mov [ebp+_frm],ecx - mov [ebp+_pri],eax ; also store PRI, ... - mov [ebp+_alt],edx ; ... and ALT - - ; return - sub stp,edi ; make STP relative to DAT again - xchg esp,esi ; switch back to caller's stack - - add esp,4*9 ; remove temporary data - - pop ebx ; restore registers that have to be preserved - pop ebp ; when using __cdecl convention - pop esi - pop edi - - ret - - -err_stack: - mov eax,AMX_ERR_STACKERR - jmp _return_popstack - -err_stacklow: - mov eax,AMX_ERR_STACKLOW - jmp _return_popstack - -_CHKMARGIN_STACK: ; some run-time check routines - cmp esp,stp - lea ebp,[esp-STACKRESERVE] - jg err_stacklow - sub ebp,edi - cmp hea,ebp - jg err_stack - ret - -err_heaplow: - mov eax,AMX_ERR_HEAPLOW - jmp _return_popstack - -_CHKMARGIN_HEAP: - cmp esp,stp - jg err_stacklow - mov ebp,amx - mov ebp,[ebp+_hlw] - cmp DWORD ptr hea,ebp - jl err_heaplow - ret - -err_memaccess: - mov eax,AMX_ERR_MEMACCESS - jmp _return_popstack - -_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx - cmp eax,stp - jae err_memaccess - cmp eax,hea - jb veax1 - lea ebp,[eax+edi] - cmp ebp,esp - jb err_memaccess - veax1: - ret - -_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx - cmp edx,stp - jae err_memaccess - cmp edx,hea - jb vedx1 - lea ebp,[edx+edi] - cmp ebp,esp - jb err_memaccess - vedx1: - ret - -JIT_OP_SDIV: - mov ebp,edx - xor edx,eax ; Check signs of the operands. - cdq - js sdiv_fiddle ; If the signs of the operands are different - ; we'll have to fiddle around to achieve - ; proper rounding towards minus infinity. - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp ; default behavior is right in the other cases - ret - - sdiv_fiddle: - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp - or edx,edx - jz sdiv_goon ; If there's no remainder the result is correct - add edx,ebp ; else fix the result values. - dec eax ; Amazing, how simple this is... - sdiv_goon: - ret - - ALIGN 4 - -JIT_OP_RETN: - _POP ebx ; pop frame - _POP ecx ; get return address - - mov frm,ebx - _POP ebp - - add ebx,edi - add esp,ebp ; remove data from stack - - jmp ecx - - -JIT_OP_MOVS: ;length of block to copy is already in ECX - push edi - push esi - lea esi,[edi+eax] - lea edi,[edi+edx] - - push ecx ; I hope the blocks to copy are properly - shr ecx,2 ; aligned, so I don't do anything about that. - rep movsd - pop ecx - and ecx,3 - rep movsb - - pop esi - pop edi - ret - -JIT_OP_CMPS: ;length of block to compare is already in ECX - push edi - push esi - lea esi,[edi+edx] - lea edi,[edi+eax] - - xor eax,eax ; This is surely not the fastest way to do this - repe cmpsb ; but the most simple one. - je cmps1 - sbb eax,eax - sbb eax,0ffffffffh - cmps1: - pop esi - pop edi - ret - - -JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX - push edi - lea edi,[edi+edx] - - shr ecx,2 ;length in 32-bit cells - rep stosd ;the value to use is already in EAX - - pop edi - ret - -JIT_OP_BOUNDS: - cmp eax,0 - jl err_bounds - cmp eax,ebp - jg err_bounds - ret -err_bounds: - mov eax,AMX_ERR_BOUNDS - jmp _return_popstack - -_CHKDIVIDEZERO: - or ebp,ebp ; check for divide by zero - jz err_divide - ret -err_divide: - mov eax,AMX_ERR_DIVIDE - jmp _return_popstack - -JIT_OP_SYSREQ: - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; ebx & ecx are invalid by now - - lea ebx,pri ; 3rd param: addr. of retval - lea ecx,[esp+4] ; 4th param: parameter array - - xchg esp,esi ; switch to caller stack - - push ecx - push ebx - push eax ; 2nd param: function number - push ebp ; 1st param: amx - call [ebp+_callback] - _DROPARGS 16 ; remove args from stack - - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov eax,pri ; get retval into eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_SYSREQ_D: ; (TR) - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov eax,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],eax ; eax & ecx are invalid by now - - lea edx,[esp+4] ; 2nd param: parameter array - xchg esp,esi ; switch to caller stack - push edx - push ebp ; 1st param: amx - call ebx ; direct call - _DROPARGS 8 ; remove args from stack - xchg esp,esi ; switch back to AMX stack - mov ebp,amx ; get amx into EBP - cmp [ebp+_error],AMX_ERR_NONE - jne _return_popstack; return error code, if any - - ; return value is in eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_BREAK: -IF DEBUGSUPPORT EQ 0 -ELSE - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - - mov [ebp+_pri],eax ; store values in AMX structure: PRI, - mov [ebp+_alt],edx ; ALT, - mov [ebp+_stk],ecx ; STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - ;??? storing CIP is not very useful, because the code changed (during JIT compile) - - xchg esp,esi ; switch to caller stack - push ebp ; 1st param: amx - call [ebp+_debug] - _DROPARGS 4 ; remove args from stack - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov ebp,amx ; get amx into EBP - mov eax,[ebp+_pri] ; restore values - mov edx,[ebp+_alt] ; ALT, - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame -ENDIF - ret - - -JIT_OP_SWITCH: - pop ebp ; pop return address = table address - mov ecx,[ebp] ; ECX = number of records - lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case - jecxz op_switch_jump ; special case: no cases at all - op_switch_loop: - cmp eax,[ebp-8] ; PRI == case label? - je op_switch_jump ; found, jump - sub ebp,8 ; position to preceding case - loop op_switch_loop ; check next case, or fall through - op_switch_jump: -IF FORCERELOCATABLE EQ 0 - jmp [ebp-4] ; jump to the case instructions -ELSE - add ebp,[ebp-4] ; add offset to make absolute adddress - jmp ebp -ENDIF - -_amx_exec_jit ENDP - -; -; The caller of asm_runJIT() can determine the maximum size of the compiled -; code by multiplying the result of this function by the number of opcodes in -; Pawn module. -; -; unsigned long getMaxCodeSize_(); -; -_getMaxCodeSize PROC - - mov eax,MAXCODESIZE - ret - -_getMaxCodeSize ENDP - - -IFNDEF @Version - ; Microsoft MASM 6.x gives the error message "Register assumed to - ; ERROR" when I put the code lables in the data segment. I cannot find - ; a proper way around it. -.DATA -ENDIF - ALIGN 4 ; This is essential to avoid misalignment stalls. - -end_code DD 0 ; pointer to the end of the source code - -compiled_code DD 0 ; pointer to compiled code (including preamble) - -amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) - -reloc_num DD 0 ; counts the addresses in the relocation table (jumps) - -lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh - -; -; A list of the "run-time-library" functions that are called via indirect calls. -; So these calls don't have to be relocated. This gives also the possibility to -; replace some of these with shorter/faster non-debug or non-checking versions, -; without changing the compiled code. Instead this table could be changed... -; -verify_adr_eax DD _VERIFYADDRESS_eax -verify_adr_edx DD _VERIFYADDRESS_edx -chk_marginstack DD _CHKMARGIN_STACK -chk_marginheap DD _CHKMARGIN_HEAP -chk_dividezero DD _CHKDIVIDEZERO -jit_return DD _return -jit_retn DD JIT_OP_RETN -jit_sdiv DD JIT_OP_SDIV -jit_movs DD JIT_OP_MOVS -jit_cmps DD JIT_OP_CMPS -jit_fill DD JIT_OP_FILL -jit_bounds DD JIT_OP_BOUNDS -jit_sysreq DD JIT_OP_SYSREQ -jit_sysreq_d DD JIT_OP_SYSREQ_D -jit_break DD JIT_OP_BREAK -jit_switch DD JIT_OP_SWITCH - -; -; The table for the browser/relocator function. -; - PUBLIC _amx_opcodelist_jit -_amx_opcodelist_jit: - DD OP_INVALID - DD OP_LOAD_PRI - DD OP_LOAD_ALT - DD OP_LOAD_S_PRI - DD OP_LOAD_S_ALT - DD OP_LREF_PRI - DD OP_LREF_ALT - DD OP_LREF_S_PRI - DD OP_LREF_S_ALT - DD OP_LOAD_I - DD OP_LODB_I - DD OP_CONST_PRI - DD OP_CONST_ALT - DD OP_ADDR_PRI - DD OP_ADDR_ALT - DD OP_STOR_PRI - DD OP_STOR_ALT - DD OP_STOR_S_PRI - DD OP_STOR_S_ALT - DD OP_SREF_PRI - DD OP_SREF_ALT - DD OP_SREF_S_PRI - DD OP_SREF_S_ALT - DD OP_STOR_I - DD OP_STRB_I - DD OP_LIDX - DD OP_LIDX_B - DD OP_IDXADDR - DD OP_IDXADDR_B - DD OP_ALIGN_PRI - DD OP_ALIGN_ALT - DD OP_LCTRL - DD OP_SCTRL - DD OP_MOVE_PRI - DD OP_MOVE_ALT - DD OP_XCHG - DD OP_PUSH_PRI - DD OP_PUSH_ALT - DD OP_PICK - DD OP_PUSH_C - DD OP_PUSH - DD OP_PUSH_S - DD OP_POP_PRI - DD OP_POP_ALT - DD OP_STACK - DD OP_HEAP - DD OP_PROC - DD OP_RET - DD OP_RETN - DD OP_CALL - DD OP_CALL_I - DD OP_JUMP - DD OP_JREL - DD OP_JZER - DD OP_JNZ - DD OP_JEQ - DD OP_JNEQ - DD OP_JLESS - DD OP_JLEQ - DD OP_JGRTR - DD OP_JGEQ - DD OP_JSLESS - DD OP_JSLEQ - DD OP_JSGRTR - DD OP_JSGEQ - DD OP_SHL - DD OP_SHR - DD OP_SSHR - DD OP_SHL_C_PRI - DD OP_SHL_C_ALT - DD OP_SHR_C_PRI - DD OP_SHR_C_ALT - DD OP_SMUL - DD OP_SDIV - DD OP_SDIV_ALT - DD OP_UMUL - DD OP_UDIV - DD OP_UDIV_ALT - DD OP_ADD - DD OP_SUB - DD OP_SUB_ALT - DD OP_AND - DD OP_OR - DD OP_XOR - DD OP_NOT - DD OP_NEG - DD OP_INVERT - DD OP_ADD_C - DD OP_SMUL_C - DD OP_ZERO_PRI - DD OP_ZERO_ALT - DD OP_ZERO - DD OP_ZERO_S - DD OP_SIGN_PRI - DD OP_SIGN_ALT - DD OP_EQ - DD OP_NEQ - DD OP_LESS - DD OP_LEQ - DD OP_GRTR - DD OP_GEQ - DD OP_SLESS - DD OP_SLEQ - DD OP_SGRTR - DD OP_SGEQ - DD OP_EQ_C_PRI - DD OP_EQ_C_ALT - DD OP_INC_PRI - DD OP_INC_ALT - DD OP_INC - DD OP_INC_S - DD OP_INC_I - DD OP_DEC_PRI - DD OP_DEC_ALT - DD OP_DEC - DD OP_DEC_S - DD OP_DEC_I - DD OP_MOVS - DD OP_CMPS - DD OP_FILL - DD OP_HALT - DD OP_BOUNDS - DD OP_SYSREQ_PRI - DD OP_SYSREQ_C - DD OP_FILE - DD OP_LINE - DD OP_SYMBOL - DD OP_SRANGE - DD OP_JUMP_PRI - DD OP_SWITCH - DD OP_CASETBL - DD OP_SWAP_PRI ; TR - DD OP_SWAP_ALT ; TR - DD OP_PUSHADDR ; TR - DD OP_NOP ; TR - DD OP_SYSREQ_D ; TR - DD OP_SYMTAG ; TR - DD OP_BREAK ; TR - -END +; ASMJITS.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" +; scripting language +; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES + +; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" +; (27 disks, no output, DOS4/GW under Win95) with this implementation of the +; JIT compiler. + +; NOTE 1: +; There is only one pass implemented in this version. This means there is no +; way of knowing the size of the compiled code before it has actually been com- +; piled. So the only chance the caller has, is to count the number of opcodes +; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to +; obtain a size value big enough to hold the entire code (and data, including +; the stack and heap, after adding their sizes). Afterwards it can realloc() +; this memory block to the actually needed smaller size. + +; NOTE 2: +; The compiler destroys the opcode addresses of the given source by storing the +; respective compiled code's addresses there for the final address relocation +; step. + +; NOTE 3: +; During execution of the compiled code with amx_exec_jit() the x86 processor's +; stack is switched into the data section of the abstract machine. This means +; that there should always be enough memory left between HEA and STK to provide +; stack space for occurring interrupts! (see the STACKRESERVE variable) + +; NOTE 4: +; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I +; instructions, I have to tell that they don't work as expected in a JIT +; compiled program, because there is no easy way of transforming AMX code +; addresses and JIT translated ones. This might be fixed in a future version. + +; NX ("No eXecute") and XD (eXecution Denied) bits +; (by Thiadmer Riemersma) +; +; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit +; processors) and Intel came with the same design, but calling it differently. +; The purpose is to make "buffer overrun" security holes impossible, or at least +; very, very difficult, by marking the stack and the heap as memory regions +; such that an attempt to execute processor instructions will cause a processor +; exception (of course, a buffer overrun that is not explictly handled will then +; crash the application --instead of executing the rogue code). +; +; For JIT compilers, this has the impact that you are not allowed to execute the +; code that the JIT has generated. To do that, you must adjust the attributes +; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to +; allocate a memory block with the appropriate fags; on Linux (with a recent +; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the +; function VirtualProtect() to change the page attributes of an existing memory +; block, but there are caveats in its use: if the block spans multiple pages, +; these pages must be consecutive, and if there are blocks of memory in a page +; unrelated to the JIT, their page attributes will change too. +; +; The JIT compiler itself requires only read-write access (this is the default +; for a memory block that you allocate). The execution of the JIT-compiled code +; requires full access to the memory block: read, write and execute. It needs +; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the +; first lookup (this is an optimization, look up the address of the native +; function only once). For processors that do not support the NX/XD bit, +; execution of code is implicitly supported if read access is supported. +; +; During compilation, the JIT compiler requires write-access to its own code +; segment: the JIT-compiler patches P-code parameters into its own code segment +; during compilation. This is handled in the support code for amx_InitJIT. +; +; +; CALLING CONVENTIONS +; (by Thiadmer Riemersma) +; +; This version is the JIT that uses the "stack calling convention". In the +; original implementation, this meant __cdecl; both for the calling convention +; for the _asm_runJIT routine itself as for the callback functions. +; The current release supports __stdcall for the callback functions; to +; use it, you need to assemble the file with STDECL defined (Since STDCALL is +; a reserved word on the assembler, I had to choose a different name for the +; macro, hence STDECL.) + +; Revision History +; ---------------- +; 17 february 2005 by Thiadmer Riemersma (TR) +; Addition of the BREAK opcode, removal of the older debugging opcode +; table. There should now be some debug support (if enabled during the +; build of the JIT compiler), but not enough to run a debugger: the JIT +; compiler does not keep a list that relates the code addresses of the +; P-code versus the native code. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; +; SYMTAG is a no-op). +; Support __stdcall calling convention for for the native function "hook" +; function (the __cdecl calling convention is also still supported). +; 14 October 2002 by Thiadmer Riemersma (TR) +; Corrected the amx_s structure. The _hlw field was missing, which caused +; errors for arguments to native functions that were passed by reference. +; 2002/08/05 TR +; * store the status of the abstract machine in the AMX structure upon +; return, so that the machine can be restarted (OP_SLEEP) +; * added OP_NOP (for alignment, it is ignored by the JIT) +; * make sure the JIT does not crash when we NULL is passed for the +; return value +; 2000/03/03 MP +; * _amx_opcodelist is equipped with an underscore, again 8-P +; * added SRANGE as a no-op, so debugging info doesn't upset the JIT +; compiler anymore +; * added note about LCTRL, SCTRL and CALL.I +; 2000/03/02 MP +; * made JIT support __cdecl calling conventions +; * removed some rather unnecessary pops in the epilog of amx_exec_asm +; * changed the template for CALL into a DB byte sequence (tasm 4.1 +; didn't like the immediate value) +; 1999/12/07 MP +; * fixed crash caused by JIT compiler not saving registers +; 1999/08/06 MP - design change: closer to the "iron" with native stack +; * The JIT compiler now generates relocatable code for case tables by +; setting FORCERELOCATABLE = 1. +; * removed all debug hook code +; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ +; pushes/pops are possible +; * removed the run-time functions for the CALL, CALL_I and RET op-codes, +; they are now inline +; * All these changes gained around 80% performance increase for the +; hanoi bench. +; 1999/08/05 MP +; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address +; was stored for the LINE byte code (i.e. SWITCH would jump to totally +; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and +; OP_SCTRL (for the no-op case). +; 1999/08/04 MP +; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. +; arrays have not been tested.) +; * hacked relocation code to support absoulute addresses for CASETBL +; (This assumes that no generated address will be greater than +; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) +; * The run-time function for SWITCH uses a (hopefully) faster algorithm +; to compute the destination address: It searches backwards now. +; 1999/07/08 MP - initial revision + +.386 +.MODEL FLAT + +IFDEF @Version ; for Microsoft MASM 6.x + OPTION OLDSTRUCTS + OPTION M510 +ENDIF + +; +; Support for the BREAK opcode (callback to the debugger): 0 = no, all other +; values = yes. Beware that the compiled code runs slower when this is enabled, +; and that debug support is still fairly minimal. +; +DEBUGSUPPORT = 0 + +; +; If this is set to 1 the JIT generates relocatable code for case tables, too. +; If set to 0, a faster variant for switch (using absolute addresses) is +; generated. I consider setting it to 0 a bad idea. +; +FORCERELOCATABLE = 1 + +; +; Determines how much memory should be reserved for occurring interrupts. +; (If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes +; for interrupts that occur in real mode and are promoted to protected mode.) +; This value _MUST_ be greater than 64 (for AMX needs) and should be at least +; 128 (to serve interrupts). +; +STACKRESERVE = 256 + +; +; This variable controls the generation of memory range checks at run-time. +; You should set this to 0, only when you are sure that there are no range +; violations in your Pawn programs and you really need those 5% speed gain. +; +DORUNTIMECHECKS = 1 + +IFNDEF JIT + JIT = 1 +ENDIF +INCLUDE amxdef.asm + +; +; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +; +_PUSH MACRO v + push DWORD ptr v + ENDM + +; +; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +; +_POP MACRO v + pop DWORD ptr v + ENDM + +; +; For determining the biggest native code section generated for ONE Pawn +; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) +; +MAXCODESIZE = 0 + +; +; This is the work horse of the whole JIT: It actually copies the code. +; +GO_ON MACRO from, to, opsize ;opsize may be 4, 8 or 12 (default=4) + mov esi,OFFSET from ;get source address of JIT code + CODESIZE = to-from + mov ecx,CODESIZE ;get number of bytes to copy + ;all the following could (should?) be in a sub-routine... + ;determining the maximum size of a single code snippet + IF MAXCODESIZE LT CODESIZE + MAXCODESIZE = CODESIZE + ENDIF + mov [ebx],edi ;store address for jump-correction + IFB ;add operand size to the source ptr + add ebx,4 + ELSE + add ebx,opsize + ENDIF + rep movsb + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ;go on with the next opcode + ENDM + +; +; Modify the argument of an x86 instruction with the Pawn opcode's parameter +; before copying the code. +; +putval MACRO where + mov eax,[ebx+4] + mov DWORD ptr [where],eax + ENDM + +; +; Add an entry to the table of addresses which have to be relocated after the +; code compilation is done. +; +RELOC MACRO adr, dest + mov ebp,[reloc_num] + IFB + mov eax,[ebx+4] + ELSE + lea eax,[dest] + ENDIF + mov [edx+ebp],eax ; write absolute destination + lea eax,[edi+adr] + mov [edx+ebp+4],eax ; write address of jump operand + add DWORD ptr [reloc_num],8 + ENDM + +_DROPARGS MACRO n ; (TR) remove function arguments from the stack + IFNDEF STDECL ; (for __cdecl calling convention only) + add esp,n + ENDIF + ENDM + + +.CODE + + + PUBLIC _asm_runJIT + PUBLIC _amx_exec_jit + PUBLIC _getMaxCodeSize + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; void asm_runJIT( AMX_HEADER *amxh, JumpAddressArray *jumps, void *dest ) ; +; eax edx ebx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; asm_runJIT() assumes that the code of this module is allready browsed and +; relocated for the JIT compiler. It also assumes that both the jumps array and +; the dest memory block are large enough to hold all the data it has to write +; to them, as well as that the prefix (header) has already been copied to dest. + +_asm_runJIT PROC + push ebp + push ebx + push edi + push esi + + mov eax,[esp+20] ; get amxh + mov edx,[esp+24] ; get jumps array + mov ebx,[esp+28] ; get destination + + mov [amxhead],eax ; save pointer to AMX_HEADER struct + mov ecx,[eax+_cod] ; get offset of start of code + mov eax,[eax+_dat] ; offset of start of data = end of code + mov edi,ecx + add ecx,[amxhead] ; compute the real pointer + add eax,[amxhead] ; dito + add edi,ebx ; get write pointer into EDI + mov [compiled_code],ebx + mov [end_code],eax ; Store end-of-code address, so JIT + ; compiler knows when to stop. + mov DWORD ptr [reloc_num],0 ; init the index into the jumps array + + mov ebx,ecx + jmp DWORD ptr [ecx] ; start compiling + + ; The compiler will jump back here when code generation is complete. + +code_gen_done: ; Now copy the data section. + mov ebp,[amxhead] ; get source AMX_HEADER start address + add edi,3 ; DAT follows directly after COD + and edi,0fffffffch ; align it on a DWORD boundary + push edi ; save data start pointer + mov esi,[end_code] ; get start of data segment + mov ecx,[ebp]._h_hea + sub ecx,[ebp]._dat ; compute length of array to copy + rep movsb ; copy the data + + ; Now adjust the register values in the compiled AMX_HEADER. + ; COD stays the same since the size of AMX_HEADER doesn't change in + ; compiled mode. + mov ebx,[compiled_code] ; get compiled AMX's header address + pop esi ; recall data start pointer + sub esi,ebx ; DAT = size of code + size of prefix + mov [ebx]._dat,esi ; write corrected DAT register + + ;HEA and STP are already relative to DAT, so we don't need to fix them. + + ; Now the calls/jumps in the compiled code have to be relocated. + sub ecx,ecx ; reset offset into relocation table + cmp ecx,[reloc_num] + jae reloc_code_done ; if there's nothing to fix, skip this part + reloc_code_loop: + mov eax,[edx+ecx] ; get destination address + mov edi,[edx+ecx+4] ; determine where to write the relocated value + add ecx,8 ; set pointer to next entry in relocation table + add edi,4 ; base address from where the offset is taken +IF FORCERELOCATABLE EQ 0 + ;MP: hack to suport absolute addresses for the CASETBL instruction + test eax,80000000h ; check whether it is an absolute address + pushf + and eax,7fffffffh ; clear the flag bit for absolute addresses + popf + mov eax,[eax] ; translate into compiled absolute address + jne write_reloc ; leave out the subtraction if absolute +ELSE + mov eax,[eax] ; translate into compiled absolute address +ENDIF + sub eax,edi ; make a relative offset + write_reloc: + mov [edi-4],eax ; write the relocated address + cmp ecx,[reloc_num] + jb reloc_code_loop + +reloc_code_done: + ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) + add ebp,[ebp]._cod ; make all addresses relative to COD, not base + mov eax,[ebx]._h_cip + add eax,ebp ; get absolute source CIP + mov eax,[eax] ; translate CIP to compiled address + sub eax,ebx ; make it relative to base + sub eax,[ebx]._cod ; and now relative to COD + mov [ebx]._h_cip,eax; store relocated CIP + mov edi,[ebx]._publics + sub esi,esi + mov ecx,[ebx]._natives + sub ecx,edi ; ECX = _natives - _publics = public table size + mov si,[ebx]._defsize + or ecx,ecx + jz reloc_done ; If there are no publics, we are done. + reloc_publics_loop: + mov eax,[ebx+edi] ; get public function offset + add eax,ebp ; make it a source address + mov eax,[eax] ; translate to compiled address + sub eax,ebx ; make it an offset relative to base + sub eax,[ebx]._cod ; and now relative to COD + mov [ebx+edi],eax ; write corrected address back + add edi,esi ; step to next public function entry + sub ecx,esi + ja reloc_publics_loop + +reloc_done: + mov eax,0 + pop esi + pop edi + pop ebx + pop ebp + ret + +OP_LOAD_PRI: +;nop; + putval j_load_pri+2 + GO_ON j_load_pri, OP_LOAD_ALT, 8 + + j_load_pri: + mov eax,[edi+12345678h] + +OP_LOAD_ALT: +;nop; + putval j_load_alt+2 + GO_ON j_load_alt, OP_LOAD_S_PRI, 8 + + j_load_alt: + mov edx,[edi+12345678h] + +;good +OP_LOAD_S_PRI: +;nop; + putval j_load_s_pri+2 + GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 + + j_load_s_pri: + mov eax,[ebx+12345678h] + +;good +OP_LOAD_S_ALT: +;nop; + putval j_load_s_alt+2 + GO_ON j_load_s_alt, OP_LOAD_I, 8 + + j_load_s_alt: + mov edx,[ebx+12345678h] + +OP_LOAD_I: +;nop; + GO_ON j_load_i, OP_LODB_I + + j_load_i: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +OP_LODB_I: +;nop; + mov eax,[ebx+4] + mov eax,DWORD ptr [(lodb_and-4)+eax*4] + mov DWORD ptr [j_lodb_i_sm+1],eax ;modify AND instruction + GO_ON j_lodb_i, OP_LREF_PRI, 8 + + j_lodb_i: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_eax] +ENDIF + mov eax,[edi+eax] ;subject to misalignment stalls + j_lodb_i_sm: + and eax,12345678h + +OP_LREF_PRI: +;nop; + putval j_lref_pri+2 + GO_ON j_lref_pri, OP_LREF_ALT, 8 + + j_lref_pri: + mov eax,[edi+12345678h] + mov eax,[edi+eax] + +OP_LREF_ALT: +;nop; + putval j_lref_alt+2 + GO_ON j_lref_alt, OP_LREF_S_PRI, 8 + + j_lref_alt: + mov edx,[edi+12345678h] + mov edx,[edi+edx] + +OP_LREF_S_PRI: +;nop; + putval j_lref_s_pri+2 + GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 + + j_lref_s_pri: + mov eax,[ebx+12345678h] + mov eax,[edi+eax] + +OP_LREF_S_ALT: +;nop; + putval j_lref_s_alt+2 + GO_ON j_lref_s_alt, OP_CONST_PRI, 8 + + j_lref_s_alt: + mov edx,[ebx+12345678h] + mov edx,[edi+edx] + +;good +OP_CONST_PRI: +;nop; + putval j_const_pri+1 + GO_ON j_const_pri, OP_CONST_ALT, 8 + + j_const_pri: + mov eax,12345678h + +;good +OP_CONST_ALT: +;nop; + putval j_const_alt+1 + GO_ON j_const_alt, OP_ADDR_PRI, 8 + + j_const_alt: + mov edx,12345678h + +;good +OP_ADDR_PRI: +;nop; + putval j_addr_pri+1 + GO_ON j_addr_pri, OP_ADDR_ALT, 8 + + j_addr_pri: + mov eax,12345678h + add eax,frm + +;good +OP_ADDR_ALT: +;nop; + putval j_addr_alt+1 + GO_ON j_addr_alt, OP_STOR_PRI, 8 + + j_addr_alt: + mov edx,12345678h + add edx,frm + +OP_STOR_PRI: +;nop; + putval j_stor_pri+2 + GO_ON j_stor_pri, OP_STOR_ALT, 8 + + j_stor_pri: + mov [edi+12345678h],eax + +OP_STOR_ALT: +;nop; + putval j_stor_alt+2 + GO_ON j_stor_alt, OP_STOR_S_PRI, 8 + + j_stor_alt: + mov [edi+12345678h],edx + +;good +OP_STOR_S_PRI: +;nop; + putval j_stor_s_pri+2 + GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 + + j_stor_s_pri: + mov [ebx+12345678h],eax + +;good +OP_STOR_S_ALT: +;nop; + putval j_stor_s_alt+2 + GO_ON j_stor_s_alt, OP_STOR_I, 8 + + j_stor_s_alt: + mov [ebx+12345678h],edx + +;good +OP_STOR_I: +;nop; + GO_ON j_stor_i, OP_STRB_I + + j_stor_i: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_edx] +ENDIF + mov [edi+edx],eax + +OP_STRB_I: +;nop; + mov eax,[ebx+4] + cmp eax,1 + jne strb_not1byte + GO_ON j_strb_i_1b, strb_not1byte, 8 + j_strb_i_1b: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_edx] +ENDIF + mov [edi+edx],al + + strb_not1byte: + cmp eax,4 + je strb_4byte + GO_ON j_strb_i_2b, strb_4byte, 8 + j_strb_i_2b: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_edx] +ENDIF + mov [edi+edx],ax + + strb_4byte: + GO_ON j_strb_i_4b, OP_SREF_PRI, 8 + j_strb_i_4b: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_edx] +ENDIF + mov [edi+edx],eax + +OP_SREF_PRI: +;nop; + putval j_sref_pri+2 + GO_ON j_sref_pri, OP_SREF_ALT, 8 + + j_sref_pri: + mov ebp,[edi+12345678h] + mov [edi+ebp],eax + +OP_SREF_ALT: +;nop; + putval j_sref_alt+2 + GO_ON j_sref_alt, OP_SREF_S_PRI, 8 + + j_sref_alt: + mov ebp,[edi+12345678h] + mov [edi+ebp],edx + +OP_SREF_S_PRI: +;nop; + putval j_sref_s_pri+2 + GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 + + j_sref_s_pri: + mov ebp,[ebx+12345678h] + mov [edi+ebp],eax + +OP_SREF_S_ALT: +;nop; + putval j_sref_s_alt+2 + GO_ON j_sref_s_alt, OP_LIDX, 8 + + j_sref_s_alt: + mov ebp,[ebx+12345678h] + mov [edi+ebp],edx + +;good +OP_LIDX: +;nop; + GO_ON j_lidx, OP_LIDX_B + + j_lidx: + lea eax,[edx+4*eax] +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +OP_LIDX_B: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_lidx_b+2],al + GO_ON j_lidx_b, OP_IDXADDR, 8 + + j_lidx_b: + shl eax,12h + add eax,edx +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [verify_adr_eax] +ENDIF + mov eax,[edi+eax] + +;good +OP_IDXADDR: +;nop; + GO_ON j_idxaddr, OP_IDXADDR_B + + j_idxaddr: + lea eax,[edx+4*eax] + +OP_IDXADDR_B: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_idxaddr_b+2],al + GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 + + j_idxaddr_b: + shl eax,12h + add eax,edx + +OP_ALIGN_PRI: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov DWORD ptr [j_align_pri+1],eax + GO_ON j_align_pri, OP_ALIGN_ALT, 8 + + j_align_pri: + xor eax,12345678h + +OP_ALIGN_ALT: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov DWORD ptr [j_align_alt+1],eax + GO_ON j_align_alt, OP_LCTRL, 8 + + j_align_alt: + xor edx,12345678h + +OP_LCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,0 + jne lctrl_1 + GO_ON j_lctrl_0, lctrl_1, 8 + j_lctrl_0: + mov eax,code ; 1=COD + lctrl_1: + cmp eax,1 + jne lctrl_2 + GO_ON j_lctrl_1, lctrl_2, 8 + j_lctrl_1: + mov eax,edi ; 1=DAT + lctrl_2: + cmp eax,2 + jne lctrl_3 + GO_ON j_lctrl_2, lctrl_3, 8 + j_lctrl_2: + mov eax,hea ; 2=HEA + lctrl_3: + cmp eax,3 + jne lctrl_4 + GO_ON j_lctrl_3, lctrl_4, 8 + j_lctrl_3: + mov ebp,amx + mov eax,[ebp+_stp] + lctrl_4: + cmp eax,4 + jne lctrl_5 + GO_ON j_lctrl_4, lctrl_5, 8 + j_lctrl_4: + mov eax,esp ; 4=STK + sub eax,edi + lctrl_5: + cmp eax,5 + jne lctrl_6 + GO_ON j_lctrl_5, lctrl_6, 8 + j_lctrl_5: + mov eax,frm ; 5=FRM + lctrl_6: + mov DWORD ptr [j_lctrl_6+1],edi + GO_ON j_lctrl_6, OP_SCTRL, 8 + j_lctrl_6: + mov eax,12345678h ; 6=CIP + + +OP_SCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,2 + jne sctrl_4 + GO_ON j_sctrl_2, sctrl_4, 8 + j_sctrl_2: + mov hea,eax ; 2=HEA + sctrl_4: + cmp eax,4 + jne sctrl_5 + GO_ON j_sctrl_4, sctrl_5, 8 + j_sctrl_4: + ;mov esp,eax ; 4=STK + ;add esp,edi ; relocate stack + lea esp,[eax+edi] + sctrl_5: + cmp eax,5 + jne sctrl_ignore + GO_ON j_sctrl_5, sctrl_ignore, 8 + j_sctrl_5: + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate frame + sctrl_ignore: + mov [ebx],edi + add ebx,8 + jmp DWORD ptr [ebx] + +OP_MOVE_PRI: +;nop; + GO_ON j_move_pri, OP_MOVE_ALT + + j_move_pri: + mov eax,edx + +;good +OP_MOVE_ALT: +;nop; + GO_ON j_move_alt, OP_XCHG + + j_move_alt: + mov edx,eax + +OP_XCHG: +;nop; + GO_ON j_xchg, OP_PUSH_PRI + + j_xchg: ;one might use pushes/pops for pre-586's + xchg eax,edx + +;good +OP_PUSH_PRI: +;nop; + GO_ON j_push_pri, OP_PUSH_ALT + + j_push_pri: + _PUSH eax + +;good +OP_PUSH_ALT: +;nop; + GO_ON j_push_alt, OP_PUSH_R_PRI + + j_push_alt: + _PUSH edx + +OP_PUSH_R_PRI: +;nop; + putval j_push_r_pri+1 + GO_ON j_push_r_pri, OP_PUSH_C, 8 + + j_push_r_pri: + mov ecx,12345678h + j_push_loop: + _PUSH eax + loop j_push_loop + ;dec ecx + ;jnz j_push_loop + +;good +OP_PUSH_C: +;nop; + putval j_push_c+1 + GO_ON j_push_c, OP_PUSH, 8 + + j_push_c: + _PUSH 12345678h + +OP_PUSH: +;nop; + putval j_push+2 + GO_ON j_push, OP_PUSH_S, 8 + + j_push: + _PUSH [edi+12345678h] + +;good +OP_PUSH_S: +;nop; + putval j_push_s+2 + GO_ON j_push_s, OP_POP_PRI, 8 + + j_push_s: + _PUSH [ebx+12345678h] + +OP_POP_PRI: +;nop; + GO_ON j_pop_pri, OP_POP_ALT + + j_pop_pri: + _POP eax + +;good +OP_POP_ALT: +;nop; + GO_ON j_pop_alt, OP_STACK + + j_pop_alt: + _POP edx + +;good +OP_STACK: +;nop; + putval j_stack+4 + GO_ON j_stack, OP_HEAP, 8 + + j_stack: + mov edx,esp + add esp,12345678h + sub edx,edi +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [chk_marginstack] +ENDIF + +;good +OP_HEAP: +;nop; + putval j_heap_call-4 + GO_ON j_heap, OP_PROC, 8 + + j_heap: + mov edx,hea + add DWORD ptr hea,12345678h + j_heap_call: +IF DORUNTIMECHECKS NE 0 + call DWORD ptr [chk_marginheap] +ENDIF +;good +OP_PROC: +;nop; + GO_ON j_proc, OP_RET + + j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK + _PUSH frm ; push old frame (for RET/RETN) + mov frm,esp ; get new frame + mov ebx,esp ; already relocated + sub frm,edi ; relocate frame + +OP_RET: +;nop; + GO_ON j_ret, OP_RETN + + j_ret: + _POP ebx ; pop frame + mov frm,ebx + add ebx,edi + ret + ;call [jit_ret] + +;good +OP_RETN: +;nop; + GO_ON j_retn, OP_CALL + + j_retn: + jmp DWORD ptr [jit_retn] + +;good +OP_CALL: +;nop; + RELOC 1 + GO_ON j_call, OP_CALL_I, 8 + + j_call: + ;call 12345678h ; tasm chokes on this out of a sudden + db 0e8h, 0, 0, 0, 0 + +OP_CALL_I: +;nop; + GO_ON j_call_i, OP_JUMP + + j_call_i: + call eax + +;good +OP_JUMP: +;nop; + RELOC 1 + GO_ON j_jump, OP_JREL, 8 + + j_jump: + DB 0e9h + DD 12345678h + +OP_JREL: +;nop; + mov eax,[ebx+4] + ; create an absolute address from the relative one + RELOC 1, eax+ebx+8 + GO_ON j_jump, OP_JREL, 8 + +;good +OP_JZER: +;nop; + RELOC 4 + GO_ON j_jzer, OP_JNZ, 8 + + j_jzer: + or eax,eax + DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) + +;good +OP_JNZ: +;nop; + RELOC 4 + GO_ON j_jnz, OP_JEQ, 8 + + j_jnz: + or eax,eax + DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 + +;good +OP_JEQ: +;nop; + RELOC 4 + GO_ON j_jeq, OP_JNEQ, 8 + + j_jeq: + cmp eax,edx + DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) + +OP_JNEQ: +;nop; + RELOC 4 + GO_ON j_jneq, OP_JLESS, 8 + + j_jneq: + cmp eax,edx + DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) + +OP_JLESS: +;nop; + RELOC 4 + GO_ON j_jless, OP_JLEQ, 8 + + j_jless: + cmp eax,edx + DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) + +OP_JLEQ: +;nop; + RELOC 4 + GO_ON j_jleq, OP_JGRTR, 8 + + j_jleq: + cmp eax,edx + DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) + +OP_JGRTR: +;nop; + RELOC 4 + GO_ON j_jgrtr, OP_JGEQ, 8 + + j_jgrtr: + cmp eax,edx + DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) + +OP_JGEQ: +;nop; + RELOC 4 + GO_ON j_jgeq, OP_JSLESS, 8 + + j_jgeq: + cmp eax,edx + DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) + +OP_JSLESS: +;nop; + RELOC 4 + GO_ON j_jsless, OP_JSLEQ, 8 + + j_jsless: + cmp eax,edx + DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 + +;good +OP_JSLEQ: +;nop; + RELOC 4 + GO_ON j_jsleq, OP_JSGRTR, 8 + + j_jsleq: + cmp eax,edx + DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 + +OP_JSGRTR: +;nop; + RELOC 4 + GO_ON j_jsgrtr, OP_JSGEQ, 8 + + j_jsgrtr: + cmp eax,edx + DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 + +OP_JSGEQ: +;nop; + RELOC 4 + GO_ON j_jsgeq, OP_SHL, 8 + + j_jsgeq: + cmp eax,edx + DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 + +OP_SHL: +;nop; + GO_ON j_shl, OP_SHR + j_shl: + mov ecx,edx ; TODO: save ECX if used as special register + shl eax,cl + +OP_SHR: +;nop; + GO_ON j_shr, OP_SSHR + j_shr: + mov ecx,edx ; TODO: save ECX if used as special register + shr eax,cl + +OP_SSHR: +;nop; + GO_ON j_sshr, OP_SHL_C_PRI + j_sshr: + mov ecx,edx ; TODO: save ECX if used as special register + sar eax,cl + +OP_SHL_C_PRI: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shl_c_pri+2],al + GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 + j_shl_c_pri: + shl eax,12h + +OP_SHL_C_ALT: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shl_c_alt+2],al + GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 + j_shl_c_alt: + shl edx,12h + +OP_SHR_C_PRI: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shr_c_pri+2],al + GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 + j_shr_c_pri: + shr eax,12h + +OP_SHR_C_ALT: +;nop; + mov al,[ebx+4] + mov BYTE ptr [j_shr_c_alt+2],al + GO_ON j_shr_c_alt, OP_SMUL, 8 + j_shr_c_alt: + shr edx,12h + +OP_SMUL: +;nop; + GO_ON j_smul, OP_SDIV + j_smul: + push edx + imul edx + pop edx + +;good +OP_SDIV: +;nop; + GO_ON j_sdiv, OP_SDIV_ALT + j_sdiv: + call DWORD ptr [jit_sdiv] + +OP_SDIV_ALT: +;nop; + GO_ON j_sdiv_alt, OP_UMUL + j_sdiv_alt: + xchg eax,edx + call DWORD ptr [jit_sdiv] + +OP_UMUL: +;nop; + GO_ON j_umul, OP_UDIV + j_umul: + push edx + mul edx + pop edx + +OP_UDIV: +;nop; + GO_ON j_udiv, OP_UDIV_ALT + j_udiv: + mov ebp,edx + sub edx,edx + call DWORD ptr [chk_dividezero] + div ebp + +OP_UDIV_ALT: +;nop; + GO_ON j_udiv_alt, OP_ADD + j_udiv_alt: + mov ebp,eax + mov eax,edx + sub edx,edx + call DWORD ptr [chk_dividezero] + div ebp + +;good +OP_ADD: +;nop; + GO_ON j_add, OP_SUB + j_add: + add eax,edx + +;good +OP_SUB: +;nop; + GO_ON j_sub, OP_SUB_ALT + j_sub: + sub eax,edx + +;good +OP_SUB_ALT: +;nop; + GO_ON j_sub_alt, OP_AND + j_sub_alt: + neg eax + add eax,edx + +OP_AND: +;nop; + GO_ON j_and, OP_OR + j_and: + and eax,edx + +OP_OR: +;nop; + GO_ON j_or, OP_XOR + j_or: + or eax,edx + +OP_XOR: +;nop; + GO_ON j_xor, OP_NOT + j_xor: + xor eax,edx + +OP_NOT: +;nop; + GO_ON j_not, OP_NEG + j_not: + neg eax ; sets CF iff EAX != 0 + sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) + inc eax ; -1 => 0 and 0 => 1 + +OP_NEG: +;nop; + GO_ON j_neg, OP_INVERT + j_neg: + neg eax + +OP_INVERT: +;nop; + GO_ON j_invert, OP_ADD_C + j_invert: + not eax + +;good +OP_ADD_C: +;nop; + putval j_add_c+1 + GO_ON j_add_c, OP_SMUL_C, 8 + j_add_c: + add eax,12345678h + +;good +OP_SMUL_C: +;nop; + putval j_smul_c+3 + GO_ON j_smul_c, OP_ZERO_PRI, 8 + j_smul_c: + push edx + imul eax,12345678h + pop edx + +;good +OP_ZERO_PRI: +;nop; + GO_ON j_zero_pri, OP_ZERO_ALT + j_zero_pri: + sub eax,eax + +;good +OP_ZERO_ALT: +;nop; + GO_ON j_zero_alt, OP_ZERO + j_zero_alt: + sub edx,edx + +OP_ZERO: +;nop; + putval j_zero+2 + GO_ON j_zero, OP_ZERO_S, 8 + j_zero: + mov DWORD ptr [edi+12345678h],0 + +OP_ZERO_S: +;nop; + putval j_zero_s+2 + GO_ON j_zero_s, OP_SIGN_PRI, 8 + j_zero_s: + mov DWORD ptr [ebx+12345678h],0 + +OP_SIGN_PRI: +;nop; + GO_ON j_sign_pri, OP_SIGN_ALT + j_sign_pri: + shl eax,24 + sar eax,24 + +OP_SIGN_ALT: +;nop; + GO_ON j_sign_alt, OP_EQ + j_sign_alt: + shl edx,24 + sar edx,24 + +OP_EQ: +;nop; + GO_ON j_eq, OP_NEQ + j_eq: + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + +OP_NEQ: +;nop; + GO_ON j_neq, OP_LESS + j_neq: + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + +OP_LESS: +;nop; + GO_ON j_less, OP_LEQ + j_less: + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + +OP_LEQ: +;nop; + GO_ON j_leq, OP_GRTR + j_leq: + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + +OP_GRTR: +;nop; + GO_ON j_grtr, OP_GEQ + j_grtr: + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + +OP_GEQ: +;nop; + GO_ON j_geq, OP_SLESS + j_geq: + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + +;good +OP_SLESS: +;nop; + GO_ON j_sless, OP_SLEQ + j_sless: + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + +OP_SLEQ: +;nop; + GO_ON j_sleq, OP_SGRTR + j_sleq: + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + +OP_SGRTR: +;nop; + GO_ON j_sgrtr, OP_SGEQ + j_sgrtr: + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + +OP_SGEQ: +;nop; + GO_ON j_sgeq, OP_EQ_C_PRI + j_sgeq: + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + +OP_EQ_C_PRI: +;nop; + putval j_eq_c_pri+1 + GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 + j_eq_c_pri: + cmp eax,12345678h ; PRI == value ? + mov eax,0 + sete al + +OP_EQ_C_ALT: +;nop; + putval j_eq_c_alt+4 + GO_ON j_eq_c_alt, OP_INC_PRI, 8 + j_eq_c_alt: + sub eax,eax + cmp edx,12345678h ; ALT == value ? + sete al + +OP_INC_PRI: +;nop; + GO_ON j_inc_pri, OP_INC_ALT + j_inc_pri: + inc eax + +OP_INC_ALT: +;nop; + GO_ON j_inc_alt, OP_INC + j_inc_alt: + inc edx + +OP_INC: +;nop; + putval j_inc+2 + GO_ON j_inc, OP_INC_S, 8 + j_inc: + inc DWORD ptr [edi+12345678h] + +;good +OP_INC_S: +;nop; + putval j_inc_s+2 + GO_ON j_inc_s, OP_INC_I, 8 + j_inc_s: + inc DWORD ptr [ebx+12345678h] + +OP_INC_I: +;nop; + GO_ON j_inc_i, OP_DEC_PRI + j_inc_i: + inc DWORD ptr [edi+eax] + +OP_DEC_PRI: +;nop; + GO_ON j_dec_pri, OP_DEC_ALT + j_dec_pri: + dec eax + +OP_DEC_ALT: +;nop; + GO_ON j_dec_alt, OP_DEC + j_dec_alt: + dec edx + +OP_DEC: +;nop; + putval j_dec+2 + GO_ON j_dec, OP_DEC_S, 8 + j_dec: + dec DWORD ptr [edi+12345678h] + +OP_DEC_S: +;nop; + putval j_dec_s+2 + GO_ON j_dec_s, OP_DEC_I, 8 + j_dec_s: + dec DWORD ptr [ebx+12345678h] + +OP_DEC_I: +;nop; + GO_ON j_dec_i, OP_MOVS + j_dec_i: + dec DWORD ptr [edi+eax] + +OP_MOVS: +;nop; + putval j_movs+1 + GO_ON j_movs, OP_CMPS, 8 + j_movs: + mov ecx,12345678h ;TODO: save ECX if used as special register + call DWORD ptr [jit_movs] + +OP_CMPS: +;nop; + putval j_cmps+1 + GO_ON j_cmps, OP_FILL, 8 + j_cmps: + mov ecx,12345678h ;TODO: save ECX if used as special register + call DWORD ptr [jit_cmps] + +OP_FILL: +;nop; + putval j_fill+1 + GO_ON j_fill, OP_HALT, 8 + j_fill: + mov ecx,12345678h ;TODO: save ECX if used as special register + call DWORD ptr [jit_fill] + +;good +OP_HALT: +;nop; + putval j_halt_sm+1 + GO_ON j_halt, OP_BOUNDS, 8 + j_halt: + cmp DWORD PTR retval,0 + je j_halt_no_value + mov ebp,retval + mov [ebp],eax + j_halt_no_value: + j_halt_sm: + mov eax,12345678h + jmp DWORD ptr [jit_return] + +;good +OP_BOUNDS: +;nop; + putval j_bounds+1 + GO_ON j_bounds, OP_SYSREQ_C, 8 + j_bounds: + mov ebp,12345678h + call DWORD ptr [jit_bounds] + +;good +OP_SYSREQ_C: +;nop; + putval j_sysreq_c+1 + GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 + j_sysreq_c: + mov eax,12345678h ; get function number + j_sysreq: + call DWORD ptr [jit_sysreq] + +OP_SYSREQ_PRI: +;nop; + GO_ON j_sysreq, OP_SYSREQ_PRI + +OP_FILE: ;opcode is simply ignored +;nop; + mov eax,[ebx+4] ;get size + mov [ebx],edi + lea ebx,[ebx+eax+8] ;move on to next opcode + cmp ebx,DWORD ptr [end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ;go on with the next opcode + +OP_LINE: ;ignored +;nop; + mov [ebx],edi ; no line number support: ignore opcode + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + +OP_SYMBOL: ;ignored + mov [ebx],edi + mov eax,[ebx+4] ; get size + lea ebx,[ebx+eax+8] ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +OP_SRANGE: ;ignored + mov [ebx],edi ; store relocated address + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +;not tested +OP_JUMP_PRI: + GO_ON j_jump_pri, OP_SWITCH + + j_jump_pri: ; MP: This opcode makes sense only in con- + jmp [eax] ; junction with a possibility to get the + ; address of a code location... + + +;good +OP_SWITCH: + lea eax,[edi+6] ; The case table will be copied directly + neg eax ; after the run-time call to [jit_switch]. + and eax,3 ; We should align this table on a DWORD + mov ecx,eax ; boundary. + mov al,90h ; 90h = opcode of x86 NOP instruction + rep stosb ; Write the right number of NOPs. + mov [ebx],edi ; store address of SWITCH for relocation step + mov esi,OFFSET j_switch + mov ecx,6 + rep movsb ; copy the call instruction + mov esi,[ebx+4] ; get address of CASETBL instruction + add ebx,8 ; set instruction pointer to next opcode + add esi,4 ; point esi to first entry: (count, default adr) + mov ecx,[esi] ; get number of cases (excluding default) + inc ecx + mov ebp,[reloc_num] + j_case_loop: + mov eax,[esi] ; get case value + stosd ; write it + mov eax,[esi+4] ; get destination address +IF FORCERELOCATABLE EQ 0 + or eax,80000000h ; add flag for "absolute address" +ENDIF + mov [edx+ebp],eax ; write dest. adr. into relocation table + mov eax,[esi+4] ; get destination address (again) + add esi,8 ; set ESI to next case + mov [edx+ebp+4],edi ; write adr. to patch into relocation table + add ebp,8 ; promote relocation pointer + stosd ; write dest. adr. + dec ecx + jnz j_case_loop + mov DWORD ptr [reloc_num],ebp ; write back updated reloc_num + + jmp [ebx] ; GO_ON to next op-code + + j_switch: + call DWORD ptr [jit_switch] + +;good +OP_CASETBL: ; compiles to nothing, SWITCH does all the work + mov eax,[ebx+4] ; get count of cases + lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer + jmp [ebx] ; GO_ON with next op-code + + +OP_SWAP_PRI: ; TR + GO_ON j_swap_pri, OP_SWAP_ALT + + j_swap_pri: + _POP ebp + _PUSH eax + mov eax,ebp + + +OP_SWAP_ALT: ; TR + GO_ON j_swap_alt, OP_PUSHADDR + + j_swap_alt: + _POP ebp + _PUSH edx + mov edx,ebp + + +OP_PUSHADDR: ; TR + putval j_pushaddr+1 + GO_ON j_pushaddr, OP_NOP, 8 + + j_pushaddr: + mov ebp,12345678h ;get address (offset from frame) + add ebp,frm + _PUSH ebp + + +OP_NOP: ; TR + GO_ON j_nop, OP_SYSREQ_D + j_nop: ; code alignment is ignored by the JIT + + +OP_SYSREQ_D: +;nop; + putval j_sysreq_d+1 + GO_ON j_sysreq_d, OP_SYMTAG, 8 + j_sysreq_d: + mov ebx,12345678h ; get function address + call DWORD ptr [jit_sysreq_d] + + +OP_SYMTAG: ;ignored (TR) + mov [ebx],edi ; store relocated address + add ebx,8 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode + + +OP_BREAK: +IF DEBUGSUPPORT EQ 0 + mov [ebx],edi ; no line number support: ignore opcode + add ebx,4 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD ptr [ebx] ; go on with the next opcode +ELSE + GO_ON j_break, OP_INVALID + j_break: + mov ebp,amx + cmp DWORD [ebp+_debug], 0 + je $+4 ; jump around the "call" statement + call [jit_break] +ENDIF + +OP_INVALID: ; break from the compiler with an error code + mov eax,AMX_ERR_INVINSTR + pop esi + pop edi + pop ecx + pop ebp + ret + + +_asm_runJIT ENDP + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell amx_exec( cell *regs, cell *retval, cell stp, cell hea ); +; eax edx ebx ecx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +_amx_exec_jit PROC + + push edi + push esi + push ebp + push ebx ; due to __cdecl + + ; __cdecl overhead + mov eax, [esp+20] ; get address of amx regs structure + mov edx, [esp+24] ; get address of retval + mov ebx, [esp+28] ; get stp + mov ecx, [esp+32] ; get hea + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push DWORD ptr [eax+28] ; store pointer to code segment + push DWORD ptr [eax+24] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push DWORD ptr[eax+20]; store FRM + + stk equ [esi+32] ; define some aliases to registers that will + alt equ [esi+28] ; be stored on the stack when the code is + pri equ [esi+24] ; actually beeing executed + code equ [esi+20] + amx equ [esi+16] + retval equ [esi+12] + stp equ [esi+8] + hea equ [esi+4] + frm equ [esi] ; FRM is NOT stored in ebp, FRM+DAT is being held + ; in ebx instead. + + mov edx,[eax+4] ; get ALT + mov ecx,[eax+8] ; get CIP + mov edi,[eax+12] ; get pointer to data segment + mov esi,[eax+16] ; get STK !!changed, now ECX free as counter!! + mov ebx,[eax+20] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + add esi,edi ; ESP will contain DAT+STK + xchg esp,esi ; switch to AMX stack + + add stp,edi ; make STP absolute address for run-time checks + + _POP ebp ; AMX pseudo-return address, ignored + ; Call compiled code via CALL NEAR
+ call ecx + +return_to_caller: + cmp DWORD PTR retval,0 + je return_to_caller_no_value + mov ebp,retval + mov [ebp],eax ; provide return value + + return_to_caller_no_value: + mov eax,AMX_ERR_NONE + jmp _return + +_return_popstack: + add esp,4 ; Correct ESP, because we just come from a + ; runtime error checking routine. +_return: + ; store machine state + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... + mov ecx,hea ; ... HEA, ... + mov [ebp+_hea],ecx + mov ecx,ebx ; ... and FRM + sub ecx,edi ; (correct FRM) + mov [ebp+_frm],ecx + mov [ebp+_pri],eax ; also store PRI, ... + mov [ebp+_alt],edx ; ... and ALT + + ; return + sub stp,edi ; make STP relative to DAT again + xchg esp,esi ; switch back to caller's stack + + add esp,4*9 ; remove temporary data + + pop ebx ; restore registers that have to be preserved + pop ebp ; when using __cdecl convention + pop esi + pop edi + + ret + + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return_popstack + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return_popstack + +_CHKMARGIN_STACK: ; some run-time check routines + cmp esp,stp + lea ebp,[esp-STACKRESERVE] + jg err_stacklow + sub ebp,edi + cmp hea,ebp + jg err_stack + ret + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return_popstack + +_CHKMARGIN_HEAP: + cmp esp,stp + jg err_stacklow + mov ebp,amx + mov ebp,[ebp+_hlw] + cmp DWORD ptr hea,ebp + jl err_heaplow + ret + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return_popstack + +_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx + cmp eax,stp + jae err_memaccess + cmp eax,hea + jb veax1 + lea ebp,[eax+edi] + cmp ebp,esp + jb err_memaccess + veax1: + ret + +_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx + cmp edx,stp + jae err_memaccess + cmp edx,hea + jb vedx1 + lea ebp,[edx+edi] + cmp ebp,esp + jb err_memaccess + vedx1: + ret + +JIT_OP_SDIV: + mov ebp,edx + xor edx,eax ; Check signs of the operands. + cdq + js sdiv_fiddle ; If the signs of the operands are different + ; we'll have to fiddle around to achieve + ; proper rounding towards minus infinity. + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp ; default behavior is right in the other cases + ret + + sdiv_fiddle: + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp + or edx,edx + jz sdiv_goon ; If there's no remainder the result is correct + add edx,ebp ; else fix the result values. + dec eax ; Amazing, how simple this is... + sdiv_goon: + ret + + ALIGN 4 + +JIT_OP_RETN: + _POP ebx ; pop frame + _POP ecx ; get return address + + mov frm,ebx + _POP ebp + + add ebx,edi + add esp,ebp ; remove data from stack + + jmp ecx + + +JIT_OP_MOVS: ;length of block to copy is already in ECX + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx ; I hope the blocks to copy are properly + shr ecx,2 ; aligned, so I don't do anything about that. + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + ret + +JIT_OP_CMPS: ;length of block to compare is already in ECX + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax ; This is surely not the fastest way to do this + repe cmpsb ; but the most simple one. + je cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + ret + + +JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX + push edi + lea edi,[edi+edx] + + shr ecx,2 ;length in 32-bit cells + rep stosd ;the value to use is already in EAX + + pop edi + ret + +JIT_OP_BOUNDS: + cmp eax,0 + jl err_bounds + cmp eax,ebp + jg err_bounds + ret +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return_popstack + +_CHKDIVIDEZERO: + or ebp,ebp ; check for divide by zero + jz err_divide + ret +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return_popstack + +JIT_OP_SYSREQ: + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + lea ebx,pri ; 3rd param: addr. of retval + lea ecx,[esp+4] ; 4th param: parameter array + + xchg esp,esi ; switch to caller stack + + push ecx + push ebx + push eax ; 2nd param: function number + push ebp ; 1st param: amx + call [ebp+_callback] + _DROPARGS 16 ; remove args from stack + + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_SYSREQ_D: ; (TR) + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov eax,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + lea edx,[esp+4] ; 2nd param: parameter array + xchg esp,esi ; switch to caller stack + push edx + push ebp ; 1st param: amx + call ebx ; direct call + _DROPARGS 8 ; remove args from stack + xchg esp,esi ; switch back to AMX stack + mov ebp,amx ; get amx into EBP + cmp [ebp+_error],AMX_ERR_NONE + jne _return_popstack; return error code, if any + + ; return value is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_BREAK: +IF DEBUGSUPPORT EQ 0 +ELSE + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + + mov [ebp+_pri],eax ; store values in AMX structure: PRI, + mov [ebp+_alt],edx ; ALT, + mov [ebp+_stk],ecx ; STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + ;??? storing CIP is not very useful, because the code changed (during JIT compile) + + xchg esp,esi ; switch to caller stack + push ebp ; 1st param: amx + call [ebp+_debug] + _DROPARGS 4 ; remove args from stack + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov ebp,amx ; get amx into EBP + mov eax,[ebp+_pri] ; restore values + mov edx,[ebp+_alt] ; ALT, + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame +ENDIF + ret + + +JIT_OP_SWITCH: + pop ebp ; pop return address = table address + mov ecx,[ebp] ; ECX = number of records + lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case + jecxz op_switch_jump ; special case: no cases at all + op_switch_loop: + cmp eax,[ebp-8] ; PRI == case label? + je op_switch_jump ; found, jump + sub ebp,8 ; position to preceding case + loop op_switch_loop ; check next case, or fall through + op_switch_jump: +IF FORCERELOCATABLE EQ 0 + jmp [ebp-4] ; jump to the case instructions +ELSE + add ebp,[ebp-4] ; add offset to make absolute adddress + jmp ebp +ENDIF + +_amx_exec_jit ENDP + +; +; The caller of asm_runJIT() can determine the maximum size of the compiled +; code by multiplying the result of this function by the number of opcodes in +; Pawn module. +; +; unsigned long getMaxCodeSize_(); +; +_getMaxCodeSize PROC + + mov eax,MAXCODESIZE + ret + +_getMaxCodeSize ENDP + + +IFNDEF @Version + ; Microsoft MASM 6.x gives the error message "Register assumed to + ; ERROR" when I put the code lables in the data segment. I cannot find + ; a proper way around it. +.DATA +ENDIF + ALIGN 4 ; This is essential to avoid misalignment stalls. + +end_code DD 0 ; pointer to the end of the source code + +compiled_code DD 0 ; pointer to compiled code (including preamble) + +amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) + +reloc_num DD 0 ; counts the addresses in the relocation table (jumps) + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + +; +; A list of the "run-time-library" functions that are called via indirect calls. +; So these calls don't have to be relocated. This gives also the possibility to +; replace some of these with shorter/faster non-debug or non-checking versions, +; without changing the compiled code. Instead this table could be changed... +; +verify_adr_eax DD _VERIFYADDRESS_eax +verify_adr_edx DD _VERIFYADDRESS_edx +chk_marginstack DD _CHKMARGIN_STACK +chk_marginheap DD _CHKMARGIN_HEAP +chk_dividezero DD _CHKDIVIDEZERO +jit_return DD _return +jit_retn DD JIT_OP_RETN +jit_sdiv DD JIT_OP_SDIV +jit_movs DD JIT_OP_MOVS +jit_cmps DD JIT_OP_CMPS +jit_fill DD JIT_OP_FILL +jit_bounds DD JIT_OP_BOUNDS +jit_sysreq DD JIT_OP_SYSREQ +jit_sysreq_d DD JIT_OP_SYSREQ_D +jit_break DD JIT_OP_BREAK +jit_switch DD JIT_OP_SWITCH + +; +; The table for the browser/relocator function. +; + PUBLIC _amx_opcodelist_jit +_amx_opcodelist_jit: + DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_I + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE + DD OP_LINE + DD OP_SYMBOL + DD OP_SRANGE + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI ; TR + DD OP_SWAP_ALT ; TR + DD OP_PUSHADDR ; TR + DD OP_NOP ; TR + DD OP_SYSREQ_D ; TR + DD OP_SYMTAG ; TR + DD OP_BREAK ; TR + +END diff --git a/amx-deps/src/amx/amxjitsn.asm b/amx-deps/src/amx/amxjitsn.asm index 1b3c0ed..338faa5 100644 --- a/amx-deps/src/amx/amxjitsn.asm +++ b/amx-deps/src/amx/amxjitsn.asm @@ -1,2400 +1,2404 @@ -; AMXJITSN.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" -; scripting language -; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES - -; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" -; (27 disks, no output, DOS4/GW under Win95) with this implementation of the -; JIT compiler. - -; NOTE 1: -; There is only one pass implemented in this version. This means there is no -; way of knowing the size of the compiled code before it has actually been com- -; piled. So the only chance the caller has, is to count the number of opcodes -; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to -; obtain a size value big enough to hold the entire code (and data, including -; the stack and heap, after adding their sizes). Afterwards it can realloc() -; this memory block to the actually needed smaller size. - -; NOTE 2: -; The compiler destroys the opcode addresses of the given source by storing the -; respective compiled code's addresses there for the final address relocation -; step. - -; NOTE 3: -; During execution of the compiled code with amx_exec_jit() the x86 processor's -; stack is switched into the data section of the abstract machine. This means -; that there should always be enough memory left between HEA and STK to provide -; stack space for occurring interrupts! (see the STACKRESERVE variable) - -; NOTE 4: -; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I -; instructions, I have to tell that they don't work as expected in a JIT -; compiled program, because there is no easy way of transforming AMX code -; addresses and JIT translated ones. This might be fixed in a future version. - -; NX ("No eXecute") and XD (eXecution Denied) bits -; (by Thiadmer Riemersma) -; -; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit -; processors) and Intel came with the same design, but calling it differently. -; The purpose is to make "buffer overrun" security holes impossible, or at least -; very, very difficult, by marking the stack and the heap as memory regions -; such that an attempt to execute processor instructions will cause a processor -; exception (of course, a buffer overrun that is not explictly handled will then -; crash the application --instead of executing the rogue code). -; -; For JIT compilers, this has the impact that you are not allowed to execute the -; code that the JIT has generated. To do that, you must adjust the attributes -; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to -; allocate a memory block with the appropriate fags; on Linux (with a recent -; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the -; function VirtualProtect() to change the page attributes of an existing memory -; block, but there are caveats in its use: if the block spans multiple pages, -; these pages must be consecutive, and if there are blocks of memory in a page -; unrelated to the JIT, their page attributes will change too. -; -; The JIT compiler itself requires only read-write access (this is the default -; for a memory block that you allocate). The execution of the JIT-compiled code -; requires full access to the memory block: read, write and execute. It needs -; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the -; first lookup (this is an optimization, look up the address of the native -; function only once). For processors that do not support the NX/XD bit, -; execution of code is implicitly supported if read access is supported. -; -; During compilation, the JIT compiler requires write-access to its own code -; segment: the JIT-compiler patches P-code parameters into its own code segment -; during compilation. This is handled in the support code for amx_InitJIT. -; -; -; CALLING CONVENTIONS -; (by Thiadmer Riemersma) -; -; This version is the JIT that uses the "stack calling convention". In the -; original implementation, this meant __cdecl; both for the calling convention -; for the _asm_runJIT routine itself as for the callback functions. -; The current release supports __stdcall for the callback functions; to -; use it, you need to assemble the file with STDECL defined (Since STDCALL is -; a reserved word on the assembler, I had to choose a different name for the -; macro, hence STDECL.) - -; Revision History -; ---------------- -; 26 august 2007 by Thiadmer Riemersma -; Minor clean-up; removed unneeded parameter. -; 28 july 2005 -; Bug fix for the switch table, in the situation where only the default -; case was present. Bug found by Bailopan. -; 17 february 2005 by Thiadmer Riemersma (TR) -; Addition of the BREAK opcode, removal of the older debugging opcode -; table. There should now be some debug support (if enabled during the -; build of the JIT compiler), but not enough to run a debugger: the JIT -; compiler does not keep a list that relates the code addresses of the -; P-code versus the native code. -; 29 June 2004 by G.W.M. Vissers -; Translated the thing into NASM. The actual generation of the code is -; put into the data section because the code modifies itself whereas the -; text section is usually read-only in the Unix ELF format. -; 6 march 2004 by Thiadmer Riemersma -; Corrected a bug in OP_FILL, where a cell preceding the array would -; be overwritten (zero'ed out). This bug was brought to my attention -; by Robert Daniels. -; 22 december 2003 by Thiadmer Riemersma (TR) -; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; -; SYMTAG is a no-op). -; Support __stdcall calling convention for for the native function "hook" -; function (the __cdecl calling convention is also still supported). -; 14 October 2002 by Thiadmer Riemersma (TR) -; Corrected the amx_s structure. The _hlw field was missing, which caused -; errors for arguments to native functions that were passed by reference. -; 2002/08/05 TR -; * store the status of the abstract machine in the AMX structure upon -; return, so that the machine can be restarted (OP_SLEEP) -; * added OP_NOP (for alignment, it is ignored by the JIT) -; * make sure the JIT does not crash when we NULL is passed for the -; return value -; 2000/03/03 MP -; * _amx_opcodelist is equipped with an underscore, again 8-P -; * added SRANGE as a no-op, so debugging info doesn't upset the JIT -; compiler anymore -; * added note about LCTRL, SCTRL and CALL.I -; 2000/03/02 MP -; * made JIT support __cdecl calling conventions -; * removed some rather unnecessary pops in the epilog of amx_exec_asm -; * changed the template for CALL into a DB byte sequence (tasm 4.1 -; didn't like the immediate value) -; 1999/12/07 MP -; * fixed crash caused by JIT compiler not saving registers -; 1999/08/06 MP - design change: closer to the "iron" with native stack -; * The JIT compiler now generates relocatable code for case tables by -; setting FORCERELOCATABLE = 1. -; * removed all debug hook code -; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ -; pushes/pops are possible -; * removed the run-time functions for the CALL, CALL_I and RET op-codes, -; they are now inline -; * All these changes gained around 80% performance increase for the -; hanoi bench. -; 1999/08/05 MP -; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address -; was stored for the LINE byte code (i.e. SWITCH would jump to totally -; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and -; OP_SCTRL (for the no-op case). -; 1999/08/04 MP -; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. -; arrays have not been tested.) -; * hacked relocation code to support absoulute addresses for CASETBL -; (This assumes that no generated address will be greater than -; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) -; * The run-time function for SWITCH uses a (hopefully) faster algorithm -; to compute the destination address: It searches backwards now. -; 1999/07/08 MP - initial revision - - -; -; Support for the BREAK opcode (callback to the debugger): 0 = no, all other -; values = yes. Beware that the compiled code runs slower when this is enabled, -; and that debug support is still fairly minimal. -; -; GWMV: to generate LINE opcode, %define DEBUGSUPPORT -; -%undef DEBUGSUPPORT - -; -; If this is set to 1 the JIT generates relocatable code for case tables, too. -; If set to 0, a faster variant for switch (using absolute addresses) is -; generated. I consider setting it to 0 a bad idea. -; -; GWMV: to use absolute addresses, %undef FORCERELOCATABLE -; -%define FORCERELOCATABLE - -; -; Determines how much memory should be reserved for occurring interrupts. -; (If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes -; for interrupts that occur in real mode and are promoted to protected mode.) -; This value _MUST_ be greater than 64 (for AMX needs) and should be at least -; 128 (to serve interrupts). -; -%define STACKRESERVE 256 - -; -; This variable controls the generation of memory range checks at run-time. -; You should set this to 0, only when you are sure that there are no range -; violations in your Pawn programs and you really need those 5% speed gain. -; -; GWMV: To disable runtime checks, %undef it, instread of setting it to zero -; -%define DORUNTIMECHECKS - -%define JIT 1 -%include "amxdefn.asm" - -; GWMV: -; Nasm can't do the next as equivalence statements, since the value of -; esi is not determined at compile time -%define stk [esi+32] ; define some aliases to registers that will -%define alt [esi+28] ; be stored on the stack when the code is -%define pri [esi+24] ; actually beeing executed -%define code [esi+20] -%define amx [esi+16] -%define retval [esi+12] -%define stp [esi+8] -%define hea [esi+4] -%define frm [esi] ; FRM is NOT stored in ebp, FRM+DAT is being held - ; in ebx instead. - -; -; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) -; -%macro _PUSH 1 - push dword %1 -%endmacro - -; -; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) -; -%macro _POP 1 - pop dword %1 -%endmacro - - -; -; For determining the biggest native code section generated for ONE Pawn -; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) -; -; GWMV: Do NOT see the following macro. See CHECKCODESIZE instead. -; -%assign MAXCODESIZE 0 - -; -; This is the work horse of the whole JIT: It actually copies the code. -%macro GO_ON 2-3 4 - mov esi, %1 ;get source address of JIT code - mov ecx,%2-%1 ;get number of bytes to copy - mov [ebx],edi ;store address for jump-correction - add ebx,%3 - rep movsb - cmp ebx,[end_code] - jae code_gen_done - jmp dword [ebx] ;go on with the next opcode -%endmacro - -; GWMV: -; Nasm can't handle the determination of the maximum code size as was done -; in the Masm implementation, since it only does two passes. This macro is -; called *after* the code for each Pawn instruction. -%macro CHECKCODESIZE 1 - %if MAXCODESIZE < $-%1 - %assign MAXCODESIZE $-%1 - %endif -%endmacro - -; -; Modify the argument of an x86 instruction with the Pawn opcode's parameter -; before copying the code. -; -%macro putval 1 - mov eax,[ebx+4] - mov dword [%1],eax -%endmacro - -; -; Add an entry to the table of addresses which have to be relocated after the -; code compilation is done. -; -%macro RELOC 1-2 ; adr, dest - mov ebp,[reloc_num] - %if %0 < 2 - mov eax,[ebx+4] - %else - lea eax,[%2] - %endif - mov [edx+ebp],eax ; write absolute destination - lea eax,[edi+%1] - mov [edx+ebp+4],eax ; write address of jump operand - add dword [reloc_num],8 -%endmacro - -%macro _DROPARGS 1 ; (TR) remove function arguments from the stack - %ifndef STDECL ; (for __cdecl calling convention only) - add esp,%1 - %endif -%endmacro - - -section .text - - -global asm_runJIT, _asm_runJIT -global amx_exec_jit, _amx_exec_jit -global getMaxCodeSize, _getMaxCodeSize - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -; void asm_runJIT( AMX *amxh, JumpAddressArray *jumps, void *dest ) ; -; ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; asm_runJIT() assumes that the code of this module is allready browsed and -; relocated for the JIT compiler. It also assumes that both the jumps array and -; the dest memory block are large enough to hold all the data it has to write -; to them, as well as that the prefix (header) has already been copied to dest. - -asm_runJIT: -_asm_runJIT: - push ebp - push ebx - push edi - push esi - - mov eax,[esp+20] ; get amxh - mov edx,[esp+24] ; get jumps array - mov ebx,[esp+28] ; get destination - - mov [amxhead],eax ; save pointer to AMX_HEADER struct - mov ecx,[eax+_cod] ; get offset of start of code - mov eax,[eax+_dat] ; offset of start of data = end of code - mov edi,ecx - add ecx,[amxhead] ; compute the real pointer - add eax,[amxhead] ; dito - add edi,ebx ; get write pointer into EDI - mov [compiled_code],ebx - mov [end_code],eax ; Store end-of-code address, so JIT - ; compiler knows when to stop. - mov dword [reloc_num],0 ; init the index into the jumps array - - mov ebx,ecx - jmp dword [ecx] ; start compiling - - ; The compiler will jump back here when code generation is complete. - -code_gen_done: ; Now copy the data section. - mov ebp,[amxhead] ; get source AMX_HEADER start address - add edi,3 ; DAT follows directly after COD - and edi,0fffffffch ; align it on a DWORD boundary - push edi ; save data start pointer - mov esi,[end_code] ; get start of data segment - mov ecx,[ebp+_h_hea] - sub ecx,[ebp+_dat] ; compute length of array to copy - rep movsb ; copy the data - - ; Now adjust the register values in the compiled AMX_HEADER. - ; COD stays the same since the size of AMX_HEADER doesn't change in - ; compiled mode. - mov ebx,[compiled_code] ; get compiled AMX's header address - pop esi ; recall data start pointer - sub esi,ebx ; DAT = size of code + size of prefix - mov [ebx+_dat],esi ; write corrected DAT register - - ;HEA and STP are already relative to DAT, so we don't need to fix them. - - ; Now the calls/jumps in the compiled code have to be relocated. - sub ecx,ecx ; reset offset into relocation table - cmp ecx,[reloc_num] - jae reloc_code_done ; if there's nothing to fix, skip this part - reloc_code_loop: - mov eax,[edx+ecx] ; get destination address - mov edi,[edx+ecx+4] ; determine where to write the relocated value - add ecx,8 ; set pointer to next entry in relocation table - add edi,4 ; base address from where the offset is taken -%ifndef FORCERELOCATABLE - ;MP: hack to suport absolute addresses for the CASETBL instruction - test eax,80000000h ; check whether it is an absolute address - pushf - and eax,7fffffffh ; clear the flag bit for absolute addresses - popf - mov eax,[eax] ; translate into compiled absolute address - jne write_reloc ; leave out the subtraction if absolute -%else - mov eax,[eax] ; translate into compiled absolute address -%endif - sub eax,edi ; make a relative offset - write_reloc: - mov [edi-4],eax ; write the relocated address - cmp ecx,[reloc_num] - jb reloc_code_loop - -reloc_code_done: - ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) - add ebp,[ebp+_cod] ; make all addresses relative to COD, not base - mov eax,[ebx+_h_cip] - add eax,ebp ; get absolute source CIP - mov eax,[eax] ; translate CIP to compiled address - sub eax,ebx ; make it relative to base - sub eax,[ebx+_cod] ; and now relative to COD - mov [ebx+_h_cip],eax; store relocated CIP - mov edi,[ebx+_publics] - sub esi,esi - mov ecx,[ebx+_natives] - sub ecx,edi ; ECX = _natives - _publics = public table size - mov si,[ebx+_defsize] - or ecx,ecx - jz reloc_done ; If there are no publics, we are done. - reloc_publics_loop: - mov eax,[ebx+edi] ; get public function offset - add eax,ebp ; make it a source address - mov eax,[eax] ; translate to compiled address - sub eax,ebx ; make it an offset relative to base - sub eax,[ebx+_cod] ; and now relative to COD - mov [ebx+edi],eax ; write corrected address back - add edi,esi ; step to next public function entry - sub ecx,esi - ja reloc_publics_loop - -reloc_done: - mov eax,0 - pop esi - pop edi - pop ebx - pop ebp - ret - -OP_LOAD_PRI: -;nop; - putval j_load_pri+2 - GO_ON j_load_pri, OP_LOAD_ALT, 8 - - j_load_pri: - mov eax,[edi+12345678h] - CHECKCODESIZE j_load_pri - -OP_LOAD_ALT: -;nop; - putval j_load_alt+2 - GO_ON j_load_alt, OP_LOAD_S_PRI, 8 - - j_load_alt: - mov edx,[edi+12345678h] - CHECKCODESIZE j_load_alt - -;good -OP_LOAD_S_PRI: -;nop; - putval j_load_s_pri+2 - GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 - - j_load_s_pri: - mov eax,[ebx+12345678h] - CHECKCODESIZE j_load_s_pri - -;good -OP_LOAD_S_ALT: -;nop; - putval j_load_s_alt+2 - GO_ON j_load_s_alt, OP_LOAD_I, 8 - - j_load_s_alt: - mov edx,[ebx+12345678h] - CHECKCODESIZE j_load_s_alt - -OP_LOAD_I: -;nop; - GO_ON j_load_i, OP_LODB_I - - j_load_i: -%ifdef DORUNTIMECHECKS - call [verify_adr_eax] -%endif - mov eax,[edi+eax] - CHECKCODESIZE j_load_i - -OP_LODB_I: -;nop; - mov eax,[ebx+4] - mov eax,dword [(lodb_and-4)+eax*4] - mov dword [j_lodb_i_sm+1],eax ;modify AND instruction - GO_ON j_lodb_i, OP_LREF_PRI, 8 - - j_lodb_i: -%ifdef DORUNTIMECHECKS - call [verify_adr_eax] -%endif - mov eax,[edi+eax] ;subject to misalignment stalls - j_lodb_i_sm: - and eax,12345678h - CHECKCODESIZE j_lodb_i - -OP_LREF_PRI: -;nop; - putval j_lref_pri+2 - GO_ON j_lref_pri, OP_LREF_ALT, 8 - - j_lref_pri: - mov eax,[edi+12345678h] - mov eax,[edi+eax] - CHECKCODESIZE j_lref_pri - -OP_LREF_ALT: -;nop; - putval j_lref_alt+2 - GO_ON j_lref_alt, OP_LREF_S_PRI, 8 - - j_lref_alt: - mov edx,[edi+12345678h] - mov edx,[edi+edx] - CHECKCODESIZE j_lref_alt - -OP_LREF_S_PRI: -;nop; - putval j_lref_s_pri+2 - GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 - - j_lref_s_pri: - mov eax,[ebx+12345678h] - mov eax,[edi+eax] - CHECKCODESIZE j_lref_s_pri - -OP_LREF_S_ALT: -;nop; - putval j_lref_s_alt+2 - GO_ON j_lref_s_alt, OP_CONST_PRI, 8 - - j_lref_s_alt: - mov edx,[ebx+12345678h] - mov edx,[edi+edx] - CHECKCODESIZE j_lref_s_alt - -;good -OP_CONST_PRI: -;nop; - putval j_const_pri+1 - GO_ON j_const_pri, OP_CONST_ALT, 8 - - j_const_pri: - mov eax,12345678h - CHECKCODESIZE j_const_pri - -;good -OP_CONST_ALT: -;nop; - putval j_const_alt+1 - GO_ON j_const_alt, OP_ADDR_PRI, 8 - - j_const_alt: - mov edx,12345678h - CHECKCODESIZE j_const_alt - -;good -OP_ADDR_PRI: -;nop; - putval j_addr_pri+1 - GO_ON j_addr_pri, OP_ADDR_ALT, 8 - - j_addr_pri: - mov eax,12345678h - add eax,frm - CHECKCODESIZE j_addr_pri - -;good -OP_ADDR_ALT: -;nop; - putval j_addr_alt+1 - GO_ON j_addr_alt, OP_STOR_PRI, 8 - - j_addr_alt: - mov edx,12345678h - add edx,frm - CHECKCODESIZE j_addr_alt - -OP_STOR_PRI: -;nop; - putval j_stor_pri+2 - GO_ON j_stor_pri, OP_STOR_ALT, 8 - - j_stor_pri: - mov [edi+12345678h],eax - CHECKCODESIZE j_stor_pri - -OP_STOR_ALT: -;nop; - putval j_stor_alt+2 - GO_ON j_stor_alt, OP_STOR_S_PRI, 8 - - j_stor_alt: - mov [edi+12345678h],edx - CHECKCODESIZE j_stor_alt - -;good -OP_STOR_S_PRI: -;nop; - putval j_stor_s_pri+2 - GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 - - j_stor_s_pri: - mov [ebx+12345678h],eax - CHECKCODESIZE j_stor_s_pri - -;good -OP_STOR_S_ALT: -;nop; - putval j_stor_s_alt+2 - GO_ON j_stor_s_alt, OP_STOR_I, 8 - - j_stor_s_alt: - mov [ebx+12345678h],edx - CHECKCODESIZE j_stor_s_alt - -;good -OP_STOR_I: -;nop; - GO_ON j_stor_i, OP_STRB_I - - j_stor_i: -%ifdef DORUNTIMECHECKS - call [verify_adr_edx] -%endif - mov [edi+edx],eax - CHECKCODESIZE j_stor_i - -OP_STRB_I: -;nop; - mov eax,[ebx+4] - cmp eax,1 - jne strb_not1byte - GO_ON j_strb_i_1b, strb_not1byte, 8 - j_strb_i_1b: -%ifdef DORUNTIMECHECKS - call [verify_adr_edx] -%endif - mov [edi+edx],al - CHECKCODESIZE j_strb_i_1b - - strb_not1byte: - cmp eax,4 - je strb_4byte - GO_ON j_strb_i_2b, strb_4byte, 8 - j_strb_i_2b: -%ifdef DORUNTIMECHECKS - call [verify_adr_edx] -%endif - mov [edi+edx],ax - CHECKCODESIZE j_strb_i_2b - - strb_4byte: - GO_ON j_strb_i_4b, OP_SREF_PRI, 8 - j_strb_i_4b: -%ifdef DORUNTIMECHECKS - call [verify_adr_edx] -%endif - mov [edi+edx],eax - CHECKCODESIZE j_strb_i_4b - -OP_SREF_PRI: -;nop; - putval j_sref_pri+2 - GO_ON j_sref_pri, OP_SREF_ALT, 8 - - j_sref_pri: - mov ebp,[edi+12345678h] - mov [edi+ebp],eax - CHECKCODESIZE j_sref_pri - -OP_SREF_ALT: -;nop; - putval j_sref_alt+2 - GO_ON j_sref_alt, OP_SREF_S_PRI, 8 - - j_sref_alt: - mov ebp,[edi+12345678h] - mov [edi+ebp],edx - CHECKCODESIZE j_sref_alt - -OP_SREF_S_PRI: -;nop; - putval j_sref_s_pri+2 - GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 - - j_sref_s_pri: - mov ebp,[ebx+12345678h] - mov [edi+ebp],eax - CHECKCODESIZE j_sref_s_pri - -OP_SREF_S_ALT: -;nop; - putval j_sref_s_alt+2 - GO_ON j_sref_s_alt, OP_LIDX, 8 - - j_sref_s_alt: - mov ebp,[ebx+12345678h] - mov [edi+ebp],edx - CHECKCODESIZE j_sref_s_alt - -;good -OP_LIDX: -;nop; - GO_ON j_lidx, OP_LIDX_B - - j_lidx: - lea eax,[edx+4*eax] -%ifdef DORUNTIMECHECKS - call [verify_adr_eax] -%endif - mov eax,[edi+eax] - CHECKCODESIZE j_lidx - -OP_LIDX_B: -;nop; - mov al,[ebx+4] - mov byte [j_lidx_b+2],al - GO_ON j_lidx_b, OP_IDXADDR, 8 - - j_lidx_b: - shl eax,12h - add eax,edx -%ifdef DORUNTIMECHECKS - call [verify_adr_eax] -%endif - mov eax,[edi+eax] - CHECKCODESIZE j_lidx_b - -;good -OP_IDXADDR: -;nop; - GO_ON j_idxaddr, OP_IDXADDR_B - - j_idxaddr: - lea eax,[edx+4*eax] - CHECKCODESIZE j_idxaddr - -OP_IDXADDR_B: -;nop; - mov al,[ebx+4] - mov byte [j_idxaddr_b+2],al - GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 - - j_idxaddr_b: - shl eax,12h - add eax,edx - CHECKCODESIZE j_idxaddr_b - -OP_ALIGN_PRI: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov dword [j_align_pri+1],eax - GO_ON j_align_pri, OP_ALIGN_ALT, 8 - - j_align_pri: - xor eax,12345678h - CHECKCODESIZE j_align_pri - -OP_ALIGN_ALT: -;nop; - mov eax,4 - sub eax,[ebx+4] - mov dword [j_align_alt+1],eax - GO_ON j_align_alt, OP_LCTRL, 8 - - j_align_alt: - xor edx,12345678h - CHECKCODESIZE j_align_alt - -OP_LCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,0 - jne lctrl_1 - GO_ON j_lctrl_0, lctrl_1, 8 - j_lctrl_0: - mov eax,code ; 1=COD - CHECKCODESIZE j_lctrl_0 - lctrl_1: - cmp eax,1 - jne lctrl_2 - GO_ON j_lctrl_1, lctrl_2, 8 - j_lctrl_1: - mov eax,edi ; 1=DAT - CHECKCODESIZE j_lctrl_1 - lctrl_2: - cmp eax,2 - jne lctrl_3 - GO_ON j_lctrl_2, lctrl_3, 8 - j_lctrl_2: - mov eax,hea ; 2=HEA - CHECKCODESIZE j_lctrl_2 - lctrl_3: - cmp eax,3 - jne lctrl_4 - GO_ON j_lctrl_3, lctrl_4, 8 - j_lctrl_3: - mov ebp,amx - mov eax,[ebp+_stp] - CHECKCODESIZE j_lctrl_3 - lctrl_4: - cmp eax,4 - jne lctrl_5 - GO_ON j_lctrl_4, lctrl_5, 8 - j_lctrl_4: - mov eax,esp ; 4=STK - sub eax,edi - CHECKCODESIZE j_lctrl_4 - lctrl_5: - cmp eax,5 - jne lctrl_6 - GO_ON j_lctrl_5, lctrl_6, 8 - j_lctrl_5: - mov eax,frm ; 5=FRM - CHECKCODESIZE j_lctrl_5 - lctrl_6: - mov dword [j_lctrl_6+1],edi - GO_ON j_lctrl_6, OP_SCTRL, 8 - j_lctrl_6: - mov eax,12345678h ; 6=CIP - CHECKCODESIZE j_lctrl_6 - - -OP_SCTRL: -;nop; - mov eax,[ebx+4] - cmp eax,2 - jne sctrl_4 - GO_ON j_sctrl_2, sctrl_4, 8 - j_sctrl_2: - mov hea,eax ; 2=HEA - CHECKCODESIZE j_sctrl_2 - sctrl_4: - cmp eax,4 - jne sctrl_5 - GO_ON j_sctrl_4, sctrl_5, 8 - j_sctrl_4: - ;mov esp,eax ; 4=STK - ;add esp,edi ; relocate stack - lea esp,[eax+edi] - CHECKCODESIZE j_sctrl_4 - sctrl_5: - cmp eax,5 - jne sctrl_ignore - GO_ON j_sctrl_5, sctrl_ignore, 8 - j_sctrl_5: - mov ebx,eax ; 5=FRM - mov frm,eax - add ebx,edi ; relocate frame - CHECKCODESIZE j_sctrl_5 - sctrl_ignore: - mov [ebx],edi - add ebx,8 - jmp dword [ebx] - -OP_MOVE_PRI: -;nop; - GO_ON j_move_pri, OP_MOVE_ALT - - j_move_pri: - mov eax,edx - CHECKCODESIZE j_move_pri - -;good -OP_MOVE_ALT: -;nop; - GO_ON j_move_alt, OP_XCHG - - j_move_alt: - mov edx,eax - CHECKCODESIZE j_move_alt - -OP_XCHG: -;nop; - GO_ON j_xchg, OP_PUSH_PRI - - j_xchg: ;one might use pushes/pops for pre-586's - xchg eax,edx - CHECKCODESIZE j_xchg - -;good -OP_PUSH_PRI: -;nop; - GO_ON j_push_pri, OP_PUSH_ALT - - j_push_pri: - _PUSH eax - CHECKCODESIZE j_push_pri - -;good -OP_PUSH_ALT: -;nop; - GO_ON j_push_alt, OP_PICK - - j_push_alt: - _PUSH edx - CHECKCODESIZE j_push_alt - -OP_PICK: -;nop; - putval j_pick+2 - GO_ON j_pick, OP_PUSH_C, 8 - - j_pick: - mov eax,[esp+12345678h] - CHECKCODESIZE j_pick - -;good -OP_PUSH_C: -;nop; - putval j_push_c+1 - GO_ON j_push_c, OP_PUSH, 8 - - j_push_c: - _PUSH 12345678h - CHECKCODESIZE j_push_c - -OP_PUSH: -;nop; - putval j_push+2 - GO_ON j_push, OP_PUSH_S, 8 - - j_push: - _PUSH [edi+12345678h] - CHECKCODESIZE j_push - -;good -OP_PUSH_S: -;nop; - putval j_push_s+2 - GO_ON j_push_s, OP_POP_PRI, 8 - - j_push_s: - _PUSH [ebx+12345678h] - CHECKCODESIZE j_push_s - -OP_POP_PRI: -;nop; - GO_ON j_pop_pri, OP_POP_ALT - - j_pop_pri: - _POP eax - CHECKCODESIZE j_pop_pri - -;good -OP_POP_ALT: -;nop; - GO_ON j_pop_alt, OP_STACK - - j_pop_alt: - _POP edx - CHECKCODESIZE j_pop_alt - -;good -OP_STACK: -;nop; - putval j_stack+4 - GO_ON j_stack, OP_HEAP, 8 - - j_stack: - mov edx,esp - add esp,12345678h - sub edx,edi -%ifdef DORUNTIMECHECKS - call [chk_marginstack] -%endif - CHECKCODESIZE j_stack - -;good -OP_HEAP: -;nop; - putval j_heap_call-4 - GO_ON j_heap, OP_PROC, 8 - - j_heap: - mov edx,hea - add dword hea,12345678h - j_heap_call: -%ifdef DORUNTIMECHECKS - call [chk_marginheap] -%endif - CHECKCODESIZE j_heap - -;good -OP_PROC: -;nop; - GO_ON j_proc, OP_RET - - j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK - _PUSH frm ; push old frame (for RET/RETN) - mov frm,esp ; get new frame - mov ebx,esp ; already relocated - sub frm,edi ; relocate frame - CHECKCODESIZE j_proc - -OP_RET: -;nop; - GO_ON j_ret, OP_RETN - - j_ret: - _POP ebx ; pop frame - mov frm,ebx - add ebx,edi - ret - ;call [jit_ret] - CHECKCODESIZE j_ret - -;good -OP_RETN: -;nop; - GO_ON j_retn, OP_CALL - - j_retn: - jmp [jit_retn] - CHECKCODESIZE j_retn - -;good -OP_CALL: -;nop; - RELOC 1 - GO_ON j_call, OP_CALL_I, 8 - - j_call: - ;call 12345678h ; tasm chokes on this out of a sudden - db 0e8h, 0, 0, 0, 0 - CHECKCODESIZE j_call - -OP_CALL_I: -;nop; - GO_ON j_call_i, OP_JUMP - - j_call_i: - call eax - CHECKCODESIZE j_call_i - -;good -OP_JUMP: -;nop; - RELOC 1 - GO_ON j_jump, OP_JREL, 8 - - j_jump: - DB 0e9h - DD 12345678h - CHECKCODESIZE j_jump - -OP_JREL: -;nop; - mov eax,[ebx+4] - ; create an absolute address from the relative one - RELOC 1, eax+ebx+8 - ; GWMV: is the next line really correct!? - GO_ON j_jump, OP_JREL, 8 - -;good -OP_JZER: -;nop; - RELOC 4 - GO_ON j_jzer, OP_JNZ, 8 - - j_jzer: - or eax,eax - DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jzer - -;good -OP_JNZ: -;nop; - RELOC 4 - GO_ON j_jnz, OP_JEQ, 8 - - j_jnz: - or eax,eax - DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 - CHECKCODESIZE j_jnz - -;good -OP_JEQ: -;nop; - RELOC 4 - GO_ON j_jeq, OP_JNEQ, 8 - - j_jeq: - cmp eax,edx - DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jeq - -OP_JNEQ: -;nop; - RELOC 4 - GO_ON j_jneq, OP_JLESS, 8 - - j_jneq: - cmp eax,edx - DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jneq - -OP_JLESS: -;nop; - RELOC 4 - GO_ON j_jless, OP_JLEQ, 8 - - j_jless: - cmp eax,edx - DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jless - -OP_JLEQ: -;nop; - RELOC 4 - GO_ON j_jleq, OP_JGRTR, 8 - - j_jleq: - cmp eax,edx - DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jleq - -OP_JGRTR: -;nop; - RELOC 4 - GO_ON j_jgrtr, OP_JGEQ, 8 - - j_jgrtr: - cmp eax,edx - DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) - CHECKCODESIZE j_jgrtr - -OP_JGEQ: -;nop; - RELOC 4 - GO_ON j_jgeq, OP_JSLESS, 8 - - j_jgeq: - cmp eax,edx - DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) - CHECKCODESIZE j_jgeq - -OP_JSLESS: -;nop; - RELOC 4 - GO_ON j_jsless, OP_JSLEQ, 8 - - j_jsless: - cmp eax,edx - DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 - CHECKCODESIZE j_jsless - -;good -OP_JSLEQ: -;nop; - RELOC 4 - GO_ON j_jsleq, OP_JSGRTR, 8 - - j_jsleq: - cmp eax,edx - DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 - CHECKCODESIZE j_jsleq - -OP_JSGRTR: -;nop; - RELOC 4 - GO_ON j_jsgrtr, OP_JSGEQ, 8 - - j_jsgrtr: - cmp eax,edx - DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 - CHECKCODESIZE j_jsgrtr - -OP_JSGEQ: -;nop; - RELOC 4 - GO_ON j_jsgeq, OP_SHL, 8 - - j_jsgeq: - cmp eax,edx - DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 - CHECKCODESIZE j_jsgeq - -OP_SHL: -;nop; - GO_ON j_shl, OP_SHR - j_shl: - mov ecx,edx ; TODO: save ECX if used as special register - shl eax,cl - CHECKCODESIZE j_shl - -OP_SHR: -;nop; - GO_ON j_shr, OP_SSHR - j_shr: - mov ecx,edx ; TODO: save ECX if used as special register - shr eax,cl - CHECKCODESIZE j_shr - -OP_SSHR: -;nop; - GO_ON j_sshr, OP_SHL_C_PRI - j_sshr: - mov ecx,edx ; TODO: save ECX if used as special register - sar eax,cl - CHECKCODESIZE j_sshr - -OP_SHL_C_PRI: -;nop; - mov al,[ebx+4] - mov byte [j_shl_c_pri+2],al - GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 - j_shl_c_pri: - shl eax,12h - CHECKCODESIZE j_shl_c_pri - -OP_SHL_C_ALT: -;nop; - mov al,[ebx+4] - mov byte [j_shl_c_alt+2],al - GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 - j_shl_c_alt: - shl edx,12h - CHECKCODESIZE j_shl_c_alt - -OP_SHR_C_PRI: -;nop; - mov al,[ebx+4] - mov byte [j_shr_c_pri+2],al - GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 - j_shr_c_pri: - shr eax,12h - CHECKCODESIZE j_shr_c_pri - -OP_SHR_C_ALT: -;nop; - mov al,[ebx+4] - mov byte [j_shr_c_alt+2],al - GO_ON j_shr_c_alt, OP_SMUL, 8 - j_shr_c_alt: - shr edx,12h - CHECKCODESIZE j_shr_c_alt - -OP_SMUL: -;nop; - GO_ON j_smul, OP_SDIV - j_smul: - push edx - imul edx - pop edx - CHECKCODESIZE j_smul - -;good -OP_SDIV: -;nop; - GO_ON j_sdiv, OP_SDIV_ALT - j_sdiv: - call [jit_sdiv] - CHECKCODESIZE j_sdiv - -OP_SDIV_ALT: -;nop; - GO_ON j_sdiv_alt, OP_UMUL - j_sdiv_alt: - xchg eax,edx - call [jit_sdiv] - CHECKCODESIZE j_sdiv_alt - -OP_UMUL: -;nop; - GO_ON j_umul, OP_UDIV - j_umul: - push edx - mul edx - pop edx - CHECKCODESIZE j_umul - -OP_UDIV: -;nop; - GO_ON j_udiv, OP_UDIV_ALT - j_udiv: - mov ebp,edx - sub edx,edx - call [chk_dividezero] - div ebp - CHECKCODESIZE j_udiv - -OP_UDIV_ALT: -;nop; - GO_ON j_udiv_alt, OP_ADD - j_udiv_alt: - mov ebp,eax - mov eax,edx - sub edx,edx - call [chk_dividezero] - div ebp - CHECKCODESIZE j_udiv_alt - -;good -OP_ADD: -;nop; - GO_ON j_add, OP_SUB - j_add: - add eax,edx - CHECKCODESIZE j_add - -;good -OP_SUB: -;nop; - GO_ON j_sub, OP_SUB_ALT - j_sub: - sub eax,edx - CHECKCODESIZE j_sub - -;good -OP_SUB_ALT: -;nop; - GO_ON j_sub_alt, OP_AND - j_sub_alt: - neg eax - add eax,edx - CHECKCODESIZE j_sub_alt - -OP_AND: -;nop; - GO_ON j_and, OP_OR - j_and: - and eax,edx - CHECKCODESIZE j_and - -OP_OR: -;nop; - GO_ON j_or, OP_XOR - j_or: - or eax,edx - CHECKCODESIZE j_or - -OP_XOR: -;nop; - GO_ON j_xor, OP_NOT - j_xor: - xor eax,edx - CHECKCODESIZE j_xor - -OP_NOT: -;nop; - GO_ON j_not, OP_NEG - j_not: - neg eax ; sets CF iff EAX != 0 - sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) - inc eax ; -1 => 0 and 0 => 1 - CHECKCODESIZE j_not - -OP_NEG: -;nop; - GO_ON j_neg, OP_INVERT - j_neg: - neg eax - CHECKCODESIZE j_neg - -OP_INVERT: -;nop; - GO_ON j_invert, OP_ADD_C - j_invert: - not eax - CHECKCODESIZE j_invert - -;good -OP_ADD_C: -;nop; - putval j_add_c+1 - GO_ON j_add_c, OP_SMUL_C, 8 - j_add_c: - add eax,12345678h - CHECKCODESIZE j_add_c - -;good -OP_SMUL_C: -;nop; - putval j_smul_c+3 - GO_ON j_smul_c, OP_ZERO_PRI, 8 - j_smul_c: - push edx - imul eax,12345678h - pop edx - CHECKCODESIZE j_smul_c - -;good -OP_ZERO_PRI: -;nop; - GO_ON j_zero_pri, OP_ZERO_ALT - j_zero_pri: - sub eax,eax - CHECKCODESIZE j_zero_pri - -;good -OP_ZERO_ALT: -;nop; - GO_ON j_zero_alt, OP_ZERO - j_zero_alt: - sub edx,edx - CHECKCODESIZE j_zero_alt - -OP_ZERO: -;nop; - putval j_zero+2 - GO_ON j_zero, OP_ZERO_S, 8 - j_zero: - mov dword [edi+12345678h],0 - CHECKCODESIZE j_zero - -OP_ZERO_S: -;nop; - putval j_zero_s+2 - GO_ON j_zero_s, OP_SIGN_PRI, 8 - j_zero_s: - mov dword [ebx+12345678h],0 - CHECKCODESIZE j_zero_s - -OP_SIGN_PRI: -;nop; - GO_ON j_sign_pri, OP_SIGN_ALT - j_sign_pri: - shl eax,24 - sar eax,24 - CHECKCODESIZE j_sign_pri - -OP_SIGN_ALT: -;nop; - GO_ON j_sign_alt, OP_EQ - j_sign_alt: - shl edx,24 - sar edx,24 - CHECKCODESIZE j_sign_alt - -OP_EQ: -;nop; - GO_ON j_eq, OP_NEQ - j_eq: - cmp eax,edx ; PRI == ALT ? - mov eax,0 - sete al - CHECKCODESIZE j_eq - -OP_NEQ: -;nop; - GO_ON j_neq, OP_LESS - j_neq: - cmp eax,edx ; PRI != ALT ? - mov eax,0 - setne al - CHECKCODESIZE j_neq - -OP_LESS: -;nop; - GO_ON j_less, OP_LEQ - j_less: - cmp eax,edx ; PRI < ALT ? (unsigned) - mov eax,0 - setb al - CHECKCODESIZE j_less - -OP_LEQ: -;nop; - GO_ON j_leq, OP_GRTR - j_leq: - cmp eax,edx ; PRI <= ALT ? (unsigned) - mov eax,0 - setbe al - CHECKCODESIZE j_leq - -OP_GRTR: -;nop; - GO_ON j_grtr, OP_GEQ - j_grtr: - cmp eax,edx ; PRI > ALT ? (unsigned) - mov eax,0 - seta al - CHECKCODESIZE j_grtr - -OP_GEQ: -;nop; - GO_ON j_geq, OP_SLESS - j_geq: - cmp eax,edx ; PRI >= ALT ? (unsigned) - mov eax,0 - setae al - CHECKCODESIZE j_geq - -;good -OP_SLESS: -;nop; - GO_ON j_sless, OP_SLEQ - j_sless: - cmp eax,edx ; PRI < ALT ? (signed) - mov eax,0 - setl al - CHECKCODESIZE j_sless - -OP_SLEQ: -;nop; - GO_ON j_sleq, OP_SGRTR - j_sleq: - cmp eax,edx ; PRI <= ALT ? (signed) - mov eax,0 - setle al - CHECKCODESIZE j_sleq - -OP_SGRTR: -;nop; - GO_ON j_sgrtr, OP_SGEQ - j_sgrtr: - cmp eax,edx ; PRI > ALT ? (signed) - mov eax,0 - setg al - CHECKCODESIZE j_sgrtr - -OP_SGEQ: -;nop; - GO_ON j_sgeq, OP_EQ_C_PRI - j_sgeq: - cmp eax,edx ; PRI >= ALT ? (signed) - mov eax,0 - setge al - CHECKCODESIZE j_sgeq - -OP_EQ_C_PRI: -;nop; - putval j_eq_c_pri+1 - GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 - j_eq_c_pri: - cmp eax,12345678h ; PRI == value ? - mov eax,0 - sete al - CHECKCODESIZE j_eq_c_pri - -OP_EQ_C_ALT: -;nop; - putval j_eq_c_alt+4 - GO_ON j_eq_c_alt, OP_INC_PRI, 8 - j_eq_c_alt: - sub eax,eax - cmp edx,12345678h ; ALT == value ? - sete al - CHECKCODESIZE j_eq_c_alt - -OP_INC_PRI: -;nop; - GO_ON j_inc_pri, OP_INC_ALT - j_inc_pri: - inc eax - CHECKCODESIZE j_inc_pri - -OP_INC_ALT: -;nop; - GO_ON j_inc_alt, OP_INC - j_inc_alt: - inc edx - CHECKCODESIZE j_inc_alt - -OP_INC: -;nop; - putval j_inc+2 - GO_ON j_inc, OP_INC_S, 8 - j_inc: - inc dword [edi+12345678h] - CHECKCODESIZE j_inc - -;good -OP_INC_S: -;nop; - putval j_inc_s+2 - GO_ON j_inc_s, OP_INC_I, 8 - j_inc_s: - inc dword [ebx+12345678h] - CHECKCODESIZE j_inc_s - -OP_INC_I: -;nop; - GO_ON j_inc_i, OP_DEC_PRI - j_inc_i: - inc dword [edi+eax] - CHECKCODESIZE j_inc_i - -OP_DEC_PRI: -;nop; - GO_ON j_dec_pri, OP_DEC_ALT - j_dec_pri: - dec eax - CHECKCODESIZE j_dec_pri - -OP_DEC_ALT: -;nop; - GO_ON j_dec_alt, OP_DEC - j_dec_alt: - dec edx - CHECKCODESIZE j_dec_alt - -OP_DEC: -;nop; - putval j_dec+2 - GO_ON j_dec, OP_DEC_S, 8 - j_dec: - dec dword [edi+12345678h] - CHECKCODESIZE j_dec - -OP_DEC_S: -;nop; - putval j_dec_s+2 - GO_ON j_dec_s, OP_DEC_I, 8 - j_dec_s: - dec dword [ebx+12345678h] - CHECKCODESIZE j_dec_s - -OP_DEC_I: -;nop; - GO_ON j_dec_i, OP_MOVS - j_dec_i: - dec dword [edi+eax] - CHECKCODESIZE j_dec_i - -OP_MOVS: -;nop; - putval j_movs+1 - GO_ON j_movs, OP_CMPS, 8 - j_movs: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_movs] - CHECKCODESIZE j_movs - -OP_CMPS: -;nop; - putval j_cmps+1 - GO_ON j_cmps, OP_FILL, 8 - j_cmps: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_cmps] - CHECKCODESIZE j_cmps - -OP_FILL: -;nop; - putval j_fill+1 - GO_ON j_fill, OP_HALT, 8 - j_fill: - mov ecx,12345678h ;TODO: save ECX if used as special register - call [jit_fill] - CHECKCODESIZE j_fill - -;good -OP_HALT: -;nop; - putval j_halt_sm+1 - GO_ON j_halt, OP_BOUNDS, 8 - j_halt: - cmp dword retval,0 - je j_halt_no_value - mov ebp,retval - mov [ebp],eax - j_halt_no_value: - j_halt_sm: - mov eax,12345678h - jmp [jit_return] - CHECKCODESIZE j_halt - -;good -OP_BOUNDS: -;nop; - putval j_bounds+1 - GO_ON j_bounds, OP_SYSREQ_C, 8 - j_bounds: - mov ebp,12345678h - call [jit_bounds] - CHECKCODESIZE j_bounds - -;good -OP_SYSREQ_C: -;nop; - putval j_sysreq_c+1 - GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 - j_sysreq_c: - mov eax,12345678h ; get function number - j_sysreq: - call [jit_sysreq] - CHECKCODESIZE j_sysreq_c - ; GWMV: oh well, it may look stupid, but I don't want to miss anything - CHECKCODESIZE j_sysreq - -OP_SYSREQ_PRI: -;nop; - GO_ON j_sysreq, OP_SYSREQ_PRI - -OP_FILE: ;opcode is simply ignored -;nop; - mov eax,[ebx+4] ;get size - mov [ebx],edi - lea ebx,[ebx+eax+8] ;move on to next opcode - cmp ebx,dword [end_code] - jae code_gen_done - jmp dword [ebx] ;go on with the next opcode - -OP_LINE: -;nop; - mov [ebx],edi ; no line number support: ignore opcode - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp dword [ebx] ; go on with the next opcode - -OP_SYMBOL: ;ignored - mov [ebx],edi - mov eax,[ebx+4] ; get size - lea ebx,[ebx+eax+8] ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp dword [ebx] ; go on with the next opcode - - -OP_SRANGE: ;ignored - mov [ebx],edi ; store relocated address - add ebx,12 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp dword [ebx] ; go on with the next opcode - - -;not tested -OP_JUMP_PRI: - GO_ON j_jump_pri, OP_SWITCH - - j_jump_pri: ; MP: This opcode makes sense only in con- - jmp [eax] ; junction with a possibility to get the - ; address of a code location... - CHECKCODESIZE j_jump_pri - - -;good -OP_SWITCH: - lea eax,[edi+6] ; The case table will be copied directly - neg eax ; after the run-time call to [jit_switch]. - and eax,3 ; We should align this table on a DWORD - mov ecx,eax ; boundary. - mov al,90h ; 90h = opcode of x86 NOP instruction - rep stosb ; Write the right number of NOPs. - mov [ebx],edi ; store address of SWITCH for relocation step - mov esi, j_switch - mov ecx,6 - rep movsb ; copy the call instruction - mov esi,[ebx+4] ; get address of CASETBL instruction - add ebx,8 ; set instruction pointer to next opcode - add esi,4 ; point esi to first entry: (count, default adr) - mov ecx,[esi] ; get number of cases (excluding default) - inc ecx - mov ebp,[reloc_num] - j_case_loop: - mov eax,[esi] ; get case value - stosd ; write it - mov eax,[esi+4] ; get destination address -%ifndef FORCERELOCATABLE - or eax,80000000h ; add flag for "absolute address" -%endif - mov [edx+ebp],eax ; write dest. adr. into relocation table - mov eax,[esi+4] ; get destination address (again) - add esi,8 ; set ESI to next case - mov [edx+ebp+4],edi ; write adr. to patch into relocation table - add ebp,8 ; promote relocation pointer - stosd ; write dest. adr. - dec ecx - jnz j_case_loop - mov dword [reloc_num],ebp ; write back updated reloc_num - - jmp [ebx] ; GO_ON to next op-code - - j_switch: - call [jit_switch] - -;good -OP_CASETBL: ; compiles to nothing, SWITCH does all the work - mov eax,[ebx+4] ; get count of cases - lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer - jmp [ebx] ; GO_ON with next op-code - - -OP_SWAP_PRI: ; TR - GO_ON j_swap_pri, OP_SWAP_ALT - - j_swap_pri: - _POP ebp - _PUSH eax - mov eax,ebp - CHECKCODESIZE j_swap_pri - - -OP_SWAP_ALT: ; TR - GO_ON j_swap_alt, OP_PUSHADDR - - j_swap_alt: - _POP ebp - _PUSH edx - mov edx,ebp - CHECKCODESIZE j_swap_alt - - -OP_PUSHADDR: ; TR - putval j_pushaddr+1 - GO_ON j_pushaddr, OP_NOP, 8 - - j_pushaddr: - mov ebp,12345678h ;get address (offset from frame) - add ebp,frm - _PUSH ebp - CHECKCODESIZE j_pushaddr - - -OP_NOP: ; TR - GO_ON j_nop, OP_SYSREQ_D - j_nop: ; code alignment is ignored by the JIT - CHECKCODESIZE j_nop - - -OP_SYSREQ_D: -;nop; - putval j_sysreq_d+1 - GO_ON j_sysreq_d, OP_SYMTAG, 8 - j_sysreq_d: - mov ebx,12345678h ; get function address - call [jit_sysreq_d] - CHECKCODESIZE j_sysreq_d - - -OP_SYMTAG: ;ignored (TR) - mov [ebx],edi ; store relocated address - add ebx,8 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp dword [ebx] ; go on with the next opcode - - -OP_BREAK: -%ifndef DEBUGSUPPORT - mov [ebx],edi ; no line number support: ignore opcode - add ebx,4 ; move on to next opcode - cmp ebx,[end_code] - jae code_gen_done - jmp DWORD [ebx] ; go on with the next opcode -%else - GO_ON j_break, OP_INVALID - j_break: - mov ebp,amx - cmp DWORD [ebp+_debug], 0 - je $+4 ; jump around the "call" statement - call [jit_break] - CHECKCODESIZE j_break -%endif - -OP_INVALID: ; break from the compiler with an error code - mov eax,AMX_ERR_INVINSTR - pop esi - pop edi - pop ecx - pop ebp - ret - - -section .text - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; ; -;cell asm_exec_jit( AMX *amx, cell *retval, char *data ) ; -; ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -amx_exec_jit: -_amx_exec_jit: - push edi - push esi - push ebp - push ebx ; due to __cdecl - - ; __cdecl overhead - mov eax, [esp+20] ; get address of AMX structure - mov edx, [esp+24] ; get address of retval - mov ebx, [esp+28] ; get pointer to data section - - sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs - - push DWORD [eax+_codeseg] ; store pointer to code segment - push eax ; store pointer to AMX - push edx ; store address of retval - push DWORD [eax+_stp] ; store STP - push DWORD [eax+_hea] ; store HEA - push DWORD [eax+_frm] ; store FRM - - mov edi,ebx ; get pointer to data segment - mov edx,[eax+_alt] ; get ALT - mov ecx,[eax+_cip] ; get CIP (N.B. different from ASM interpreter) - mov esi,[eax+_stk] ; get STK (N.B. different from ASM interpreter) - mov ebx,[eax+_frm] ; get FRM - mov eax,[eax+_pri] ; get PRI - add ebx,edi ; relocate frame - - add esi,edi ; ESP will contain DAT+STK - xchg esp,esi ; switch to AMX stack - - add stp,edi ; make STP absolute address for run-time checks - - _POP ebp ; AMX pseudo-return address, ignored - ; Call compiled code via CALL NEAR
- call ecx - -return_to_caller: - cmp dword retval,0 - je return_to_caller_no_value - mov ebp,retval - mov [ebp],eax ; provide return value - - return_to_caller_no_value: - mov eax,AMX_ERR_NONE - jmp _return - -_return_popstack: - add esp,4 ; Correct ESP, because we just come from a - ; runtime error checking routine. -_return: - ; store machine state - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... - mov ecx,hea ; ... HEA, ... - mov [ebp+_hea],ecx - mov ecx,ebx ; ... and FRM - sub ecx,edi ; (correct FRM) - mov [ebp+_frm],ecx - mov [ebp+_pri],eax ; also store PRI, ... - mov [ebp+_alt],edx ; ... and ALT - - ; return - sub stp,edi ; make STP relative to DAT again - xchg esp,esi ; switch back to caller's stack - - add esp,4*9 ; remove temporary data - - pop ebx ; restore registers that have to be preserved - pop ebp ; when using __cdecl convention - pop esi - pop edi - - ret - - -err_stack: - mov eax,AMX_ERR_STACKERR - jmp _return_popstack - -err_stacklow: - mov eax,AMX_ERR_STACKLOW - jmp _return_popstack - -_CHKMARGIN_STACK: ; some run-time check routines - cmp esp,stp - lea ebp,[esp-STACKRESERVE] - jg err_stacklow - sub ebp,edi - cmp hea,ebp - jg err_stack - ret - -err_heaplow: - mov eax,AMX_ERR_HEAPLOW - jmp _return_popstack - -_CHKMARGIN_HEAP: - cmp esp,stp - jg err_stacklow - mov ebp,amx - mov ebp,[ebp+_hlw] - cmp DWORD hea,ebp - jl err_heaplow - ret - -err_memaccess: - mov eax,AMX_ERR_MEMACCESS - jmp _return_popstack - -_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx - cmp eax,stp - jae err_memaccess - cmp eax,hea - jb veax1 - lea ebp,[eax+edi] - cmp ebp,esp - jb err_memaccess - veax1: - ret - -_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx - cmp edx,stp - jae err_memaccess - cmp edx,hea - jb vedx1 - lea ebp,[edx+edi] - cmp ebp,esp - jb err_memaccess - vedx1: - ret - -JIT_OP_SDIV: - mov ebp,edx - xor edx,eax ; Check signs of the operands. - cdq - js sdiv_fiddle ; If the signs of the operands are different - ; we'll have to fiddle around to achieve - ; proper rounding towards minus infinity. - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp ; default behavior is right in the other cases - ret - - sdiv_fiddle: - or ebp,ebp ; check for divide by zero - jz err_divide - idiv ebp - or edx,edx - jz sdiv_goon ; If there's no remainder the result is correct - add edx,ebp ; else fix the result values. - dec eax ; Amazing, how simple this is... - sdiv_goon: - ret - - ALIGN 4 - -JIT_OP_RETN: - _POP ebx ; pop frame - _POP ecx ; get return address - - mov frm,ebx - _POP ebp - - add ebx,edi - add esp,ebp ; remove data from stack - - jmp ecx - - -JIT_OP_MOVS: ;length of block to copy is already in ECX - push edi - push esi - lea esi,[edi+eax] - lea edi,[edi+edx] - - push ecx ; I hope the blocks to copy are properly - shr ecx,2 ; aligned, so I don't do anything about that. - rep movsd - pop ecx - and ecx,3 - rep movsb - - pop esi - pop edi - ret - -JIT_OP_CMPS: ;length of block to compare is already in ECX - push edi - push esi - lea esi,[edi+edx] - lea edi,[edi+eax] - - xor eax,eax ; This is surely not the fastest way to do this - repe cmpsb ; but the most simple one. - je cmps1 - sbb eax,eax - sbb eax,0ffffffffh - cmps1: - pop esi - pop edi - ret - - -JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX - push edi - lea edi,[edi+edx] - - shr ecx,2 ;length in 32-bit cells - rep stosd ;the value to use is already in EAX - - pop edi - ret - -JIT_OP_BOUNDS: - cmp eax,0 - jl err_bounds - cmp eax,ebp - jg err_bounds - ret -err_bounds: - mov eax,AMX_ERR_BOUNDS - jmp _return_popstack - -_CHKDIVIDEZERO: - or ebp,ebp ; check for divide by zero - jz err_divide - ret -err_divide: - mov eax,AMX_ERR_DIVIDE - jmp _return_popstack - -JIT_OP_SYSREQ: - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx - - lea ebx,pri ; 3rd param: addr. of retval - lea ecx,[esp+4] ; 4th param: parameter array - - xchg esp,esi ; switch to caller stack - - push ecx - push ebx - push eax ; 2nd param: function number - push ebp ; 1st param: amx - call [ebp+_callback] - _DROPARGS 16 ; remove args from stack - - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov eax,pri ; get retval into eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_SYSREQ_D: ; (TR) - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - mov alt,edx ; save ALT - - mov [ebp+_stk],ecx ; store values in AMX structure: STK, - mov ecx,hea ; HEA, - mov eax,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],eax ; eax & ecx are invalid by now - - lea edx,[esp+4] ; 2nd param: parameter array - xchg esp,esi ; switch to caller stack - push edx - push ebp ; 1st param: amx - call ebx ; direct call - _DROPARGS 8 ; remove args from stack - xchg esp,esi ; switch back to AMX stack - mov ebp,amx ; get amx into EBP - cmp dword [ebp+_error],AMX_ERR_NONE - jne _return_popstack; return error code, if any - - ; return value is in eax (PRI) - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame - ret - - -JIT_OP_BREAK: -%ifdef DEBUGSUPPORT - mov ecx,esp ; get STK into ECX - mov ebp,amx ; get amx into EBP - - sub ecx,edi ; correct STK - - mov [ebp+_pri],eax ; store values in AMX structure: PRI, - mov [ebp+_alt],edx ; ALT, - mov [ebp+_stk],ecx ; STK, - mov ecx,hea ; HEA, - mov ebx,frm ; and FRM - mov [ebp+_hea],ecx - mov [ebp+_frm],ebx ; EBX & ECX are invalid by now - ;??? storing CIP is not very useful, because the code changed (during JIT compile) - - xchg esp,esi ; switch to caller stack - push ebp ; 1st param: amx - call [ebp+_debug] - _DROPARGS 4 ; remove args from stack - xchg esp,esi ; switch back to AMX stack - cmp eax,AMX_ERR_NONE - jne _return_popstack; return error code, if any - - mov ebp,amx ; get amx into EBP - mov eax,[ebp+_pri] ; restore values - mov edx,[ebp+_alt] ; ALT, - mov edx,alt ; restore ALT - mov ebx,frm ; restore FRM - add ebx,edi ; relocate frame -%endif - ret - - -JIT_OP_SWITCH: - pop ebp ; pop return address = table address - mov ecx,[ebp] ; ECX = number of records - lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case - jecxz op_switch_jump ; special case: no cases at all - op_switch_loop: - cmp eax,[ebp-8] ; PRI == case label? - je op_switch_jump ; found, jump - sub ebp,8 ; position to preceding case - loop op_switch_loop ; check next case, or fall through - op_switch_jump: -%ifndef FORCERELOCATABLE - jmp [ebp-4] ; jump to the case instructions -%else - add ebp,[ebp-4] ; add offset to make absolute adddress - jmp ebp -%endif - - -; The caller of asm_runJIT() can determine the maximum size of the compiled -; code by multiplying the result of this function by the number of opcodes in -; Pawn module. -; -; unsigned long getMaxCodeSize_(); -; -getMaxCodeSize: -_getMaxCodeSize: - mov eax,MAXCODESIZE - ret - -section .data - ALIGN 4 ; This is essential to avoid misalignment stalls. - -end_code DD 0 ; pointer to the end of the source code - -compiled_code DD 0 ; pointer to compiled code (including preamble) - -amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) - -reloc_num DD 0 ; counts the addresses in the relocation table (jumps) - -lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh - -; -; A list of the "run-time-library" functions that are called via indirect calls. -; So these calls don't have to be relocated. This gives also the possibility to -; replace some of these with shorter/faster non-debug or non-checking versions, -; without changing the compiled code. Instead this table could be changed... -; -verify_adr_eax DD _VERIFYADDRESS_eax -verify_adr_edx DD _VERIFYADDRESS_edx -chk_marginstack DD _CHKMARGIN_STACK -chk_marginheap DD _CHKMARGIN_HEAP -chk_dividezero DD _CHKDIVIDEZERO -jit_return DD _return -jit_retn DD JIT_OP_RETN -jit_sdiv DD JIT_OP_SDIV -jit_movs DD JIT_OP_MOVS -jit_cmps DD JIT_OP_CMPS -jit_fill DD JIT_OP_FILL -jit_bounds DD JIT_OP_BOUNDS -jit_sysreq DD JIT_OP_SYSREQ -jit_sysreq_d DD JIT_OP_SYSREQ_D -jit_break DD JIT_OP_BREAK -jit_switch DD JIT_OP_SWITCH - -; -; The table for the browser/relocator function. -; - -global amx_opcodelist_jit, _amx_opcodelist_jit - -amx_opcodelist_jit: -_amx_opcodelist_jit: - DD OP_INVALID - DD OP_LOAD_PRI - DD OP_LOAD_ALT - DD OP_LOAD_S_PRI - DD OP_LOAD_S_ALT - DD OP_LREF_PRI - DD OP_LREF_ALT - DD OP_LREF_S_PRI - DD OP_LREF_S_ALT - DD OP_LOAD_I - DD OP_LODB_I - DD OP_CONST_PRI - DD OP_CONST_ALT - DD OP_ADDR_PRI - DD OP_ADDR_ALT - DD OP_STOR_PRI - DD OP_STOR_ALT - DD OP_STOR_S_PRI - DD OP_STOR_S_ALT - DD OP_SREF_PRI - DD OP_SREF_ALT - DD OP_SREF_S_PRI - DD OP_SREF_S_ALT - DD OP_STOR_I - DD OP_STRB_I - DD OP_LIDX - DD OP_LIDX_B - DD OP_IDXADDR - DD OP_IDXADDR_B - DD OP_ALIGN_PRI - DD OP_ALIGN_ALT - DD OP_LCTRL - DD OP_SCTRL - DD OP_MOVE_PRI - DD OP_MOVE_ALT - DD OP_XCHG - DD OP_PUSH_PRI - DD OP_PUSH_ALT - DD OP_PICK - DD OP_PUSH_C - DD OP_PUSH - DD OP_PUSH_S - DD OP_POP_PRI - DD OP_POP_ALT - DD OP_STACK - DD OP_HEAP - DD OP_PROC - DD OP_RET - DD OP_RETN - DD OP_CALL - DD OP_CALL_I - DD OP_JUMP - DD OP_JREL - DD OP_JZER - DD OP_JNZ - DD OP_JEQ - DD OP_JNEQ - DD OP_JLESS - DD OP_JLEQ - DD OP_JGRTR - DD OP_JGEQ - DD OP_JSLESS - DD OP_JSLEQ - DD OP_JSGRTR - DD OP_JSGEQ - DD OP_SHL - DD OP_SHR - DD OP_SSHR - DD OP_SHL_C_PRI - DD OP_SHL_C_ALT - DD OP_SHR_C_PRI - DD OP_SHR_C_ALT - DD OP_SMUL - DD OP_SDIV - DD OP_SDIV_ALT - DD OP_UMUL - DD OP_UDIV - DD OP_UDIV_ALT - DD OP_ADD - DD OP_SUB - DD OP_SUB_ALT - DD OP_AND - DD OP_OR - DD OP_XOR - DD OP_NOT - DD OP_NEG - DD OP_INVERT - DD OP_ADD_C - DD OP_SMUL_C - DD OP_ZERO_PRI - DD OP_ZERO_ALT - DD OP_ZERO - DD OP_ZERO_S - DD OP_SIGN_PRI - DD OP_SIGN_ALT - DD OP_EQ - DD OP_NEQ - DD OP_LESS - DD OP_LEQ - DD OP_GRTR - DD OP_GEQ - DD OP_SLESS - DD OP_SLEQ - DD OP_SGRTR - DD OP_SGEQ - DD OP_EQ_C_PRI - DD OP_EQ_C_ALT - DD OP_INC_PRI - DD OP_INC_ALT - DD OP_INC - DD OP_INC_S - DD OP_INC_I - DD OP_DEC_PRI - DD OP_DEC_ALT - DD OP_DEC - DD OP_DEC_S - DD OP_DEC_I - DD OP_MOVS - DD OP_CMPS - DD OP_FILL - DD OP_HALT - DD OP_BOUNDS - DD OP_SYSREQ_PRI - DD OP_SYSREQ_C - DD OP_FILE - DD OP_LINE - DD OP_SYMBOL - DD OP_SRANGE - DD OP_JUMP_PRI - DD OP_SWITCH - DD OP_CASETBL - DD OP_SWAP_PRI ; TR - DD OP_SWAP_ALT ; TR - DD OP_PUSHADDR ; TR - DD OP_NOP ; TR - DD OP_SYSREQ_D ; TR - DD OP_SYMTAG ; TR - DD OP_BREAK ; TR - -END +; AMXJITSN.ASM: Just-In-Time compiler for the Abstract Machine of the "Pawn" +; scripting language +; (C) 1999-2000, Marc Peter; beta version; provided AS IS WITHOUT ANY WARRANTIES + +; I reached >155 million instr./sec on my AMD K6-2/366 with the Hanoi "bench" +; (27 disks, no output, DOS4/GW under Win95) with this implementation of the +; JIT compiler. + +; NOTE 1: +; There is only one pass implemented in this version. This means there is no +; way of knowing the size of the compiled code before it has actually been com- +; piled. So the only chance the caller has, is to count the number of opcodes +; (in amx_BrowseRelocate()) and multiply this count with a "safe" factor to +; obtain a size value big enough to hold the entire code (and data, including +; the stack and heap, after adding their sizes). Afterwards it can realloc() +; this memory block to the actually needed smaller size. + +; NOTE 2: +; The compiler destroys the opcode addresses of the given source by storing the +; respective compiled code's addresses there for the final address relocation +; step. + +; NOTE 3: +; During execution of the compiled code with amx_exec_jit() the x86 processor's +; stack is switched into the data section of the abstract machine. This means +; that there should always be enough memory left between HEA and STK to provide +; stack space for occurring interrupts! (see the STACKRESERVE variable) + +; NOTE 4: +; Although the Pawn compiler doesn't generate the LCTRL, SCTRL and CALL.I +; instructions, I have to tell that they don't work as expected in a JIT +; compiled program, because there is no easy way of transforming AMX code +; addresses and JIT translated ones. This might be fixed in a future version. + +; NX ("No eXecute") and XD (eXecution Denied) bits +; (by Thiadmer Riemersma) +; +; AMD defined a bit "No eXecute" for the page table entries (for its 64-bit +; processors) and Intel came with the same design, but calling it differently. +; The purpose is to make "buffer overrun" security holes impossible, or at least +; very, very difficult, by marking the stack and the heap as memory regions +; such that an attempt to execute processor instructions will cause a processor +; exception (of course, a buffer overrun that is not explictly handled will then +; crash the application --instead of executing the rogue code). +; +; For JIT compilers, this has the impact that you are not allowed to execute the +; code that the JIT has generated. To do that, you must adjust the attributes +; for the memory page. For Microsoft Windows, you can use VirtualAlloc() to +; allocate a memory block with the appropriate fags; on Linux (with a recent +; kernel), you would use vmalloc_exec(). Microsoft Windows also offers the +; function VirtualProtect() to change the page attributes of an existing memory +; block, but there are caveats in its use: if the block spans multiple pages, +; these pages must be consecutive, and if there are blocks of memory in a page +; unrelated to the JIT, their page attributes will change too. +; +; The JIT compiler itself requires only read-write access (this is the default +; for a memory block that you allocate). The execution of the JIT-compiled code +; requires full access to the memory block: read, write and execute. It needs +; write access, because the SYSREQ.C opcode is patched to SYSREQ.D after the +; first lookup (this is an optimization, look up the address of the native +; function only once). For processors that do not support the NX/XD bit, +; execution of code is implicitly supported if read access is supported. +; +; During compilation, the JIT compiler requires write-access to its own code +; segment: the JIT-compiler patches P-code parameters into its own code segment +; during compilation. This is handled in the support code for amx_InitJIT. +; +; +; CALLING CONVENTIONS +; (by Thiadmer Riemersma) +; +; This version is the JIT that uses the "stack calling convention". In the +; original implementation, this meant __cdecl; both for the calling convention +; for the _asm_runJIT routine itself as for the callback functions. +; The current release supports __stdcall for the callback functions; to +; use it, you need to assemble the file with STDECL defined (Since STDCALL is +; a reserved word on the assembler, I had to choose a different name for the +; macro, hence STDECL.) + +; Revision History +; ---------------- +; 28 july 2005 +; Bug fix for the switch table, in the situation where only the default +; case was present. Bug found by Bailopan. +; 17 february 2005 by Thiadmer Riemersma (TR) +; Addition of the BREAK opcode, removal of the older debugging opcode +; table. There should now be some debug support (if enabled during the +; build of the JIT compiler), but not enough to run a debugger: the JIT +; compiler does not keep a list that relates the code addresses of the +; P-code versus the native code. +; 29 June 2004 by G.W.M. Vissers +; Translated the thing into NASM. The actual generation of the code is +; put into the data section because the code modifies itself whereas the +; text section is usually read-only in the Unix ELF format. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added the SYMTAG and SYSCALL.D opcodes (these are not really supported; +; SYMTAG is a no-op). +; Support __stdcall calling convention for for the native function "hook" +; function (the __cdecl calling convention is also still supported). +; 14 October 2002 by Thiadmer Riemersma (TR) +; Corrected the amx_s structure. The _hlw field was missing, which caused +; errors for arguments to native functions that were passed by reference. +; 2002/08/05 TR +; * store the status of the abstract machine in the AMX structure upon +; return, so that the machine can be restarted (OP_SLEEP) +; * added OP_NOP (for alignment, it is ignored by the JIT) +; * make sure the JIT does not crash when we NULL is passed for the +; return value +; 2000/03/03 MP +; * _amx_opcodelist is equipped with an underscore, again 8-P +; * added SRANGE as a no-op, so debugging info doesn't upset the JIT +; compiler anymore +; * added note about LCTRL, SCTRL and CALL.I +; 2000/03/02 MP +; * made JIT support __cdecl calling conventions +; * removed some rather unnecessary pops in the epilog of amx_exec_asm +; * changed the template for CALL into a DB byte sequence (tasm 4.1 +; didn't like the immediate value) +; 1999/12/07 MP +; * fixed crash caused by JIT compiler not saving registers +; 1999/08/06 MP - design change: closer to the "iron" with native stack +; * The JIT compiler now generates relocatable code for case tables by +; setting FORCERELOCATABLE = 1. +; * removed all debug hook code +; * exchanged meaning of ESP and ESI in asm_exec(): now low-level calls/ +; pushes/pops are possible +; * removed the run-time functions for the CALL, CALL_I and RET op-codes, +; they are now inline +; * All these changes gained around 80% performance increase for the +; hanoi bench. +; 1999/08/05 MP +; * fixed OP_LINE in the case of NODBGCALLS==1, where no compiled address +; was stored for the LINE byte code (i.e. SWITCH would jump to totally +; wrong addresses). The same fix was applied to OP_FILL, OP_FILE and +; OP_SCTRL (for the no-op case). +; 1999/08/04 MP +; * updated with 4 new opcodes (SRANGE does nothing at the moment; 2dim. +; arrays have not been tested.) +; * hacked relocation code to support absoulute addresses for CASETBL +; (This assumes that no generated address will be greater than +; 0x7fffffff. Bit no. 31 is used as flag for absolute addresses.) +; * The run-time function for SWITCH uses a (hopefully) faster algorithm +; to compute the destination address: It searches backwards now. +; 1999/07/08 MP - initial revision + + +; +; Support for the BREAK opcode (callback to the debugger): 0 = no, all other +; values = yes. Beware that the compiled code runs slower when this is enabled, +; and that debug support is still fairly minimal. +; +; GWMV: to generate LINE opcode, %define DEBUGSUPPORT +; +%undef DEBUGSUPPORT + +; +; If this is set to 1 the JIT generates relocatable code for case tables, too. +; If set to 0, a faster variant for switch (using absolute addresses) is +; generated. I consider setting it to 0 a bad idea. +; +; GWMV: to use absolute addresses, %undef FORCERELOCATABLE +; +%define FORCERELOCATABLE + +; +; Determines how much memory should be reserved for occurring interrupts. +; (If my memory serves me right, DOS4/G(W) provides a stack of 512 bytes +; for interrupts that occur in real mode and are promoted to protected mode.) +; This value _MUST_ be greater than 64 (for AMX needs) and should be at least +; 128 (to serve interrupts). +; +%define STACKRESERVE 256 + +; +; This variable controls the generation of memory range checks at run-time. +; You should set this to 0, only when you are sure that there are no range +; violations in your Pawn programs and you really need those 5% speed gain. +; +; GWMV: To disable runtime checks, %undef it, instread of setting it to zero +; +%define DORUNTIMECHECKS + +%define JIT 1 +%include "amxdefn.asm" + +; GWMV: +; Nasm can't do the next as equivalence statements, since the value of +; esi is not determined at compile time +%define stk [esi+32] ; define some aliases to registers that will +%define alt [esi+28] ; be stored on the stack when the code is +%define pri [esi+24] ; actually beeing executed +%define code [esi+20] +%define amx [esi+16] +%define retval [esi+12] +%define stp [esi+8] +%define hea [esi+4] +%define frm [esi] ; FRM is NOT stored in ebp, FRM+DAT is being held + ; in ebx instead. + +; +; #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +; +%macro _PUSH 1 + push dword %1 +%endmacro + +; +; #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +; +%macro _POP 1 + pop dword %1 +%endmacro + + +; +; For determining the biggest native code section generated for ONE Pawn +; opcode. (See the following macro and the PUBLIC function getMaxCodeSize().) +; +; GWMV: Do NOT see the following macro. See CHECKCODESIZE instead. +; +%assign MAXCODESIZE 0 + +; +; This is the work horse of the whole JIT: It actually copies the code. +%macro GO_ON 2-3 4 + mov esi, %1 ;get source address of JIT code + mov ecx,%2-%1 ;get number of bytes to copy + mov [ebx],edi ;store address for jump-correction + add ebx,%3 + rep movsb + cmp ebx,[end_code] + jae code_gen_done + jmp dword [ebx] ;go on with the next opcode +%endmacro + +; GWMV: +; Nasm can't handle the determination of the maximum code size as was done +; in the Masm implementation, since it only does two passes. This macro is +; called *after* the code for each Pawn instruction. +%macro CHECKCODESIZE 1 + %if MAXCODESIZE < $-%1 + %assign MAXCODESIZE $-%1 + %endif +%endmacro + +; +; Modify the argument of an x86 instruction with the Pawn opcode's parameter +; before copying the code. +; +%macro putval 1 + mov eax,[ebx+4] + mov dword [%1],eax +%endmacro + +; +; Add an entry to the table of addresses which have to be relocated after the +; code compilation is done. +; +%macro RELOC 1-2 ; adr, dest + mov ebp,[reloc_num] + %if %0 < 2 + mov eax,[ebx+4] + %else + lea eax,[%2] + %endif + mov [edx+ebp],eax ; write absolute destination + lea eax,[edi+%1] + mov [edx+ebp+4],eax ; write address of jump operand + add dword [reloc_num],8 +%endmacro + +%macro _DROPARGS 1 ; (TR) remove function arguments from the stack + %ifndef STDECL ; (for __cdecl calling convention only) + add esp,%1 + %endif +%endmacro + + +section .text + + +global asm_runJIT, _asm_runJIT +global amx_exec_jit, _amx_exec_jit +global getMaxCodeSize, _getMaxCodeSize + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; void asm_runJIT( AMX_HEADER *amxh, JumpAddressArray *jumps, void *dest ) ; +; eax edx ebx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; asm_runJIT() assumes that the code of this module is allready browsed and +; relocated for the JIT compiler. It also assumes that both the jumps array and +; the dest memory block are large enough to hold all the data it has to write +; to them, as well as that the prefix (header) has already been copied to dest. + +asm_runJIT: +_asm_runJIT: + push ebp + push ebx + push edi + push esi + + mov eax,[esp+20] ; get amxh + mov edx,[esp+24] ; get jumps array + mov ebx,[esp+28] ; get destination + + mov [amxhead],eax ; save pointer to AMX_HEADER struct + mov ecx,[eax+_cod] ; get offset of start of code + mov eax,[eax+_dat] ; offset of start of data = end of code + mov edi,ecx + add ecx,[amxhead] ; compute the real pointer + add eax,[amxhead] ; dito + add edi,ebx ; get write pointer into EDI + mov [compiled_code],ebx + mov [end_code],eax ; Store end-of-code address, so JIT + ; compiler knows when to stop. + mov dword [reloc_num],0 ; init the index into the jumps array + + mov ebx,ecx + jmp dword [ecx] ; start compiling + + ; The compiler will jump back here when code generation is complete. + +code_gen_done: ; Now copy the data section. + mov ebp,[amxhead] ; get source AMX_HEADER start address + add edi,3 ; DAT follows directly after COD + and edi,0fffffffch ; align it on a DWORD boundary + push edi ; save data start pointer + mov esi,[end_code] ; get start of data segment + mov ecx,[ebp+_h_hea] + sub ecx,[ebp+_dat] ; compute length of array to copy + rep movsb ; copy the data + + ; Now adjust the register values in the compiled AMX_HEADER. + ; COD stays the same since the size of AMX_HEADER doesn't change in + ; compiled mode. + mov ebx,[compiled_code] ; get compiled AMX's header address + pop esi ; recall data start pointer + sub esi,ebx ; DAT = size of code + size of prefix + mov [ebx+_dat],esi ; write corrected DAT register + + ;HEA and STP are already relative to DAT, so we don't need to fix them. + + ; Now the calls/jumps in the compiled code have to be relocated. + sub ecx,ecx ; reset offset into relocation table + cmp ecx,[reloc_num] + jae reloc_code_done ; if there's nothing to fix, skip this part + reloc_code_loop: + mov eax,[edx+ecx] ; get destination address + mov edi,[edx+ecx+4] ; determine where to write the relocated value + add ecx,8 ; set pointer to next entry in relocation table + add edi,4 ; base address from where the offset is taken +%ifndef FORCERELOCATABLE + ;MP: hack to suport absolute addresses for the CASETBL instruction + test eax,80000000h ; check whether it is an absolute address + pushf + and eax,7fffffffh ; clear the flag bit for absolute addresses + popf + mov eax,[eax] ; translate into compiled absolute address + jne write_reloc ; leave out the subtraction if absolute +%else + mov eax,[eax] ; translate into compiled absolute address +%endif + sub eax,edi ; make a relative offset + write_reloc: + mov [edi-4],eax ; write the relocated address + cmp ecx,[reloc_num] + jb reloc_code_loop + +reloc_code_done: + ; Relocate the addresses in the AMX_HEADER structure. (CIP and publics) + add ebp,[ebp+_cod] ; make all addresses relative to COD, not base + mov eax,[ebx+_h_cip] + add eax,ebp ; get absolute source CIP + mov eax,[eax] ; translate CIP to compiled address + sub eax,ebx ; make it relative to base + sub eax,[ebx+_cod] ; and now relative to COD + mov [ebx+_h_cip],eax; store relocated CIP + mov edi,[ebx+_publics] + sub esi,esi + mov ecx,[ebx+_natives] + sub ecx,edi ; ECX = _natives - _publics = public table size + mov si,[ebx+_defsize] + or ecx,ecx + jz reloc_done ; If there are no publics, we are done. + reloc_publics_loop: + mov eax,[ebx+edi] ; get public function offset + add eax,ebp ; make it a source address + mov eax,[eax] ; translate to compiled address + sub eax,ebx ; make it an offset relative to base + sub eax,[ebx+_cod] ; and now relative to COD + mov [ebx+edi],eax ; write corrected address back + add edi,esi ; step to next public function entry + sub ecx,esi + ja reloc_publics_loop + +reloc_done: + mov eax,0 + pop esi + pop edi + pop ebx + pop ebp + ret + +OP_LOAD_PRI: +;nop; + putval j_load_pri+2 + GO_ON j_load_pri, OP_LOAD_ALT, 8 + + j_load_pri: + mov eax,[edi+12345678h] + CHECKCODESIZE j_load_pri + +OP_LOAD_ALT: +;nop; + putval j_load_alt+2 + GO_ON j_load_alt, OP_LOAD_S_PRI, 8 + + j_load_alt: + mov edx,[edi+12345678h] + CHECKCODESIZE j_load_alt + +;good +OP_LOAD_S_PRI: +;nop; + putval j_load_s_pri+2 + GO_ON j_load_s_pri, OP_LOAD_S_ALT, 8 + + j_load_s_pri: + mov eax,[ebx+12345678h] + CHECKCODESIZE j_load_s_pri + +;good +OP_LOAD_S_ALT: +;nop; + putval j_load_s_alt+2 + GO_ON j_load_s_alt, OP_LOAD_I, 8 + + j_load_s_alt: + mov edx,[ebx+12345678h] + CHECKCODESIZE j_load_s_alt + +OP_LOAD_I: +;nop; + GO_ON j_load_i, OP_LODB_I + + j_load_i: +%ifdef DORUNTIMECHECKS + call [verify_adr_eax] +%endif + mov eax,[edi+eax] + CHECKCODESIZE j_load_i + +OP_LODB_I: +;nop; + mov eax,[ebx+4] + mov eax,dword [(lodb_and-4)+eax*4] + mov dword [j_lodb_i_sm+1],eax ;modify AND instruction + GO_ON j_lodb_i, OP_LREF_PRI, 8 + + j_lodb_i: +%ifdef DORUNTIMECHECKS + call [verify_adr_eax] +%endif + mov eax,[edi+eax] ;subject to misalignment stalls + j_lodb_i_sm: + and eax,12345678h + CHECKCODESIZE j_lodb_i + +OP_LREF_PRI: +;nop; + putval j_lref_pri+2 + GO_ON j_lref_pri, OP_LREF_ALT, 8 + + j_lref_pri: + mov eax,[edi+12345678h] + mov eax,[edi+eax] + CHECKCODESIZE j_lref_pri + +OP_LREF_ALT: +;nop; + putval j_lref_alt+2 + GO_ON j_lref_alt, OP_LREF_S_PRI, 8 + + j_lref_alt: + mov edx,[edi+12345678h] + mov edx,[edi+edx] + CHECKCODESIZE j_lref_alt + +OP_LREF_S_PRI: +;nop; + putval j_lref_s_pri+2 + GO_ON j_lref_s_pri, OP_LREF_S_ALT, 8 + + j_lref_s_pri: + mov eax,[ebx+12345678h] + mov eax,[edi+eax] + CHECKCODESIZE j_lref_s_pri + +OP_LREF_S_ALT: +;nop; + putval j_lref_s_alt+2 + GO_ON j_lref_s_alt, OP_CONST_PRI, 8 + + j_lref_s_alt: + mov edx,[ebx+12345678h] + mov edx,[edi+edx] + CHECKCODESIZE j_lref_s_alt + +;good +OP_CONST_PRI: +;nop; + putval j_const_pri+1 + GO_ON j_const_pri, OP_CONST_ALT, 8 + + j_const_pri: + mov eax,12345678h + CHECKCODESIZE j_const_pri + +;good +OP_CONST_ALT: +;nop; + putval j_const_alt+1 + GO_ON j_const_alt, OP_ADDR_PRI, 8 + + j_const_alt: + mov edx,12345678h + CHECKCODESIZE j_const_alt + +;good +OP_ADDR_PRI: +;nop; + putval j_addr_pri+1 + GO_ON j_addr_pri, OP_ADDR_ALT, 8 + + j_addr_pri: + mov eax,12345678h + add eax,frm + CHECKCODESIZE j_addr_pri + +;good +OP_ADDR_ALT: +;nop; + putval j_addr_alt+1 + GO_ON j_addr_alt, OP_STOR_PRI, 8 + + j_addr_alt: + mov edx,12345678h + add edx,frm + CHECKCODESIZE j_addr_alt + +OP_STOR_PRI: +;nop; + putval j_stor_pri+2 + GO_ON j_stor_pri, OP_STOR_ALT, 8 + + j_stor_pri: + mov [edi+12345678h],eax + CHECKCODESIZE j_stor_pri + +OP_STOR_ALT: +;nop; + putval j_stor_alt+2 + GO_ON j_stor_alt, OP_STOR_S_PRI, 8 + + j_stor_alt: + mov [edi+12345678h],edx + CHECKCODESIZE j_stor_alt + +;good +OP_STOR_S_PRI: +;nop; + putval j_stor_s_pri+2 + GO_ON j_stor_s_pri, OP_STOR_S_ALT, 8 + + j_stor_s_pri: + mov [ebx+12345678h],eax + CHECKCODESIZE j_stor_s_pri + +;good +OP_STOR_S_ALT: +;nop; + putval j_stor_s_alt+2 + GO_ON j_stor_s_alt, OP_STOR_I, 8 + + j_stor_s_alt: + mov [ebx+12345678h],edx + CHECKCODESIZE j_stor_s_alt + +;good +OP_STOR_I: +;nop; + GO_ON j_stor_i, OP_STRB_I + + j_stor_i: +%ifdef DORUNTIMECHECKS + call [verify_adr_edx] +%endif + mov [edi+edx],eax + CHECKCODESIZE j_stor_i + +OP_STRB_I: +;nop; + mov eax,[ebx+4] + cmp eax,1 + jne strb_not1byte + GO_ON j_strb_i_1b, strb_not1byte, 8 + j_strb_i_1b: +%ifdef DORUNTIMECHECKS + call [verify_adr_edx] +%endif + mov [edi+edx],al + CHECKCODESIZE j_strb_i_1b + + strb_not1byte: + cmp eax,4 + je strb_4byte + GO_ON j_strb_i_2b, strb_4byte, 8 + j_strb_i_2b: +%ifdef DORUNTIMECHECKS + call [verify_adr_edx] +%endif + mov [edi+edx],ax + CHECKCODESIZE j_strb_i_2b + + strb_4byte: + GO_ON j_strb_i_4b, OP_SREF_PRI, 8 + j_strb_i_4b: +%ifdef DORUNTIMECHECKS + call [verify_adr_edx] +%endif + mov [edi+edx],eax + CHECKCODESIZE j_strb_i_4b + +OP_SREF_PRI: +;nop; + putval j_sref_pri+2 + GO_ON j_sref_pri, OP_SREF_ALT, 8 + + j_sref_pri: + mov ebp,[edi+12345678h] + mov [edi+ebp],eax + CHECKCODESIZE j_sref_pri + +OP_SREF_ALT: +;nop; + putval j_sref_alt+2 + GO_ON j_sref_alt, OP_SREF_S_PRI, 8 + + j_sref_alt: + mov ebp,[edi+12345678h] + mov [edi+ebp],edx + CHECKCODESIZE j_sref_alt + +OP_SREF_S_PRI: +;nop; + putval j_sref_s_pri+2 + GO_ON j_sref_s_pri, OP_SREF_S_ALT, 8 + + j_sref_s_pri: + mov ebp,[ebx+12345678h] + mov [edi+ebp],eax + CHECKCODESIZE j_sref_s_pri + +OP_SREF_S_ALT: +;nop; + putval j_sref_s_alt+2 + GO_ON j_sref_s_alt, OP_LIDX, 8 + + j_sref_s_alt: + mov ebp,[ebx+12345678h] + mov [edi+ebp],edx + CHECKCODESIZE j_sref_s_alt + +;good +OP_LIDX: +;nop; + GO_ON j_lidx, OP_LIDX_B + + j_lidx: + lea eax,[edx+4*eax] +%ifdef DORUNTIMECHECKS + call [verify_adr_eax] +%endif + mov eax,[edi+eax] + CHECKCODESIZE j_lidx + +OP_LIDX_B: +;nop; + mov al,[ebx+4] + mov byte [j_lidx_b+2],al + GO_ON j_lidx_b, OP_IDXADDR, 8 + + j_lidx_b: + shl eax,12h + add eax,edx +%ifdef DORUNTIMECHECKS + call [verify_adr_eax] +%endif + mov eax,[edi+eax] + CHECKCODESIZE j_lidx_b + +;good +OP_IDXADDR: +;nop; + GO_ON j_idxaddr, OP_IDXADDR_B + + j_idxaddr: + lea eax,[edx+4*eax] + CHECKCODESIZE j_idxaddr + +OP_IDXADDR_B: +;nop; + mov al,[ebx+4] + mov byte [j_idxaddr_b+2],al + GO_ON j_idxaddr_b, OP_ALIGN_PRI, 8 + + j_idxaddr_b: + shl eax,12h + add eax,edx + CHECKCODESIZE j_idxaddr_b + +OP_ALIGN_PRI: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov dword [j_align_pri+1],eax + GO_ON j_align_pri, OP_ALIGN_ALT, 8 + + j_align_pri: + xor eax,12345678h + CHECKCODESIZE j_align_pri + +OP_ALIGN_ALT: +;nop; + mov eax,4 + sub eax,[ebx+4] + mov dword [j_align_alt+1],eax + GO_ON j_align_alt, OP_LCTRL, 8 + + j_align_alt: + xor edx,12345678h + CHECKCODESIZE j_align_alt + +OP_LCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,0 + jne lctrl_1 + GO_ON j_lctrl_0, lctrl_1, 8 + j_lctrl_0: + mov eax,code ; 1=COD + CHECKCODESIZE j_lctrl_0 + lctrl_1: + cmp eax,1 + jne lctrl_2 + GO_ON j_lctrl_1, lctrl_2, 8 + j_lctrl_1: + mov eax,edi ; 1=DAT + CHECKCODESIZE j_lctrl_1 + lctrl_2: + cmp eax,2 + jne lctrl_3 + GO_ON j_lctrl_2, lctrl_3, 8 + j_lctrl_2: + mov eax,hea ; 2=HEA + CHECKCODESIZE j_lctrl_2 + lctrl_3: + cmp eax,3 + jne lctrl_4 + GO_ON j_lctrl_3, lctrl_4, 8 + j_lctrl_3: + mov ebp,amx + mov eax,[ebp+_stp] + CHECKCODESIZE j_lctrl_3 + lctrl_4: + cmp eax,4 + jne lctrl_5 + GO_ON j_lctrl_4, lctrl_5, 8 + j_lctrl_4: + mov eax,esp ; 4=STK + sub eax,edi + CHECKCODESIZE j_lctrl_4 + lctrl_5: + cmp eax,5 + jne lctrl_6 + GO_ON j_lctrl_5, lctrl_6, 8 + j_lctrl_5: + mov eax,frm ; 5=FRM + CHECKCODESIZE j_lctrl_5 + lctrl_6: + mov dword [j_lctrl_6+1],edi + GO_ON j_lctrl_6, OP_SCTRL, 8 + j_lctrl_6: + mov eax,12345678h ; 6=CIP + CHECKCODESIZE j_lctrl_6 + + +OP_SCTRL: +;nop; + mov eax,[ebx+4] + cmp eax,2 + jne sctrl_4 + GO_ON j_sctrl_2, sctrl_4, 8 + j_sctrl_2: + mov hea,eax ; 2=HEA + CHECKCODESIZE j_sctrl_2 + sctrl_4: + cmp eax,4 + jne sctrl_5 + GO_ON j_sctrl_4, sctrl_5, 8 + j_sctrl_4: + ;mov esp,eax ; 4=STK + ;add esp,edi ; relocate stack + lea esp,[eax+edi] + CHECKCODESIZE j_sctrl_4 + sctrl_5: + cmp eax,5 + jne sctrl_ignore + GO_ON j_sctrl_5, sctrl_ignore, 8 + j_sctrl_5: + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate frame + CHECKCODESIZE j_sctrl_5 + sctrl_ignore: + mov [ebx],edi + add ebx,8 + jmp dword [ebx] + +OP_MOVE_PRI: +;nop; + GO_ON j_move_pri, OP_MOVE_ALT + + j_move_pri: + mov eax,edx + CHECKCODESIZE j_move_pri + +;good +OP_MOVE_ALT: +;nop; + GO_ON j_move_alt, OP_XCHG + + j_move_alt: + mov edx,eax + CHECKCODESIZE j_move_alt + +OP_XCHG: +;nop; + GO_ON j_xchg, OP_PUSH_PRI + + j_xchg: ;one might use pushes/pops for pre-586's + xchg eax,edx + CHECKCODESIZE j_xchg + +;good +OP_PUSH_PRI: +;nop; + GO_ON j_push_pri, OP_PUSH_ALT + + j_push_pri: + _PUSH eax + CHECKCODESIZE j_push_pri + +;good +OP_PUSH_ALT: +;nop; + GO_ON j_push_alt, OP_PUSH_R_PRI + + j_push_alt: + _PUSH edx + CHECKCODESIZE j_push_alt + +OP_PUSH_R_PRI: +;nop; + putval j_push_r_pri+1 + GO_ON j_push_r_pri, OP_PUSH_C, 8 + + j_push_r_pri: + mov ecx,12345678h + j_push_loop: + _PUSH eax + loop j_push_loop + ;dec ecx + ;jnz j_push_loop + CHECKCODESIZE j_push_r_pri + +;good +OP_PUSH_C: +;nop; + putval j_push_c+1 + GO_ON j_push_c, OP_PUSH, 8 + + j_push_c: + _PUSH 12345678h + CHECKCODESIZE j_push_c + +OP_PUSH: +;nop; + putval j_push+2 + GO_ON j_push, OP_PUSH_S, 8 + + j_push: + _PUSH [edi+12345678h] + CHECKCODESIZE j_push + +;good +OP_PUSH_S: +;nop; + putval j_push_s+2 + GO_ON j_push_s, OP_POP_PRI, 8 + + j_push_s: + _PUSH [ebx+12345678h] + CHECKCODESIZE j_push_s + +OP_POP_PRI: +;nop; + GO_ON j_pop_pri, OP_POP_ALT + + j_pop_pri: + _POP eax + CHECKCODESIZE j_pop_pri + +;good +OP_POP_ALT: +;nop; + GO_ON j_pop_alt, OP_STACK + + j_pop_alt: + _POP edx + CHECKCODESIZE j_pop_alt + +;good +OP_STACK: +;nop; + putval j_stack+4 + GO_ON j_stack, OP_HEAP, 8 + + j_stack: + mov edx,esp + add esp,12345678h + sub edx,edi +%ifdef DORUNTIMECHECKS + call [chk_marginstack] +%endif + CHECKCODESIZE j_stack + +;good +OP_HEAP: +;nop; + putval j_heap_call-4 + GO_ON j_heap, OP_PROC, 8 + + j_heap: + mov edx,hea + add dword hea,12345678h + j_heap_call: +%ifdef DORUNTIMECHECKS + call [chk_marginheap] +%endif + CHECKCODESIZE j_heap + +;good +OP_PROC: +;nop; + GO_ON j_proc, OP_RET + + j_proc: ;[STK] = FRM, STK = STK - cell size, FRM = STK + _PUSH frm ; push old frame (for RET/RETN) + mov frm,esp ; get new frame + mov ebx,esp ; already relocated + sub frm,edi ; relocate frame + CHECKCODESIZE j_proc + +OP_RET: +;nop; + GO_ON j_ret, OP_RETN + + j_ret: + _POP ebx ; pop frame + mov frm,ebx + add ebx,edi + ret + ;call [jit_ret] + CHECKCODESIZE j_ret + +;good +OP_RETN: +;nop; + GO_ON j_retn, OP_CALL + + j_retn: + jmp [jit_retn] + CHECKCODESIZE j_retn + +;good +OP_CALL: +;nop; + RELOC 1 + GO_ON j_call, OP_CALL_I, 8 + + j_call: + ;call 12345678h ; tasm chokes on this out of a sudden + db 0e8h, 0, 0, 0, 0 + CHECKCODESIZE j_call + +OP_CALL_I: +;nop; + GO_ON j_call_i, OP_JUMP + + j_call_i: + call eax + CHECKCODESIZE j_call_i + +;good +OP_JUMP: +;nop; + RELOC 1 + GO_ON j_jump, OP_JREL, 8 + + j_jump: + DB 0e9h + DD 12345678h + CHECKCODESIZE j_jump + +OP_JREL: +;nop; + mov eax,[ebx+4] + ; create an absolute address from the relative one + RELOC 1, eax+ebx+8 + ; GWMV: is the next line really correct!? + GO_ON j_jump, OP_JREL, 8 + +;good +OP_JZER: +;nop; + RELOC 4 + GO_ON j_jzer, OP_JNZ, 8 + + j_jzer: + or eax,eax + DB 0fh, 84h, 0, 0, 0, 0 ;jz NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jzer + +;good +OP_JNZ: +;nop; + RELOC 4 + GO_ON j_jnz, OP_JEQ, 8 + + j_jnz: + or eax,eax + DB 0fh, 85h, 0, 0, 0, 0 ;jnz NEAR 0 + CHECKCODESIZE j_jnz + +;good +OP_JEQ: +;nop; + RELOC 4 + GO_ON j_jeq, OP_JNEQ, 8 + + j_jeq: + cmp eax,edx + DB 0fh, 84h, 0, 0, 0, 0 ;je NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jeq + +OP_JNEQ: +;nop; + RELOC 4 + GO_ON j_jneq, OP_JLESS, 8 + + j_jneq: + cmp eax,edx + DB 0fh, 85h, 0, 0, 0, 0 ;jne NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jneq + +OP_JLESS: +;nop; + RELOC 4 + GO_ON j_jless, OP_JLEQ, 8 + + j_jless: + cmp eax,edx + DB 0fh, 82h, 0, 0, 0, 0 ;jb NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jless + +OP_JLEQ: +;nop; + RELOC 4 + GO_ON j_jleq, OP_JGRTR, 8 + + j_jleq: + cmp eax,edx + DB 0fh, 86h, 0, 0, 0, 0 ;jbe NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jleq + +OP_JGRTR: +;nop; + RELOC 4 + GO_ON j_jgrtr, OP_JGEQ, 8 + + j_jgrtr: + cmp eax,edx + DB 0fh, 87h, 0, 0, 0, 0 ;ja NEAR 0 (tasm sucks a bit) + CHECKCODESIZE j_jgrtr + +OP_JGEQ: +;nop; + RELOC 4 + GO_ON j_jgeq, OP_JSLESS, 8 + + j_jgeq: + cmp eax,edx + DB 0fh, 83h, 0, 0, 0, 0 ;jae NEAR 0 (unsigned comparison) + CHECKCODESIZE j_jgeq + +OP_JSLESS: +;nop; + RELOC 4 + GO_ON j_jsless, OP_JSLEQ, 8 + + j_jsless: + cmp eax,edx + DB 0fh, 8ch, 0, 0, 0, 0 ;jl NEAR 0 + CHECKCODESIZE j_jsless + +;good +OP_JSLEQ: +;nop; + RELOC 4 + GO_ON j_jsleq, OP_JSGRTR, 8 + + j_jsleq: + cmp eax,edx + DB 0fh, 8eh, 0, 0, 0, 0 ;jle NEAR 0 + CHECKCODESIZE j_jsleq + +OP_JSGRTR: +;nop; + RELOC 4 + GO_ON j_jsgrtr, OP_JSGEQ, 8 + + j_jsgrtr: + cmp eax,edx + DB 0fh, 8Fh, 0, 0, 0, 0 ;jg NEAR 0 + CHECKCODESIZE j_jsgrtr + +OP_JSGEQ: +;nop; + RELOC 4 + GO_ON j_jsgeq, OP_SHL, 8 + + j_jsgeq: + cmp eax,edx + DB 0fh, 8dh, 0, 0, 0, 0 ;jge NEAR 0 + CHECKCODESIZE j_jsgeq + +OP_SHL: +;nop; + GO_ON j_shl, OP_SHR + j_shl: + mov ecx,edx ; TODO: save ECX if used as special register + shl eax,cl + CHECKCODESIZE j_shl + +OP_SHR: +;nop; + GO_ON j_shr, OP_SSHR + j_shr: + mov ecx,edx ; TODO: save ECX if used as special register + shr eax,cl + CHECKCODESIZE j_shr + +OP_SSHR: +;nop; + GO_ON j_sshr, OP_SHL_C_PRI + j_sshr: + mov ecx,edx ; TODO: save ECX if used as special register + sar eax,cl + CHECKCODESIZE j_sshr + +OP_SHL_C_PRI: +;nop; + mov al,[ebx+4] + mov byte [j_shl_c_pri+2],al + GO_ON j_shl_c_pri, OP_SHL_C_ALT, 8 + j_shl_c_pri: + shl eax,12h + CHECKCODESIZE j_shl_c_pri + +OP_SHL_C_ALT: +;nop; + mov al,[ebx+4] + mov byte [j_shl_c_alt+2],al + GO_ON j_shl_c_alt, OP_SHR_C_PRI, 8 + j_shl_c_alt: + shl edx,12h + CHECKCODESIZE j_shl_c_alt + +OP_SHR_C_PRI: +;nop; + mov al,[ebx+4] + mov byte [j_shr_c_pri+2],al + GO_ON j_shr_c_pri, OP_SHR_C_ALT, 8 + j_shr_c_pri: + shr eax,12h + CHECKCODESIZE j_shr_c_pri + +OP_SHR_C_ALT: +;nop; + mov al,[ebx+4] + mov byte [j_shr_c_alt+2],al + GO_ON j_shr_c_alt, OP_SMUL, 8 + j_shr_c_alt: + shr edx,12h + CHECKCODESIZE j_shr_c_alt + +OP_SMUL: +;nop; + GO_ON j_smul, OP_SDIV + j_smul: + push edx + imul edx + pop edx + CHECKCODESIZE j_smul + +;good +OP_SDIV: +;nop; + GO_ON j_sdiv, OP_SDIV_ALT + j_sdiv: + call [jit_sdiv] + CHECKCODESIZE j_sdiv + +OP_SDIV_ALT: +;nop; + GO_ON j_sdiv_alt, OP_UMUL + j_sdiv_alt: + xchg eax,edx + call [jit_sdiv] + CHECKCODESIZE j_sdiv_alt + +OP_UMUL: +;nop; + GO_ON j_umul, OP_UDIV + j_umul: + push edx + mul edx + pop edx + CHECKCODESIZE j_umul + +OP_UDIV: +;nop; + GO_ON j_udiv, OP_UDIV_ALT + j_udiv: + mov ebp,edx + sub edx,edx + call [chk_dividezero] + div ebp + CHECKCODESIZE j_udiv + +OP_UDIV_ALT: +;nop; + GO_ON j_udiv_alt, OP_ADD + j_udiv_alt: + mov ebp,eax + mov eax,edx + sub edx,edx + call [chk_dividezero] + div ebp + CHECKCODESIZE j_udiv_alt + +;good +OP_ADD: +;nop; + GO_ON j_add, OP_SUB + j_add: + add eax,edx + CHECKCODESIZE j_add + +;good +OP_SUB: +;nop; + GO_ON j_sub, OP_SUB_ALT + j_sub: + sub eax,edx + CHECKCODESIZE j_sub + +;good +OP_SUB_ALT: +;nop; + GO_ON j_sub_alt, OP_AND + j_sub_alt: + neg eax + add eax,edx + CHECKCODESIZE j_sub_alt + +OP_AND: +;nop; + GO_ON j_and, OP_OR + j_and: + and eax,edx + CHECKCODESIZE j_and + +OP_OR: +;nop; + GO_ON j_or, OP_XOR + j_or: + or eax,edx + CHECKCODESIZE j_or + +OP_XOR: +;nop; + GO_ON j_xor, OP_NOT + j_xor: + xor eax,edx + CHECKCODESIZE j_xor + +OP_NOT: +;nop; + GO_ON j_not, OP_NEG + j_not: + neg eax ; sets CF iff EAX != 0 + sbb eax,eax ; EAX == -1 iff CF set (zero otherwise) + inc eax ; -1 => 0 and 0 => 1 + CHECKCODESIZE j_not + +OP_NEG: +;nop; + GO_ON j_neg, OP_INVERT + j_neg: + neg eax + CHECKCODESIZE j_neg + +OP_INVERT: +;nop; + GO_ON j_invert, OP_ADD_C + j_invert: + not eax + CHECKCODESIZE j_invert + +;good +OP_ADD_C: +;nop; + putval j_add_c+1 + GO_ON j_add_c, OP_SMUL_C, 8 + j_add_c: + add eax,12345678h + CHECKCODESIZE j_add_c + +;good +OP_SMUL_C: +;nop; + putval j_smul_c+3 + GO_ON j_smul_c, OP_ZERO_PRI, 8 + j_smul_c: + push edx + imul eax,12345678h + pop edx + CHECKCODESIZE j_smul_c + +;good +OP_ZERO_PRI: +;nop; + GO_ON j_zero_pri, OP_ZERO_ALT + j_zero_pri: + sub eax,eax + CHECKCODESIZE j_zero_pri + +;good +OP_ZERO_ALT: +;nop; + GO_ON j_zero_alt, OP_ZERO + j_zero_alt: + sub edx,edx + CHECKCODESIZE j_zero_alt + +OP_ZERO: +;nop; + putval j_zero+2 + GO_ON j_zero, OP_ZERO_S, 8 + j_zero: + mov dword [edi+12345678h],0 + CHECKCODESIZE j_zero + +OP_ZERO_S: +;nop; + putval j_zero_s+2 + GO_ON j_zero_s, OP_SIGN_PRI, 8 + j_zero_s: + mov dword [ebx+12345678h],0 + CHECKCODESIZE j_zero_s + +OP_SIGN_PRI: +;nop; + GO_ON j_sign_pri, OP_SIGN_ALT + j_sign_pri: + shl eax,24 + sar eax,24 + CHECKCODESIZE j_sign_pri + +OP_SIGN_ALT: +;nop; + GO_ON j_sign_alt, OP_EQ + j_sign_alt: + shl edx,24 + sar edx,24 + CHECKCODESIZE j_sign_alt + +OP_EQ: +;nop; + GO_ON j_eq, OP_NEQ + j_eq: + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + CHECKCODESIZE j_eq + +OP_NEQ: +;nop; + GO_ON j_neq, OP_LESS + j_neq: + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + CHECKCODESIZE j_neq + +OP_LESS: +;nop; + GO_ON j_less, OP_LEQ + j_less: + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + CHECKCODESIZE j_less + +OP_LEQ: +;nop; + GO_ON j_leq, OP_GRTR + j_leq: + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + CHECKCODESIZE j_leq + +OP_GRTR: +;nop; + GO_ON j_grtr, OP_GEQ + j_grtr: + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + CHECKCODESIZE j_grtr + +OP_GEQ: +;nop; + GO_ON j_geq, OP_SLESS + j_geq: + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + CHECKCODESIZE j_geq + +;good +OP_SLESS: +;nop; + GO_ON j_sless, OP_SLEQ + j_sless: + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + CHECKCODESIZE j_sless + +OP_SLEQ: +;nop; + GO_ON j_sleq, OP_SGRTR + j_sleq: + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + CHECKCODESIZE j_sleq + +OP_SGRTR: +;nop; + GO_ON j_sgrtr, OP_SGEQ + j_sgrtr: + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + CHECKCODESIZE j_sgrtr + +OP_SGEQ: +;nop; + GO_ON j_sgeq, OP_EQ_C_PRI + j_sgeq: + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + CHECKCODESIZE j_sgeq + +OP_EQ_C_PRI: +;nop; + putval j_eq_c_pri+1 + GO_ON j_eq_c_pri, OP_EQ_C_ALT, 8 + j_eq_c_pri: + cmp eax,12345678h ; PRI == value ? + mov eax,0 + sete al + CHECKCODESIZE j_eq_c_pri + +OP_EQ_C_ALT: +;nop; + putval j_eq_c_alt+4 + GO_ON j_eq_c_alt, OP_INC_PRI, 8 + j_eq_c_alt: + sub eax,eax + cmp edx,12345678h ; ALT == value ? + sete al + CHECKCODESIZE j_eq_c_alt + +OP_INC_PRI: +;nop; + GO_ON j_inc_pri, OP_INC_ALT + j_inc_pri: + inc eax + CHECKCODESIZE j_inc_pri + +OP_INC_ALT: +;nop; + GO_ON j_inc_alt, OP_INC + j_inc_alt: + inc edx + CHECKCODESIZE j_inc_alt + +OP_INC: +;nop; + putval j_inc+2 + GO_ON j_inc, OP_INC_S, 8 + j_inc: + inc dword [edi+12345678h] + CHECKCODESIZE j_inc + +;good +OP_INC_S: +;nop; + putval j_inc_s+2 + GO_ON j_inc_s, OP_INC_I, 8 + j_inc_s: + inc dword [ebx+12345678h] + CHECKCODESIZE j_inc_s + +OP_INC_I: +;nop; + GO_ON j_inc_i, OP_DEC_PRI + j_inc_i: + inc dword [edi+eax] + CHECKCODESIZE j_inc_i + +OP_DEC_PRI: +;nop; + GO_ON j_dec_pri, OP_DEC_ALT + j_dec_pri: + dec eax + CHECKCODESIZE j_dec_pri + +OP_DEC_ALT: +;nop; + GO_ON j_dec_alt, OP_DEC + j_dec_alt: + dec edx + CHECKCODESIZE j_dec_alt + +OP_DEC: +;nop; + putval j_dec+2 + GO_ON j_dec, OP_DEC_S, 8 + j_dec: + dec dword [edi+12345678h] + CHECKCODESIZE j_dec + +OP_DEC_S: +;nop; + putval j_dec_s+2 + GO_ON j_dec_s, OP_DEC_I, 8 + j_dec_s: + dec dword [ebx+12345678h] + CHECKCODESIZE j_dec_s + +OP_DEC_I: +;nop; + GO_ON j_dec_i, OP_MOVS + j_dec_i: + dec dword [edi+eax] + CHECKCODESIZE j_dec_i + +OP_MOVS: +;nop; + putval j_movs+1 + GO_ON j_movs, OP_CMPS, 8 + j_movs: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_movs] + CHECKCODESIZE j_movs + +OP_CMPS: +;nop; + putval j_cmps+1 + GO_ON j_cmps, OP_FILL, 8 + j_cmps: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_cmps] + CHECKCODESIZE j_cmps + +OP_FILL: +;nop; + putval j_fill+1 + GO_ON j_fill, OP_HALT, 8 + j_fill: + mov ecx,12345678h ;TODO: save ECX if used as special register + call [jit_fill] + CHECKCODESIZE j_fill + +;good +OP_HALT: +;nop; + putval j_halt_sm+1 + GO_ON j_halt, OP_BOUNDS, 8 + j_halt: + cmp dword retval,0 + je j_halt_no_value + mov ebp,retval + mov [ebp],eax + j_halt_no_value: + j_halt_sm: + mov eax,12345678h + jmp [jit_return] + CHECKCODESIZE j_halt + +;good +OP_BOUNDS: +;nop; + putval j_bounds+1 + GO_ON j_bounds, OP_SYSREQ_C, 8 + j_bounds: + mov ebp,12345678h + call [jit_bounds] + CHECKCODESIZE j_bounds + +;good +OP_SYSREQ_C: +;nop; + putval j_sysreq_c+1 + GO_ON j_sysreq_c, OP_SYSREQ_PRI, 8 + j_sysreq_c: + mov eax,12345678h ; get function number + j_sysreq: + call [jit_sysreq] + CHECKCODESIZE j_sysreq_c + ; GWMV: oh well, it may look stupid, but I don't want to miss anything + CHECKCODESIZE j_sysreq + +OP_SYSREQ_PRI: +;nop; + GO_ON j_sysreq, OP_SYSREQ_PRI + +OP_FILE: ;opcode is simply ignored +;nop; + mov eax,[ebx+4] ;get size + mov [ebx],edi + lea ebx,[ebx+eax+8] ;move on to next opcode + cmp ebx,dword [end_code] + jae code_gen_done + jmp dword [ebx] ;go on with the next opcode + +OP_LINE: +;nop; + mov [ebx],edi ; no line number support: ignore opcode + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp dword [ebx] ; go on with the next opcode + +OP_SYMBOL: ;ignored + mov [ebx],edi + mov eax,[ebx+4] ; get size + lea ebx,[ebx+eax+8] ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp dword [ebx] ; go on with the next opcode + + +OP_SRANGE: ;ignored + mov [ebx],edi ; store relocated address + add ebx,12 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp dword [ebx] ; go on with the next opcode + + +;not tested +OP_JUMP_PRI: + GO_ON j_jump_pri, OP_SWITCH + + j_jump_pri: ; MP: This opcode makes sense only in con- + jmp [eax] ; junction with a possibility to get the + ; address of a code location... + CHECKCODESIZE j_jump_pri + + +;good +OP_SWITCH: + lea eax,[edi+6] ; The case table will be copied directly + neg eax ; after the run-time call to [jit_switch]. + and eax,3 ; We should align this table on a DWORD + mov ecx,eax ; boundary. + mov al,90h ; 90h = opcode of x86 NOP instruction + rep stosb ; Write the right number of NOPs. + mov [ebx],edi ; store address of SWITCH for relocation step + mov esi, j_switch + mov ecx,6 + rep movsb ; copy the call instruction + mov esi,[ebx+4] ; get address of CASETBL instruction + add ebx,8 ; set instruction pointer to next opcode + add esi,4 ; point esi to first entry: (count, default adr) + mov ecx,[esi] ; get number of cases (excluding default) + inc ecx + mov ebp,[reloc_num] + j_case_loop: + mov eax,[esi] ; get case value + stosd ; write it + mov eax,[esi+4] ; get destination address +%ifndef FORCERELOCATABLE + or eax,80000000h ; add flag for "absolute address" +%endif + mov [edx+ebp],eax ; write dest. adr. into relocation table + mov eax,[esi+4] ; get destination address (again) + add esi,8 ; set ESI to next case + mov [edx+ebp+4],edi ; write adr. to patch into relocation table + add ebp,8 ; promote relocation pointer + stosd ; write dest. adr. + dec ecx + jnz j_case_loop + mov dword [reloc_num],ebp ; write back updated reloc_num + + jmp [ebx] ; GO_ON to next op-code + + j_switch: + call [jit_switch] + +;good +OP_CASETBL: ; compiles to nothing, SWITCH does all the work + mov eax,[ebx+4] ; get count of cases + lea ebx,[ebx+8*eax+(8+4)] ; adjust instruction pointer + jmp [ebx] ; GO_ON with next op-code + + +OP_SWAP_PRI: ; TR + GO_ON j_swap_pri, OP_SWAP_ALT + + j_swap_pri: + _POP ebp + _PUSH eax + mov eax,ebp + CHECKCODESIZE j_swap_pri + + +OP_SWAP_ALT: ; TR + GO_ON j_swap_alt, OP_PUSHADDR + + j_swap_alt: + _POP ebp + _PUSH edx + mov edx,ebp + CHECKCODESIZE j_swap_alt + + +OP_PUSHADDR: ; TR + putval j_pushaddr+1 + GO_ON j_pushaddr, OP_NOP, 8 + + j_pushaddr: + mov ebp,12345678h ;get address (offset from frame) + add ebp,frm + _PUSH ebp + CHECKCODESIZE j_pushaddr + + +OP_NOP: ; TR + GO_ON j_nop, OP_SYSREQ_D + j_nop: ; code alignment is ignored by the JIT + CHECKCODESIZE j_nop + + +OP_SYSREQ_D: +;nop; + putval j_sysreq_d+1 + GO_ON j_sysreq_d, OP_SYMTAG, 8 + j_sysreq_d: + mov ebx,12345678h ; get function address + call [jit_sysreq_d] + CHECKCODESIZE j_sysreq_d + + +OP_SYMTAG: ;ignored (TR) + mov [ebx],edi ; store relocated address + add ebx,8 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp dword [ebx] ; go on with the next opcode + + +OP_BREAK: +%ifndef DEBUGSUPPORT + mov [ebx],edi ; no line number support: ignore opcode + add ebx,4 ; move on to next opcode + cmp ebx,[end_code] + jae code_gen_done + jmp DWORD [ebx] ; go on with the next opcode +%else + GO_ON j_break, OP_INVALID + j_break: + mov ebp,amx + cmp DWORD [ebp+_debug], 0 + je $+4 ; jump around the "call" statement + call [jit_break] + CHECKCODESIZE j_break +%endif + +OP_INVALID: ; break from the compiler with an error code + mov eax,AMX_ERR_INVINSTR + pop esi + pop edi + pop ecx + pop ebp + ret + + +section .text + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell amx_exec( cell *regs, cell *retval, cell stp, cell hea ); +; eax edx ebx ecx ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +amx_exec_jit: +_amx_exec_jit: + push edi + push esi + push ebp + push ebx ; due to __cdecl + + ; __cdecl overhead + mov eax, [esp+20] ; get address of amx regs structure + mov edx, [esp+24] ; get address of retval + mov ebx, [esp+28] ; get stp + mov ecx, [esp+32] ; get hea + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push dword [eax+28] ; store pointer to code segment + push dword [eax+24] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push dword [eax+20]; store FRM + + mov edx,[eax+4] ; get ALT + mov ecx,[eax+8] ; get CIP + mov edi,[eax+12] ; get pointer to data segment + mov esi,[eax+16] ; get STK !!changed, now ECX free as counter!! + mov ebx,[eax+20] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + add esi,edi ; ESP will contain DAT+STK + xchg esp,esi ; switch to AMX stack + + add stp,edi ; make STP absolute address for run-time checks + + _POP ebp ; AMX pseudo-return address, ignored + ; Call compiled code via CALL NEAR
+ call ecx + +return_to_caller: + cmp dword retval,0 + je return_to_caller_no_value + mov ebp,retval + mov [ebp],eax ; provide return value + + return_to_caller_no_value: + mov eax,AMX_ERR_NONE + jmp _return + +_return_popstack: + add esp,4 ; Correct ESP, because we just come from a + ; runtime error checking routine. +_return: + ; store machine state + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov [ebp+_stk],ecx ; store values in AMX structure: STK, ... + mov ecx,hea ; ... HEA, ... + mov [ebp+_hea],ecx + mov ecx,ebx ; ... and FRM + sub ecx,edi ; (correct FRM) + mov [ebp+_frm],ecx + mov [ebp+_pri],eax ; also store PRI, ... + mov [ebp+_alt],edx ; ... and ALT + + ; return + sub stp,edi ; make STP relative to DAT again + xchg esp,esi ; switch back to caller's stack + + add esp,4*9 ; remove temporary data + + pop ebx ; restore registers that have to be preserved + pop ebp ; when using __cdecl convention + pop esi + pop edi + + ret + + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return_popstack + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return_popstack + +_CHKMARGIN_STACK: ; some run-time check routines + cmp esp,stp + lea ebp,[esp-STACKRESERVE] + jg err_stacklow + sub ebp,edi + cmp hea,ebp + jg err_stack + ret + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return_popstack + +_CHKMARGIN_HEAP: + cmp esp,stp + jg err_stacklow + mov ebp,amx + mov ebp,[ebp+_hlw] + cmp DWORD hea,ebp + jl err_heaplow + ret + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return_popstack + +_VERIFYADDRESS_eax: ; used in load.i, store.i & lidx + cmp eax,stp + jae err_memaccess + cmp eax,hea + jb veax1 + lea ebp,[eax+edi] + cmp ebp,esp + jb err_memaccess + veax1: + ret + +_VERIFYADDRESS_edx: ; used in load.i, store.i & lidx + cmp edx,stp + jae err_memaccess + cmp edx,hea + jb vedx1 + lea ebp,[edx+edi] + cmp ebp,esp + jb err_memaccess + vedx1: + ret + +JIT_OP_SDIV: + mov ebp,edx + xor edx,eax ; Check signs of the operands. + cdq + js sdiv_fiddle ; If the signs of the operands are different + ; we'll have to fiddle around to achieve + ; proper rounding towards minus infinity. + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp ; default behavior is right in the other cases + ret + + sdiv_fiddle: + or ebp,ebp ; check for divide by zero + jz err_divide + idiv ebp + or edx,edx + jz sdiv_goon ; If there's no remainder the result is correct + add edx,ebp ; else fix the result values. + dec eax ; Amazing, how simple this is... + sdiv_goon: + ret + + ALIGN 4 + +JIT_OP_RETN: + _POP ebx ; pop frame + _POP ecx ; get return address + + mov frm,ebx + _POP ebp + + add ebx,edi + add esp,ebp ; remove data from stack + + jmp ecx + + +JIT_OP_MOVS: ;length of block to copy is already in ECX + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx ; I hope the blocks to copy are properly + shr ecx,2 ; aligned, so I don't do anything about that. + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + ret + +JIT_OP_CMPS: ;length of block to compare is already in ECX + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax ; This is surely not the fastest way to do this + repe cmpsb ; but the most simple one. + je cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + ret + + +JIT_OP_FILL: ;length (in bytes) of block to fill is already in ECX + push edi + lea edi,[edi+edx] + + shr ecx,2 ;length in 32-bit cells + rep stosd ;the value to use is already in EAX + + pop edi + ret + +JIT_OP_BOUNDS: + cmp eax,0 + jl err_bounds + cmp eax,ebp + jg err_bounds + ret +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return_popstack + +_CHKDIVIDEZERO: + or ebp,ebp ; check for divide by zero + jz err_divide + ret +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return_popstack + +JIT_OP_SYSREQ: + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx + + lea ebx,pri ; 3rd param: addr. of retval + lea ecx,[esp+4] ; 4th param: parameter array + + xchg esp,esi ; switch to caller stack + + push ecx + push ebx + push eax ; 2nd param: function number + push ebp ; 1st param: amx + call [ebp+_callback] + _DROPARGS 16 ; remove args from stack + + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_SYSREQ_D: ; (TR) + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure: STK, + mov ecx,hea ; HEA, + mov eax,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + lea edx,[esp+4] ; 2nd param: parameter array + xchg esp,esi ; switch to caller stack + push edx + push ebp ; 1st param: amx + call ebx ; direct call + _DROPARGS 8 ; remove args from stack + xchg esp,esi ; switch back to AMX stack + mov ebp,amx ; get amx into EBP + cmp dword [ebp+_error],AMX_ERR_NONE + jne _return_popstack; return error code, if any + + ; return value is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame + ret + + +JIT_OP_BREAK: +%ifdef DEBUGSUPPORT + mov ecx,esp ; get STK into ECX + mov ebp,amx ; get amx into EBP + + sub ecx,edi ; correct STK + + mov [ebp+_pri],eax ; store values in AMX structure: PRI, + mov [ebp+_alt],edx ; ALT, + mov [ebp+_stk],ecx ; STK, + mov ecx,hea ; HEA, + mov ebx,frm ; and FRM + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + ;??? storing CIP is not very useful, because the code changed (during JIT compile) + + xchg esp,esi ; switch to caller stack + push ebp ; 1st param: amx + call [ebp+_debug] + _DROPARGS 4 ; remove args from stack + xchg esp,esi ; switch back to AMX stack + cmp eax,AMX_ERR_NONE + jne _return_popstack; return error code, if any + + mov ebp,amx ; get amx into EBP + mov eax,[ebp+_pri] ; restore values + mov edx,[ebp+_alt] ; ALT, + mov edx,alt ; restore ALT + mov ebx,frm ; restore FRM + add ebx,edi ; relocate frame +%endif + ret + + +JIT_OP_SWITCH: + pop ebp ; pop return address = table address + mov ecx,[ebp] ; ECX = number of records + lea ebp,[ebp+ecx*8+8] ; set pointer _after_ LAST case + jecxz op_switch_jump ; special case: no cases at all + op_switch_loop: + cmp eax,[ebp-8] ; PRI == case label? + je op_switch_jump ; found, jump + sub ebp,8 ; position to preceding case + loop op_switch_loop ; check next case, or fall through + op_switch_jump: +%ifndef FORCERELOCATABLE + jmp [ebp-4] ; jump to the case instructions +%else + add ebp,[ebp-4] ; add offset to make absolute adddress + jmp ebp +%endif + + +; The caller of asm_runJIT() can determine the maximum size of the compiled +; code by multiplying the result of this function by the number of opcodes in +; Pawn module. +; +; unsigned long getMaxCodeSize_(); +; +getMaxCodeSize: +_getMaxCodeSize: + mov eax,MAXCODESIZE + ret + +section .data + ALIGN 4 ; This is essential to avoid misalignment stalls. + +end_code DD 0 ; pointer to the end of the source code + +compiled_code DD 0 ; pointer to compiled code (including preamble) + +amxhead DD 0 ; pointer to the AMX_HEADER struct (arg #1 to runJIT) + +reloc_num DD 0 ; counts the addresses in the relocation table (jumps) + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + +; +; A list of the "run-time-library" functions that are called via indirect calls. +; So these calls don't have to be relocated. This gives also the possibility to +; replace some of these with shorter/faster non-debug or non-checking versions, +; without changing the compiled code. Instead this table could be changed... +; +verify_adr_eax DD _VERIFYADDRESS_eax +verify_adr_edx DD _VERIFYADDRESS_edx +chk_marginstack DD _CHKMARGIN_STACK +chk_marginheap DD _CHKMARGIN_HEAP +chk_dividezero DD _CHKDIVIDEZERO +jit_return DD _return +jit_retn DD JIT_OP_RETN +jit_sdiv DD JIT_OP_SDIV +jit_movs DD JIT_OP_MOVS +jit_cmps DD JIT_OP_CMPS +jit_fill DD JIT_OP_FILL +jit_bounds DD JIT_OP_BOUNDS +jit_sysreq DD JIT_OP_SYSREQ +jit_sysreq_d DD JIT_OP_SYSREQ_D +jit_break DD JIT_OP_BREAK +jit_switch DD JIT_OP_SWITCH + +; +; The table for the browser/relocator function. +; + +global amx_opcodelist_jit, _amx_opcodelist_jit + +amx_opcodelist_jit: +_amx_opcodelist_jit: + DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_I + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE + DD OP_LINE + DD OP_SYMBOL + DD OP_SRANGE + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI ; TR + DD OP_SWAP_ALT ; TR + DD OP_PUSHADDR ; TR + DD OP_NOP ; TR + DD OP_SYSREQ_D ; TR + DD OP_SYMTAG ; TR + DD OP_BREAK ; TR + +END diff --git a/amx-deps/src/amx/amxpool.c b/amx-deps/src/amx/amxpool.c deleted file mode 100644 index b20cc7d..0000000 --- a/amx-deps/src/amx/amxpool.c +++ /dev/null @@ -1,272 +0,0 @@ -/* Simple allocation from a memory pool, with automatic release of - * least-recently used blocks (LRU blocks). - * - * These routines are as simple as possible, and they are neither re-entrant - * nor thread-safe. Their purpose is to have a standard implementation for - * systems where overlays are used and malloc() is not available. - * - * The algorithm uses a first-fit strategy. It keeps all blocks in a single - * list (both used blocks and free blocks are in the same list). Every memory - * block must have a unique number that identifies the block. This unique - * number allows to search for the presence of the block in the pool and for - * "conditional allocation". - * - * - * Copyright (c) ITB CompuPhase, 2007-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxpool.c 3935 2008-03-06 13:18:13Z thiadmer $ - */ -#include -#include "amx.h" -#include "amxpool.h" - -#if !defined NULL - #define NULL ((void*)0) -#endif - -#define MIN_BLOCKSIZE 32 -#define PROTECT_LRU 0xffff - -typedef struct tagARENA { - unsigned blocksize; - short index; /* overlay index, -1 if free */ - unsigned short lru; -} ARENA; - -static void *pool_base; -static unsigned pool_size; -static unsigned short pool_lru; - -static void touchblock(ARENA *hdr); -static ARENA *findblock(int index); - -/* amx_poolinit() initializes the memory pool for the allocated blocks. - * If parameter pool is NULL, the existing pool is cleared (without changing - * its position or size). - */ -void amx_poolinit(void *pool, unsigned size) -{ - assert(pool!=NULL || pool_base!=NULL); - if (pool!=NULL) { - assert(size>sizeof(ARENA)); - /* save parameters in global variables, then "free" the entire pool */ - pool_base=pool; - pool_size=size; - } /* if */ - pool_lru=0; - amx_poolfree(NULL); -} - -/* amx_poolfree() releases a block allocated earlier. The parameter must have - * the same value as that returned by an earlier call to amx_poolalloc(). That - * is, the "block" parameter must point directly behind the arena header of the - * block. - * When parameter "block" is NULL, the pool is re-initialized (meaning that - * all blocks are freed). - */ -void amx_poolfree(void *block) -{ - ARENA *hdr,*hdr2; - unsigned sz; - - assert(pool_base!=NULL); - assert(pool_size>sizeof(ARENA)); - - /* special case: if "block" is NULL, create a single free space */ - if (block==NULL) { - /* store an arena header at the start of the pool */ - hdr=(ARENA*)pool_base; - hdr->blocksize=pool_size-sizeof(ARENA); - hdr->index=-1; - hdr->lru=0; - } else { - hdr=(ARENA*)((char*)block-sizeof(ARENA)); - assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size); - assert(hdr->blocksizeindex=-1; - - /* try to coalesce with the next block */ - hdr2=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA)); - if (hdr2->index==-1) - hdr->blocksize+=hdr2->blocksize+sizeof(ARENA); - - /* try to coalesce with the previous block */ - if ((void*)hdr!=pool_base) { - sz=pool_size; - hdr2=(ARENA*)pool_base; - while (sz>0 && (char*)hdr2+hdr2->blocksize+sizeof(ARENA)!=(char*)hdr) { - assert(sz<=pool_size); - sz-=hdr2->blocksize+sizeof(ARENA); - hdr2=(ARENA*)((char*)hdr2+hdr2->blocksize+sizeof(ARENA)); - } /* while */ - assert((char*)hdr2+hdr2->blocksize+sizeof(ARENA)==(char*)hdr); - if (hdr2->index==-1) - hdr2->blocksize+=hdr->blocksize+sizeof(ARENA); - } /* if */ - } /* if */ -} - -/* amx_poolalloc() allocates the requested number of bytes from the pool and - * returns a header to the start of it. Every block in the pool is prefixed - * with an "arena header"; the return value of this function points just - * behind this arena header. - * - * The block with the specified "index" should not already exist in the pool. - * In other words, parameter "index" should be unique for every of memory block, - * and the block should not change in size. Use amx_poolfind() to verify whether - * a block is already in the pool (and optionally amx_poolfree() to remove it). - * - * If no block of sufficient size is available, the routine frees blocks until - * the requested amount of memory can be allocated. There is no intelligent - * algorithm involved: the routine just frees the least-recently used block at - * every iteration (without considering the size of the block or whether that - * block is adjacent to a free block). - */ -void *amx_poolalloc(unsigned size,int index) -{ - ARENA *hdr,*hdrlru; - unsigned sz; - unsigned short minlru; - - assert(size>0); - assert(index>=0 && index<=SHRT_MAX); - assert(findblock(index)==NULL); - - /* align the size to a cell boundary */ - if ((size % sizeof(cell))!=0) - size+=sizeof(cell)-(size % sizeof(cell)); - if (size+sizeof(ARENA)>pool_size) - return NULL; /* requested block does not fit in the pool */ - - /* find a block large enough to get the size plus an arena header; at - * the same time, detect the block with the lowest LRU - * if no block of sufficient size can be found, the routine then frees - * the block with the lowest LRU count and tries again - */ - do { - sz=pool_size; - hdr=(ARENA*)pool_base; - hdrlru=hdr; - minlru=USHRT_MAX; - while (sz>0) { - assert(sz<=pool_size); - assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size); - if (hdr->index==-1 && hdr->blocksize>=size) - break; - if (hdr->index!=-1 && hdr->lrulru; - hdrlru=hdr; - } /* if */ - sz-=hdr->blocksize+sizeof(ARENA); - hdr=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA)); - } /* while */ - assert(sz<=pool_size); - if (sz==0) { - /* free up memory and try again */ - assert(hdrlru->index!=-1); - amx_poolfree((char*)hdrlru+sizeof(ARENA)); - } /* if */ - } while (sz==0); - - /* see whether to allocate the entire free block, or to cut it in two blocks */ - if (hdr->blocksize>size+MIN_BLOCKSIZE+sizeof(ARENA)) { - /* cut the block in two */ - ARENA *next=(ARENA*)((char*)hdr+size+sizeof(ARENA)); - next->blocksize=hdr->blocksize-size-sizeof(ARENA); - next->index=-1; - next->lru=0; - } else { - size=hdr->blocksize; - } /* if */ - hdr->blocksize=size; - hdr->index=(short)index; - touchblock(hdr); /* set LRU field */ - - return (void*)((char*)hdr+sizeof(ARENA)); -} - -/* amx_poolfind() returns the address of the memory block with the given index, - * or NULL if no such block exists. Parameter "index" should not be -1, because - * -1 represents a free block (actually, only positive values are valid). - * When amx_poolfind() finds the block, it increments its LRU count. - */ -void *amx_poolfind(int index) -{ - ARENA *hdr=findblock(index); - if (hdr==NULL) - return NULL; - touchblock(hdr); - return (void*)((char*)hdr+sizeof(ARENA)); -} - -int amx_poolprotect(int index) -{ - ARENA *hdr=findblock(index); - if (hdr==NULL) - return AMX_ERR_GENERAL; - hdr->lru=PROTECT_LRU; - return AMX_ERR_NONE; -} - -static ARENA *findblock(int index) -{ - ARENA *hdr; - unsigned sz; - - assert(index>=0); - sz=pool_size; - hdr=(ARENA*)pool_base; - while (sz>0 && hdr->index!=index) { - assert(sz<=pool_size); - assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size); - sz-=hdr->blocksize+sizeof(ARENA); - hdr=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA)); - } /* while */ - assert(sz<=pool_size); - return (sz>0 && hdr->index==index) ? hdr : NULL; -} - -static void touchblock(ARENA *hdr) -{ - assert(hdr!=NULL); - if (++pool_lru >= PROTECT_LRU) - pool_lru=0; - hdr->lru=pool_lru; - - /* special case: if the overlay LRU count wrapped back to zero, set the - * LRU count of all blocks to zero, but set the count of the block just - * touched to 1 (skip blocks marked as protected, too) - */ - if (pool_lru==0) { - ARENA *hdr2; - unsigned sz=pool_size; - hdr2=(ARENA*)pool_base; - while (sz>0) { - assert(sz<=pool_size); - if (hdr2->lru!=PROTECT_LRU) - hdr2->lru=0; - sz-=hdr2->blocksize+sizeof(ARENA); - hdr2=(ARENA*)((char*)hdr2+hdr2->blocksize+sizeof(ARENA)); - } /* while */ - assert(sz==0); - hdr->lru=++pool_lru; - } /* if */ -} diff --git a/amx-deps/src/amx/amxpool.h b/amx-deps/src/amx/amxpool.h deleted file mode 100644 index 5ed415d..0000000 --- a/amx-deps/src/amx/amxpool.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Simple allocation from a memory pool, with automatic release of - * least-recently used blocks (LRU blocks). - * - * Copyright (c) ITB CompuPhase, 2007-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxpool.h 3931 2008-03-04 17:02:34Z thiadmer $ - */ -#ifndef AMXPOOL_H_INCLUDED -#define AMXPOOL_H_INCLUDED - -void amx_poolinit(void *pool, unsigned size); -void *amx_poolalloc(unsigned size, int index); -void amx_poolfree(void *block); -void *amx_poolfind(int index); -int amx_poolprotect(int index); - - -#endif /* AMXPOOL_H_INCLUDED */ diff --git a/amx-deps/src/amx/amxprocess.c b/amx-deps/src/amx/amxprocess.c index a22d081..c5c7cf6 100644 --- a/amx-deps/src/amx/amxprocess.c +++ b/amx-deps/src/amx/amxprocess.c @@ -1,932 +1,933 @@ -/* Process control and Foreign Function Interface module for the Pawn AMX - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxprocess.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include "osdefs.h" -#if defined __WIN32__ || defined __MSDOS__ - #include -#endif -#if defined __WIN32__ || defined _Windows - #include -#elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - #include - #include - #include - #include - /* The package libffi library (required for compiling this extension module - * under Unix/Linux) is not included, because its license is more restrictive - * than that of Pawn (even if ever so slightly). Recent versions of the GCC - * compiler include libffi. A separate download of the libffi package is - * available at http://sources.redhat.com/libffi/ and - * http://sablevm.org/download/snapshot/. - */ - #include -#endif -#include "amx.h" - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _istdigit isdigit -# define _tgetenv getenv -# define _tcscat strcat -# define _tcschr strchr -# define _tcscmp strcmp -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _tcsncmp strncmp -# define _tcspbrk strpbrk -# define _tcsrchr strrchr -# define _tcstol strtol -#endif - - -#define MAXPARAMS 32 /* maximum number of parameters to a called function */ - - -typedef struct tagMODlIST { - struct tagMODlIST _FAR *next; - TCHAR _FAR *name; - unsigned long inst; - AMX *amx; -} MODLIST; - -typedef struct tagPARAM { - union { - void *ptr; - long val; - } v; - unsigned char type; - unsigned char size; - int range; -} PARAM; - -#define BYREF 0x80 /* stored in the "type" field fo the PARAM structure */ - -static MODLIST ModRoot = { NULL }; - -/* pipes for I/O redirection */ -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - static HANDLE newstdin,newstdout,read_stdout,write_stdin; -#elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - static int pipe_to[2]={-1,-1}; - static int pipe_from[2]={-1,-1}; - void *inst_ffi=NULL; /* open handle for libffi */ -#endif - - -static const TCHAR *skippath(const TCHAR *name) -{ - const TCHAR *ptr; - - assert(name != NULL); - if ((ptr = _tcsrchr(name, __T(DIRSEP_CHAR))) == NULL) - ptr = name; - else - ptr++; - assert(ptr != NULL); - return ptr; -} - -static MODLIST _FAR *findlib(MODLIST *root, AMX *amx, const TCHAR *name) -{ - MODLIST _FAR *item; - const TCHAR *ptr = skippath(name); - - for (item = root->next; item != NULL; item = item->next) - if (_tcscmp(item->name, ptr) == 0 && item->amx == amx) - return item; - return NULL; -} - -static MODLIST _FAR *addlib(MODLIST *root, AMX *amx, const TCHAR *name) -{ - MODLIST _FAR *item; - const TCHAR *ptr = skippath(name); - - assert(findlib(root, amx, name) == NULL); /* should not already be there */ - - if ((item = malloc(sizeof(MODLIST))) == NULL) - goto error; - memset(item, 0, sizeof(MODLIST)); - - assert(ptr != NULL); - if ((item->name = malloc((_tcslen(ptr) + 1) * sizeof(TCHAR))) == NULL) - goto error; - _tcscpy(item->name, ptr); - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - item->inst = (unsigned long)LoadLibrary(name); - #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) - if (item->inst <= 32) - item->inst = 0; - #endif - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - /* also load the FFI library, if this is the first call */ - inst_ffi=dlopen("libffi.so",RTLD_NOW); - if (inst_ffi==NULL) - inst_ffi=dlopen("libffi-2.00-beta.so",RTLD_NOW); - if (inst_ffi==NULL) - goto error; /* failed to load either the old library or the new libbrary */ - item->inst = (unsigned long)dlopen(name,RTLD_NOW); - #else - #error Unsupported environment - #endif - if (item->inst == 0) - goto error; - - item->amx = amx; - - item->next = root->next; - root->next = item; - return item; - -error: - if (item != NULL) { - if (item->name != NULL) - free(item->name); - if (item->inst != 0) { - #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - FreeLibrary((HINSTANCE)item->inst); - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - dlclose((void*)item->inst); - #else - #error Unsupported environment - #endif - } /* if */ - free(item); - } /* if */ - return NULL; -} - -static int freelib(MODLIST *root, AMX *amx, const TCHAR *name) -{ - MODLIST _FAR *item, _FAR *prev; - const TCHAR *ptr; - int count = 0; - - ptr = (name != NULL) ? skippath(name) : NULL; - - for (prev = root, item = prev->next; item != NULL; prev = item, item = prev->next) { - if ((amx == NULL || amx == item->amx) && (ptr == NULL || _tcscmp(item->name, ptr) == 0)) { - prev->next = item->next; /* unlink first */ - assert(item->inst != 0); - #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - FreeLibrary((HINSTANCE)item->inst); - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - dlclose((void*)item->inst); - #else - #error Unsupported environment - #endif - assert(item->name != NULL); - free(item->name); - free(item); - count++; - } /* if */ - } /* for */ - #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - if (amx==NULL && name==NULL && inst_ffi!=NULL) - dlclose(inst_ffi); - #endif - return count; -} - - -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - -typedef long (CALLBACK* LIBFUNC)(); - -/* push() -** -** This function is the kind of programming trick that you don't even dare to -** dream about! With the usual C calling convention, the caller cleans up the -** stack after calling the function. This allows C functions to be flexible -** with parameters, both in number and in type. -** With the Pascal calling convention, used here, the callee (the function) -** cleans up the stack. But here, function push() doesn't know about any -** parameters. We neither declare any, nor indicate that the function has no -** parameters (i.e. the function is not declared having 'void' parameters). -** When we call function push(), the caller thinks the function cleans up the -** stack (because of the Pascal calling convention), while the function does -** not know that it has parameters, so it cannot clean them. As a result, -** nobody cleans up the stack. Ergo, The parameter you pass to function push() -** stays on the stack. -*/ -static void PASCAL push() { } - -LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname) -{ - FARPROC lpfn; - - assert(inst!=0); - lpfn=GetProcAddress((HINSTANCE)inst,functionname); - #if defined __WIN32__ - if (lpfn==NULL && strlen(functionname)<128-1) { - char str[128]; - strcpy(str,functionname); - #if defined UNICODE - strcat(str,"W"); - #else - strcat(str,"A"); - #endif - lpfn = GetProcAddress((HINSTANCE)inst,str); - } /* if */ - #endif - return (LIBFUNC)lpfn; -} - -#else - -typedef long (* LIBFUNC)(); - -LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname) -{ - assert(inst!=0); - return (LIBFUNC)dlsym((void*)inst, functionname); -} - -#endif - -static void *fillarray(AMX *amx, PARAM *param, cell *cptr) -{ - int i; - void *vptr; - - vptr = malloc(param->range * (param->size / 8)); - if (vptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return NULL; - } /* if */ - - assert(param->range > 1); - if (param->size == 8) { - unsigned char *ptr = (unsigned char *)vptr; - for (i = 0; i < param->range; i++) - *ptr++ = (unsigned char)*cptr++; - } else if (param->size == 16) { - unsigned short *ptr = (unsigned short *)vptr; - for (i = 0; i < param->range; i++) - *ptr++ = (unsigned short)*cptr++; - } else { - unsigned long *ptr = (unsigned long *)vptr; - for (i = 0; i < param->range; i++) - *ptr++ = (unsigned long)*cptr++; - } /* for */ - - return vptr; -} - -/* libcall(const libname[], const funcname[], const typestring[], ...) - * - * Loads the DLL or shared library if not yet loaded (the name comparison is - * case sensitive). - * - * typestring format: - * Whitespace is permitted between the types, but not inside the type - * specification. The string "ii[4]&u16s" is equivalent to "i i[4] &u16 s", - * but easier on the eye. - * - * types: - * i = signed integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux - * u = unsigned integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux - * f = IEEE floating point, 32-bit - * p = packed string - * s = unpacked string - * The difference between packed and unpacked strings is only relevant when - * the parameter is passed by reference (see below). - * - * pass-by-value and pass-by-reference: - * By default, parameters are passed by value. To pass a parameter by - * reference, prefix the type letter with an "&": - * &i = signed integer passed by reference - * i = signed integer passed by value - * Same for '&u' versus 'u' and '&f' versus 'f'. - * - * Arrays are passed by "copy & copy-back". That is, libcall() allocates a - * block of dynamic memory to copy the array into. On return from the foreign - * function, libcall() copies the array back to the abstract machine. The - * net effect is similar to pass by reference, but the foreign function does - * not work in the AMX stack directly. During the copy and the copy-back - * operations, libcall() may also transform the array elements, for example - * between 16-bit and 32-bit elements. This is done because Pawn only - * supports a single cell size, which may not fit the required integer size - * of the foreign function. - * - * See "element ranges" for the syntax of passing an array. - * - * Strings may either be passed by copy, or by "copy & copy-back". When the - * string is an output parameter (for the foreign function), the size of the - * array that will hold the return string must be indicated between square - * brackets behind the type letter (see "element ranges"). When the string - * is "input only", this is not needed --libcall() will determine the length - * of the input string itself. - * - * The tokens 'p' and 's' are equivalent, but 'p[10]' and 's[10]' are not - * equivalent: the latter syntaxes determine whether the output from the - * foreign function will be stored as a packed or an unpacked string. - * - * element sizes: - * Add an integer behind the type letter; for example, 'i16' refers to a - * 16-bit signed integer. Note that the value behind the type letter must - * be either 8, 16 or 32. - * - * You should only use element size specifiers on the 'i' and 'u' types. That - * is, do not use these specifiers on 'f', 's' and 'p'. - * - * element ranges: - * For passing arrays, the size of the array may be given behind the type - * letter and optional element size. The token 'u[4]' indicates an array of - * four unsigned integers, which are typically 32-bit. The token 'i16[8]' - * is an array of 8 signed 16-bit integers. Arrays are always passed by - * "copy & copy-back" - * - * When compiled as Unicode, this library converts all strings to Unicode - * strings. - * - * The calling convention for the foreign functions is assumed: - * - "__stdcall" for Win32, - * - "far pascal" for Win16 - * - and the GCC default for Unix/Linux (_cdecl) - * - * C++ name mangling of the called function is not handled (there is no standard - * convention for name mangling, so there is no portable way to convert C++ - * function names to mangled names). Win32 name mangling (used by default by - * Microsoft compilers on functions declared as __stdcall) is also not handled. - * - * Returns the value of the called function. - */ -static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) -{ - const TCHAR *libname, *funcname, *typestring; - MODLIST *item; - int paramidx, typeidx, idx; - PARAM ps[MAXPARAMS]; - cell *cptr,result; - LIBFUNC LibFunc; - #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - ffi_cif cif; - ffi_type *ptypes[MAXPARAMS]; - void *pvalues[MAXPARAMS]; - #endif - - amx_StrParam(amx, params[1], libname); - item = findlib(&ModRoot, amx, libname); - if (item == NULL) - item = addlib(&ModRoot, amx, libname); - if (item == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - /* library is loaded, get the function */ - amx_StrParam(amx, params[2], funcname); - LibFunc=(LIBFUNC)SearchProcAddress(item->inst, funcname); - if (LibFunc==NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - /* decode the parameters */ - paramidx=typeidx=0; - amx_StrParam(amx, params[3], typestring); - while (paramidx < MAXPARAMS && typestring[typeidx]!=__T('\0')) { - /* skip white space */ - while (typestring[typeidx]!=__T('\0') && typestring[typeidx]<=__T(' ')) - typeidx++; - if (typestring[typeidx]==__T('\0')) - break; - /* save "pass-by-reference" token */ - ps[paramidx].type=0; - if (typestring[typeidx]==__T('&')) { - ps[paramidx].type=BYREF; - typeidx++; - } /* if */ - /* store type character */ - ps[paramidx].type |= (unsigned char)typestring[typeidx]; - typeidx++; - /* set default size, then check for an explicit size */ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - ps[paramidx].size=32; - #elif defined _Windows - ps[paramidx].size=16; - #endif - if (_istdigit(typestring[typeidx])) { - ps[paramidx].size=(unsigned char)_tcstol(&typestring[typeidx],NULL,10); - while (_istdigit(typestring[typeidx])) - typeidx++; - } /* if */ - /* set default range, then check for an explicit range */ - ps[paramidx].range=1; - if (typestring[typeidx]=='[') { - ps[paramidx].range=_tcstol(&typestring[typeidx+1],NULL,10); - while (typestring[typeidx]!=']' && typestring[typeidx]!='\0') - typeidx++; - ps[paramidx].type |= BYREF; /* arrays are always passed by reference */ - typeidx++; /* skip closing ']' too */ - } /* if */ - /* get pointer to parameter */ - amx_GetAddr(amx,params[paramidx+4],&cptr); - switch (ps[paramidx].type) { - case 'i': /* signed integer */ - case 'u': /* unsigned integer */ - case 'f': /* floating point */ - assert(ps[paramidx].range==1); - ps[paramidx].v.val=(int)*cptr; - break; - case 'i' | BYREF: - case 'u' | BYREF: - case 'f' | BYREF: - ps[paramidx].v.ptr=cptr; - if (ps[paramidx].range>1) { - /* convert array and pass by address */ - ps[paramidx].v.ptr = fillarray(amx, &ps[paramidx], cptr); - } /* if */ - break; - case 'p': - case 's': - case 'p' | BYREF: - case 's' | BYREF: - if (ps[paramidx].type=='s' || ps[paramidx].type=='p') { - int len; - /* get length of input string */ - amx_StrLen(cptr,&len); - len++; /* include '\0' */ - /* check max. size */ - if (len1,UNLIMITED); - break; - default: - /* invalid parameter type */ - return amx_RaiseError(amx, AMX_ERR_NATIVE); - } /* switch */ - paramidx++; - } /* while */ - if ((params[0]/sizeof(cell)) - 3 != (size_t)paramidx) - return amx_RaiseError(amx, AMX_ERR_NATIVE); /* format string does not match number of parameters */ - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - /* push the parameters to the stack (left-to-right in 16-bit; right-to-left - * in 32-bit) - */ -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - for (idx=paramidx-1; idx>=0; idx--) { -#else - for (idx=0; idx1,UNLIMITED); - free(ps[idx].v.ptr); - break; - case 'i': - case 'u': - case 'f': - assert(ps[idx].range==1); - break; - case 'i' | BYREF: - case 'u' | BYREF: - case 'f' | BYREF: - amx_GetAddr(amx,params[idx+4],&cptr); - if (ps[idx].range==1) { - /* modify directly in the AMX (no memory block was allocated */ - switch (ps[idx].size) { - case 8: - *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((signed char)*cptr) : (*cptr & 0xff); - break; - case 16: - *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((short)*cptr) : (*cptr & 0xffff); - break; - } /* switch */ - } else { - int i; - for (i=0; i 0; -} - -static void closepipe(void) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - if (newstdin!=NULL) { - CloseHandle(newstdin); - newstdin=NULL; - } /* if */ - if (newstdout!=NULL) { - CloseHandle(newstdout); - newstdout=NULL; - } /* if */ - if (read_stdout!=NULL) { - CloseHandle(read_stdout); - read_stdout=NULL; - } /* if */ - if (write_stdin!=NULL) { - CloseHandle(write_stdin); - write_stdin=NULL; - } /* if */ - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - if (pipe_to[0]>=0) { - close(pipe_to[0]); - pipe_to[0]=-1; - } /* if */ - if (pipe_to[1]>=0) { - close(pipe_to[1]); - pipe_to[1]=-1; - } /* if */ - if (pipe_from[0]>=0) { - close(pipe_from[0]); - pipe_from[0]=-1; - } /* if */ - if (pipe_from[1]>=0) { - close(pipe_from[1]); - pipe_from[1]=-1; - } /* if */ - #endif -} - -/* PID: procexec(const commandline[]) - * Executes a program. Returns an "id" representing the new process (or 0 on - * failure). - */ -static cell AMX_NATIVE_CALL n_procexec(AMX *amx, const cell *params) -{ - TCHAR *pgmname; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - BOOL IsWinNT; - OSVERSIONINFO VerInfo; - STARTUPINFO si; - SECURITY_ATTRIBUTES sa; - SECURITY_DESCRIPTOR sd; - PROCESS_INFORMATION pi; - #elif defined _Windows - HINSTANCE hinst; - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - pid_t pid; - #endif - - amx_StrParam(amx,params[1],pgmname); - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - /* most of this code comes from a "Borland Network" article, combined - * with some knowledge gained from a CodeProject article - */ - closepipe(); - - VerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); - GetVersionEx(&VerInfo); - IsWinNT = VerInfo.dwPlatformId==VER_PLATFORM_WIN32_NT; - - if (IsWinNT) { //initialize security descriptor (Windows NT) - InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); - sa.lpSecurityDescriptor = &sd; - } else { - sa.lpSecurityDescriptor = NULL; - } /* if */ - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; //allow inheritable handles - - if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) { //create stdin pipe - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) { //create stdout pipe - closepipe(); - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - GetStartupInfo(&si); //set startupinfo for the spawned process - si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOWNORMAL; - si.hStdOutput = newstdout; - si.hStdError = newstdout; //set the new handles for the child process - si.hStdInput = newstdin; - - /* spawn the child process */ - if (!CreateProcess(NULL,(TCHAR*)pgmname,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { - closepipe(); - return 0; - } /* if */ - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - Sleep(100); - return pi.dwProcessId; - #elif defined _Windows - hinst=WinExec(pgmname,SW_SHOW); - if (hinst<=32) - hinst=0; - return (cell)hinst; - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - /* set up communication pipes first */ - closepipe(); - if (pipe(pipe_to)!=0 || pipe(pipe_from)!=0) { - closepipe(); - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - /* attempt to fork */ - if ((pid=fork())<0) { - closepipe(); - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ - - if (pid==0) { - /* this is the child process */ - #define MAX_ARGS 10 - TCHAR *args[MAX_ARGS]; - int i; - dup2(pipe_to[0],STDIN_FILENO); /* replace stdin with the in side of the pipe */ - dup2(pipe_from[1],STDOUT_FILENO); /* replace stdout with the out side of the pipe */ - close(pipe_to[0]); /* the pipes are no longer needed */ - close(pipe_to[1]); - close(pipe_from[0]); - close(pipe_from[1]); - pipe_to[0]=-1; - pipe_to[1]=-1; - pipe_from[0]=-1; - pipe_from[1]=-1; - /* split off the option(s) */ - assert(MAX_ARGS>=2); /* args[0] is reserved */ - memset(args,0,MAX_ARGS*sizeof(TCHAR*)); - args[0]=pgmname; - for (i=1; i0 && (line[index-1]==__T('\r') || line[index-1]==__T('\n'))) - index--; - line[index]=__T('\0'); - - amx_GetAddr(amx,params[1],&cptr); - amx_SetString(cptr,line,params[4],sizeof(TCHAR)>1,params[2]); - return 1; -} - -/* procwait(PID:pid) - * Waits until the process has terminated. - */ -static cell AMX_NATIVE_CALL n_procwait(AMX *amx, const cell *params) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - HANDLE hProcess; - DWORD exitcode; - #endif - - (void)amx; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)params[1]); - if (hProcess != NULL) { - while (GetExitCodeProcess(hProcess,&exitcode) && exitcode==STILL_ACTIVE) - Sleep(100); - CloseHandle(hProcess); - } /* if */ - #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ - waitpid((pid_t)params[1],NULL,WNOHANG); - #endif - return 0; -} - - -#if defined __cplusplus - extern "C" -#endif -AMX_NATIVE_INFO ffi_Natives[] = { - { "libcall", n_libcall }, - { "libfree", n_libfree }, - { "procexec", n_procexec }, - { "procread", n_procread }, - { "procwrite", n_procwrite }, - { "procwait", n_procwait }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_ProcessInit(AMX *amx) -{ - return amx_Register(amx, ffi_Natives, -1); -} - -int AMXEXPORT amx_ProcessCleanup(AMX *amx) -{ - freelib(&ModRoot, amx, NULL); - closepipe(); - return AMX_ERR_NONE; -} +/* Process control and Foreign Function Interface module for the Pawn AMX + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxprocess.c 3664 2006-11-08 12:09:25Z thiadmer $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include + #include + #include + #include + /* The package libffi library (required for compiling this extension module + * under Unix/Linux) is not included, because its license is more restrictive + * than that of Pawn (even if ever so slightly). Recent versions of the GCC + * compiler include libffi. A separate download of the libffi package is + * available at http://sources.redhat.com/libffi/ and + * http://sablevm.org/download/snapshot/. + */ + #include +#endif +#include "osdefs.h" +#include "amx.h" + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _istdigit isdigit +# define _tgetenv getenv +# define _tcscat strcat +# define _tcschr strchr +# define _tcscmp strcmp +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _tcsncmp strncmp +# define _tcspbrk strpbrk +# define _tcsrchr strrchr +# define _tcstol strtol +#endif + + +#define MAXPARAMS 32 /* maximum number of parameters to a called function */ + + +typedef struct tagMODlIST { + struct tagMODlIST _FAR *next; + TCHAR _FAR *name; + unsigned long inst; + AMX *amx; +} MODLIST; + +typedef struct tagPARAM { + union { + void *ptr; + long val; + } v; + unsigned char type; + unsigned char size; + int range; +} PARAM; + +#define BYREF 0x80 /* stored in the "type" field fo the PARAM structure */ + +static MODLIST ModRoot = { NULL }; + +/* pipes for I/O redirection */ +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + static HANDLE newstdin,newstdout,read_stdout,write_stdin; +#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + static int pipe_to[2]={-1,-1}; + static int pipe_from[2]={-1,-1}; + void *inst_ffi=NULL; /* open handle for libffi */ +#endif + + +static const TCHAR *skippath(const TCHAR *name) +{ + const TCHAR *ptr; + + assert(name != NULL); + if ((ptr = _tcsrchr(name, __T(DIRSEP_CHAR))) == NULL) + ptr = name; + else + ptr++; + assert(ptr != NULL); + return ptr; +} + +static MODLIST _FAR *findlib(MODLIST *root, AMX *amx, const TCHAR *name) +{ + MODLIST _FAR *item; + const TCHAR *ptr = skippath(name); + + for (item = root->next; item != NULL; item = item->next) + if (_tcscmp(item->name, ptr) == 0 && item->amx == amx) + return item; + return NULL; +} + +static MODLIST _FAR *addlib(MODLIST *root, AMX *amx, const TCHAR *name) +{ + MODLIST _FAR *item; + const TCHAR *ptr = skippath(name); + + assert(findlib(root, amx, name) == NULL); /* should not already be there */ + + if ((item = malloc(sizeof(MODLIST))) == NULL) + goto error; + memset(item, 0, sizeof(MODLIST)); + + assert(ptr != NULL); + if ((item->name = malloc((_tcslen(ptr) + 1) * sizeof(TCHAR))) == NULL) + goto error; + _tcscpy(item->name, ptr); + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + item->inst = (unsigned long)LoadLibrary(name); + #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) + if (item->inst <= 32) + item->inst = 0; + #endif + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + /* also load the FFI library, if this is the first call */ + inst_ffi=dlopen("libffi.so",RTLD_NOW); + if (inst_ffi==NULL) + inst_ffi=dlopen("libffi-2.00-beta.so",RTLD_NOW); + if (inst_ffi==NULL) + goto error; /* failed to load either the old library or the new libbrary */ + item->inst = (unsigned long)dlopen(name,RTLD_NOW); + #else + #error Unsupported environment + #endif + if (item->inst == 0) + goto error; + + item->amx = amx; + + item->next = root->next; + root->next = item; + return item; + +error: + if (item != NULL) { + if (item->name != NULL) + free(item->name); + if (item->inst != 0) { + #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + FreeLibrary((HINSTANCE)item->inst); + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + dlclose((void*)item->inst); + #else + #error Unsupported environment + #endif + } /* if */ + free(item); + } /* if */ + return NULL; +} + +static int freelib(MODLIST *root, AMX *amx, const TCHAR *name) +{ + MODLIST _FAR *item, _FAR *prev; + const TCHAR *ptr; + int count = 0; + + ptr = (name != NULL) ? skippath(name) : NULL; + + for (prev = root, item = prev->next; item != NULL; prev = item, item = prev->next) { + if ((amx == NULL || amx == item->amx) && (ptr == NULL || _tcscmp(item->name, ptr) == 0)) { + prev->next = item->next; /* unlink first */ + assert(item->inst != 0); + #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + FreeLibrary((HINSTANCE)item->inst); + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + dlclose((void*)item->inst); + #else + #error Unsupported environment + #endif + assert(item->name != NULL); + free(item->name); + free(item); + count++; + } /* if */ + } /* for */ + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + if (amx==NULL && name==NULL && inst_ffi!=NULL) + dlclose(inst_ffi); + #endif + return count; +} + + +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + +typedef long (CALLBACK* LIBFUNC)(); + +/* push() +** +** This function is the kind of programming trick that you don't even dare to +** dream about! With the usual C calling convention, the caller cleans up the +** stack after calling the function. This allows C functions to be flexible +** with parameters, both in number and in type. +** With the Pascal calling convention, used here, the callee (the function) +** cleans up the stack. But here, function push() doesn't know about any +** parameters. We neither declare any, nor indicate that the function has no +** parameters (i.e. the function is not declared having 'void' parameters). +** When we call function push(), the caller thinks the function cleans up the +** stack (because of the Pascal calling convention), while the function does +** not know that it has parameters, so it cannot clean them. As a result, +** nobody cleans up the stack. Ergo, The parameter you pass to function push() +** stays on the stack. +*/ +static void PASCAL push() { } + +LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname) +{ + FARPROC lpfn; + + assert(inst!=0); + lpfn=GetProcAddress((HINSTANCE)inst,functionname); + #if defined __WIN32__ + if (lpfn==NULL && strlen(functionname)<128-1) { + char str[128]; + strcpy(str,functionname); + #if defined UNICODE + strcat(str,"W"); + #else + strcat(str,"A"); + #endif + lpfn = GetProcAddress((HINSTANCE)inst,str); + } /* if */ + #endif + return (LIBFUNC)lpfn; +} + +#else + +typedef long (* LIBFUNC)(); + +LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname) +{ + assert(inst!=0); + return (LIBFUNC)dlsym((void*)inst, functionname); +} + +#endif + +static void *fillarray(AMX *amx, PARAM *param, cell *cptr) +{ + int i; + void *vptr; + + vptr = malloc(param->range * (param->size / 8)); + if (vptr == NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return NULL; + } /* if */ + + assert(param->range > 1); + if (param->size == 8) { + unsigned char *ptr = (unsigned char *)vptr; + for (i = 0; i < param->range; i++) + *ptr++ = (unsigned char)*cptr++; + } else if (param->size == 16) { + unsigned short *ptr = (unsigned short *)vptr; + for (i = 0; i < param->range; i++) + *ptr++ = (unsigned short)*cptr++; + } else { + unsigned long *ptr = (unsigned long *)vptr; + for (i = 0; i < param->range; i++) + *ptr++ = (unsigned long)*cptr++; + } /* for */ + + return vptr; +} + +/* libcall(const libname[], const funcname[], const typestring[], ...) + * + * Loads the DLL or shared library if not yet loaded (the name comparison is + * case sensitive). + * + * typestring format: + * Whitespace is permitted between the types, but not inside the type + * specification. The string "ii[4]&u16s" is equivalent to "i i[4] &u16 s", + * but easier on the eye. + * + * types: + * i = signed integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux + * u = unsigned integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux + * f = IEEE floating point, 32-bit + * p = packed string + * s = unpacked string + * The difference between packed and unpacked strings is only relevant when + * the parameter is passed by reference (see below). + * + * pass-by-value and pass-by-reference: + * By default, parameters are passed by value. To pass a parameter by + * reference, prefix the type letter with an "&": + * &i = signed integer passed by reference + * i = signed integer passed by value + * Same for '&u' versus 'u' and '&f' versus 'f'. + * + * Arrays are passed by "copy & copy-back". That is, libcall() allocates a + * block of dynamic memory to copy the array into. On return from the foreign + * function, libcall() copies the array back to the abstract machine. The + * net effect is similar to pass by reference, but the foreign function does + * not work in the AMX stack directly. During the copy and the copy-back + * operations, libcall() may also transform the array elements, for example + * between 16-bit and 32-bit elements. This is done because Pawn only + * supports a single cell size, which may not fit the required integer size + * of the foreign function. + * + * See "element ranges" for the syntax of passing an array. + * + * Strings may either be passed by copy, or by "copy & copy-back". When the + * string is an output parameter (for the foreign function), the size of the + * array that will hold the return string must be indicated between square + * brackets behind the type letter (see "element ranges"). When the string + * is "input only", this is not needed --libcall() will determine the length + * of the input string itself. + * + * The tokens 'p' and 's' are equivalent, but 'p[10]' and 's[10]' are not + * equivalent: the latter syntaxes determine whether the output from the + * foreign function will be stored as a packed or an unpacked string. + * + * element sizes: + * Add an integer behind the type letter; for example, 'i16' refers to a + * 16-bit signed integer. Note that the value behind the type letter must + * be either 8, 16 or 32. + * + * You should only use element size specifiers on the 'i' and 'u' types. That + * is, do not use these specifiers on 'f', 's' and 'p'. + * + * element ranges: + * For passing arrays, the size of the array may be given behind the type + * letter and optional element size. The token 'u[4]' indicates an array of + * four unsigned integers, which are typically 32-bit. The token 'i16[8]' + * is an array of 8 signed 16-bit integers. Arrays are always passed by + * "copy & copy-back" + * + * When compiled as Unicode, this library converts all strings to Unicode + * strings. + * + * The calling convention for the foreign functions is assumed: + * - "__stdcall" for Win32, + * - "far pascal" for Win16 + * - and the GCC default for Unix/Linux (_cdecl) + * + * C++ name mangling of the called function is not handled (there is no standard + * convention for name mangling, so there is no portable way to convert C++ + * function names to mangled names). Win32 name mangling (used by default by + * Microsoft compilers on functions declared as __stdcall) is also not handled. + * + * Returns the value of the called function. + */ +static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) +{ + const TCHAR *libname, *funcname, *typestring; + MODLIST *item; + int paramidx, typeidx, idx; + PARAM ps[MAXPARAMS]; + cell *cptr,result; + LIBFUNC LibFunc; + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + ffi_cif cif; + ffi_type *ptypes[MAXPARAMS]; + void *pvalues[MAXPARAMS]; + #endif + + amx_StrParam(amx, params[1], libname); + item = findlib(&ModRoot, amx, libname); + if (item == NULL) + item = addlib(&ModRoot, amx, libname); + if (item == NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + /* library is loaded, get the function */ + amx_StrParam(amx, params[2], funcname); + LibFunc=(LIBFUNC)SearchProcAddress(item->inst, funcname); + if (LibFunc==NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + /* decode the parameters */ + paramidx=typeidx=0; + amx_StrParam(amx, params[3], typestring); + while (paramidx < MAXPARAMS && typestring[typeidx]!=__T('\0')) { + /* skip white space */ + while (typestring[typeidx]!=__T('\0') && typestring[typeidx]<=__T(' ')) + typeidx++; + if (typestring[typeidx]==__T('\0')) + break; + /* save "pass-by-reference" token */ + ps[paramidx].type=0; + if (typestring[typeidx]==__T('&')) { + ps[paramidx].type=BYREF; + typeidx++; + } /* if */ + /* store type character */ + ps[paramidx].type |= (unsigned char)typestring[typeidx]; + typeidx++; + /* set default size, then check for an explicit size */ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + ps[paramidx].size=32; + #elif defined _Windows + ps[paramidx].size=16; + #endif + if (_istdigit(typestring[typeidx])) { + ps[paramidx].size=(unsigned char)_tcstol(&typestring[typeidx],NULL,10); + while (_istdigit(typestring[typeidx])) + typeidx++; + } /* if */ + /* set default range, then check for an explicit range */ + ps[paramidx].range=1; + if (typestring[typeidx]=='[') { + ps[paramidx].range=_tcstol(&typestring[typeidx+1],NULL,10); + while (typestring[typeidx]!=']' && typestring[typeidx]!='\0') + typeidx++; + ps[paramidx].type |= BYREF; /* arrays are always passed by reference */ + typeidx++; /* skip closing ']' too */ + } /* if */ + /* get pointer to parameter */ + amx_GetAddr(amx,params[paramidx+4],&cptr); + switch (ps[paramidx].type) { + case 'i': /* signed integer */ + case 'u': /* unsigned integer */ + case 'f': /* floating point */ + assert(ps[paramidx].range==1); + ps[paramidx].v.val=(int)*cptr; + break; + case 'i' | BYREF: + case 'u' | BYREF: + case 'f' | BYREF: + ps[paramidx].v.ptr=cptr; + if (ps[paramidx].range>1) { + /* convert array and pass by address */ + ps[paramidx].v.ptr = fillarray(amx, &ps[paramidx], cptr); + } /* if */ + break; + case 'p': + case 's': + case 'p' | BYREF: + case 's' | BYREF: + if (ps[paramidx].type=='s' || ps[paramidx].type=='p') { + int len; + /* get length of input string */ + amx_StrLen(cptr,&len); + len++; /* include '\0' */ + /* check max. size */ + if (len1,UNLIMITED); + break; + default: + /* invalid parameter type */ + return amx_RaiseError(amx, AMX_ERR_NATIVE); + } /* switch */ + paramidx++; + } /* while */ + if ((params[0]/sizeof(cell)) - 3 != (size_t)paramidx) + return amx_RaiseError(amx, AMX_ERR_NATIVE); /* format string does not match number of parameters */ + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + /* push the parameters to the stack (left-to-right in 16-bit; right-to-left + * in 32-bit) + */ +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + for (idx=paramidx-1; idx>=0; idx--) { +#else + for (idx=0; idx1,UNLIMITED); + free(ps[idx].v.ptr); + break; + case 'i': + case 'u': + case 'f': + assert(ps[idx].range==1); + break; + case 'i' | BYREF: + case 'u' | BYREF: + case 'f' | BYREF: + amx_GetAddr(amx,params[idx+4],&cptr); + if (ps[idx].range==1) { + /* modify directly in the AMX (no memory block was allocated */ + switch (ps[idx].size) { + case 8: + *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((signed char)*cptr) : (*cptr & 0xff); + break; + case 16: + *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((short)*cptr) : (*cptr & 0xffff); + break; + } /* switch */ + } else { + int i; + for (i=0; i 0; +} + +static void closepipe(void) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + if (newstdin!=NULL) { + CloseHandle(newstdin); + newstdin=NULL; + } /* if */ + if (newstdout!=NULL) { + CloseHandle(newstdout); + newstdout=NULL; + } /* if */ + if (read_stdout!=NULL) { + CloseHandle(read_stdout); + read_stdout=NULL; + } /* if */ + if (write_stdin!=NULL) { + CloseHandle(write_stdin); + write_stdin=NULL; + } /* if */ + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + if (pipe_to[0]>=0) { + close(pipe_to[0]); + pipe_to[0]=-1; + } /* if */ + if (pipe_to[1]>=0) { + close(pipe_to[1]); + pipe_to[1]=-1; + } /* if */ + if (pipe_from[0]>=0) { + close(pipe_from[0]); + pipe_from[0]=-1; + } /* if */ + if (pipe_from[1]>=0) { + close(pipe_from[1]); + pipe_from[1]=-1; + } /* if */ + #endif +} + +/* PID: procexec(const commandline[]) + * Executes a program. Returns an "id" representing the new process (or 0 on + * failure). + */ +static cell AMX_NATIVE_CALL n_procexec(AMX *amx, const cell *params) +{ + TCHAR *pgmname; + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + BOOL IsWinNT; + OSVERSIONINFO VerInfo; + STARTUPINFO si; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + PROCESS_INFORMATION pi; + #elif defined _Windows + HINSTANCE hinst; + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + pid_t pid; + #endif + + amx_StrParam(amx,params[1],pgmname); + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + /* most of this code comes from a "Borland Network" article, combined + * with some knowledge gained from a CodeProject article + */ + closepipe(); + + VerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&VerInfo); + IsWinNT = VerInfo.dwPlatformId==VER_PLATFORM_WIN32_NT; + + if (IsWinNT) { //initialize security descriptor (Windows NT) + InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.lpSecurityDescriptor = &sd; + } else { + sa.lpSecurityDescriptor = NULL; + } /* if */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; //allow inheritable handles + + if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) { //create stdin pipe + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) { //create stdout pipe + closepipe(); + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + GetStartupInfo(&si); //set startupinfo for the spawned process + si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + si.hStdOutput = newstdout; + si.hStdError = newstdout; //set the new handles for the child process + si.hStdInput = newstdin; + + /* spawn the child process */ + if (!CreateProcess(NULL,(TCHAR*)pgmname,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { + closepipe(); + return 0; + } /* if */ + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + Sleep(100); + return pi.dwProcessId; + #elif defined _Windows + hinst=WinExec(pgmname,SW_SHOW); + if (hinst<=32) + hinst=0; + return (cell)hinst; + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + /* set up communication pipes first */ + closepipe(); + if (pipe(pipe_to)!=0 || pipe(pipe_from)!=0) { + closepipe(); + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + /* attempt to fork */ + if ((pid=fork())<0) { + closepipe(); + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + if (pid==0) { + /* this is the child process */ + #define MAX_ARGS 10 + TCHAR *args[MAX_ARGS]; + int i; + dup2(pipe_to[0],STDIN_FILENO); /* replace stdin with the in side of the pipe */ + dup2(pipe_from[1],STDOUT_FILENO); /* replace stdout with the out side of the pipe */ + close(pipe_to[0]); /* the pipes are no longer needed */ + close(pipe_to[1]); + close(pipe_from[0]); + close(pipe_from[1]); + pipe_to[0]=-1; + pipe_to[1]=-1; + pipe_from[0]=-1; + pipe_from[1]=-1; + /* split off the option(s) */ + assert(MAX_ARGS>=2); /* args[0] is reserved */ + memset(args,0,MAX_ARGS*sizeof(TCHAR*)); + args[0]=pgmname; + for (i=1; i0 && (line[index-1]==__T('\r') || line[index-1]==__T('\n'))) + index--; + line[index]=__T('\0'); + + amx_GetAddr(amx,params[1],&cptr); + amx_SetString(cptr,line,params[4],sizeof(TCHAR)>1,params[2]); + return 1; +} + +/* procwait(PID:pid) + * Waits until the process has terminated. + */ +static cell AMX_NATIVE_CALL n_procwait(AMX *amx, const cell *params) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + HANDLE hProcess; + DWORD exitcode; + #endif + + (void)amx; + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)params[1]); + if (hProcess != NULL) { + while (GetExitCodeProcess(hProcess,&exitcode) && exitcode==STILL_ACTIVE) + Sleep(100); + CloseHandle(hProcess); + } /* if */ + #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + waitpid((pid_t)params[1],NULL,WNOHANG); + #endif + return 0; +} + + +#if defined __cplusplus + extern "C" +#endif +AMX_NATIVE_INFO ffi_Natives[] = { + { "libcall", n_libcall }, + { "libfree", n_libfree }, + { "procexec", n_procexec }, + { "procread", n_procread }, + { "procwrite", n_procwrite }, + { "procwait", n_procwait }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_ProcessInit(AMX *amx) +{ + return amx_Register(amx, ffi_Natives, -1); +} + +int AMXEXPORT amx_ProcessCleanup(AMX *amx) +{ + freelib(&ModRoot, amx, NULL); + closepipe(); + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxstring.c b/amx-deps/src/amx/amxstring.c index b20ea07..03bb5bf 100644 --- a/amx-deps/src/amx/amxstring.c +++ b/amx-deps/src/amx/amxstring.c @@ -1,923 +1,1030 @@ -/* String functions for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2005-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxstring.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ - #include -#endif -#include "osdefs.h" -#include "amx.h" -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - #include -#endif - -#define MAX_FORMATSTR 256 - -#define CHARBITS (8*sizeof(char)) - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _tcscat strcat -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcslen strlen -#endif -#include "amxcons.h" - -#if !defined isdigit -# define isdigit(c) ((unsigned)((c)-'0')<10u) -#endif - - -/* dest the destination buffer; the buffer must point to the start of a cell - * source the source buffer, this must be aligned to a cell edge - * len the number of characters (bytes) to copy - * offs the offset in dest, in characters (bytes) - */ -static int amx_StrPack(cell *dest,cell *source,int len,int offs) -{ - int i; - - if ((ucell)*source>UNPACKEDMAX && offs%sizeof(cell)==0) { - /* source string is already packed and the destination is cell-aligned */ - unsigned char* pdest=(unsigned char*)dest+offs; - i=(len+sizeof(cell)-1)/sizeof(cell); - memmove(pdest,source,i*sizeof(cell)); - /* zero-terminate */ - #if BYTE_ORDER==BIG_ENDIAN - pdest+=len; - for (i=len; i==len || i%sizeof(cell)!=0; i++) - *pdest++='\0'; - #else - i=(len/sizeof(cell))*sizeof(cell); - pdest+=i; - len=(len==i) ? sizeof(cell) : sizeof(cell)-(len-i); - assert(len>0 && len<=sizeof(cell)); - for (i=0; iUNPACKEDMAX) { - /* source string is packed, destination is not aligned */ - cell mask,c; - dest+=offs/sizeof(cell); /* increment whole number of cells */ - offs%=sizeof(cell); /* get remainder */ - mask=(~(ucell)0) >> (offs*CHARBITS); - c=*dest & ~mask; - for (i=0; i> (offs*CHARBITS)) & mask); - c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask; - dest++; - source++; - } /* for */ - /* set the zero byte in the last cell */ - mask=(~(ucell)0) >> (((offs+len)%sizeof(cell))*CHARBITS); - *(dest-1) &= ~mask; - } else { - /* source string is unpacked: pack string, from top-down */ - cell c=0; - if (offs!=0) { - /* get the last cell in "dest" and mask of the characters that must be changed */ - cell mask; - dest+=offs/sizeof(cell); /* increment whole number of cells */ - offs%=sizeof(cell); /* get remainder */ - mask=(~(ucell)0) >> (offs*CHARBITS); - c=(*dest & ~mask) >> ((sizeof(cell)-offs)*CHARBITS); - } /* if */ - /* for proper alignement, add the offset to both the starting and the ending - * criterion (so that the number of iterations stays the same) - */ - assert(offs>=0 && offsUNPACKEDMAX) { - /* unpack string, from bottom up (so string can be unpacked in place) */ - cell c; - int i; - for (i=len-1; i>=0; i--) { - c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS; - dest[i]=c & UCHAR_MAX; - } /* for */ - dest[len]=0; /* zero-terminate */ - } else { - /* source string is already unpacked */ - while (len-->0) - *dest++=*source++; - *dest=0; - } /* if */ - return AMX_ERR_NONE; -} - -static unsigned char *packedptr(cell *string,int index) -{ - unsigned char *ptr=(unsigned char *)(string+index/sizeof(cell)); - #if BYTE_ORDER==BIG_ENDIAN - ptr+=index & (sizeof(cell)-1); - #else - ptr+=(sizeof(cell)-1) - (index & (sizeof(cell)-1)); - #endif - return ptr; -} - -static cell extractchar(cell *string,int index,int mklower) -{ - cell c; - - if ((ucell)*string>UNPACKEDMAX) - c=*packedptr(string,index); - else - c=string[index]; - if (mklower) { - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - c=(cell)CharLower((LPTSTR)c); - #elif defined _Windows - c=(cell)AnsiLower((LPSTR)c); - #else - if ((unsigned int)(c-'A')<26u) - c+='a'-'A'; - #endif - } /* if */ - return c; -} - -static int verify_addr(AMX *amx,cell addr) -{ - int err; - cell *cdest; - - err=amx_GetAddr(amx,addr,&cdest); - if (err!=AMX_ERR_NONE) - amx_RaiseError(amx,err); - return err; -} - -/* strlen(const string[]) - */ -static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params) -{ - cell *cptr; - int len = 0; - - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - amx_StrLen(cptr,&len); - return len; -} - -/* strpack(dest[], const source[], maxlength=sizeof dest) - */ -static cell AMX_NATIVE_CALL n_strpack(AMX *amx,const cell *params) -{ - cell *cdest,*csrc; - int len,needed,err; - size_t lastaddr; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[2],&csrc); - amx_StrLen(csrc,&len); - if ((unsigned)len>params[3]*sizeof(cell)-1) - len=params[3]*sizeof(cell)-1; - needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ - assert(needed>0); - lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - amx_GetAddr(amx,params[1],&cdest); - err=amx_StrPack(cdest,csrc,len,0); - if (err!=AMX_ERR_NONE) - return amx_RaiseError(amx,err); - - return len; -} - -/* strunpack(dest[], const source[], maxlength=sizeof dest) - */ -static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const cell *params) -{ - cell *cdest,*csrc; - int len,err; - size_t lastaddr; - - /* calculate number of cells needed for (unpacked) destination */ - amx_GetAddr(amx,params[2],&csrc); - amx_StrLen(csrc,&len); - assert(len>=0); - if (len>=params[3]) - len=params[3]-1; - lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - amx_GetAddr(amx,params[1],&cdest); - err=amx_StrUnpack(cdest,csrc,len); - if (err!=AMX_ERR_NONE) - return amx_RaiseError(amx,err); - - return len; -} - -/* strcat(dest[], const source[], maxlength=sizeof dest) - * packed/unpacked attribute is taken from dest[], or from source[] if dest[] - * is an empty string. - */ -static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params) -{ - cell *cdest,*csrc; - int len,len2,needed; - int packed,err; - size_t lastaddr; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[2],&csrc); - amx_GetAddr(amx,params[1],&cdest); - amx_StrLen(csrc,&len); - amx_StrLen(cdest,&len2); - packed=(*cdest==0) ? ((ucell)*csrc>UNPACKEDMAX) : ((ucell)*cdest>UNPACKEDMAX); - if (packed) { - if ((unsigned)(len+len2)>params[3]*sizeof(cell)-1) - len=params[3]*sizeof(cell)-len2-1; - needed=(len+len2+sizeof(cell))/sizeof(cell); /* # of cells needed */ - assert(needed>0); - lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); - } else { - if (len+len2>params[3]-1) - len=params[3]-len2-1; - lastaddr=(size_t)(params[1]+sizeof(cell)*(len+len2+1)-1); - } /* if */ - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - if (packed) { - err=amx_StrPack(cdest,csrc,len,len2); - } else { - /* destination string must either be unpacked, or empty */ - assert((ucell)*cdest<=UNPACKEDMAX || len2==0); - err=amx_StrUnpack(cdest+len2,csrc,len); - } /* if */ - if (err!=AMX_ERR_NONE) - return amx_RaiseError(amx,err); - - return len; -} - -/* strcopy(dest[], const source[], maxlength=sizeof dest) - * packed/unpacked attribute from source[] - */ -static cell AMX_NATIVE_CALL n_strcopy(AMX *amx,const cell *params) -{ - cell *cdest,*csrc; - int len,needed; - int packed,err; - size_t lastaddr; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[2],&csrc); - amx_GetAddr(amx,params[1],&cdest); - amx_StrLen(csrc,&len); - packed=(ucell)*csrc>UNPACKEDMAX; - if (packed) { - if ((unsigned)len>params[3]*sizeof(cell)-1) - len=params[3]*sizeof(cell)-1; - needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ - assert(needed>0); - lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); - } else { - if (len>params[3]-1) - len=params[3]-1; - lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); - } /* if */ - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - if (packed) - err=amx_StrPack(cdest,csrc,len,0); - else - err=amx_StrUnpack(cdest,csrc,len); - if (err!=AMX_ERR_NONE) - return amx_RaiseError(amx,err); - - return len; -} - -static int compare(cell *cstr1,cell *cstr2,int ignorecase,int length,int offs1) -{ - int index; - cell c1=0,c2=0; - - for (index=0; indexc2) - return 1; - return 0; -} - -/* strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax) - */ -static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params) -{ - cell *cstr1,*cstr2; - int len1,len2,len; - cell result; - - amx_GetAddr(amx,params[1],&cstr1); - amx_GetAddr(amx,params[2],&cstr2); - - /* get the maximum length to compare */ - amx_StrLen(cstr1,&len1); - amx_StrLen(cstr2,&len2); - len=len1; - if (len>len2) - len=len2; - if (len>params[4]) - len=params[4]; - if (len==0) - return (params[4]==0) ? 0 : len1-len2; - - result=compare(cstr1,cstr2,params[3],len,0); - if (result==0 && len!=params[4]) - result=len1-len2; - return result; -} - -/* strfind(const string[], const sub[], bool:ignorecase=false, offset=0) - */ -static cell AMX_NATIVE_CALL n_strfind(AMX *amx,const cell *params) -{ - cell *cstr,*csub; - int lenstr,lensub,offs; - cell c,f; - - amx_GetAddr(amx,params[1],&cstr); - amx_GetAddr(amx,params[2],&csub); - - /* get the maximum length to compare */ - amx_StrLen(cstr,&lenstr); - amx_StrLen(csub,&lensub); - if (lensub==0) - return -1; - - /* get the start character of the substring, for quicker searching */ - f=extractchar(csub,0,params[3]); - assert(f!=0); /* string length is already checked */ - - for (offs=(int)params[4]; offs+lensub<=lenstr; offs++) { - /* find the initial character */ - c=extractchar(csub,0,params[3]); - assert(c!=0); /* string length is already checked */ - if (c!=f) - continue; - if (compare(cstr,csub,params[3],lensub,offs)==0) - return offs; - } /* for */ - return -1; -} - -/* strmid(dest[], const source[], start, end, maxlength=sizeof dest) - * packed/unpacked attribute is taken from source[] - */ -static cell AMX_NATIVE_CALL n_strmid(AMX *amx,const cell *params) -{ - cell *cdest,*csrc; - int len,needed,err; - int soffs,doffs; - size_t lastaddr; - unsigned char *ptr; - unsigned char c; - int start=params[3]; - int end=params[4]; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[2],&csrc); - amx_GetAddr(amx,params[1],&cdest); - amx_StrLen(csrc,&len); - - /* clamp the start/end parameters */ - if (start<0) - start=0; - else if (start>len) - start=len; - if (endlen) - end=len; - - len=end-start; - if ((ucell)*csrc>UNPACKEDMAX) { - if ((unsigned)len>params[5]*sizeof(cell)-1) - len=params[5]*sizeof(cell)-1; - needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ - assert(needed>0); - lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); - } else { - if (len>params[5]-1) - len=params[5]-1; - lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); - } /* if */ - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - if ((ucell)*csrc>UNPACKEDMAX) { - /* first align the source to a cell boundary */ - for (doffs=0,soffs=start; (soffs & (sizeof(cell)-1))!=0 && len>0; soffs++,doffs++,len--) { - ptr=packedptr(csrc,soffs); - c=*ptr; - ptr=packedptr(cdest,doffs); - *ptr=c; - } /* for */ - if (len==0) { - /* nothing left to do, zero-terminate */ - ptr=packedptr(cdest,doffs); - *ptr='\0'; - err=AMX_ERR_NONE; - } else { - err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs); - } /* if */ - } else { - err=amx_StrUnpack(cdest,csrc+start,len); - } /* if */ - if (err!=AMX_ERR_NONE) - return amx_RaiseError(amx,err); - - return len; -} - -/* strdel(string[], start, end) - */ -static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params) -{ - cell *cstr; - int index,offs,length; - unsigned char *ptr; - unsigned char c; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[1],&cstr); - amx_StrLen(cstr,&length); - index=(int)params[2]; - offs=(int)params[3]-index; - if (index>=length || offs<=0) - return 0; - if (index+offs>length) - offs=length-index; - - index--; /* prepare for increment in the top of the loop */ - if (((ucell)*cstr>UNPACKEDMAX)) { - do { - index++; - ptr=packedptr(cstr,index+offs); - c=*ptr; - ptr=packedptr(cstr,index); - *ptr=c; - } while (c!='\0'); - if (index==0) - *cstr=0; - } else { - do { - index++; - cstr[index]=cstr[index+offs]; - } while (cstr[index]!=0); - } /* if */ - - return 1; -} - -/* strins(string[], const substr[], offset, maxlength=sizeof string) - */ -static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params) -{ - cell *cstr,*csub; - int index,lenstr,lensub,count; - int needed; - size_t lastaddr; - unsigned char *ptr; - cell c; - - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[1],&cstr); - amx_GetAddr(amx,params[2],&csub); - amx_StrLen(cstr,&lenstr); - amx_StrLen(csub,&lensub); - index=(int)params[3]; - if (index>lenstr) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - if (((ucell)*cstr>UNPACKEDMAX)) { - needed=(lenstr+lensub+sizeof(cell))/sizeof(cell); /* # of cells needed */ - assert(needed>0); - lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); - } else { - lastaddr=(size_t)(params[1]+sizeof(cell)*(lenstr+lensub+1)-1); - } /* if */ - if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) - return amx_RaiseError(amx,AMX_ERR_NATIVE); - - if (*cstr==0) { - /* current string is empty (and the insertion point is zero), just make a copy */ - assert(index==0); - if ((ucell)*csub>UNPACKEDMAX) - amx_StrPack(cstr,csub,lensub,0); - else - amx_StrUnpack(cstr,csub,lensub); - return 1; - } /* if */ - - if (((ucell)*cstr>UNPACKEDMAX)) { - /* make room for the new characters */ - for (count=lenstr+lensub; count>index; count--) { - ptr=packedptr(cstr,count-lensub); - c=*ptr; - ptr=packedptr(cstr,count); - *ptr=(unsigned char)c; - } /* for */ - /* copy in the new characters */ - for (count=0; countindex; count--) - cstr[count]=cstr[count-lensub]; - /* copy in the new characters */ - for (count=0; count=2*sizeof(cell)) - offset=params[2]; - if (offset<0) - offset=0; - else if (offset>=len && len>0) - offset=len-1; - - /* skip a number of cells */ - while (offset>=(int)sizeof(cell)) { - cstr++; - offset-=sizeof(cell); - len-=sizeof(cell); - } /* while */ - - if (len>=(int)sizeof str) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; - } /* if */ - amx_GetString(str,cstr,sizeof(TCHAR)>1,sizeof str); - assert(offset<(int)sizeof(cell) && offset>=0); - ptr=str+offset; - result=0; - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip whitespace */ - if (*ptr=='-') { /* handle sign */ - negate=1; - ptr++; - } else if (*ptr=='+') { - ptr++; - } /* if */ - while (isdigit(*ptr)) { - result=result*10 + (*ptr-'0'); - ptr++; - } /* while */ - if (negate) - result=-result; - return result; -} - -/* valstr(dest[], value, bool:pack=false) */ -static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params) -{ - TCHAR str[50]; - cell value,mult; - cell *cstr; - int len,result,negate=0; - - /* find out how many digits are needed */ - mult=10; - len=1; - value=params[2]; - if (value<0) { - negate=1; - len++; - value=-value; - } /* if */ - while (value>=mult) { - len++; - mult*=10; - } /* while */ - - /* put in the string */ - result=len; - str[len--]='\0'; - while (len>=negate) { - str[len--]=(char)((value % 10)+'0'); - value/=10; - } /* while */ - if (negate) - str[0]='-'; - amx_GetAddr(amx,params[1],&cstr); - amx_SetString(cstr,str,params[3],sizeof(TCHAR)>1,UNLIMITED); - return result; -} - -/* ispacked(const string[]) */ -static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,const cell *params) -{ - cell *cstr; - amx_GetAddr(amx,params[1],&cstr); - return *cstr>=UNPACKEDMAX; -} - - -/* single character decode and encode */ -#define BITMASK 0x3f -#define DEC(c) (((c) - ' ') & BITMASK) -#define ENC(c) (char)(((c) & BITMASK) == 0 ? 0x60 : ((c) & BITMASK) + ' ') - -static int uudecode(unsigned char *target, char *source) -{ - int len, retval; - - len = DEC(*source++); - retval = len; - while (len > 0) { - if (len-- > 0) - *target++ = (unsigned char)(( DEC(source[0]) << 2 ) | ( DEC(source[1]) >> 4 )); - if (len-- > 0) - *target++ = (unsigned char)(( DEC(source[1]) << 4 ) | ( DEC(source[2]) >> 2 )); - if (len-- > 0) - *target++ = (unsigned char)(( DEC(source[2]) << 6 ) | DEC(source[3]) ); - source += 4; - } /* while */ - return retval; -} - -static int uuencode(char *target, unsigned char *source, int length) -{ - int split[4]={0,0,0,0}; - - if (length > BITMASK) - return 0; /* can encode up to 64 bytes */ - - *target++ = ENC(length); - while (length > 0) { - split[0] = source[0] >> 2; /* split first byte to char. 0 & 1 */ - split[1] = source[0] << 4; - if (length > 1) { - split[1] |= source[1] >> 4; /* split 2nd byte to char. 1 & 2 */ - split[2] = source[1] << 2; - if (length > 2) { - split[2] |= source[2] >> 6; /* split 3th byte to char. 2 & 3 */ - split[3] = source[2]; - } /* if */ - } /* if */ - - *target++ = ENC(split[0]); - *target++ = ENC(split[1]); - if (length > 1) - *target++ = ENC(split[2]); - if (length > 2) - *target++ = ENC(split[3]); - source += 3; - length -= 3; - } /* while */ - - *target = '\0'; /* end string */ - return 1; -} - -/* uudecode(dest[], const source[], maxlength=sizeof dest) - * Returns the number of bytes (not cells) decoded; if the dest buffer is - * too small, not all bytes are stored. - * Always creates a (packed) array (not a string; the array is not - * zero-terminated). - * A buffer may be decoded "in-place"; the destination size is always smaller - * than the source size. - * Endian issues (for multi-byte values in the data stream) are not handled. - */ -static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,const cell *params) -{ - cell *cstr; - unsigned char dst[BITMASK+2]; - char src[BITMASK+BITMASK/3+2]; - int len; - size_t size; - - /* get the source */ - amx_GetAddr(amx,params[2],&cstr); - amx_GetString(src,cstr,0,sizeof src); - /* decode */ - len=uudecode(dst,src); - /* store */ - amx_GetAddr(amx,params[1],&cstr); - size=len; - if (size>params[3]*sizeof(cell)) - size=params[3]*sizeof(cell); - memcpy(cstr,dst,size); - return len; -} - -/* uuencode(dest[], const source[], numbytes, maxlength=sizeof dest) - * Returns the number of characters encoded, excluding the zero string - * terminator; if the dest buffer is too small, not all bytes are stored. - * Always creates a packed string. This string has a newline character at the - * end. A buffer may be encoded "in-place" if the destination is large enough. - * Endian issues (for multi-byte values in the data stream) are not handled. - */ -static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params) -{ - cell *cstr; - unsigned char src[BITMASK+2]; - char dst[BITMASK+BITMASK/3+2]; - - /* get the source */ - amx_GetAddr(amx,params[2],&cstr); - /* encode (and check for errors) */ - if (uuencode(dst,src,params[3])) { - if (params[4]>0) { - amx_GetAddr(amx,params[1],&cstr); - *cstr=0; - } /* if */ - return 0; - } /* if */ - /* always add a \n */ - assert(strlen(dst)+1params[5]*(int)sizeof(cell)) - return 0; - amx_GetAddr(amx,params[1],&cdest); - amx_GetAddr(amx,params[2],&csrc); - pdest=(unsigned char*)cdest+params[3]; - psrc=(unsigned char*)csrc; - memmove(pdest,psrc,params[4]); - return 1; -} - -#if !defined AMX_NOSTRFMT - static int str_putstr(void *dest,const TCHAR *str) - { - if (_tcslen((TCHAR*)dest)+_tcslen(str)1,(int)params[2]); - return 1; - #endif -} - - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO string_Natives[] = { - { "ispacked", n_ispacked }, - { "memcpy", n_memcpy }, - { "strcat", n_strcat }, - { "strcmp", n_strcmp }, - { "strcopy", n_strcopy }, - { "strdel", n_strdel }, - { "strfind", n_strfind }, - { "strformat", n_strformat }, - { "strins", n_strins }, - { "strlen", n_strlen }, - { "strmid", n_strmid }, - { "strpack", n_strpack }, - { "strunpack", n_strunpack }, - { "strval", n_strval }, - { "uudecode", n_uudecode }, - { "uuencode", n_uuencode }, - { "valstr", n_valstr }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_StringInit(AMX *amx) -{ - return amx_Register(amx, string_Natives, -1); -} - -int AMXEXPORT amx_StringCleanup(AMX *amx) -{ - (void)amx; - return AMX_ERR_NONE; -} +/* String functions for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2005-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxstring.c 5514 2016-05-20 14:26:51Z $ + */ + +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #include +#endif +#include "osdefs.h" +#include "amx.h" +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif + +#define MAX_FORMATSTR 256 + +#define CHARBITS (8*sizeof(char)) + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcscat strcat +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcslen strlen +#endif +#include "amxcons.h" + +#if !defined isdigit +# define isdigit(c) ((unsigned)((c)-'0')<10u) +#endif +#if !defined sizearray +# define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + + +/* dest the destination buffer; the buffer must point to the start of a cell + * source the source buffer, this must be aligned to a cell edge + * len the number of characters (bytes) to copy, excluding the zero terminator + * offs the offset in dest, in characters (bytes) + */ +static int amx_StrPack(cell *dest,cell *source,int len,int offs) +{ + int i; + + if ((ucell)*source>UNPACKEDMAX && offs%sizeof(cell)==0) { + /* source string is already packed and the destination is cell-aligned */ + unsigned char* pdest=(unsigned char*)dest+offs; + i=(len+sizeof(cell)-1)/sizeof(cell); + memmove(pdest,source,i*sizeof(cell)); + /* zero-terminate */ + #if BYTE_ORDER==BIG_ENDIAN + pdest+=len; + for (i=len; i==len || i%sizeof(cell)!=0; i++) + *pdest++='\0'; + #else + i=(len/sizeof(cell))*sizeof(cell); + pdest+=i; + len=(len==i) ? sizeof(cell) : sizeof(cell)-(len-i); + assert(len>0 && len<=sizeof(cell)); + for (i=0; iUNPACKEDMAX) { + /* source string is packed, destination is not aligned */ + cell mask,c; + dest+=offs/sizeof(cell); /* increment whole number of cells */ + offs%=sizeof(cell); /* get remainder */ + mask=(~(ucell)0) >> (offs*CHARBITS); + c=*dest & ~mask; + for (i=0; i> (offs*CHARBITS)) & mask); + c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask; + dest++; + source++; + } /* for */ + /* set the zero byte in the last cell */ + mask=(~(ucell)0) >> (((offs+len)%sizeof(cell))*CHARBITS); + *(dest-1) &= ~mask; + } else { + /* source string is unpacked: pack string, from top-down */ + cell c=0; + if (offs!=0) { + /* get the last cell in "dest" and mask of the characters that must be changed */ + cell mask; + dest+=offs/sizeof(cell); /* increment whole number of cells */ + offs%=sizeof(cell); /* get remainder */ + mask=(~(ucell)0) >> (offs*CHARBITS); + c=(*dest & ~mask) >> ((sizeof(cell)-offs)*CHARBITS); + } /* if */ + /* for proper alignement, add the offset to both the starting and the ending + * criterion (so that the number of iterations stays the same) + */ + assert(offs>=0 && offsUNPACKEDMAX) { + /* unpack string, from bottom up (so string can be unpacked in place) */ + cell c; + int i; + for (i=len-1; i>=0; i--) { + c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS; + dest[i]=c & UCHAR_MAX; + } /* for */ + dest[len]=0; /* zero-terminate */ + } else { + /* source string is already unpacked */ + while (len-->0) + *dest++=*source++; + *dest=0; + } /* if */ + return AMX_ERR_NONE; +} + +static unsigned char *packedptr(cell *string,int index) +{ + unsigned char *ptr=(unsigned char *)(string+index/sizeof(cell)); + #if BYTE_ORDER==BIG_ENDIAN + ptr+=index & (sizeof(cell)-1); + #else + ptr+=(sizeof(cell)-1) - (index & (sizeof(cell)-1)); + #endif + return ptr; +} + +static cell extractchar(cell *string,int index,int mklower) +{ + cell c; + + if ((ucell)*string>UNPACKEDMAX) + c=*packedptr(string,index); + else + c=string[index]; + if (mklower) { + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + c=(cell)CharLower((LPTSTR)c); + #elif defined _Windows + c=(cell)AnsiLower((LPSTR)c); + #else + if ((unsigned int)(c-'A')<26u) + c+='a'-'A'; + #endif + } /* if */ + return c; +} + +/* strlen(const string[]) + */ +static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params) +{ + cell *cptr; + int len = 0; + + (void)(amx); + cptr=amx_Address(amx,params[1]); + amx_StrLen(cptr,&len); + return len; +} + +/* strpack(dest[], const source[], maxlength=sizeof dest) + */ +static cell AMX_NATIVE_CALL n_strpack(AMX *amx,const cell *params) +{ + cell *cdest,*csrc; + int len,err; + + csrc=amx_Address(amx,params[2]); + amx_StrLen(csrc,&len); + if ((unsigned)len>params[3]*sizeof(cell)-1) + len=params[3]*sizeof(cell)-1; + + cdest=amx_Address(amx,params[1]); + err=amx_StrPack(cdest,csrc,len,0); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strunpack(dest[], const source[], maxlength=sizeof dest) + */ +static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const cell *params) +{ + cell *cdest,*csrc; + int len,err; + + csrc=amx_Address(amx,params[2]); + amx_StrLen(csrc,&len); + assert(len>=0); + if (len>=params[3]) + len=params[3]-1; + + cdest=amx_Address(amx,params[1]); + err=amx_StrUnpack(cdest,csrc,len); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strcat(dest[], const source[], maxlength=sizeof dest) + * packed/unpacked attribute is taken from dest[], or from source[] if dest[] + * is an empty string. + */ +static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params) +{ + cell *cdest,*csrc; + int len,len2; + int packed,err; + + /* calculate number of cells needed for (packed) destination */ + csrc=amx_Address(amx,params[2]); + cdest=amx_Address(amx,params[1]); + amx_StrLen(csrc,&len); + amx_StrLen(cdest,&len2); + packed=(*cdest==0) ? ((ucell)*csrc>UNPACKEDMAX) : ((ucell)*cdest>UNPACKEDMAX); + if (packed) { + if ((unsigned)(len+len2)>params[3]*sizeof(cell)-1) + len=params[3]*sizeof(cell)-len2-1; + } else { + if (len+len2>params[3]-1) + len=params[3]-len2-1; + } /* if */ + + if (packed) { + err=amx_StrPack(cdest,csrc,len,len2); + } else { + /* destination string must either be unpacked, or empty */ + assert((ucell)*cdest<=UNPACKEDMAX || len2==0); + err=amx_StrUnpack(cdest+len2,csrc,len); + } /* if */ + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strcopy(dest[], const source[], maxlength=sizeof dest) + * packed/unpacked attribute from source[] + */ +static cell AMX_NATIVE_CALL n_strcopy(AMX *amx,const cell *params) +{ + cell *cdest,*csrc; + int len,packed,err; + + /* calculate number of cells needed for (packed) destination */ + csrc=amx_Address(amx,params[2]); + cdest=amx_Address(amx,params[1]); + amx_StrLen(csrc,&len); + packed=(ucell)*csrc>UNPACKEDMAX; + if (packed) { + if ((unsigned)len>params[3]*sizeof(cell)-1) + len=params[3]*sizeof(cell)-1; + } else { + if (len>params[3]-1) + len=params[3]-1; + } /* if */ + + if (packed) + err=amx_StrPack(cdest,csrc,len,0); + else + err=amx_StrUnpack(cdest,csrc,len); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +static int compare(cell *cstr1,cell *cstr2,int ignorecase,int length,int offs1) +{ + int index; + cell c1=0,c2=0; + + for (index=0; indexc2) + return 1; + return 0; +} + +/* strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax) + */ +static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params) +{ + cell *cstr1,*cstr2; + int len1,len2,len; + cell result; + + (void)(amx); + cstr1=amx_Address(amx,params[1]); + cstr2=amx_Address(amx,params[2]); + + /* get the maximum length to compare */ + amx_StrLen(cstr1,&len1); + amx_StrLen(cstr2,&len2); + len=len1; + if (len>len2) + len=len2; + if (len>params[4]) + len=params[4]; + if (len==0) { + if (params[4]==0 || len1==len2) + result=0; + else + result=(len1len) + start=len; + if (endlen) + end=len; + + len=end-start; + if ((ucell)*csrc>UNPACKEDMAX) { + if ((unsigned)len>params[5]*sizeof(cell)-1) + len=params[5]*sizeof(cell)-1; + } else { + if (len>params[5]-1) + len=params[5]-1; + } /* if */ + + if ((ucell)*csrc>UNPACKEDMAX) { + /* first align the source to a cell boundary */ + for (doffs=0,soffs=start; (soffs & (sizeof(cell)-1))!=0 && len>0; soffs++,doffs++,len--) { + ptr=packedptr(csrc,soffs); + c=*ptr; + ptr=packedptr(cdest,doffs); + *ptr=c; + } /* for */ + if (len==0) { + /* nothing left to do, zero-terminate */ + ptr=packedptr(cdest,doffs); + *ptr='\0'; + err=AMX_ERR_NONE; + } else { + err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs); + } /* if */ + } else { + err=amx_StrUnpack(cdest,csrc+start,len); + } /* if */ + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strdel(string[], start, end) + */ +static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params) +{ + cell *cstr; + int index,offs,length; + unsigned char *ptr; + unsigned char c; + + (void)(amx); + /* calculate number of cells needed for (packed) destination */ + cstr=amx_Address(amx,params[1]); + amx_StrLen(cstr,&length); + index=(int)params[2]; + offs=(int)params[3]-index; + if (index>=length || offs<=0) + return 0; + if (index+offs>length) + offs=length-index; + + index--; /* prepare for increment in the top of the loop */ + if (((ucell)*cstr>UNPACKEDMAX)) { + do { + index++; + ptr=packedptr(cstr,index+offs); + c=*ptr; + ptr=packedptr(cstr,index); + *ptr=c; + } while (c!='\0'); + if (index==0) + *cstr=0; + } else { + do { + index++; + cstr[index]=cstr[index+offs]; + } while (cstr[index]!=0); + } /* if */ + + return 1; +} + +/* strins(string[], const substr[], offset, maxlength=sizeof string) + */ +static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params) +{ + cell *cstr,*csub; + int index,lenstr,lensub,maxlen,count; + unsigned char *ptr; + cell c; + + /* calculate number of cells needed for (packed) destination */ + cstr=amx_Address(amx,params[1]); + csub=amx_Address(amx,params[2]); + amx_StrLen(cstr,&lenstr); + amx_StrLen(csub,&lensub); + index=(int)params[3]; + maxlen=(int)params[4]; + if ((ucell)*cstr>UNPACKEDMAX) + maxlen*=sizeof(cell); + maxlen-=1; + if (index>lenstr || index>maxlen) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + if (lenstr==0) { + /* current string is empty (and the insertion point is zero), just make a copy */ + assert(index==0); + if (lensub>maxlen) + lensub=maxlen; + if ((ucell)*csub>UNPACKEDMAX) + amx_StrPack(cstr,csub,lensub,0); + else + amx_StrUnpack(cstr,csub,lensub); + return 1; + } /* if */ + + lenstr+=lensub; /* length after insertion */ + if (lenstr>=maxlen) + lenstr=maxlen-1; + if ((ucell)*cstr>UNPACKEDMAX) { + /* make room for the new characters */ + for (count=lenstr; count>index; count--) { + ptr=packedptr(cstr,count-lensub); + c=*ptr; + ptr=packedptr(cstr,count); + *ptr=(unsigned char)c; + } /* for */ + /* copy in the new characters */ + for (count=0; countindex; count--) + cstr[count]=cstr[count-lensub]; + /* copy in the new characters */ + for (count=0; count=2*sizeof(cell)) + offset=params[2]; + if (offset<0) + offset=0; + else if (offset>=len) + offset=len-1; + + /* skip a number of cells */ + if ((ucell)*cstr>UNPACKEDMAX) { + /* packed string */ + while (offset>=(int)sizeof(cell)) { + cstr++; + offset-=sizeof(cell); + len-=sizeof(cell); + } /* while */ + } else { + /* unpacked string, one character per cell */ + while (offset>0) { + cstr++; + offset--; + len--; + } /* while */ + } /* if */ + + amx_GetString(str,cstr,sizeof(TCHAR)>1,sizeof str); + assert(offset<(int)sizeof(cell) && offset>=0); + ptr=str+offset; + result=0; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip whitespace */ + if (*ptr=='-') { /* handle sign */ + negate=1; + ptr++; + } else if (*ptr=='+') { + ptr++; + } /* if */ + while (isdigit(*ptr)) { + result=result*10 + (*ptr-'0'); + ptr++; + } /* while */ + if (negate) + result=-result; + return result; +} + +/* valstr(dest[], value, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params) +{ + TCHAR str[50]; + cell value,temp; + cell *cstr; + int len,result,negate=0; + + (void)(amx); + /* find out how many digits are needed */ + len=1; + value=params[2]; + if (value<0) { + negate=1; + len++; + value=-value; + } /* if */ + for (temp=value; temp>=10; temp/=10) + len++; + assert(len<=sizearray(str)); + + /* put in the string */ + result=len; + str[len--]='\0'; + while (len>=negate) { + str[len--]=(char)((value % 10)+'0'); + value/=10; + } /* while */ + if (negate) + str[0]='-'; + cstr=amx_Address(amx,params[1]); + amx_SetString(cstr,str,params[3],sizeof(TCHAR)>1,sizearray(str)); + return result; +} + +/* ispacked(const string[]) */ +static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,const cell *params) +{ + cell *cstr=amx_Address(amx,params[1]); + (void)(amx); + return *cstr>=UNPACKEDMAX; +} + + +/* single character decode and encode */ +#define BITMASK 0x3f +#define DEC(c) (((c) - ' ') & BITMASK) +#define ENC(c) (char)(((c) & BITMASK) == 0 ? 0x60 : ((c) & BITMASK) + ' ') + +static int uudecode(unsigned char *target, char *source) +{ + int len, retval; + + len = DEC(*source++); + retval = len; + while (len > 0) { + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[0]) << 2 ) | ( DEC(source[1]) >> 4 )); + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[1]) << 4 ) | ( DEC(source[2]) >> 2 )); + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[2]) << 6 ) | DEC(source[3]) ); + source += 4; + } /* while */ + return retval; +} + +static int uuencode(char *target, unsigned char *source, int length) +{ + int split[4]={0,0,0,0}; + + if (length > BITMASK) + return 0; /* can encode up to 64 bytes */ + + *target++ = ENC(length); + while (length > 0) { + split[0] = source[0] >> 2; /* split first byte to char. 0 & 1 */ + split[1] = source[0] << 4; + if (length > 1) { + split[1] |= source[1] >> 4; /* split 2nd byte to char. 1 & 2 */ + split[2] = source[1] << 2; + if (length > 2) { + split[2] |= source[2] >> 6; /* split 3th byte to char. 2 & 3 */ + split[3] = source[2]; + } /* if */ + } /* if */ + + *target++ = ENC(split[0]); + *target++ = ENC(split[1]); + if (length > 1) + *target++ = ENC(split[2]); + if (length > 2) + *target++ = ENC(split[3]); + source += 3; + length -= 3; + } /* while */ + + *target = '\0'; /* end string */ + return 1; +} + +/* uudecode(dest[], const source[], maxlength=sizeof dest) + * Returns the number of bytes (not cells) decoded; if the dest buffer is + * too small, not all bytes are stored. + * Always creates a (packed) array (not a string; the array is not + * zero-terminated). + * A buffer may be decoded "in-place"; the destination size is always smaller + * than the source size. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,const cell *params) +{ + cell *cstr; + unsigned char dst[BITMASK+2]; + char src[BITMASK+BITMASK/3+2]; + int len; + size_t size; + + (void)(amx); + /* get the source */ + cstr=amx_Address(amx,params[2]); + amx_GetString(src,cstr,0,sizeof src); + /* decode */ + len=uudecode(dst,src); + /* store */ + cstr=amx_Address(amx,params[1]); + size=len; + if (size>params[3]*sizeof(cell)) + size=params[3]*sizeof(cell); + memcpy(cstr,dst,size); + return len; +} + +/* uuencode(dest[], const source[], numbytes, maxlength=sizeof dest) + * Returns the number of characters encoded, excluding the zero string + * terminator; if the dest buffer is too small, not all bytes are stored. + * Always creates a packed string. This string has a newline character at the + * end. A buffer may be encoded "in-place" if the destination is large enough. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params) +{ + cell *cstr; + unsigned char src[BITMASK+2]; + char dst[BITMASK+BITMASK/3+2]; + + (void)(amx); + /* get the source */ + cstr=amx_Address(amx,params[2]); + amx_GetString((char *)src,cstr,0,sizeof src); + /* encode (and check for errors) */ + if (uuencode(dst,src,params[3])) { + if (params[4]>0) { + cstr=amx_Address(amx,params[1]); + *cstr=0; + } /* if */ + return 0; + } /* if */ + /* always add a \n */ + assert(strlen(dst)+1='0' && str[idx_src+1]<='9') + p=str[idx_src+1]-'0'; + else if (str[idx_src+1]>='A' && str[idx_src+1]<='F') + p=str[idx_src+1]-'A'+10; + else if (str[idx_src+1]>='a' && str[idx_src+1]<='f') + p=str[idx_src+1]-'a'+10; + else + p=-1; + if (p>=0) { + if (str[idx_src+2]>='0' && str[idx_src+2]<='9') + q=str[idx_src+2]-'0'; + else if (str[idx_src+2]>='A' && str[idx_src+2]<='F') + q=str[idx_src+2]-'A'+10; + else if (str[idx_src+2]>='a' && str[idx_src+2]<='f') + q=str[idx_src+2]-'a'+10; + else + q=-1; + } /* if */ + if (p>=0 && q >=0) { + assert(p<=15 && q<=15); + str[idx_dst]=(TCHAR)((p<<4) | q); + idx_src+=3; + } else { + /* invalid '%xx' syntax, copy literal '%' */ + str[idx_dst]=str[idx_src++]; + } /* if */ + } else { + str[idx_dst]=str[idx_src++]; + } /* if */ + idx_dst++; + } /* while */ + str[idx_dst]='\0'; + + /* store */ + cstr=amx_Address(amx,params[1]); + amx_SetString(cstr,str,1,0,params[4]); /* store as packed ot unpacked */ + + return idx_dst; +} + +#define INVALIDURI(c) ((c)<',' \ + || (c)>'9' && (c)<'A' \ + || (c)>'Z' && (c)<'_' \ + || (c)>'_' && (c)<'a' \ + || (c)>'z' && (unsigned)(c)<0xa1) + +#define TOHEX(c) (TCHAR)((c)<10 ? '0'+(c) : 'A'-10+(c)) + +/* urlencode(dest[], const source[], maxlength=sizeof dest, bool:pack=false) + * Returns the number of characters encoded, excluding the zero string + * terminator; if the dest buffer is too small, not all bytes are stored. + * Always creates a packed string. This string has a newline character at the + * end. A buffer may be encoded "in-place" if the destination is large enough. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_urlencode(AMX *amx,const cell *params) +{ + cell *cstr; + int length,destlen,count,lastwidth; + TCHAR *str; + + /* allocate memory and get the source */ + (void)(amx); + if ((length=(int)params[3])==0) + return 0; + if ((str = (TCHAR*)alloca(length * sizeof(TCHAR)))==NULL) + return 0; + cstr=amx_Address(amx,params[2]); + amx_GetString((char*)str, cstr, sizeof(TCHAR)>1, length); + + /* run through the string and determine the new length */ + destlen=1; /* space for the '\0' terminator */ + lastwidth=0; + for (count=0; str[count]!='\0' && destlenlength) { /* correct for overrun */ + destlen-=lastwidth; + count--; + } /* if */ + assert(destlen<=length); + assert(count>=0); + + /* store string terminator */ + assert(destlen>0); + str[--destlen]='\0'; + /* convert string from end to start */ + while (--count>=0) { + assert(destlen>count); + if (INVALIDURI(str[count])) { + str[--destlen]=TOHEX(str[count] & 0x0f); + str[--destlen]=TOHEX((str[count] >> 4) & 0x0f); + str[--destlen]='%'; + } else { + str[--destlen]=str[count]; + } /* if */ + } /* while */ + assert(destlen==0); + + /* store the result */ + cstr=amx_Address(amx,params[1]); + amx_SetString(cstr,str,1,0,params[4]); /* store as packed ot unpacked */ + + return (cell)strlen(str); +} + +/* memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest) + * This function can align byte strings in cell arrays, or concatenate two + * byte strings in two arrays. The parameter "index" is a byte offset; "numbytes" + * is the number of bytes to copy. Parameter "maxlength", however, is in cells. + * This function allows copying in-place, for aligning memory buffers. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_memcpy(AMX *amx,const cell *params) +{ + cell *cdest,*csrc; + unsigned char *pdest,*psrc; + + (void)(amx); + if (params[3]<0 || params[4]<0 || (params[3]+params[4])>params[5]*(int)sizeof(cell)) + return 0; + cdest=amx_Address(amx,params[1]); + csrc=amx_Address(amx,params[2]); + pdest=(unsigned char*)cdest+params[3]; + psrc=(unsigned char*)csrc; + memmove(pdest,psrc,params[4]); + return 1; +} + +#if !defined AMX_NOSTRFMT + static int str_putstr(void *dest,const TCHAR *str) + { + if (_tcslen((TCHAR*)dest)+_tcslen(str)1,(int)params[2]); + return 1; + #endif +} + + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO string_Natives[] = { + { "ispacked", n_ispacked }, + { "memcpy", n_memcpy }, + { "strcat", n_strcat }, + { "strcmp", n_strcmp }, + { "strcopy", n_strcopy }, + { "strdel", n_strdel }, + { "strfind", n_strfind }, + { "strformat", n_strformat }, + { "strins", n_strins }, + { "strlen", n_strlen }, + { "strmid", n_strmid }, + { "strpack", n_strpack }, + { "strunpack", n_strunpack }, + { "strval", n_strval }, + { "uudecode", n_uudecode }, + { "uuencode", n_uuencode }, + { "urldecode", n_urldecode }, + { "urlencode", n_urlencode }, + { "valstr", n_valstr }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_StringInit(AMX *amx) +{ + return amx_Register(amx, string_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_StringCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxstring.def b/amx-deps/src/amx/amxstring.def index 8958cbf..9c464e1 100644 --- a/amx-deps/src/amx/amxstring.def +++ b/amx-deps/src/amx/amxstring.def @@ -1,6 +1,6 @@ -NAME amxString -DESCRIPTION 'Pawn AMX: string manipulation routines' - -EXPORTS - amx_StringInit - amx_StringCleanup +NAME amxString +DESCRIPTION 'Pawn AMX: string manipulation routines' + +EXPORTS + amx_StringInit + amx_StringCleanup diff --git a/amx-deps/src/amx/amxstring.rc b/amx-deps/src/amx/amxstring.rc index ee16547..2b06eb1 100644 --- a/amx-deps/src/amx/amxstring.rc +++ b/amx-deps/src/amx/amxstring.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 1 -#define BUILD 0 -#define VERSIONSTR "1.1.0\0" -#define VERSIONNAME "amxString.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: String manipulation routines\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxString\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 4 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "4.0.0\0" +#define VERSIONNAME "amxString.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: String manipulation routines\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxString\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/amxtime.c b/amx-deps/src/amx/amxtime.c index a7114cf..81c5a32 100644 --- a/amx-deps/src/amx/amxtime.c +++ b/amx-deps/src/amx/amxtime.c @@ -1,453 +1,489 @@ -/* Date/time module for the Pawn Abstract Machine - * - * Copyright (c) ITB CompuPhase, 2001-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxtime.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include -#include "amx.h" -#if defined __WIN32__ || defined _WIN32 || defined _Windows - #include - #include -#endif - -#define CELLMIN (-1 << (8*sizeof(cell) - 1)) - -#define SECONDS_PER_MINUTE 60 -#define SECONDS_PER_HOUR 3600 -#define SECONDS_PER_DAY 86400 -#define SECONDS_PER_YEAR 31556952 /* based on 365.2425 days per year */ - -#if !defined CLOCKS_PER_SEC - #define CLOCKS_PER_SEC CLK_TCK -#endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - static int timerset = 0; - /* timeGetTime() is more accurate on WindowsNT if timeBeginPeriod(1) is set */ - #define INIT_TIMER() \ - if (!timerset) { \ - timeBeginPeriod(1); \ - timerset=1; \ - } -#else - #define INIT_TIMER() -#endif -static unsigned long timestamp; -static unsigned long timelimit; -static int timerepeat; - -static const unsigned char monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -static int wrap(int value, int min, int max) -{ - if (valuemax) - value=min; - return value; -} - -static unsigned long gettimestamp(void) -{ - unsigned long value; - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - value=timeGetTime(); /* this value is already in milliseconds */ - #else - value=clock(); - #if CLOCKS_PER_SEC<1000 - /* convert to milliseconds */ - value=(cell)((1000L * value) / CLOCKS_PER_SEC); - #elif CLOCKS_PER_SEC>1000 - /* convert to milliseconds */ - value=(cell)(value/(CLOCKS_PER_SEC/1000)); - #endif - #endif - return value; -} - -void stamp2datetime(unsigned long sec1970, - int *year, int *month, int *day, - int *hour, int *minute, int *second) -{ - int days, seconds; - - /* find the year */ - assert(year!=NULL); - for (*year = 1970; ; *year += 1) { - days = 365 + ((*year & 0x03) == 0); /* clumsy "leap-year" routine, fails for 2100 */ - seconds = days * SECONDS_PER_DAY; - if ((unsigned long)seconds > sec1970) - break; - sec1970 -= seconds; - } /* if */ - - /* find the month */ - assert(month!=NULL); - for (*month = 1; ; *month += 1) { - days = monthdays[*month - 1]; - seconds = days * SECONDS_PER_DAY; - if ((unsigned long)seconds > sec1970) - break; - sec1970 -= seconds; - } /* if */ - - /* find the day */ - assert(day!=NULL); - for (*day = 1; sec1970 >= SECONDS_PER_DAY; *day += 1) - sec1970 -= SECONDS_PER_DAY; - - /* find the hour */ - assert(hour!=NULL); - for (*hour = 0; sec1970 >= SECONDS_PER_HOUR; *hour += 1) - sec1970 -= SECONDS_PER_HOUR; - - /* find the minute */ - assert(minute!=NULL); - for (*minute = 0; sec1970 >= SECONDS_PER_MINUTE; *minute += 1) - sec1970 -= SECONDS_PER_MINUTE; - - /* remainder is the number of seconds */ - assert(second!=NULL); - *second = (int)sec1970; -} - -static void settime(cell hour,cell minute,cell second) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - SYSTEMTIME systim; - - GetLocalTime(&systim); - if (hour!=CELLMIN) - systim.wHour=(WORD)wrap((int)hour,0,23); - if (minute!=CELLMIN) - systim.wMinute=(WORD)wrap((int)minute,0,59); - if (second!=CELLMIN) - systim.wSecond=(WORD)wrap((int)second,0,59); - SetLocalTime(&systim); - #else - /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you - * must have "root" permission to call stime() - */ - time_t sec1970; - struct tm gtm; - - time(&sec1970); - gtm=*localtime(&sec1970); - if (hour!=CELLMIN) - gtm.tm_hour=wrap((int)hour,0,23); - if (minute!=CELLMIN) - gtm.tm_min=wrap((int)minute,0,59); - if (second!=CELLMIN) - gtm.tm_sec=wrap((int)second,0,59); - sec1970=mktime(>m); - stime(&sec1970); - #endif -} - -static void setdate(cell year,cell month,cell day) -{ - int maxday; - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - SYSTEMTIME systim; - - GetLocalTime(&systim); - if (year!=CELLMIN) - systim.wYear=(WORD)wrap((int)year,1970,2099); - if (month!=CELLMIN) - systim.wMonth=(WORD)wrap((int)month,1,12); - maxday=monthdays[systim.wMonth - 1]; - if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0))) - maxday++; - if (day!=CELLMIN) - systim.wDay=(WORD)wrap((int)day,1,maxday); - SetLocalTime(&systim); - #else - /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you - * must have "root" permission to call stime() - */ - time_t sec1970; - struct tm gtm; - - time(&sec1970); - gtm=*localtime(&sec1970); - if (year!=CELLMIN) - gtm.tm_year=year-1900; - if (month!=CELLMIN) - gtm.tm_mon=month-1; - if (day!=CELLMIN) - gtm.tm_mday=day; - sec1970=mktime(>m); - stime(&sec1970); - #endif -} - - -/* settime(hour, minute, second) - * Always returns 0 - */ -static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params) -{ - (void)amx; - settime(params[1],params[2],params[3]); - return 0; -} - -/* gettime(&hour, &minute, &second) - * The return value is the number of seconds since 1 January 1970 (Unix system - * time). - */ -static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params) -{ - time_t sec1970; - struct tm gtm; - cell *cptr; - - assert(params[0]==(int)(3*sizeof(cell))); - - time(&sec1970); - - /* on DOS/Windows, the timezone is usually not set for the C run-time - * library; in that case gmtime() and localtime() return the same value - */ - gtm=*localtime(&sec1970); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_hour; - if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_min; - if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_sec; - - /* the time() function returns the number of seconds since January 1 1970 - * in Universal Coordinated Time (the successor to Greenwich Mean Time) - */ - return sec1970; -} - -/* setdate(year, month, day) - * Always returns 0 - */ -static cell AMX_NATIVE_CALL n_setdate(AMX *amx, const cell *params) -{ - (void)amx; - setdate(params[1],params[2],params[3]); - return 0; -} - -/* getdate(&year, &month, &day) - * The return value is the number of days since the start of the year. January - * 1 is day 1 of the year. - */ -static cell AMX_NATIVE_CALL n_getdate(AMX *amx, const cell *params) -{ - time_t sec1970; - struct tm gtm; - cell *cptr; - - assert(params[0]==(int)(3*sizeof(cell))); - - time(&sec1970); - - gtm=*localtime(&sec1970); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_year+1900; - if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_mon+1; - if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_mday; - - return gtm.tm_yday+1; -} - -/* tickcount(&granularity) - * Returns the number of milliseconds since start-up. For a 32-bit cell, this - * count overflows after approximately 24 days of continuous operation. - */ -static cell AMX_NATIVE_CALL n_tickcount(AMX *amx, const cell *params) -{ - cell *cptr; - - assert(params[0]==(int)sizeof(cell)); - - INIT_TIMER(); - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=1000; /* granularity = 1 ms */ - #else - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */ - #endif - return gettimestamp() & 0x7fffffff; -} - -/* delay(milliseconds) - * Pauses for (at least) the requested number of milliseconds. - */ -static cell AMX_NATIVE_CALL n_delay(AMX *amx, const cell *params) -{ - unsigned long stamp; - - (void)amx; - assert(params[0]==(int)sizeof(cell)); - - INIT_TIMER(); - stamp=gettimestamp(); - while (gettimestamp()-stamp < (unsigned long)params[1]) - /* nothing */; - return 0; -} - -/* settimer(milliseconds, bool: singleshot = false) - * Sets the delay until the @timer() callback is called. The timer may either - * be single-shot or repetitive. - */ -static cell AMX_NATIVE_CALL n_settimer(AMX *amx, const cell *params) -{ - (void)amx; - assert(params[0]==(int)(2*sizeof(cell))); - timestamp=gettimestamp(); - timelimit=params[1]; - timerepeat=(int)(params[2]==0); - return 0; -} - -/* bool: gettimer(&milliseconds, bool: &singleshot = false) - * Retrieves the timer set with settimer(); returns true if a timer - * was set up, or false otherwise. - */ -static cell AMX_NATIVE_CALL n_gettimer(AMX *amx, const cell *params) -{ - cell *cptr; - - assert(params[0]==(int)(2*sizeof(cell))); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=timelimit; - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=timerepeat; - return timelimit>0; -} - -/* settimestamp(seconds1970) sets the date and time from a single parameter: the - * number of seconds since 1 January 1970. - */ -static cell AMX_NATIVE_CALL n_settimestamp(AMX *amx, const cell *params) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - int year, month, day, hour, minute, second; - - stamp2datetime(params[1], - &year, &month, &day, - &hour, &minute, &second); - setdate(year, month, day); - settime(hour, minute, second); - #else - /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you - * must have "root" permission to call stime() - */ - time_t sec1970=(time_t)params[1]; - stime(&sec1970); - #endif - (void)amx; - - return 0; -} - -/* cvttimestamp(seconds1970, &year, &month, &day, &hour, &minute, &second) - */ -static cell AMX_NATIVE_CALL n_cvttimestamp(AMX *amx, const cell *params) -{ - int year, month, day, hour, minute, second; - - (void)amx; - stamp2datetime(params[1], - &year, &month, &day, - &hour, &minute, &second); - return 0; -} - - -#if !defined AMXTIME_NOIDLE -static AMX_IDLE PrevIdle = NULL; -static int idxTimer = -1; - -static int AMXAPI amx_TimeIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) -{ - int err=0; - - assert(idxTimer >= 0); - - if (PrevIdle != NULL) - PrevIdle(amx, Exec); - - if (timelimit>0 && (gettimestamp()-timestamp)>=timelimit) { - if (timerepeat) - timestamp+=timelimit; - else - timelimit=0; /* do not repeat single-shot timer */ - err = Exec(amx, NULL, idxTimer); - while (err == AMX_ERR_SLEEP) - err = Exec(amx, NULL, AMX_EXEC_CONT); - } /* if */ - - return err; -} -#endif - - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO time_Natives[] = { - { "gettime", n_gettime }, - { "settime", n_settime }, - { "getdate", n_getdate }, - { "setdate", n_setdate }, - { "tickcount", n_tickcount }, - { "settimer", n_settimer }, - { "gettimer", n_gettimer }, - { "delay", n_delay }, - { "settimestamp", n_settimestamp }, - { "cvttimestamp", n_cvttimestamp }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_TimeInit(AMX *amx) -{ - #if !defined AMXTIME_NOIDLE - /* see whether there is a @timer() function */ - if (amx_FindPublic(amx,"@timer",&idxTimer) == AMX_ERR_NONE) { - if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE) - PrevIdle = NULL; - amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_TimeIdle); - } /* if */ - #endif - - return amx_Register(amx, time_Natives, -1); -} - -int AMXEXPORT amx_TimeCleanup(AMX *amx) -{ - (void)amx; - #if !defined AMXTIME_NOIDLE - PrevIdle = NULL; - #endif - return AMX_ERR_NONE; -} +/* Date/time module for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2001-2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amxtime.c 5588 2016-10-25 11:13:28Z $ + */ + +#include +#include +#include "amx.h" +#if defined __WIN32__ || defined _WIN32 || defined _Windows + #include + #include +#endif +#if defined __GNUC__ || defined __clang__ + #include +#endif + +#if defined __clang__ + /* ignore this warning, because fixing the macro would make it depend on + two's-complement arithmetic */ + #pragma GCC diagnostic ignored "-Wshift-negative-value" +#endif +#define CELLMIN (-1 << (8*sizeof(cell) - 1)) + +#define SECONDS_PER_MINUTE 60 +#define SECONDS_PER_HOUR 3600 +#define SECONDS_PER_DAY 86400 +#define SECONDS_PER_YEAR 31556952 /* based on 365.2425 days per year */ + +#if !defined CLOCKS_PER_SEC + #define CLOCKS_PER_SEC CLK_TCK +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + static int timerset = 0; + /* timeGetTime() is more accurate on WindowsNT if timeBeginPeriod(1) is set */ + #define INIT_TIMER() \ + if (!timerset) { \ + timeBeginPeriod(1); \ + timerset=1; \ + } +#else + #define INIT_TIMER() +#endif +static unsigned long timestamp; +static unsigned long timelimit; +static int timerepeat; + +static const unsigned char monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static int wrap(int value, int min, int max) +{ + if (valuemax) + value=min; + return value; +} + +static unsigned long gettimestamp(void) +{ + unsigned long value; + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + value=timeGetTime(); /* this value is already in milliseconds */ + #elif defined __linux || defined __linux__ || defined __LINUX__ || defined __APPLE__ + struct timeval tv; + gettimeofday(&tv, NULL); + value = ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + #else + value=clock(); + #if CLOCKS_PER_SEC<1000 + /* convert to milliseconds */ + value=(cell)((1000L * value) / CLOCKS_PER_SEC); + #elif CLOCKS_PER_SEC>1000 + /* convert to milliseconds */ + value=(cell)(value/(CLOCKS_PER_SEC/1000)); + #endif + #endif + return value; +} + +void stamp2datetime(unsigned long sec1970, + int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + int days, seconds; + + /* find the year */ + assert(year!=NULL); + for (*year = 1970; ; *year += 1) { + days = 365 + ((*year & 0x03) == 0); /* clumsy "leap-year" routine, fails for 2100 */ + seconds = days * SECONDS_PER_DAY; + if ((unsigned long)seconds > sec1970) + break; + sec1970 -= seconds; + } /* if */ + + /* find the month */ + assert(month!=NULL); + for (*month = 1; ; *month += 1) { + days = monthdays[*month - 1]; + seconds = days * SECONDS_PER_DAY; + if ((unsigned long)seconds > sec1970) + break; + sec1970 -= seconds; + } /* if */ + + /* find the day */ + assert(day!=NULL); + for (*day = 1; sec1970 >= SECONDS_PER_DAY; *day += 1) + sec1970 -= SECONDS_PER_DAY; + + /* find the hour */ + assert(hour!=NULL); + for (*hour = 0; sec1970 >= SECONDS_PER_HOUR; *hour += 1) + sec1970 -= SECONDS_PER_HOUR; + + /* find the minute */ + assert(minute!=NULL); + for (*minute = 0; sec1970 >= SECONDS_PER_MINUTE; *minute += 1) + sec1970 -= SECONDS_PER_MINUTE; + + /* remainder is the number of seconds */ + assert(second!=NULL); + *second = (int)sec1970; +} + +static void settime(cell hour,cell minute,cell second) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + SYSTEMTIME systim; + + GetLocalTime(&systim); + if (hour!=CELLMIN) + systim.wHour=(WORD)wrap((int)hour,0,23); + if (minute!=CELLMIN) + systim.wMinute=(WORD)wrap((int)minute,0,59); + if (second!=CELLMIN) + systim.wSecond=(WORD)wrap((int)second,0,59); + SetLocalTime(&systim); + #else + /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead + */ + time_t sec1970; + struct tm gtm; + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + #endif + + time(&sec1970); + gtm=*localtime(&sec1970); + if (hour!=CELLMIN) + gtm.tm_hour=wrap((int)hour,0,23); + if (minute!=CELLMIN) + gtm.tm_min=wrap((int)minute,0,59); + if (second!=CELLMIN) + gtm.tm_sec=wrap((int)second,0,59); + sec1970=mktime(>m); + #if defined __APPLE__ /* also valid for other POSIX systems */ + tv.tv_sec = sec1970; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + stime(&sec1970); + #endif + #endif +} + +static void setdate(cell year,cell month,cell day) +{ + int maxday; + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + SYSTEMTIME systim; + + GetLocalTime(&systim); + if (year!=CELLMIN) + systim.wYear=(WORD)wrap((int)year,1970,2099); + if (month!=CELLMIN) + systim.wMonth=(WORD)wrap((int)month,1,12); + maxday=monthdays[systim.wMonth - 1]; + if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0))) + maxday++; + if (day!=CELLMIN) + systim.wDay=(WORD)wrap((int)day,1,maxday); + SetLocalTime(&systim); + #else + /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead + */ + time_t sec1970; + struct tm gtm; + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + #endif + + time(&sec1970); + gtm=*localtime(&sec1970); + if (year!=CELLMIN) + gtm.tm_year=year-1900; + if (month!=CELLMIN) + gtm.tm_mon=month-1; + if (day!=CELLMIN) + gtm.tm_mday=day; + sec1970=mktime(>m); + #if defined __APPLE__ /* also valid for other POSIX systems */ + tv.tv_sec = sec1970; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + stime(&sec1970); + #endif + #endif +} + + +/* settime(hour, minute, second) + * Always returns 0 + */ +static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params) +{ + (void)amx; + settime(params[1],params[2],params[3]); + return 0; +} + +/* gettime(&hour, &minute, &second) + * The return value is the number of seconds since 1 January 1970 (Unix system + * time). + */ +static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params) +{ + time_t sec1970; + struct tm gtm; + cell *cptr; + + assert(params[0]==(int)(3*sizeof(cell))); + + time(&sec1970); + + /* on DOS/Windows, the timezone is usually not set for the C run-time + * library; in that case gmtime() and localtime() return the same value + */ + gtm=*localtime(&sec1970); + cptr=amx_Address(amx,params[1]); + *cptr=gtm.tm_hour; + cptr=amx_Address(amx,params[2]); + *cptr=gtm.tm_min; + cptr=amx_Address(amx,params[3]); + *cptr=gtm.tm_sec; + + /* the time() function returns the number of seconds since January 1 1970 + * in Universal Coordinated Time (the successor to Greenwich Mean Time) + */ + return (cell)sec1970; +} + +/* setdate(year, month, day) + * Always returns 0 + */ +static cell AMX_NATIVE_CALL n_setdate(AMX *amx, const cell *params) +{ + (void)amx; + setdate(params[1],params[2],params[3]); + return 0; +} + +/* getdate(&year, &month, &day) + * The return value is the number of days since the start of the year. January + * 1 is day 1 of the year. + */ +static cell AMX_NATIVE_CALL n_getdate(AMX *amx, const cell *params) +{ + time_t sec1970; + struct tm gtm; + cell *cptr; + + assert(params[0]==(int)(3*sizeof(cell))); + + time(&sec1970); + + gtm=*localtime(&sec1970); + cptr=amx_Address(amx,params[1]); + *cptr=gtm.tm_year+1900; + cptr=amx_Address(amx,params[2]); + *cptr=gtm.tm_mon+1; + cptr=amx_Address(amx,params[3]); + *cptr=gtm.tm_mday; + + return gtm.tm_yday+1; +} + +/* tickcount(&granularity) + * Returns the number of milliseconds since start-up. For a 32-bit cell, this + * count overflows after approximately 24 days of continuous operation. + */ +static cell AMX_NATIVE_CALL n_tickcount(AMX *amx, const cell *params) +{ + cell *cptr; + + assert(params[0]==(int)sizeof(cell)); + + INIT_TIMER(); + cptr=amx_Address(amx,params[1]); + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + *cptr=1000; /* granularity = 1 ms */ + #else + *cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */ + #endif + return gettimestamp() & 0x7fffffff; +} + +/* delay(milliseconds) + * Pauses for (at least) the requested number of milliseconds. + */ +static cell AMX_NATIVE_CALL n_delay(AMX *amx, const cell *params) +{ + unsigned long stamp; + + (void)amx; + assert(params[0]==(int)sizeof(cell)); + + INIT_TIMER(); + stamp=gettimestamp(); + while (gettimestamp()-stamp < (unsigned long)params[1]) + /* nothing */; + return 0; +} + +/* settimer(milliseconds, bool: singleshot = false) + * Sets the delay until the @timer() callback is called. The timer may either + * be single-shot or repetitive. + */ +static cell AMX_NATIVE_CALL n_settimer(AMX *amx, const cell *params) +{ + (void)amx; + assert(params[0]==(int)(2*sizeof(cell))); + timestamp=gettimestamp(); + timelimit=params[1]; + timerepeat=(int)(params[2]==0); + return 0; +} + +/* bool: gettimer(&milliseconds, bool: &singleshot = false) + * Retrieves the timer set with settimer(); returns true if a timer + * was set up, or false otherwise. + */ +static cell AMX_NATIVE_CALL n_gettimer(AMX *amx, const cell *params) +{ + cell *cptr; + + assert(params[0]==(int)(2*sizeof(cell))); + cptr=amx_Address(amx,params[1]); + *cptr=timelimit; + cptr=amx_Address(amx,params[2]); + *cptr=timerepeat; + return timelimit>0; +} + +/* settimestamp(seconds1970) sets the date and time from a single parameter: the + * number of seconds since 1 January 1970. + */ +static cell AMX_NATIVE_CALL n_settimestamp(AMX *amx, const cell *params) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + int year, month, day, hour, minute, second; + + stamp2datetime(params[1], + &year, &month, &day, + &hour, &minute, &second); + setdate(year, month, day); + settime(hour, minute, second); + #else + /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead + */ + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + tv.tv_sec = params[1]; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + time_t sec1970=(time_t)params[1]; + stime(&sec1970); + #endif + #endif + (void)amx; + + return 0; +} + +/* cvttimestamp(seconds1970, &year, &month, &day, &hour, &minute, &second) + */ +static cell AMX_NATIVE_CALL n_cvttimestamp(AMX *amx, const cell *params) +{ + int year, month, day, hour, minute, second; + + (void)amx; + stamp2datetime(params[1], + &year, &month, &day, + &hour, &minute, &second); + return 0; +} + + +#if !defined AMXTIME_NOIDLE +static AMX_IDLE PrevIdle = NULL; +static int idxTimer = -1; + +static int AMXAPI amx_TimeIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) +{ + int err=0; + + assert(idxTimer >= 0); + + if (PrevIdle != NULL) + PrevIdle(amx, Exec); + + if (timelimit>0 && (gettimestamp()-timestamp)>=timelimit) { + if (timerepeat) + timestamp+=timelimit; + else + timelimit=0; /* do not repeat single-shot timer */ + err = Exec(amx, NULL, idxTimer); + while (err == AMX_ERR_SLEEP) + err = Exec(amx, NULL, AMX_EXEC_CONT); + } /* if */ + + return err; +} +#endif + + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO time_Natives[] = { + { "gettime", n_gettime }, + { "settime", n_settime }, + { "getdate", n_getdate }, + { "setdate", n_setdate }, + { "tickcount", n_tickcount }, + { "settimer", n_settimer }, + { "gettimer", n_gettimer }, + { "delay", n_delay }, + { "settimestamp", n_settimestamp }, + { "cvttimestamp", n_cvttimestamp }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT AMXAPI amx_TimeInit(AMX *amx) +{ + #if !defined AMXTIME_NOIDLE + /* see whether there is a @timer() function */ + if (amx_FindPublic(amx,"@timer",&idxTimer) == AMX_ERR_NONE) { + if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE) + PrevIdle = NULL; + amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_TimeIdle); + } /* if */ + #endif + + return amx_Register(amx, time_Natives, -1); +} + +int AMXEXPORT AMXAPI amx_TimeCleanup(AMX *amx) +{ + (void)amx; + #if !defined AMXTIME_NOIDLE + PrevIdle = NULL; + #endif + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/amxtime.def b/amx-deps/src/amx/amxtime.def index ee1b07b..c2ea65f 100644 --- a/amx-deps/src/amx/amxtime.def +++ b/amx-deps/src/amx/amxtime.def @@ -1,6 +1,6 @@ -NAME amxTime -DESCRIPTION 'Pawn AMX: time routines' - -EXPORTS - amx_TimeInit - amx_TimeCleanup +NAME amxTime +DESCRIPTION 'Pawn AMX: time routines' + +EXPORTS + amx_TimeInit + amx_TimeCleanup diff --git a/amx-deps/src/amx/amxtime.rc b/amx-deps/src/amx/amxtime.rc index c8d119f..0836e9d 100644 --- a/amx-deps/src/amx/amxtime.rc +++ b/amx-deps/src/amx/amxtime.rc @@ -1,54 +1,54 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION 1 -#define REVISION 1 -#define BUILD 0 -#define VERSIONSTR "1.2.0\0" -#define VERSIONNAME "amxTime.dll\0" -#define VERSIONDESCRIPTION "Pawn AMX: time routines\0" -#define VERSIONCOMPANYNAME "ITB CompuPhase\0" -#define VERSIONPRODUCTNAME "amxTime\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2007 ITB CompuPhase\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VERSIONCOMPANYNAME - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", VERSIONCOPYRIGHT - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION 4 +#define REVISION 0 +#define BUILD 0 +#define VERSIONSTR "4.0.0\0" +#define VERSIONNAME "amxTime.dll\0" +#define VERSIONDESCRIPTION "Pawn AMX: time routines\0" +#define VERSIONCOMPANYNAME "ITB CompuPhase\0" +#define VERSIONPRODUCTNAME "amxTime\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSIONCOMPANYNAME + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", VERSIONCOPYRIGHT + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/amx-deps/src/amx/cmake_install.cmake b/amx-deps/src/amx/cmake_install.cmake deleted file mode 100644 index 452c949..0000000 --- a/amx-deps/src/amx/cmake_install.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Install script for directory: /root/king/amx - -# Set the install prefix -IF(NOT DEFINED CMAKE_INSTALL_PREFIX) - SET(CMAKE_INSTALL_PREFIX "/usr/local") -ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX) -STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - -# Set the install configuration name. -IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - IF(BUILD_TYPE) - STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" "" - CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") - ELSE(BUILD_TYPE) - SET(CMAKE_INSTALL_CONFIG_NAME "") - ENDIF(BUILD_TYPE) - MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") -ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - -# Set the component getting installed. -IF(NOT CMAKE_INSTALL_COMPONENT) - IF(COMPONENT) - MESSAGE(STATUS "Install component: \"${COMPONENT}\"") - SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}") - ELSE(COMPONENT) - SET(CMAKE_INSTALL_COMPONENT) - ENDIF(COMPONENT) -ENDIF(NOT CMAKE_INSTALL_COMPONENT) - -# Install shared libraries without execute permission? -IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - SET(CMAKE_INSTALL_SO_NO_EXE "1") -ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - -IF(CMAKE_INSTALL_COMPONENT) - SET(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") -ELSE(CMAKE_INSTALL_COMPONENT) - SET(CMAKE_INSTALL_MANIFEST "install_manifest.txt") -ENDIF(CMAKE_INSTALL_COMPONENT) - -FILE(WRITE "/root/king/amx/${CMAKE_INSTALL_MANIFEST}" "") -FOREACH(file ${CMAKE_INSTALL_MANIFEST_FILES}) - FILE(APPEND "/root/king/amx/${CMAKE_INSTALL_MANIFEST}" "${file}\n") -ENDFOREACH(file) diff --git a/amx-deps/src/amx/dllmain.c b/amx-deps/src/amx/dllmain.c index 4308dc7..efcc162 100644 --- a/amx-deps/src/amx/dllmain.c +++ b/amx-deps/src/amx/dllmain.c @@ -1,79 +1,79 @@ -/* DLL support functions for dynamically loadable extension libraries. - * - * Copyright (c) ITB CompuPhase, 2004-2005 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: dllmain.c 3363 2005-07-23 09:03:29Z thiadmer $ - */ - -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#include -#include - -#if !defined UNUSED_PARAM - #define UNUSED_PARAM(p) ((void)(p)) -#endif - -HINSTANCE hinstDLL; - -/* Especially Watcom C/C++ does not like DLLs that do not have a LibMain() - * set. Apparently, the start address is not set well, and some required - * initializations are not done. - */ -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - - BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID lpRes) - { - UNUSED_PARAM(lpRes); - switch (dwReason) { - case DLL_PROCESS_ATTACH: - hinstDLL=hinst; - break; - case DLL_PROCESS_DETACH: - break; - } /* switch */ - return TRUE; - } - -#else - - int FAR PASCAL LibMain(HINSTANCE hinst, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) - { - UNUSED_PARAM(wDataSeg); - UNUSED_PARAM(wHeapSize); - UNUSED_PARAM(lpszCmdLine); - hinstDLL=hinst; - return 1; - } - - int FAR PASCAL _export WEP(int param) - { - UNUSED_PARAM(param); - return 1; - } - -#endif /* __WIN32__ */ - +/* DLL support functions for dynamically loadable extension libraries. + * + * Copyright (c) ITB CompuPhase, 2004-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: dllmain.c 3363 2005-07-23 09:03:29Z thiadmer $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include + +#if !defined UNUSED_PARAM + #define UNUSED_PARAM(p) ((void)(p)) +#endif + +HINSTANCE hinstDLL; + +/* Especially Watcom C/C++ does not like DLLs that do not have a LibMain() + * set. Apparently, the start address is not set well, and some required + * initializations are not done. + */ +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + + BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID lpRes) + { + UNUSED_PARAM(lpRes); + switch (dwReason) { + case DLL_PROCESS_ATTACH: + hinstDLL=hinst; + break; + case DLL_PROCESS_DETACH: + break; + } /* switch */ + return TRUE; + } + +#else + + int FAR PASCAL LibMain(HINSTANCE hinst, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) + { + UNUSED_PARAM(wDataSeg); + UNUSED_PARAM(wHeapSize); + UNUSED_PARAM(lpszCmdLine); + hinstDLL=hinst; + return 1; + } + + int FAR PASCAL _export WEP(int param) + { + UNUSED_PARAM(param); + return 1; + } + +#endif /* __WIN32__ */ + diff --git a/amx-deps/src/amx/fixed.c b/amx-deps/src/amx/fixed.c index 68e339b..b5707b9 100644 --- a/amx-deps/src/amx/fixed.c +++ b/amx-deps/src/amx/fixed.c @@ -1,744 +1,745 @@ -/* Fixed-point arithmetic for the Pawn Abstract Machine - * - * Fixed point numbers compromise range versus number of decimals. This - * library decimal fixed point numbers with an configurable number of - * decimals. The current setting is 3 decimals. - * - * Copyright (c) ITB CompuPhase, 1998-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: fixed.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ -#include -#include /* for NULL */ -#include "amx.h" - -/* - #if defined __BORLANDC__ - #pragma resource "amxFixed.res" - #endif -*/ - -#if !defined isdigit -# define isdigit(c) ((unsigned)((c)-'0')<10u) -#endif -#define MULTIPLIER 1000L /* 10^decimals */ - - -static cell AMX_NATIVE_CALL n_fixed(AMX *amx,const cell *params) -{ - (void)amx; - return params[1] * MULTIPLIER; -} - -static cell AMX_NATIVE_CALL n_strfixed(AMX *amx,const cell *params) -{ - char str[50],*ptr; - cell *cstr,intpart,decimals; - long multiplier,divisor; - int len,sign=1; - - amx_GetAddr(amx,params[1],&cstr); - amx_StrLen(cstr,&len); - if (len>=50) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; - } /* if */ - amx_GetString(str,cstr,0,UNLIMITED); - ptr=str; - intpart=0; - decimals=0; - multiplier=MULTIPLIER; - divisor=1; - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip whitespace */ - if (*ptr=='-') { /* handle sign */ - sign=-1; - ptr++; - } else if (*ptr=='+') { - ptr++; - } /* if */ - while (isdigit(*ptr)) { - intpart=intpart*10 + (*ptr-'0'); - ptr++; - } /* while */ - if (*ptr=='.') { - ptr++; - len=0; - while (isdigit(*ptr) && len<8) { - decimals=decimals*10 + (*ptr-'0'); - if (multiplier>1) - multiplier/=10; - else - divisor*=10; - ptr++; - len++; - } /* while */ - } /* if */ - return ((intpart*MULTIPLIER) + (decimals*multiplier+(divisor/2))/divisor) * sign; -} - -/* Some C/C++ compilers have problems with long lists of definitions, so - * I create another macro to fix this. - */ -#if PAWN_CELL_SIZE!=32 - /* the assembler and compiler-supported optimizations are only implemented - * for 32-bit cells - */ - #define USE_ANSI_C 1 - -#elif defined __WATCOMC__ && defined __386__ /* Watcom C/C++ */ - /* ANSI 64-bit division routine not needed for Watcom C/C++ because - * it uses inline assembler. - */ - #define USE_ANSI_C 0 - -#elif defined _MSC_VER && _MSC_VER>=9 && defined _WIN32 /* Microsoft C/C++ */ - /* ANSI 64-bit division routine not needed for Microsoft Visual C/C++ - * because it supports 64-bit integers - */ - #define USE_ANSI_C 0 - -#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && defined __32BIT__ /* Borland C++ v.5 */ - /* ANSI 64-bit division routine not needed for Borland C++ because it - * supports 64-bit integers - */ - #define USE_ANSI_C 0 - -#elif defined __GNUC__ /* GNU GCC */ - /* ANSI 64-bit division routine not needed for GNU GCC because it - * supports 64-bit integers - */ - #define USE_ANSI_C 0 - -#else - - #define USE_ANSI_C 1 - -#endif - -#if USE_ANSI_C - #define WORDSHIFT (PAWN_CELL_SIZE/2) - - #if PAWN_CELL_SIZE==32 - typedef unsigned short word_t; - #define LOWORD(v) (word_t)((v) & 0xffffu) - #define HIWORD(v) (word_t)(((v) >> 16) & 0xffffu) - #elif PAWN_CELL_SIZE==64 - typedef uint32_t word_t; - #define LOWORD(v) (word_t)((v) & 0xffffffffu) - #define HIWORD(v) (word_t)(((v) >> 32) & 0xffffffffu) - #else - #error Unsupported cell size - #endif - - static ucell div64_32(ucell t[2], ucell divisor) - { - /* This function was adapted from source code that appeared in - * Dr. Dobb's Journal, August 1992, page 117. - */ - ucell u, v; - word_t rHigh, rLow, dHigh, dLow; - - assert(divisor!=0); - /* if the divisor is smaller than t[1], the result will not fit in a cell */ - assert(divisor>=t[1]); - - dHigh=HIWORD(divisor); - dLow=LOWORD(divisor); - - /* Underestimate high half of quotient and subtract product - * of estimate and divisor from dividend. - */ - rHigh = (word_t)(t[1] / (dHigh + 1)); - u = (ucell)rHigh * (ucell)dLow; - v = (ucell)rHigh * (ucell)dHigh; - if ((t[0] -= (u << WORDSHIFT)) > ((ucell)-1L - (u << WORDSHIFT))) - t[1]--; - t[1] -= HIWORD(u); - t[1] -= v; - - /* Correct estimate. */ - while ((t[1] > (ucell)dHigh) || ((t[1] == (ucell)dHigh) && (t[0] >= ((ucell)dLow << WORDSHIFT)))) { - if ((t[0] -= ((ucell)dLow << WORDSHIFT)) > (ucell)-1L - ((ucell)dLow << WORDSHIFT)) - t[1]--; - t[1] -= dHigh; - rHigh++; - } /* while */ - /* Underestimate low half of quotient and subtract product of - * estimate and divisor from what remains of dividend. - */ - rLow = (word_t) ((ucell)((t[1] << WORDSHIFT) + HIWORD(t[0])) / (dHigh + 1)); - u = (ucell)rLow * (ucell)dLow; - v = (ucell)rLow * (ucell)dHigh; - if ((t[0] -= u) > ((ucell)-1L - u)) - t[1]--; - if ((t[0] -= (v << WORDSHIFT)) > ((ucell)-1L - (v << WORDSHIFT))) - t[1]--; - t[1] -= HIWORD(v); - - /* Correct estimate. */ - while ((t[1] > 0) || ((t[1] == 0) && t[0] >= divisor)) { - if ((t[0] -= divisor) > ((ucell)-1L - divisor)) - t[1]--; - rLow++; - } /* while */ - - return ((ucell)rHigh << WORDSHIFT) + rLow; - } -#endif - -static cell AMX_NATIVE_CALL n_fmul(AMX *amx,const cell *params) -{ -#if !USE_ANSI_C -#if defined __WATCOMC__ && defined __386__ - - cell __fmul(void); - cell a=params[1]; - cell b=params[2]; - #if MULTIPLIER != 1000 - #error Assembler chunks must be modified for a different base - #endif - #pragma aux __fmul = \ - "mov eax, [a]" \ - "mov ebx, 1000" \ - "imul [b]" \ - "add eax, 500" \ - "adc edx, 0" \ - "idiv ebx" \ - "mov [a], eax" \ - modify [eax ebx edx]; - __fmul(); - (void)amx; - return a; - -#elif _MSC_VER>=9 && defined _WIN32 - - __int64 a=(__int64)params[1] * (__int64)params[2]; - a=(a+MULTIPLIER/2) / MULTIPLIER; - (void)amx; - return (cell)a; - -#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) - - __int64 a=(__int64)params[1] * (__int64)params[2]; - a=(a+MULTIPLIER/2) / MULTIPLIER; - (void)amx; - return (cell)a; - -#elif defined __GNUC__ - - long long a=(long long)params[1] * (long long)params[2]; - a=(a+MULTIPLIER/2) / MULTIPLIER; - (void)amx; - return (cell)a; - -#else - #error Unsupported compiler configuration, but USE_ANSI_C is false -#endif - -#else // USE_ANSI_C - - /* (Xs * Ys) == (X*Y)ss, where "s" stands for scaled. - * The desired result is (X*Y)s, so we must unscale once. - * but we cannot do this before multiplication, because of loss - * of precision, and we cannot do it after the multiplication - * because of the possible overflow. - * The technique used here is to cut the multiplicands into - * components and to multiply these components separately: - * - * Assume Xs == (A << 16) + B and Ys == (C << 16) + D, where A, B, - * C and D are 16 bit numbers. - * - * A B - * C D - * --- * - * D*B + (D*A << 16) + (C*B << 16) + (C*A << (16+16)) - * - * Thus we have built a 64-bit number, which can now be scaled back - * to 32-bit by dividing by the scale factor. - */ - #define ADD_WRAP(var,carry,expr) (((var)+=(expr)), ((carry)+=((var)<(expr)) ? 1 : 0)) - ucell a,b,c,d; - ucell v[2]; - cell sign=1; - - (void)amx; - assert(MULTIPLIER<=(1L<=9 && defined _WIN32 - - __int64 a; - cell divisor=params[2]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; - return (cell)a; - -#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) - - __int64 a; - cell divisor=params[2]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; - return (cell)a; - -#elif defined __GNUC__ - - long long a; - cell divisor=params[2]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((long long)params[1] * (long long)MULTIPLIER + (long long)(divisor/2)) / (long long)divisor; - return (cell)a; - -#else - #error Unsupported compiler configuration, but USE_ANSI_C is false -#endif - -#else // USE_ANSI_C - - /* The dividend must be scaled prior to division. The dividend - * is a 32-bit number, however, so when shifted, it will become - * a value that no longer fits in a 32-bit variable. This routine - * does the division by using only 16-bit and 32-bit values, but - * with considerable effort. - * If your compiler supports 64-bit integers, modify this routine - * to use them. If your processor can do a simple 64-bit by 32-bit - * division in assembler, write assembler chunks. - * In other words: the straight C routine that follows is correct - * and portable, but use it only as a last resort. - * - * This function was adapted from source code that appeared in - * Dr. Dobb's Journal, August 1992, page 117. - */ - - cell dividend=params[1]; - cell divisor=params[2]; - cell sign=1; - ucell b[2]; - - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; - } /* if */ - - /* make both operands positive values, but keep the sign of the result */ - if (dividend<0) { - dividend=-dividend; - sign=-sign; /* negate result */ - } /* if */ - if (divisor<0) { - divisor=-divisor; - sign=-sign; /* negate result */ - } /* if */ - - /* pre-scale the dividend into a 64-bit/128-bit number */ - b[0]=dividend*MULTIPLIER; - b[1]=(HIWORD(dividend)*MULTIPLIER) >> WORDSHIFT; - - /* add half of the divisor, to round the data */ - b[0]+=(ucell)divisor/2; - if (b[0]<(ucell)divisor/2) - b[1]+=1; /* wrap-around ocurred */ - - /* if the divisor is smaller than b[1], the result will not fit in a cell */ - if ((ucell)divisor=9 && defined _WIN32 - - __int64 a; - cell divisor=params[3]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; - return (cell)a; - -#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) - - __int64 a; - cell divisor=params[3]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; - return (cell)a; - -#elif defined __GNUC__ - - long long a; - cell divisor=params[3]; - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - a=((long long)params[1] * (long long)params[2] + (long long)(divisor/2)) / (long long)divisor; - return (cell)a; - -#else - #error Unsupported compiler configuration, but USE_ANSI_C is false -#endif - -#else // USE_ANSI_C - - ucell a,b,c,d; - ucell v[2]; - cell sign=1; - cell divisor=params[3]; - - assert(MULTIPLIER<=(1L<<16)); - - if (divisor==0) { - amx_RaiseError(amx,AMX_ERR_DIVIDE); - return 0; - } /* if */ - - /* make all three operands positive values, but keep the sign of the result */ - if (params[1]<0) { - ((cell*)params)[1]=-params[1]; - sign=-sign; /* negate result */ - } /* if */ - if (params[2]<0) { - ((cell*)params)[2]=-params[2]; - sign=-sign; /* negate result */ - } /* if */ - if (divisor<0) { - divisor=-divisor; - sign=-sign; /* negate result */ - } /* if */ - - a = HIWORD(params[1]); - b = LOWORD(params[1]); - c = HIWORD(params[2]); - d = LOWORD(params[2]); - - /* store the intermediate into a 64-bit/128-bit number */ - v[1]=c*a; - v[0]=d*b; - ADD_WRAP(v[0],v[1],d*a << WORDSHIFT); - ADD_WRAP(v[0],v[1],c*b << WORDSHIFT); - - /* add half of the divisor, to round the data */ - ADD_WRAP(v[0],v[1],(ucell)divisor/2); - - /* if the divisor is smaller than v[1], the result will not fit in a cell */ - if ((ucell)divisor=0 && (params[1] % MULTIPLIER)!=0) - value++; - break; - case 3: /* truncate: round down when > 0, round up when < 0 */ - value=params[1] / MULTIPLIER; - break; - case 4: /* round to even number when fractional part is exactly 0.5 */ - value=(params[1] + MULTIPLIER/2) / MULTIPLIER; - if ((params[1] % MULTIPLIER)==MULTIPLIER/2 && (value & 1)==1) - value--; - break; - default: /* standard (fractional part of 0.5 is rounded up */ - value=(params[1] + MULTIPLIER/2) / MULTIPLIER; - } /* switch */ - return value; -} - -/* Fixed:fsqroot(Fixed:value) */ -static cell AMX_NATIVE_CALL n_fsqroot(AMX *amx,const cell *params) -{ - cell value=params[1]; - cell low=0; - cell high=value; - cell mid[3]={8,0,0}; - - if (value<0) { - amx_RaiseError(amx, AMX_ERR_DOMAIN); - return 0; - } /* if */ - - while (high-low > 1) { - mid[1]=mid[2]=(low+high)/2; - if (n_fmul(amx,mid) < value) - low=mid[1]; - else - high=mid[1]; - } /* while */ - - /* check whether low or high mark comes closest */ - if (low!=high) { - cell deltalow, deltahigh; - mid[1]=mid[2]=low; - deltalow=value-n_fmul(amx,mid); - assert(deltalow>=0); - mid[1]=mid[2]=high; - deltahigh=n_fmul(amx,mid)-value; - assert(deltahigh>=0); - if (deltahigh<=deltalow) - low=high; /* return "high" mark (comes closer to the answer) */ - } /* if */ - - return low; -} - -/* Fixed: fpower(Fixed:value, exponent) - * note: x^n = exp(n*ln(x)), see http://yacas.sourceforge.net/Algochapter5.html - */ -static cell AMX_NATIVE_CALL n_fpower(AMX *amx,const cell *params) -{ - #define LIMIT32 146542L /* top value to calculate with extra digit, when a cell is 32-bit */ - cell result[3] = {8,0,0}; - int power=(int)params[2]; - int iter=1; - int reciprocal=0; - int isscaled=0; - - if (power<0) { - reciprocal=1; - power=-power; - } /* if */ - if (power==0) - return MULTIPLIER; - - /* quick squaring, to nearest power of 2 */ - result[1]=params[1]; - /* first try to do this with an extra digit of precision */ - if (result[1]=4); - result[1]*=10; /* scale to have an extra digit */ - isscaled=1; - while (2*iter<=power && result[1]power || result[1]>=LIMIT32*10); - if (result[1]>=LIMIT32*10) { - result[1]=(result[1]+5)/10; /* undo scaling */ - isscaled=0; - } /* if */ - while (2*iter<=power) { - iter*=2; - result[2]=result[1]; - result[1]=n_fmul(amx,result); - } /* while */ - - /* multilply with the remainder */ - if (iter=0) ? result : -result; -} - -#if defined __cplusplus - extern "C" -#endif -const AMX_NATIVE_INFO fixed_Natives[] = { - { "fixed", n_fixed }, - { "strfixed", n_strfixed }, - { "fmul", n_fmul }, - { "fdiv", n_fdiv }, - { "ffract", n_ffract }, - { "fround", n_fround }, - { "fmuldiv", n_fmuldiv }, - { "fsqroot", n_fsqroot }, - { "fpower", n_fpower }, - { "fabs", n_fabs }, - { NULL, NULL } /* terminator */ -}; - -int AMXEXPORT amx_FixedInit(AMX *amx) -{ - return amx_Register(amx,fixed_Natives,-1); -} - -int AMXEXPORT amx_FixedCleanup(AMX *amx) -{ - (void)amx; - return AMX_ERR_NONE; -} +/* Fixed-point arithmetic for the Pawn Abstract Machine + * + * Fixed point numbers compromise range versus number of decimals. This + * library decimal fixed point numbers with an configurable number of + * decimals. The current setting is 3 decimals. + * + * Copyright (c) ITB CompuPhase, 1998-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: fixed.c 3662 2006-11-07 08:44:33Z thiadmer $ + */ + +#include +#include /* for NULL */ +#include "amx.h" + +/* + #if defined __BORLANDC__ + #pragma resource "amxFixed.res" + #endif +*/ + +#if !defined isdigit +# define isdigit(c) ((unsigned)((c)-'0')<10u) +#endif +#define MULTIPLIER 1000L /* 10^decimals */ + + +static cell AMX_NATIVE_CALL n_fixed(AMX *amx,const cell *params) +{ + (void)amx; + return params[1] * MULTIPLIER; +} + +static cell AMX_NATIVE_CALL n_strfixed(AMX *amx,const cell *params) +{ + char str[50],*ptr; + cell *cstr,intpart,decimals; + long multiplier,divisor; + int len,sign=1; + + amx_GetAddr(amx,params[1],&cstr); + amx_StrLen(cstr,&len); + if (len>=50) { + amx_RaiseError(amx,AMX_ERR_NATIVE); + return 0; + } /* if */ + amx_GetString(str,cstr,0,UNLIMITED); + ptr=str; + intpart=0; + decimals=0; + multiplier=MULTIPLIER; + divisor=1; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip whitespace */ + if (*ptr=='-') { /* handle sign */ + sign=-1; + ptr++; + } else if (*ptr=='+') { + ptr++; + } /* if */ + while (isdigit(*ptr)) { + intpart=intpart*10 + (*ptr-'0'); + ptr++; + } /* while */ + if (*ptr=='.') { + ptr++; + len=0; + while (isdigit(*ptr) && len<8) { + decimals=decimals*10 + (*ptr-'0'); + if (multiplier>1) + multiplier/=10; + else + divisor*=10; + ptr++; + len++; + } /* while */ + } /* if */ + return ((intpart*MULTIPLIER) + (decimals*multiplier+(divisor/2))/divisor) * sign; +} + +/* Some C/C++ compilers have problems with long lists of definitions, so + * I create another macro to fix this. + */ +#if PAWN_CELL_SIZE!=32 + /* the assembler and compiler-supported optimizations are only implemented + * for 32-bit cells + */ + #define USE_ANSI_C 1 + +#elif defined __WATCOMC__ && defined __386__ /* Watcom C/C++ */ + /* ANSI 64-bit division routine not needed for Watcom C/C++ because + * it uses inline assembler. + */ + #define USE_ANSI_C 0 + +#elif defined _MSC_VER && _MSC_VER>=9 && defined _WIN32 /* Microsoft C/C++ */ + /* ANSI 64-bit division routine not needed for Microsoft Visual C/C++ + * because it supports 64-bit integers + */ + #define USE_ANSI_C 0 + +#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && defined __32BIT__ /* Borland C++ v.5 */ + /* ANSI 64-bit division routine not needed for Borland C++ because it + * supports 64-bit integers + */ + #define USE_ANSI_C 0 + +#elif defined __GNUC__ /* GNU GCC */ + /* ANSI 64-bit division routine not needed for GNU GCC because it + * supports 64-bit integers + */ + #define USE_ANSI_C 0 + +#else + + #define USE_ANSI_C 1 + +#endif + +#if USE_ANSI_C + #define WORDSHIFT (PAWN_CELL_SIZE/2) + + #if PAWN_CELL_SIZE==32 + typedef unsigned short word_t; + #define LOWORD(v) (word_t)((v) & 0xffffu) + #define HIWORD(v) (word_t)(((v) >> 16) & 0xffffu) + #elif PAWN_CELL_SIZE==64 + typedef uint32_t word_t; + #define LOWORD(v) (word_t)((v) & 0xffffffffu) + #define HIWORD(v) (word_t)(((v) >> 32) & 0xffffffffu) + #else + #error Unsupported cell size + #endif + + static ucell div64_32(ucell t[2], ucell divisor) + { + /* This function was adapted from source code that appeared in + * Dr. Dobb's Journal, August 1992, page 117. + */ + ucell u, v; + word_t rHigh, rLow, dHigh, dLow; + + assert(divisor!=0); + /* if the divisor is smaller than t[1], the result will not fit in a cell */ + assert(divisor>=t[1]); + + dHigh=HIWORD(divisor); + dLow=LOWORD(divisor); + + /* Underestimate high half of quotient and subtract product + * of estimate and divisor from dividend. + */ + rHigh = (word_t)(t[1] / (dHigh + 1)); + u = (ucell)rHigh * (ucell)dLow; + v = (ucell)rHigh * (ucell)dHigh; + if ((t[0] -= (u << WORDSHIFT)) > ((ucell)-1L - (u << WORDSHIFT))) + t[1]--; + t[1] -= HIWORD(u); + t[1] -= v; + + /* Correct estimate. */ + while ((t[1] > (ucell)dHigh) || ((t[1] == (ucell)dHigh) && (t[0] >= ((ucell)dLow << WORDSHIFT)))) { + if ((t[0] -= ((ucell)dLow << WORDSHIFT)) > (ucell)-1L - ((ucell)dLow << WORDSHIFT)) + t[1]--; + t[1] -= dHigh; + rHigh++; + } /* while */ + /* Underestimate low half of quotient and subtract product of + * estimate and divisor from what remains of dividend. + */ + rLow = (word_t) ((ucell)((t[1] << WORDSHIFT) + HIWORD(t[0])) / (dHigh + 1)); + u = (ucell)rLow * (ucell)dLow; + v = (ucell)rLow * (ucell)dHigh; + if ((t[0] -= u) > ((ucell)-1L - u)) + t[1]--; + if ((t[0] -= (v << WORDSHIFT)) > ((ucell)-1L - (v << WORDSHIFT))) + t[1]--; + t[1] -= HIWORD(v); + + /* Correct estimate. */ + while ((t[1] > 0) || ((t[1] == 0) && t[0] >= divisor)) { + if ((t[0] -= divisor) > ((ucell)-1L - divisor)) + t[1]--; + rLow++; + } /* while */ + + return ((ucell)rHigh << WORDSHIFT) + rLow; + } +#endif + +static cell AMX_NATIVE_CALL n_fmul(AMX *amx,const cell *params) +{ +#if !USE_ANSI_C +#if defined __WATCOMC__ && defined __386__ + + cell __fmul(void); + cell a=params[1]; + cell b=params[2]; + #if MULTIPLIER != 1000 + #error Assembler chunks must be modified for a different base + #endif + #pragma aux __fmul = \ + "mov eax, [a]" \ + "mov ebx, 1000" \ + "imul [b]" \ + "add eax, 500" \ + "adc edx, 0" \ + "idiv ebx" \ + "mov [a], eax" \ + modify [eax ebx edx]; + __fmul(); + (void)amx; + return a; + +#elif _MSC_VER>=9 && defined _WIN32 + + __int64 a=(__int64)params[1] * (__int64)params[2]; + a=(a+MULTIPLIER/2) / MULTIPLIER; + (void)amx; + return (cell)a; + +#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) + + __int64 a=(__int64)params[1] * (__int64)params[2]; + a=(a+MULTIPLIER/2) / MULTIPLIER; + (void)amx; + return (cell)a; + +#elif defined __GNUC__ + + long long a=(long long)params[1] * (long long)params[2]; + a=(a+MULTIPLIER/2) / MULTIPLIER; + (void)amx; + return (cell)a; + +#else + #error Unsupported compiler configuration, but USE_ANSI_C is false +#endif + +#else // USE_ANSI_C + + /* (Xs * Ys) == (X*Y)ss, where "s" stands for scaled. + * The desired result is (X*Y)s, so we must unscale once. + * but we cannot do this before multiplication, because of loss + * of precision, and we cannot do it after the multiplication + * because of the possible overflow. + * The technique used here is to cut the multiplicands into + * components and to multiply these components separately: + * + * Assume Xs == (A << 16) + B and Ys == (C << 16) + D, where A, B, + * C and D are 16 bit numbers. + * + * A B + * C D + * --- * + * D*B + (D*A << 16) + (C*B << 16) + (C*A << (16+16)) + * + * Thus we have built a 64-bit number, which can now be scaled back + * to 32-bit by dividing by the scale factor. + */ + #define ADD_WRAP(var,carry,expr) (((var)+=(expr)), ((carry)+=((var)<(expr)) ? 1 : 0)) + ucell a,b,c,d; + ucell v[2]; + cell sign=1; + + (void)amx; + assert(MULTIPLIER<=(1L<=9 && defined _WIN32 + + __int64 a; + cell divisor=params[2]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; + return (cell)a; + +#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) + + __int64 a; + cell divisor=params[2]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; + return (cell)a; + +#elif defined __GNUC__ + + long long a; + cell divisor=params[2]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((long long)params[1] * (long long)MULTIPLIER + (long long)(divisor/2)) / (long long)divisor; + return (cell)a; + +#else + #error Unsupported compiler configuration, but USE_ANSI_C is false +#endif + +#else // USE_ANSI_C + + /* The dividend must be scaled prior to division. The dividend + * is a 32-bit number, however, so when shifted, it will become + * a value that no longer fits in a 32-bit variable. This routine + * does the division by using only 16-bit and 32-bit values, but + * with considerable effort. + * If your compiler supports 64-bit integers, modify this routine + * to use them. If your processor can do a simple 64-bit by 32-bit + * division in assembler, write assembler chunks. + * In other words: the straight C routine that follows is correct + * and portable, but use it only as a last resort. + * + * This function was adapted from source code that appeared in + * Dr. Dobb's Journal, August 1992, page 117. + */ + + cell dividend=params[1]; + cell divisor=params[2]; + cell sign=1; + ucell b[2]; + + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_NATIVE); + return 0; + } /* if */ + + /* make both operands positive values, but keep the sign of the result */ + if (dividend<0) { + dividend=-dividend; + sign=-sign; /* negate result */ + } /* if */ + if (divisor<0) { + divisor=-divisor; + sign=-sign; /* negate result */ + } /* if */ + + /* pre-scale the dividend into a 64-bit/128-bit number */ + b[0]=dividend*MULTIPLIER; + b[1]=(HIWORD(dividend)*MULTIPLIER) >> WORDSHIFT; + + /* add half of the divisor, to round the data */ + b[0]+=(ucell)divisor/2; + if (b[0]<(ucell)divisor/2) + b[1]+=1; /* wrap-around ocurred */ + + /* if the divisor is smaller than b[1], the result will not fit in a cell */ + if ((ucell)divisor=9 && defined _WIN32 + + __int64 a; + cell divisor=params[3]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; + return (cell)a; + +#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) + + __int64 a; + cell divisor=params[3]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; + return (cell)a; + +#elif defined __GNUC__ + + long long a; + cell divisor=params[3]; + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + a=((long long)params[1] * (long long)params[2] + (long long)(divisor/2)) / (long long)divisor; + return (cell)a; + +#else + #error Unsupported compiler configuration, but USE_ANSI_C is false +#endif + +#else // USE_ANSI_C + + ucell a,b,c,d; + ucell v[2]; + cell sign=1; + cell divisor=params[3]; + + assert(MULTIPLIER<=(1L<<16)); + + if (divisor==0) { + amx_RaiseError(amx,AMX_ERR_DIVIDE); + return 0; + } /* if */ + + /* make all three operands positive values, but keep the sign of the result */ + if (params[1]<0) { + ((cell*)params)[1]=-params[1]; + sign=-sign; /* negate result */ + } /* if */ + if (params[2]<0) { + ((cell*)params)[2]=-params[2]; + sign=-sign; /* negate result */ + } /* if */ + if (divisor<0) { + divisor=-divisor; + sign=-sign; /* negate result */ + } /* if */ + + a = HIWORD(params[1]); + b = LOWORD(params[1]); + c = HIWORD(params[2]); + d = LOWORD(params[2]); + + /* store the intermediate into a 64-bit/128-bit number */ + v[1]=c*a; + v[0]=d*b; + ADD_WRAP(v[0],v[1],d*a << WORDSHIFT); + ADD_WRAP(v[0],v[1],c*b << WORDSHIFT); + + /* add half of the divisor, to round the data */ + ADD_WRAP(v[0],v[1],(ucell)divisor/2); + + /* if the divisor is smaller than v[1], the result will not fit in a cell */ + if ((ucell)divisor=0 && (params[1] % MULTIPLIER)!=0) + value++; + break; + case 3: /* truncate: round down when > 0, round up when < 0 */ + value=params[1] / MULTIPLIER; + break; + case 4: /* round to even number when fractional part is exactly 0.5 */ + value=(params[1] + MULTIPLIER/2) / MULTIPLIER; + if ((params[1] % MULTIPLIER)==MULTIPLIER/2 && (value & 1)==1) + value--; + break; + default: /* standard (fractional part of 0.5 is rounded up */ + value=(params[1] + MULTIPLIER/2) / MULTIPLIER; + } /* switch */ + return value; +} + +/* Fixed:fsqroot(Fixed:value) */ +static cell AMX_NATIVE_CALL n_fsqroot(AMX *amx,const cell *params) +{ + cell value=params[1]; + cell low=0; + cell high=value; + cell mid[3]={8,0,0}; + + if (value<0) { + amx_RaiseError(amx, AMX_ERR_DOMAIN); + return 0; + } /* if */ + + while (high-low > 1) { + mid[1]=mid[2]=(low+high)/2; + if (n_fmul(amx,mid) < value) + low=mid[1]; + else + high=mid[1]; + } /* while */ + + /* check whether low or high mark comes closest */ + if (low!=high) { + cell deltalow, deltahigh; + mid[1]=mid[2]=low; + deltalow=value-n_fmul(amx,mid); + assert(deltalow>=0); + mid[1]=mid[2]=high; + deltahigh=n_fmul(amx,mid)-value; + assert(deltahigh>=0); + if (deltahigh<=deltalow) + low=high; /* return "high" mark (comes closer to the answer) */ + } /* if */ + + return low; +} + +/* Fixed: fpower(Fixed:value, exponent) + * note: x^n = exp(n*ln(x)), see http://yacas.sourceforge.net/Algochapter5.html + */ +static cell AMX_NATIVE_CALL n_fpower(AMX *amx,const cell *params) +{ + #define LIMIT32 146542L /* top value to calculate with extra digit, when a cell is 32-bit */ + cell result[3] = {8,0,0}; + int power=(int)params[2]; + int iter=1; + int reciprocal=0; + int isscaled=0; + + if (power<0) { + reciprocal=1; + power=-power; + } /* if */ + if (power==0) + return MULTIPLIER; + + /* quick squaring, to nearest power of 2 */ + result[1]=params[1]; + /* first try to do this with an extra digit of precision */ + if (result[1]=4); + result[1]*=10; /* scale to have an extra digit */ + isscaled=1; + while (2*iter<=power && result[1]power || result[1]>=LIMIT32*10); + if (result[1]>=LIMIT32*10) { + result[1]=(result[1]+5)/10; /* undo scaling */ + isscaled=0; + } /* if */ + while (2*iter<=power) { + iter*=2; + result[2]=result[1]; + result[1]=n_fmul(amx,result); + } /* while */ + + /* multilply with the remainder */ + if (iter=0) ? result : -result; +} + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO fixed_Natives[] = { + { "fixed", n_fixed }, + { "strfixed", n_strfixed }, + { "fmul", n_fmul }, + { "fdiv", n_fdiv }, + { "ffract", n_ffract }, + { "fround", n_fround }, + { "fmuldiv", n_fmuldiv }, + { "fsqroot", n_fsqroot }, + { "fpower", n_fpower }, + { "fabs", n_fabs }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_FixedInit(AMX *amx) +{ + return amx_Register(amx,fixed_Natives,-1); +} + +int AMXEXPORT amx_FixedCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} diff --git a/amx-deps/src/amx/fpattern.c b/amx-deps/src/amx/fpattern.c index 7715a1c..44e993f 100644 --- a/amx-deps/src/amx/fpattern.c +++ b/amx-deps/src/amx/fpattern.c @@ -1,906 +1,906 @@ -/* - from http://home.flash.net/~dtribble/text/fpattern.htm - 2000/12/12 - by Perry - License statement is in comments below -*/ - -/****************************************************************************** -* fpattern.c -* Functions for matching filename patterns to filenames. -* -* Usage -* (See "fpattern.h".) -* -* Notes -* These pattern matching capabilities are modeled after those found in -* the UNIX command shells. -* -* `DELIM' must be defined to 1 if pathname separators are to be handled -* explicitly. -* -* History -* 1.00 1997-01-03 David Tribble. -* First cut. -* 1.01 1997-01-03 David Tribble. -* Added SUB pattern character. -* Added fpattern_matchn(). -* 1.02 1997-01-12 David Tribble. -* Fixed missing lowercase matching cases. -* 1.03 1997-01-13 David Tribble. -* Pathname separator code is now controlled by DELIM macro. -* 1.04 1997-01-14 David Tribble. -* Added QUOTE macro. -* 1.05 1997-01-15 David Tribble. -* Handles special case of empty pattern and empty filename. -* 1.06 1997-01-26 David Tribble. -* Changed range negation character from '^' to '!', ala Unix. -* 1.07 1997-08-02 David Tribble. -* Uses the 'FPAT_XXX' constants. -* 1.08 1998-06-28 David Tribble. -* Minor fixed for MS-VC++ (5.0). -* -* Limitations -* This code is copyrighted by the author, but permission is hereby -* granted for its unlimited use provided that the original copyright -* and authorship notices are retained intact. -* -* Other queries can be sent to: -* dtribble@technologist.com -* david.tribble@beasys.com -* dtribble@flash.net -* -* Copyright 1997-1998 by David R. Tribble, all rights reserved. -* $Id: fpattern.c 3612 2006-07-22 09:59:46Z thiadmer $ -*/ - - -/* Identification */ -#if 0 -static const char id[] = - "@(#)lib/fpattern.c 1.08"; - -static const char copyright[] = - "Copyright 1997-1998 David R. Tribble\n"; -#endif - - -/* System includes */ - -#include -#include - -#if TEST -#include -#include -#include -#if defined _WIN32 || defined __WIN32__ || defined WIN32 -#include -#define sleep(q) Sleep(q*1000) -#include -#endif -#endif - -#if defined(unix) || defined(_unix) || defined(__unix) || \ - defined(__unix__) || ( defined(__MACH__) && defined(__APPLE_CC__) ) -#define UNIX 1 -#define DOS 0 -#elif defined(__MSDOS__) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -#define UNIX 0 -#define DOS 1 -#else -#error Cannot ascertain O/S from predefined macros -#endif - - -/* Local includes */ - -#include "fpattern.h" - - -/* Local constants */ - -#ifndef NULL -# define NULL ((void *) 0) -#endif - -#ifndef FALSE -# define FALSE 0 -#endif - -#ifndef TRUE -# define TRUE 1 -#endif - -#if TEST -# define SUB '~' -#else -# define SUB FPAT_CLOSP -#endif - -#ifndef DELIM -# define DELIM 0 -#endif - -#ifndef SUBCLOS -# define SUBCLOS 0 -#endif - -#define DEL FPAT_DEL - -#if UNIX -#define DEL2 FPAT_DEL -#else /*DOS*/ -#define DEL2 FPAT_DEL2 -#endif - -#if UNIX -#define QUOTE FPAT_QUOTE -#else /*DOS*/ -#define QUOTE FPAT_QUOTE2 -#endif - - -/* Local function macros */ - -/* Warning, while this code was originally filename search code - * which was case insensitive on DOS and case sensitive on UNIX - * it's being used for name searching and should behave the same everywhere - */ -static int lowercase(int c, int keepcase) -{ - if (keepcase) - return c; - return tolower(c); -} - -/*----------------------------------------------------------------------------- -* fpattern_isvalid() -* Checks that filename pattern 'pat' is a well-formed pattern. -* -* Returns -* 1 (true) if 'pat' is a valid filename pattern, otherwise 0 (false). -* -* Caveats -* If 'pat' is null, 0 (false) is returned. -* -* If 'pat' is empty (""), 1 (true) is returned, and it is considered a -* valid (but degenerate) pattern (the only filename it matches is the -* empty ("") string). -*/ - -int fpattern_isvalid(const char *pat) -{ - int len; - char close; - - /* Check args */ - if (pat == NULL) - return (FALSE); - - /* Verify that the pattern is valid */ - for (len = 0; pat[len] != '\0'; len++) - { - switch (pat[len]) - { - case FPAT_SET_L: - case FPAT_MSET_L: - /* Char set */ - close = (char)((pat[len] == FPAT_SET_L) ? FPAT_SET_R : FPAT_MSET_R); - len++; - if (pat[len] == FPAT_SET_NOT) - len++; /* Set negation */ - - while (pat[len] != close) - { - if (pat[len] == QUOTE) - len++; /* Quoted char */ - if (pat[len] == '\0') - return (FALSE); /* Missing closing bracket */ - len++; - - if (pat[len] == FPAT_SET_THRU) - { - /* Char range */ - len++; - if (pat[len] == QUOTE) - len++; /* Quoted char */ - if (pat[len] == '\0') - return (FALSE); /* Missing closing bracket */ - len++; - } - - if (pat[len] == '\0') - return (FALSE); /* Missing closing bracket */ - } - break; - - case QUOTE: - /* Quoted char */ - len++; - if (pat[len] == '\0') - return (FALSE); /* Missing quoted char */ - break; - - case FPAT_NOT: - /* Negated pattern */ - len++; - if (pat[len] == '\0') - return (FALSE); /* Missing subpattern */ - break; - - default: - /* Valid character */ - break; - } - } - - return (TRUE); -} - - -/*----------------------------------------------------------------------------- -* fpattern_submatch() -* Attempts to match subpattern 'pat' to subfilename 'fname'. -* -* Returns -* 1 (true) if the subfilename matches, otherwise 0 (false). -* -* Caveats -* This does not assume that 'pat' is well-formed. -* -* If 'pat' is empty (""), the only filename it matches is the empty ("") -* string. -* -* Some non-empty patterns (e.g., "") will match an empty filename (""). -*/ - -static int fpattern_submatch(const char *pat, const char *fname, int flength, int keepcase) -{ - int fch; - int pch; - int i; - int yes, match; - int lo, hi; - - /* Attempt to match subpattern against subfilename */ - while (*pat != '\0') - { - fch = *fname; - pch = *pat; - pat++; - - switch (pch) - { - case FPAT_ANY: - /* Match a single char */ - #if DELIM - if (fch == DEL || fch == DEL2 || fch == '\0') - return (FALSE); - #else - if (flength == 0) - return (FALSE); - #endif - fname++; - flength--; - break; - - case FPAT_CLOS: - /* Match zero or more chars */ - #if DELIM - i = 0; - while (fname[i] != '\0' && - fname[i] != DEL && fname[i] != DEL2) - i++; - #else - i = flength; - #endif - while (i >= 0) - { - if (fpattern_submatch(pat, fname+i, flength-i, keepcase)) - return (TRUE); - i--; - } - return (FALSE); - - #if SUBCLOS - case SUB: - /* Match zero or more chars */ - i = 0; - while (i < flength && - #if DELIM - fname[i] != DEL && fname[i] != DEL2 && - #endif - fname[i] != '.') - i++; - while (i >= 0) - { - if (fpattern_submatch(pat, fname+i, flength-i, keepcase)) - return (TRUE); - i--; - } - return (FALSE); - #endif - - case QUOTE: - /* Match a quoted char */ - pch = *pat; - if (lowercase(fch, keepcase) != lowercase(pch, keepcase) || pch == '\0') - return (FALSE); - fname++; - flength--; - pat++; - break; - - case FPAT_SET_L: - /* Match char set/range */ - yes = TRUE; - if (*pat == FPAT_SET_NOT) - { - pat++; - yes = FALSE; /* Set negation */ - } - - /* Look for [s], [-], [abc], [a-c] */ - match = !yes; - while (*pat != FPAT_SET_R && *pat != '\0') - { - if (*pat == QUOTE) - pat++; /* Quoted char */ - - if (*pat == '\0') - break; - lo = *pat++; - hi = lo; - - if (*pat == FPAT_SET_THRU) - { - /* Range */ - pat++; - - if (*pat == QUOTE) - pat++; /* Quoted char */ - - if (*pat == '\0') - break; - hi = *pat++; - } - - if (*pat == '\0') - break; - - /* Compare character to set range */ - if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) && - lowercase(fch, keepcase) <= lowercase(hi, keepcase)) - match = yes; - } - - if (!match) - return (FALSE); - - if (*pat == '\0') - return (FALSE); /* Missing closing bracket */ - - fname++; - flength--; - pat++; - break; - - case FPAT_MSET_L: - /* Match zero or more characters in a char set/range */ - yes = TRUE; - if (*pat == FPAT_SET_NOT) - { - pat++; - yes = FALSE; /* Set negation */ - } - - do { - const char *org_pat = pat; - /* Look for [s], [-], [abc], [a-c] */ - match = !yes; - while (*pat != FPAT_MSET_R && *pat != '\0') - { - if (*pat == QUOTE) - pat++; /* Quoted char */ - - if (*pat == '\0') - break; - lo = *pat++; - hi = lo; - - if (*pat == FPAT_SET_THRU) - { - /* Range */ - pat++; - - if (*pat == QUOTE) - pat++; /* Quoted char */ - - if (*pat == '\0') - break; - hi = *pat++; - } - - if (*pat == '\0') - break; - - /* Compare character to set range */ - if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) && - lowercase(fch, keepcase) <= lowercase(hi, keepcase)) { - match = yes; - while (*pat != FPAT_MSET_R && *pat != '\0') - pat++; - break; - } /* if */ - } - - if (*pat == '\0') - return (FALSE); /* Missing closing bracket */ - - if (match) { - fname++; - flength--; - fch = *fname; - if (flength > 0) - pat = org_pat; - } - - } while (match && flength > 0); - - pat++; - break; - - case FPAT_NOT: - /* Match only if rest of pattern does not match */ - if (*pat == '\0') - return (FALSE); /* Missing subpattern */ - i = fpattern_submatch(pat, fname, flength, keepcase); - return !i; - -#if DELIM - case DEL: - #if DEL2 != DEL - case DEL2: - #endif - /* Match path delimiter char */ - if (fch != DEL && fch != DEL2) - return (FALSE); - fname++; - flength--; - break; -#endif - - default: - /* Match a (non-null) char exactly */ - if (lowercase(fch, keepcase) != lowercase(pch, keepcase)) - return (FALSE); - fname++; - flength--; - break; - } - } - - /* Check for complete match */ - if (flength != 0) - return (FALSE); - - /* Successful match */ - return (TRUE); -} - - -/*----------------------------------------------------------------------------- -* fpattern_match() -* Attempts to match pattern 'pat' to filename 'fname'. The comparison is case -* sensitive if 'keepcase' is true, and case insensitive otherwise. The 'flength' -* parameter allows to check partial strings, or to check strings with embedded -* zero bytes. When 'flength' is -1, it is set to the string length. -* -* Returns -* 1 (true) if the filename matches, otherwise 0 (false). -* -* Caveats -* If 'fname' is null, zero (false) is returned. -* -* If 'pat' is null, zero (false) is returned. -* -* If 'pat' is empty (""), the only filename it matches is the empty -* string (""). -* -* If 'fname' is empty, the only pattern that will match it is the empty -* string (""). -* -* If 'pat' is not a well-formed pattern, zero (false) is returned. -* -* Upper and lower case letters are treated the same; alphabetic -* characters are converted to lower case before matching occurs. -* Conversion to lower case is dependent upon the current locale setting. -*/ - -int fpattern_match(const char *pat, const char *fname, int flength, int keepcase) -{ - int rc; - - /* Check args */ - if (fname == NULL) - return (FALSE); - - if (pat == NULL) - return (FALSE); - - /* Verify that the pattern is valid, and get its length */ - if (!fpattern_isvalid(pat)) - return (FALSE); - - /* Attempt to match pattern against filename */ - if (flength < 0) - flength = strlen(fname); - if (flength == 0) - return (pat[0] == '\0'); /* Special case */ - rc = fpattern_submatch(pat, fname, flength, keepcase); - - return (rc); -} - - -/*----------------------------------------------------------------------------- -* fpattern_matchn() -* Attempts to match pattern 'pat' to filename 'fname'. -* This operates like fpattern_match() except that it does not verify that -* pattern 'pat' is well-formed, assuming that it has been checked by a -* prior call to fpattern_isvalid(). -* -* Returns -* 1 (true) if the filename matches, otherwise 0 (false). -* -* Caveats -* If 'fname' is null, zero (false) is returned. -* -* If 'pat' is null, zero (false) is returned. -* -* If 'pat' is empty (""), the only filename it matches is the empty ("") -* string. -* -* If 'pat' is not a well-formed pattern, unpredictable results may occur. -* -* Upper and lower case letters are treated the same; alphabetic -* characters are converted to lower case before matching occurs. -* Conversion to lower case is dependent upon the current locale setting. -* -* See also -* fpattern_match(). -*/ - -int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase) -{ - int rc; - - /* Check args */ - if (fname == NULL) - return (FALSE); - - if (pat == NULL) - return (FALSE); - - /* Assume that pattern is well-formed */ - - /* Attempt to match pattern against filename */ - if (flength < 0) - flength = strlen(fname); - rc = fpattern_submatch(pat, fname, flength, keepcase); - - return (rc); -} - -/* returns the largest packet that matches the pattern */ -int fpattern_matchcount(const char *pat, const char *fname, int flength, int keepcase) -{ - int len; - - if (fname == NULL) - return (FALSE); - - if (pat == NULL) - return (FALSE); - - /* Assume that pattern is well-formed */ - - /* Attempt to match pattern against filename */ - if (flength < 0) - flength = strlen(fname); - - for (len = flength; len >= 0; len--) - if (fpattern_submatch(pat, fname, flength, keepcase)) - break; - - return len; -} - - -/*---------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ - -#if TEST - - -/* Local variables */ - -static int count = 0; -static int fails = 0; -static int stop_on_fail = FALSE; - - -/*----------------------------------------------------------------------------- -* test() -*/ - -static void test(int expect, const char *fname, const char *pat) -{ - int failed; - int result; - char fbuf[80+1]; - char pbuf[80+1]; - - count++; - printf("%3d. ", count); - - if (fname == NULL) - { - printf("\n"); - } - else - { - strcpy(fbuf, fname); - printf("\"%s\"\n", fbuf); - } - - if (pat == NULL) - { - printf(" \n"); - } - else - { - strcpy(pbuf, pat); - printf(" \"%s\"\n", pbuf); - } - - result = fpattern_match(pat == NULL ? NULL : pbuf, - fname == NULL ? NULL : fbuf, - -1, FALSE); - - failed = (result != expect); - printf(" -> %c, expected %c: %s\n", - "FT"[!!result], "FT"[!!expect], failed ? "FAIL ***" : "pass"); - - if (failed) - { - fails++; - - if (stop_on_fail) - exit(1); - sleep(1); - } - - printf("\n"); -} - - -/*----------------------------------------------------------------------------- -* main() -* Test driver. -*/ - -int main(int argc, char **argv) -{ - (void) argc; /* Shut up lint */ - (void) argv; /* Shut up lint */ - -#if DEBUG - dbg_f = stdout; -#endif - - printf("==========================================\n"); - - setlocale(LC_CTYPE, ""); - -#if UNIX - printf("[O/S is UNIX]\n"); -#elif DOS - printf("[O/S is DOS]\n"); -#else - printf("[O/S is unknown]\n"); -#endif - -#if 1 /* Set to nonzero to stop on first failure */ - stop_on_fail = TRUE; -#endif - - test(0, NULL, NULL); - test(0, NULL, ""); - test(0, NULL, "abc"); - test(0, "", NULL); - test(0, "abc", NULL); - - test(1, "abc", "abc"); - test(0, "ab", "abc"); - test(0, "abcd", "abc"); - test(0, "Foo.txt", "Foo.x"); - test(1, "Foo.txt", "Foo.txt"); - test(1, "Foo.txt", "foo.txt"); - test(1, "FOO.txt", "foo.TXT"); - - test(1, "a", "?"); - test(1, "foo.txt", "f??.txt"); - test(1, "foo.txt", "???????"); - test(0, "foo.txt", "??????"); - test(0, "foo.txt", "????????"); - - test(1, "a", "`a"); - test(1, "AB", "a`b"); - test(0, "aa", "a`b"); - test(1, "a`x", "a``x"); - test(1, "a`x", "`a```x"); - test(1, "a*x", "a`*x"); - -#if DELIM - test(0, "", "/"); - test(0, "", "\\"); - test(1, "/", "/"); - test(1, "/", "\\"); - test(1, "\\", "/"); - test(1, "\\", "\\"); - - test(1, "a/b", "a/b"); - test(1, "a/b", "a\\b"); - - test(1, "/", "*/*"); - test(1, "foo/a.c", "f*/*.?"); - test(1, "foo/a.c", "*/*"); - test(0, "foo/a.c", "/*/*"); - test(0, "foo/a.c", "*/*/"); - - test(1, "/", "~/~"); - test(1, "foo/a.c", "f~/~.?"); - test(0, "foo/a.c", "~/~"); - test(1, "foo/abc", "~/~"); - test(0, "foo/a.c", "/~/~"); - test(0, "foo/a.c", "~/~/"); -#endif - - test(0, "", "*"); - test(1, "a", "*"); - test(1, "ab", "*"); - test(1, "abc", "**"); - test(1, "ab.c", "*.?"); - test(1, "ab.c", "*.*"); - test(1, "ab.c", "*?"); - test(1, "ab.c", "?*"); - test(1, "ab.c", "?*?"); - test(1, "ab.c", "?*?*"); - test(1, "ac", "a*c"); - test(1, "axc", "a*c"); - test(1, "ax-yyy.c", "a*c"); - test(1, "ax-yyy.c", "a*x-yyy.c"); - test(1, "axx/yyy.c", "a*x/*c"); - -#if SUBCLOS - test(0, "", "~"); - test(1, "a", "~"); - test(1, "ab", "~"); - test(1, "abc", "~~"); - test(1, "ab.c", "~.?"); - test(1, "ab.c", "~.~"); - test(0, "ab.c", "~?"); - test(0, "ab.c", "?~"); - test(0, "ab.c", "?~?"); - test(1, "ab.c", "?~.?"); - test(1, "ab.c", "?~?~"); - test(1, "ac", "a~c"); - test(1, "axc", "a~c"); - test(0, "ax-yyy.c", "a~c"); - test(1, "ax-yyyvc", "a~c"); - test(1, "ax-yyy.c", "a~x-yyy.c"); - test(0, "axx/yyy.c", "a~x/~c"); - test(1, "axx/yyyvc", "a~x/~c"); -#endif - - test(0, "a", "!"); - test(0, "a", "!a"); - test(1, "a", "!b"); - test(1, "abc", "!abb"); - test(0, "a", "!*"); - test(1, "abc", "!*.?"); - test(1, "abc", "!*.*"); - test(0, "", "!*"); /*!*/ - test(0, "", "!*?"); /*!*/ - test(0, "a", "!*?"); - test(0, "a", "a!*"); - test(1, "a", "a!?"); - test(1, "a", "a!*?"); - test(1, "ab", "*!?"); - test(1, "abc", "*!?"); - test(0, "ab", "?!?"); - test(1, "abc", "?!?"); - test(0, "a-b", "!a[-]b"); - test(0, "a-b", "!a[x-]b"); - test(0, "a=b", "!a[x-]b"); - test(0, "a-b", "!a[x`-]b"); - test(1, "a=b", "!a[x`-]b"); - test(0, "a-b", "!a[x---]b"); - test(1, "a=b", "!a[x---]b"); - - test(1, "abc", "a[b]c"); - test(1, "aBc", "a[b]c"); - test(1, "abc", "a[bB]c"); - test(1, "abc", "a[bcz]c"); - test(1, "azc", "a[bcz]c"); - test(0, "ab", "a[b]c"); - test(0, "ac", "a[b]c"); - test(0, "axc", "a[b]c"); - - test(0, "abc", "a[!b]c"); - test(0, "abc", "a[!bcz]c"); - test(0, "azc", "a[!bcz]c"); - test(0, "ab", "a[!b]c"); - test(0, "ac", "a[!b]c"); - test(1, "axc", "a[!b]c"); - test(1, "axc", "a[!bcz]c"); - - test(1, "a1z", "a[0-9]z"); - test(0, "a1", "a[0-9]z"); - test(0, "az", "a[0-9]z"); - test(0, "axz", "a[0-9]z"); - test(1, "a2z", "a[-0-9]z"); - test(1, "a-z", "a[-0-9]z"); - test(1, "a-b", "a[-]b"); - test(0, "a-b", "a[x-]b"); - test(0, "a=b", "a[x-]b"); - test(1, "a-b", "a[x`-]b"); - test(0, "a=b", "a[x`-]b"); - test(1, "a-b", "a[x---]b"); - test(0, "a=b", "a[x---]b"); - - test(0, "a0z", "a[!0-9]z"); - test(1, "aoz", "a[!0-9]z"); - test(0, "a1", "a[!0-9]z"); - test(0, "az", "a[!0-9]z"); - test(0, "a9Z", "a[!0-9]z"); - test(1, "acz", "a[!-0-9]z"); - test(0, "a7z", "a[!-0-9]z"); - test(0, "a-z", "a[!-0-9]z"); - test(0, "a-b", "a[!-]b"); - test(0, "a-b", "a[!x-]b"); - test(0, "a=b", "a[!x-]b"); - test(0, "a-b", "a[!x`-]b"); - test(1, "a=b", "a[!x`-]b"); - test(0, "a-b", "a[!x---]b"); - test(1, "a=b", "a[!x---]b"); - - test(1, "a!z", "a[`!0-9]z"); - test(1, "a3Z", "a[`!0-9]z"); - test(0, "A3Z", "a[`!0`-9]z"); - test(1, "a9z", "a[`!0`-9]z"); - test(1, "a-z", "a[`!0`-9]z"); - - test(1, "ac", "a{b}c"); - test(1, "abc", "a{b}c"); - test(1, "abbc", "a{b}c"); - test(1, "aBbBc", "a{b}c"); - test(1, "abc", "a{bB}c"); - test(1, "abc", "a{bpz}c"); - test(1, "azc", "a{bcz}"); - test(0, "ab", "a{b}c"); - test(0, "axc", "a{b}c"); - - printf("%d tests, %d failures\n", count, fails); - return (fails == 0 ? 0 : 1); -} - - -#endif /* TEST */ - -/* End fpattern.c */ - - +/* + from http://home.flash.net/~dtribble/text/fpattern.htm + 2000/12/12 + by Perry + License statement is in comments below +*/ + +/****************************************************************************** +* fpattern.c +* Functions for matching filename patterns to filenames. +* +* Usage +* (See "fpattern.h".) +* +* Notes +* These pattern matching capabilities are modeled after those found in +* the UNIX command shells. +* +* `DELIM' must be defined to 1 if pathname separators are to be handled +* explicitly. +* +* History +* 1.00 1997-01-03 David Tribble. +* First cut. +* 1.01 1997-01-03 David Tribble. +* Added SUB pattern character. +* Added fpattern_matchn(). +* 1.02 1997-01-12 David Tribble. +* Fixed missing lowercase matching cases. +* 1.03 1997-01-13 David Tribble. +* Pathname separator code is now controlled by DELIM macro. +* 1.04 1997-01-14 David Tribble. +* Added QUOTE macro. +* 1.05 1997-01-15 David Tribble. +* Handles special case of empty pattern and empty filename. +* 1.06 1997-01-26 David Tribble. +* Changed range negation character from '^' to '!', ala Unix. +* 1.07 1997-08-02 David Tribble. +* Uses the 'FPAT_XXX' constants. +* 1.08 1998-06-28 David Tribble. +* Minor fixed for MS-VC++ (5.0). +* +* Limitations +* This code is copyrighted by the author, but permission is hereby +* granted for its unlimited use provided that the original copyright +* and authorship notices are retained intact. +* +* Other queries can be sent to: +* dtribble@technologist.com +* david.tribble@beasys.com +* dtribble@flash.net +* +* Copyright 1997-1998 by David R. Tribble, all rights reserved. +* $Id: fpattern.c 3612 2006-07-22 09:59:46Z thiadmer $ +*/ + + +/* Identification */ +#if 0 +static const char id[] = + "@(#)lib/fpattern.c 1.08"; + +static const char copyright[] = + "Copyright 1997-1998 David R. Tribble\n"; +#endif + + +/* System includes */ + +#include +#include + +#if TEST +#include +#include +#include +#if defined _WIN32 || defined __WIN32__ || defined WIN32 +#include +#define sleep(q) Sleep(q*1000) +#include +#endif +#endif + +#if defined(unix) || defined(_unix) || defined(__unix) || \ + defined(__unix__) || ( defined(__MACH__) && defined(__APPLE_CC__) ) +#define UNIX 1 +#define DOS 0 +#elif defined(__MSDOS__) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#define UNIX 0 +#define DOS 1 +#else +#error Cannot ascertain O/S from predefined macros +#endif + + +/* Local includes */ + +#include "fpattern.h" + + +/* Local constants */ + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#if TEST +# define SUB '~' +#else +# define SUB FPAT_CLOSP +#endif + +#ifndef DELIM +# define DELIM 0 +#endif + +#ifndef SUBCLOS +# define SUBCLOS 0 +#endif + +#define DEL FPAT_DEL + +#if UNIX +#define DEL2 FPAT_DEL +#else /*DOS*/ +#define DEL2 FPAT_DEL2 +#endif + +#if UNIX +#define QUOTE FPAT_QUOTE +#else /*DOS*/ +#define QUOTE FPAT_QUOTE2 +#endif + + +/* Local function macros */ + +/* Warning, while this code was originally filename search code + * which was case insensitive on DOS and case sensitive on UNIX + * it's being used for name searching and should behave the same everywhere + */ +static int lowercase(int c, int keepcase) +{ + if (keepcase) + return c; + return tolower(c); +} + +/*----------------------------------------------------------------------------- +* fpattern_isvalid() +* Checks that filename pattern 'pat' is a well-formed pattern. +* +* Returns +* 1 (true) if 'pat' is a valid filename pattern, otherwise 0 (false). +* +* Caveats +* If 'pat' is null, 0 (false) is returned. +* +* If 'pat' is empty (""), 1 (true) is returned, and it is considered a +* valid (but degenerate) pattern (the only filename it matches is the +* empty ("") string). +*/ + +int fpattern_isvalid(const char *pat) +{ + int len; + char close; + + /* Check args */ + if (pat == NULL) + return (FALSE); + + /* Verify that the pattern is valid */ + for (len = 0; pat[len] != '\0'; len++) + { + switch (pat[len]) + { + case FPAT_SET_L: + case FPAT_MSET_L: + /* Char set */ + close = (char)((pat[len] == FPAT_SET_L) ? FPAT_SET_R : FPAT_MSET_R); + len++; + if (pat[len] == FPAT_SET_NOT) + len++; /* Set negation */ + + while (pat[len] != close) + { + if (pat[len] == QUOTE) + len++; /* Quoted char */ + if (pat[len] == '\0') + return (FALSE); /* Missing closing bracket */ + len++; + + if (pat[len] == FPAT_SET_THRU) + { + /* Char range */ + len++; + if (pat[len] == QUOTE) + len++; /* Quoted char */ + if (pat[len] == '\0') + return (FALSE); /* Missing closing bracket */ + len++; + } + + if (pat[len] == '\0') + return (FALSE); /* Missing closing bracket */ + } + break; + + case QUOTE: + /* Quoted char */ + len++; + if (pat[len] == '\0') + return (FALSE); /* Missing quoted char */ + break; + + case FPAT_NOT: + /* Negated pattern */ + len++; + if (pat[len] == '\0') + return (FALSE); /* Missing subpattern */ + break; + + default: + /* Valid character */ + break; + } + } + + return (TRUE); +} + + +/*----------------------------------------------------------------------------- +* fpattern_submatch() +* Attempts to match subpattern 'pat' to subfilename 'fname'. +* +* Returns +* 1 (true) if the subfilename matches, otherwise 0 (false). +* +* Caveats +* This does not assume that 'pat' is well-formed. +* +* If 'pat' is empty (""), the only filename it matches is the empty ("") +* string. +* +* Some non-empty patterns (e.g., "") will match an empty filename (""). +*/ + +static int fpattern_submatch(const char *pat, const char *fname, int flength, int keepcase) +{ + int fch; + int pch; + int i; + int yes, match; + int lo, hi; + + /* Attempt to match subpattern against subfilename */ + while (*pat != '\0') + { + fch = *fname; + pch = *pat; + pat++; + + switch (pch) + { + case FPAT_ANY: + /* Match a single char */ + #if DELIM + if (fch == DEL || fch == DEL2 || fch == '\0') + return (FALSE); + #else + if (flength == 0) + return (FALSE); + #endif + fname++; + flength--; + break; + + case FPAT_CLOS: + /* Match zero or more chars */ + #if DELIM + i = 0; + while (fname[i] != '\0' && + fname[i] != DEL && fname[i] != DEL2) + i++; + #else + i = flength; + #endif + while (i >= 0) + { + if (fpattern_submatch(pat, fname+i, flength-i, keepcase)) + return (TRUE); + i--; + } + return (FALSE); + + #if SUBCLOS + case SUB: + /* Match zero or more chars */ + i = 0; + while (i < flength && + #if DELIM + fname[i] != DEL && fname[i] != DEL2 && + #endif + fname[i] != '.') + i++; + while (i >= 0) + { + if (fpattern_submatch(pat, fname+i, flength-i, keepcase)) + return (TRUE); + i--; + } + return (FALSE); + #endif + + case QUOTE: + /* Match a quoted char */ + pch = *pat; + if (lowercase(fch, keepcase) != lowercase(pch, keepcase) || pch == '\0') + return (FALSE); + fname++; + flength--; + pat++; + break; + + case FPAT_SET_L: + /* Match char set/range */ + yes = TRUE; + if (*pat == FPAT_SET_NOT) + { + pat++; + yes = FALSE; /* Set negation */ + } + + /* Look for [s], [-], [abc], [a-c] */ + match = !yes; + while (*pat != FPAT_SET_R && *pat != '\0') + { + if (*pat == QUOTE) + pat++; /* Quoted char */ + + if (*pat == '\0') + break; + lo = *pat++; + hi = lo; + + if (*pat == FPAT_SET_THRU) + { + /* Range */ + pat++; + + if (*pat == QUOTE) + pat++; /* Quoted char */ + + if (*pat == '\0') + break; + hi = *pat++; + } + + if (*pat == '\0') + break; + + /* Compare character to set range */ + if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) && + lowercase(fch, keepcase) <= lowercase(hi, keepcase)) + match = yes; + } + + if (!match) + return (FALSE); + + if (*pat == '\0') + return (FALSE); /* Missing closing bracket */ + + fname++; + flength--; + pat++; + break; + + case FPAT_MSET_L: + /* Match zero or more characters in a char set/range */ + yes = TRUE; + if (*pat == FPAT_SET_NOT) + { + pat++; + yes = FALSE; /* Set negation */ + } + + do { + const char *org_pat = pat; + /* Look for [s], [-], [abc], [a-c] */ + match = !yes; + while (*pat != FPAT_MSET_R && *pat != '\0') + { + if (*pat == QUOTE) + pat++; /* Quoted char */ + + if (*pat == '\0') + break; + lo = *pat++; + hi = lo; + + if (*pat == FPAT_SET_THRU) + { + /* Range */ + pat++; + + if (*pat == QUOTE) + pat++; /* Quoted char */ + + if (*pat == '\0') + break; + hi = *pat++; + } + + if (*pat == '\0') + break; + + /* Compare character to set range */ + if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) && + lowercase(fch, keepcase) <= lowercase(hi, keepcase)) { + match = yes; + while (*pat != FPAT_MSET_R && *pat != '\0') + pat++; + break; + } /* if */ + } + + if (*pat == '\0') + return (FALSE); /* Missing closing bracket */ + + if (match) { + fname++; + flength--; + fch = *fname; + if (flength > 0) + pat = org_pat; + } + + } while (match && flength > 0); + + pat++; + break; + + case FPAT_NOT: + /* Match only if rest of pattern does not match */ + if (*pat == '\0') + return (FALSE); /* Missing subpattern */ + i = fpattern_submatch(pat, fname, flength, keepcase); + return !i; + +#if DELIM + case DEL: + #if DEL2 != DEL + case DEL2: + #endif + /* Match path delimiter char */ + if (fch != DEL && fch != DEL2) + return (FALSE); + fname++; + flength--; + break; +#endif + + default: + /* Match a (non-null) char exactly */ + if (lowercase(fch, keepcase) != lowercase(pch, keepcase)) + return (FALSE); + fname++; + flength--; + break; + } + } + + /* Check for complete match */ + if (flength != 0) + return (FALSE); + + /* Successful match */ + return (TRUE); +} + + +/*----------------------------------------------------------------------------- +* fpattern_match() +* Attempts to match pattern 'pat' to filename 'fname'. The comparison is case +* sensitive if 'keepcase' is true, and case insensitive otherwise. The 'flength' +* parameter allows to check partial strings, or to check strings with embedded +* zero bytes. When 'flength' is -1, it is set to the string length. +* +* Returns +* 1 (true) if the filename matches, otherwise 0 (false). +* +* Caveats +* If 'fname' is null, zero (false) is returned. +* +* If 'pat' is null, zero (false) is returned. +* +* If 'pat' is empty (""), the only filename it matches is the empty +* string (""). +* +* If 'fname' is empty, the only pattern that will match it is the empty +* string (""). +* +* If 'pat' is not a well-formed pattern, zero (false) is returned. +* +* Upper and lower case letters are treated the same; alphabetic +* characters are converted to lower case before matching occurs. +* Conversion to lower case is dependent upon the current locale setting. +*/ + +int fpattern_match(const char *pat, const char *fname, int flength, int keepcase) +{ + int rc; + + /* Check args */ + if (fname == NULL) + return (FALSE); + + if (pat == NULL) + return (FALSE); + + /* Verify that the pattern is valid, and get its length */ + if (!fpattern_isvalid(pat)) + return (FALSE); + + /* Attempt to match pattern against filename */ + if (flength < 0) + flength = strlen(fname); + if (flength == 0) + return (pat[0] == '\0'); /* Special case */ + rc = fpattern_submatch(pat, fname, flength, keepcase); + + return (rc); +} + + +/*----------------------------------------------------------------------------- +* fpattern_matchn() +* Attempts to match pattern 'pat' to filename 'fname'. +* This operates like fpattern_match() except that it does not verify that +* pattern 'pat' is well-formed, assuming that it has been checked by a +* prior call to fpattern_isvalid(). +* +* Returns +* 1 (true) if the filename matches, otherwise 0 (false). +* +* Caveats +* If 'fname' is null, zero (false) is returned. +* +* If 'pat' is null, zero (false) is returned. +* +* If 'pat' is empty (""), the only filename it matches is the empty ("") +* string. +* +* If 'pat' is not a well-formed pattern, unpredictable results may occur. +* +* Upper and lower case letters are treated the same; alphabetic +* characters are converted to lower case before matching occurs. +* Conversion to lower case is dependent upon the current locale setting. +* +* See also +* fpattern_match(). +*/ + +int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase) +{ + int rc; + + /* Check args */ + if (fname == NULL) + return (FALSE); + + if (pat == NULL) + return (FALSE); + + /* Assume that pattern is well-formed */ + + /* Attempt to match pattern against filename */ + if (flength < 0) + flength = strlen(fname); + rc = fpattern_submatch(pat, fname, flength, keepcase); + + return (rc); +} + +/* returns the largest packet that matches the pattern */ +int fpattern_matchcount(const char *pat, const char *fname, int flength, int keepcase) +{ + int len; + + if (fname == NULL) + return (FALSE); + + if (pat == NULL) + return (FALSE); + + /* Assume that pattern is well-formed */ + + /* Attempt to match pattern against filename */ + if (flength < 0) + flength = strlen(fname); + + for (len = flength; len >= 0; len--) + if (fpattern_submatch(pat, fname, flength, keepcase)) + break; + + return len; +} + + +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +#if TEST + + +/* Local variables */ + +static int count = 0; +static int fails = 0; +static int stop_on_fail = FALSE; + + +/*----------------------------------------------------------------------------- +* test() +*/ + +static void test(int expect, const char *fname, const char *pat) +{ + int failed; + int result; + char fbuf[80+1]; + char pbuf[80+1]; + + count++; + printf("%3d. ", count); + + if (fname == NULL) + { + printf("\n"); + } + else + { + strcpy(fbuf, fname); + printf("\"%s\"\n", fbuf); + } + + if (pat == NULL) + { + printf(" \n"); + } + else + { + strcpy(pbuf, pat); + printf(" \"%s\"\n", pbuf); + } + + result = fpattern_match(pat == NULL ? NULL : pbuf, + fname == NULL ? NULL : fbuf, + -1, FALSE); + + failed = (result != expect); + printf(" -> %c, expected %c: %s\n", + "FT"[!!result], "FT"[!!expect], failed ? "FAIL ***" : "pass"); + + if (failed) + { + fails++; + + if (stop_on_fail) + exit(1); + sleep(1); + } + + printf("\n"); +} + + +/*----------------------------------------------------------------------------- +* main() +* Test driver. +*/ + +int main(int argc, char **argv) +{ + (void) argc; /* Shut up lint */ + (void) argv; /* Shut up lint */ + +#if DEBUG + dbg_f = stdout; +#endif + + printf("==========================================\n"); + + setlocale(LC_CTYPE, ""); + +#if UNIX + printf("[O/S is UNIX]\n"); +#elif DOS + printf("[O/S is DOS]\n"); +#else + printf("[O/S is unknown]\n"); +#endif + +#if 1 /* Set to nonzero to stop on first failure */ + stop_on_fail = TRUE; +#endif + + test(0, NULL, NULL); + test(0, NULL, ""); + test(0, NULL, "abc"); + test(0, "", NULL); + test(0, "abc", NULL); + + test(1, "abc", "abc"); + test(0, "ab", "abc"); + test(0, "abcd", "abc"); + test(0, "Foo.txt", "Foo.x"); + test(1, "Foo.txt", "Foo.txt"); + test(1, "Foo.txt", "foo.txt"); + test(1, "FOO.txt", "foo.TXT"); + + test(1, "a", "?"); + test(1, "foo.txt", "f??.txt"); + test(1, "foo.txt", "???????"); + test(0, "foo.txt", "??????"); + test(0, "foo.txt", "????????"); + + test(1, "a", "`a"); + test(1, "AB", "a`b"); + test(0, "aa", "a`b"); + test(1, "a`x", "a``x"); + test(1, "a`x", "`a```x"); + test(1, "a*x", "a`*x"); + +#if DELIM + test(0, "", "/"); + test(0, "", "\\"); + test(1, "/", "/"); + test(1, "/", "\\"); + test(1, "\\", "/"); + test(1, "\\", "\\"); + + test(1, "a/b", "a/b"); + test(1, "a/b", "a\\b"); + + test(1, "/", "*/*"); + test(1, "foo/a.c", "f*/*.?"); + test(1, "foo/a.c", "*/*"); + test(0, "foo/a.c", "/*/*"); + test(0, "foo/a.c", "*/*/"); + + test(1, "/", "~/~"); + test(1, "foo/a.c", "f~/~.?"); + test(0, "foo/a.c", "~/~"); + test(1, "foo/abc", "~/~"); + test(0, "foo/a.c", "/~/~"); + test(0, "foo/a.c", "~/~/"); +#endif + + test(0, "", "*"); + test(1, "a", "*"); + test(1, "ab", "*"); + test(1, "abc", "**"); + test(1, "ab.c", "*.?"); + test(1, "ab.c", "*.*"); + test(1, "ab.c", "*?"); + test(1, "ab.c", "?*"); + test(1, "ab.c", "?*?"); + test(1, "ab.c", "?*?*"); + test(1, "ac", "a*c"); + test(1, "axc", "a*c"); + test(1, "ax-yyy.c", "a*c"); + test(1, "ax-yyy.c", "a*x-yyy.c"); + test(1, "axx/yyy.c", "a*x/*c"); + +#if SUBCLOS + test(0, "", "~"); + test(1, "a", "~"); + test(1, "ab", "~"); + test(1, "abc", "~~"); + test(1, "ab.c", "~.?"); + test(1, "ab.c", "~.~"); + test(0, "ab.c", "~?"); + test(0, "ab.c", "?~"); + test(0, "ab.c", "?~?"); + test(1, "ab.c", "?~.?"); + test(1, "ab.c", "?~?~"); + test(1, "ac", "a~c"); + test(1, "axc", "a~c"); + test(0, "ax-yyy.c", "a~c"); + test(1, "ax-yyyvc", "a~c"); + test(1, "ax-yyy.c", "a~x-yyy.c"); + test(0, "axx/yyy.c", "a~x/~c"); + test(1, "axx/yyyvc", "a~x/~c"); +#endif + + test(0, "a", "!"); + test(0, "a", "!a"); + test(1, "a", "!b"); + test(1, "abc", "!abb"); + test(0, "a", "!*"); + test(1, "abc", "!*.?"); + test(1, "abc", "!*.*"); + test(0, "", "!*"); /*!*/ + test(0, "", "!*?"); /*!*/ + test(0, "a", "!*?"); + test(0, "a", "a!*"); + test(1, "a", "a!?"); + test(1, "a", "a!*?"); + test(1, "ab", "*!?"); + test(1, "abc", "*!?"); + test(0, "ab", "?!?"); + test(1, "abc", "?!?"); + test(0, "a-b", "!a[-]b"); + test(0, "a-b", "!a[x-]b"); + test(0, "a=b", "!a[x-]b"); + test(0, "a-b", "!a[x`-]b"); + test(1, "a=b", "!a[x`-]b"); + test(0, "a-b", "!a[x---]b"); + test(1, "a=b", "!a[x---]b"); + + test(1, "abc", "a[b]c"); + test(1, "aBc", "a[b]c"); + test(1, "abc", "a[bB]c"); + test(1, "abc", "a[bcz]c"); + test(1, "azc", "a[bcz]c"); + test(0, "ab", "a[b]c"); + test(0, "ac", "a[b]c"); + test(0, "axc", "a[b]c"); + + test(0, "abc", "a[!b]c"); + test(0, "abc", "a[!bcz]c"); + test(0, "azc", "a[!bcz]c"); + test(0, "ab", "a[!b]c"); + test(0, "ac", "a[!b]c"); + test(1, "axc", "a[!b]c"); + test(1, "axc", "a[!bcz]c"); + + test(1, "a1z", "a[0-9]z"); + test(0, "a1", "a[0-9]z"); + test(0, "az", "a[0-9]z"); + test(0, "axz", "a[0-9]z"); + test(1, "a2z", "a[-0-9]z"); + test(1, "a-z", "a[-0-9]z"); + test(1, "a-b", "a[-]b"); + test(0, "a-b", "a[x-]b"); + test(0, "a=b", "a[x-]b"); + test(1, "a-b", "a[x`-]b"); + test(0, "a=b", "a[x`-]b"); + test(1, "a-b", "a[x---]b"); + test(0, "a=b", "a[x---]b"); + + test(0, "a0z", "a[!0-9]z"); + test(1, "aoz", "a[!0-9]z"); + test(0, "a1", "a[!0-9]z"); + test(0, "az", "a[!0-9]z"); + test(0, "a9Z", "a[!0-9]z"); + test(1, "acz", "a[!-0-9]z"); + test(0, "a7z", "a[!-0-9]z"); + test(0, "a-z", "a[!-0-9]z"); + test(0, "a-b", "a[!-]b"); + test(0, "a-b", "a[!x-]b"); + test(0, "a=b", "a[!x-]b"); + test(0, "a-b", "a[!x`-]b"); + test(1, "a=b", "a[!x`-]b"); + test(0, "a-b", "a[!x---]b"); + test(1, "a=b", "a[!x---]b"); + + test(1, "a!z", "a[`!0-9]z"); + test(1, "a3Z", "a[`!0-9]z"); + test(0, "A3Z", "a[`!0`-9]z"); + test(1, "a9z", "a[`!0`-9]z"); + test(1, "a-z", "a[`!0`-9]z"); + + test(1, "ac", "a{b}c"); + test(1, "abc", "a{b}c"); + test(1, "abbc", "a{b}c"); + test(1, "aBbBc", "a{b}c"); + test(1, "abc", "a{bB}c"); + test(1, "abc", "a{bpz}c"); + test(1, "azc", "a{bcz}"); + test(0, "ab", "a{b}c"); + test(0, "axc", "a{b}c"); + + printf("%d tests, %d failures\n", count, fails); + return (fails == 0 ? 0 : 1); +} + + +#endif /* TEST */ + +/* End fpattern.c */ + + diff --git a/amx-deps/src/amx/fpattern.h b/amx-deps/src/amx/fpattern.h index d014282..7ab090d 100644 --- a/amx-deps/src/amx/fpattern.h +++ b/amx-deps/src/amx/fpattern.h @@ -1,187 +1,187 @@ -/****************************************************************************** -* fpattern.h -* Functions for matching filename patterns to filenames. -* -* Usage -* Filename patterns are composed of regular (printable) characters which -* may comprise a filename as well as special pattern matching characters: -* -* . Matches a period (.). -* Note that a period in a filename is not treated any -* differently than any other character. -* -* ? Any. -* Matches any single character except '/' or '\'. -* -* * Closure. -* Matches zero or more occurences of any characters other -* than '/' or '\'. -* Leading '*' characters are allowed. -* -* SUB Substitute (^Z); optionally supported. -* Similar to '*', this matches zero or more occurences of -* any characters other than '/', '\', or '.'. -* Leading '^Z' characters are allowed. -* -* [ab] Set. -* Matches the single character 'a' or 'b'. -* If the dash '-' character is to be included, it must -* immediately follow the opening bracket '['. -* If the closing bracket ']' character is to be included, -* it must be preceded by a quote '`'. -* -* [a-z] Range. -* Matches a single character in the range 'a' to 'z'. -* Ranges and sets may be combined within the same set of -* brackets. -* -* [!R] Exclusive range. -* Matches a single character not in the range 'R'. -* If range 'R' includes the dash '-' character, the dash -* must immediately follow the caret '!'. -* -* ! Not. -* Makes the following pattern (up to the next '/') match -* any filename except those what it would normally match. -* -* / Path separator (UNIX and DOS). -* Matches a '/' or '\' pathname (directory) separator. -* Multiple separators are treated like a single -* separator. -* A leading separator indicates an absolute pathname. -* -* \ Path separator (DOS). -* Same as the '/' character. -* Note that this character must be escaped if used within -* string constants ("\\"). -* -* \ Quote (UNIX). -* Makes the next character a regular (nonspecial) -* character. -* Note that to match the quote character itself, it must -* be quoted. -* Note that this character must be escaped if used within -* string constants ("\\"). -* -* ` Quote (DOS). -* Makes the next character a regular (nonspecial) -* character. -* Note that to match the quote character itself, it must -* be quoted. -* -* Upper and lower case alphabetic characters are considered identical, -* i.e., 'a' and 'A' match each other. -* (What constitutes a lowercase letter depends on the current locale -* settings.) -* -* Spaces and control characters are treated as normal characters. -* -* Examples -* The following patterns in the left column will match the filenames in -* the middle column and will not match filenames in the right column: -* -* Pattern Will Match Will Not Match -* ------- ---------- -------------- -* a a (only) (anything else) -* a. a. (only) (anything else) -* a?c abc, acc, arc, a.c a, ac, abbc -* a*c ac, abc, abbc, acc, a.c a, ab, acb, bac -* a* a, ab, abb, a., a.b b, ba -* * a, ab, abb, a., .foo, a.foo (nothing) -* *. a., ab., abb., a.foo. a, ab, a.foo, .foo -* *.* a., a.b, ah.bc.foo a -* ^Z a, ab, abb a., .foo, a.foo -* ^Z. a., ab., abb. a, .foo, a.foo -* ^Z.* a, a., .foo, a.foo ab, abb -* *2.c 2.c, 12.c, foo2.c, foo.12.c 2x.c -* a[b-z]c abc, acc, azc (only) (anything else) -* [ab0-9]x ax, bx, 0x, 9x zx -* a[-.]b a-b, a.b (only) (anything else) -* a[!a-z]b a0b, a.b, a@b aab, azb, aa0b -* a[!-b]x a0x, a+x, acx a-x, abx, axxx -* a[-!b]x a-x, a!x, abx (only) (anything else) -* a[`]]x a]x (only) (anything else) -* a``x a`x (only) (anything else) -* oh`! oh! (only) (anything else) -* is`?it is?it (only) (anything else) -* !a?c a, ac, ab, abb, acb, a.foo abc, a.c, azc -* -* History -* 1.00 1997-01-03 David Tribble. -* First cut. -* 1.01 1997-01-03 David Tribble. -* Added '^Z' pattern character. -* Added fpattern_matchn(). -* 1.02 1997-01-26 David Tribble. -* Changed range negation character from '^' to '!', ala Unix. -* 1.03 1997-08-02 David Tribble. -* Added 'FPAT_XXX' macro constants. -* -* Limitations -* This code is copyrighted by the author, but permission is hereby -* granted for its unlimited use provided that the original copyright -* and authorship notices are retained intact. -* -* Other queries can be sent to: -* dtribble@technologist.com -* david.tribble@beasys.com -* dtribble@flash.net -* -* Copyright ©1997 by David R. Tribble, all rights reserved. -* $Id: fpattern.h 3612 2006-07-22 09:59:46Z thiadmer $ -*/ - - -#ifndef fpattern_h -#define fpattern_h 1 - -#ifdef __cplusplus -extern "C" -{ -#endif - - -/* Identification */ - -#if 0 -static const char fpattern_h_id[] = "@(#)lib/fpattern.h 1.03"; -#endif - - -/* Manifest constants */ - -#define FPAT_QUOTE '\\' /* Quotes a special char */ -#define FPAT_QUOTE2 '`' /* Quotes a special char */ -#define FPAT_DEL '/' /* Path delimiter (used only when DELIM is true) */ -#define FPAT_DEL2 '\\' /* Path delimiter (used only when DELIM is true) */ -#define FPAT_DOT '.' /* Dot char */ -#define FPAT_NOT '!' /* Exclusion */ -#define FPAT_ANY '?' /* Any one char */ -#define FPAT_CLOS '*' /* Zero or more chars */ -#define FPAT_CLOSP '\x1A' /* Zero or more nondelimiters (used only when SUBCLOS is true) */ -#define FPAT_SET_L '[' /* Set/range open bracket */ -#define FPAT_SET_R ']' /* Set/range close bracket */ -#define FPAT_MSET_L '{' /* Multi-set/range open bracket */ -#define FPAT_MSET_R '}' /* Multi-set/range close bracket*/ -#define FPAT_SET_NOT '!' /* Set exclusion */ -#define FPAT_SET_THRU '-' /* Set range of chars */ - - -/* Model-dependent aliases */ -/* omitted */ - - -/* Public functions */ - -extern int fpattern_isvalid(const char *pat); -extern int fpattern_match(const char *pat, const char *fname, int flength, int keepcase); -extern int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase); - - -#ifdef __cplusplus -} -#endif - -#endif /* fpattern_h */ - -/* End fpattern.h */ +/****************************************************************************** +* fpattern.h +* Functions for matching filename patterns to filenames. +* +* Usage +* Filename patterns are composed of regular (printable) characters which +* may comprise a filename as well as special pattern matching characters: +* +* . Matches a period (.). +* Note that a period in a filename is not treated any +* differently than any other character. +* +* ? Any. +* Matches any single character except '/' or '\'. +* +* * Closure. +* Matches zero or more occurences of any characters other +* than '/' or '\'. +* Leading '*' characters are allowed. +* +* SUB Substitute (^Z); optionally supported. +* Similar to '*', this matches zero or more occurences of +* any characters other than '/', '\', or '.'. +* Leading '^Z' characters are allowed. +* +* [ab] Set. +* Matches the single character 'a' or 'b'. +* If the dash '-' character is to be included, it must +* immediately follow the opening bracket '['. +* If the closing bracket ']' character is to be included, +* it must be preceded by a quote '`'. +* +* [a-z] Range. +* Matches a single character in the range 'a' to 'z'. +* Ranges and sets may be combined within the same set of +* brackets. +* +* [!R] Exclusive range. +* Matches a single character not in the range 'R'. +* If range 'R' includes the dash '-' character, the dash +* must immediately follow the caret '!'. +* +* ! Not. +* Makes the following pattern (up to the next '/') match +* any filename except those what it would normally match. +* +* / Path separator (UNIX and DOS). +* Matches a '/' or '\' pathname (directory) separator. +* Multiple separators are treated like a single +* separator. +* A leading separator indicates an absolute pathname. +* +* \ Path separator (DOS). +* Same as the '/' character. +* Note that this character must be escaped if used within +* string constants ("\\"). +* +* \ Quote (UNIX). +* Makes the next character a regular (nonspecial) +* character. +* Note that to match the quote character itself, it must +* be quoted. +* Note that this character must be escaped if used within +* string constants ("\\"). +* +* ` Quote (DOS). +* Makes the next character a regular (nonspecial) +* character. +* Note that to match the quote character itself, it must +* be quoted. +* +* Upper and lower case alphabetic characters are considered identical, +* i.e., 'a' and 'A' match each other. +* (What constitutes a lowercase letter depends on the current locale +* settings.) +* +* Spaces and control characters are treated as normal characters. +* +* Examples +* The following patterns in the left column will match the filenames in +* the middle column and will not match filenames in the right column: +* +* Pattern Will Match Will Not Match +* ------- ---------- -------------- +* a a (only) (anything else) +* a. a. (only) (anything else) +* a?c abc, acc, arc, a.c a, ac, abbc +* a*c ac, abc, abbc, acc, a.c a, ab, acb, bac +* a* a, ab, abb, a., a.b b, ba +* * a, ab, abb, a., .foo, a.foo (nothing) +* *. a., ab., abb., a.foo. a, ab, a.foo, .foo +* *.* a., a.b, ah.bc.foo a +* ^Z a, ab, abb a., .foo, a.foo +* ^Z. a., ab., abb. a, .foo, a.foo +* ^Z.* a, a., .foo, a.foo ab, abb +* *2.c 2.c, 12.c, foo2.c, foo.12.c 2x.c +* a[b-z]c abc, acc, azc (only) (anything else) +* [ab0-9]x ax, bx, 0x, 9x zx +* a[-.]b a-b, a.b (only) (anything else) +* a[!a-z]b a0b, a.b, a@b aab, azb, aa0b +* a[!-b]x a0x, a+x, acx a-x, abx, axxx +* a[-!b]x a-x, a!x, abx (only) (anything else) +* a[`]]x a]x (only) (anything else) +* a``x a`x (only) (anything else) +* oh`! oh! (only) (anything else) +* is`?it is?it (only) (anything else) +* !a?c a, ac, ab, abb, acb, a.foo abc, a.c, azc +* +* History +* 1.00 1997-01-03 David Tribble. +* First cut. +* 1.01 1997-01-03 David Tribble. +* Added '^Z' pattern character. +* Added fpattern_matchn(). +* 1.02 1997-01-26 David Tribble. +* Changed range negation character from '^' to '!', ala Unix. +* 1.03 1997-08-02 David Tribble. +* Added 'FPAT_XXX' macro constants. +* +* Limitations +* This code is copyrighted by the author, but permission is hereby +* granted for its unlimited use provided that the original copyright +* and authorship notices are retained intact. +* +* Other queries can be sent to: +* dtribble@technologist.com +* david.tribble@beasys.com +* dtribble@flash.net +* +* Copyright ©1997 by David R. Tribble, all rights reserved. +* $Id: fpattern.h 3612 2006-07-22 09:59:46Z thiadmer $ +*/ + + +#ifndef fpattern_h +#define fpattern_h 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* Identification */ + +#if 0 +static const char fpattern_h_id[] = "@(#)lib/fpattern.h 1.03"; +#endif + + +/* Manifest constants */ + +#define FPAT_QUOTE '\\' /* Quotes a special char */ +#define FPAT_QUOTE2 '`' /* Quotes a special char */ +#define FPAT_DEL '/' /* Path delimiter (used only when DELIM is true) */ +#define FPAT_DEL2 '\\' /* Path delimiter (used only when DELIM is true) */ +#define FPAT_DOT '.' /* Dot char */ +#define FPAT_NOT '!' /* Exclusion */ +#define FPAT_ANY '?' /* Any one char */ +#define FPAT_CLOS '*' /* Zero or more chars */ +#define FPAT_CLOSP '\x1A' /* Zero or more nondelimiters (used only when SUBCLOS is true) */ +#define FPAT_SET_L '[' /* Set/range open bracket */ +#define FPAT_SET_R ']' /* Set/range close bracket */ +#define FPAT_MSET_L '{' /* Multi-set/range open bracket */ +#define FPAT_MSET_R '}' /* Multi-set/range close bracket*/ +#define FPAT_SET_NOT '!' /* Set exclusion */ +#define FPAT_SET_THRU '-' /* Set range of chars */ + + +/* Model-dependent aliases */ +/* omitted */ + + +/* Public functions */ + +extern int fpattern_isvalid(const char *pat); +extern int fpattern_match(const char *pat, const char *fname, int flength, int keepcase); +extern int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase); + + +#ifdef __cplusplus +} +#endif + +#endif /* fpattern_h */ + +/* End fpattern.h */ diff --git a/amx-deps/src/amx/minGlue.h b/amx-deps/src/amx/minGlue.h new file mode 100644 index 0000000..36dd5e2 --- /dev/null +++ b/amx-deps/src/amx/minGlue.h @@ -0,0 +1,31 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/amx-deps/src/amx/minIni.c b/amx-deps/src/amx/minIni.c new file mode 100644 index 0000000..2ca0aaa --- /dev/null +++ b/amx-deps/src/amx/minIni.c @@ -0,0 +1,878 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * These routines are in part based on the article "Multiplatform .INI Files" + * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. + * + * Copyright (c) CompuPhase, 2008-2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.c 5690 2017-06-08 14:04:08Z thiadmer $ + */ + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#define MININI_IMPLEMENTATION +#include "minIni.h" +#if defined NDEBUG + #if defined assert + #undef assert + #endif + #define assert(e) +#else + #include +#endif + +#if !defined __T || defined INI_ANSIONLY + #include + #include + #include + #define TCHAR char + #define __T(s) s + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsicmp stricmp + #define _tcslen strlen + #define _tcsncmp strncmp + #define _tcsnicmp strnicmp + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tcstod strtod + #define _totupper toupper + #define _stprintf sprintf + #define _tfgets fgets + #define _tfputs fputs + #define _tfopen fopen + #define _tremove remove + #define _trename rename +#endif + +#if defined __linux || defined __linux__ + #define __LINUX__ +#elif defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#elif defined(_MSC_VER) + #pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */ +#endif +#if !defined strnicmp && !defined PORTABLE_STRNICMP + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define strnicmp strncasecmp + #endif +#endif +#if !defined _totupper + #include + #define _totupper toupper +#endif + +#if !defined INI_LINETERM + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define INI_LINETERM __T("\n") + #else + #define INI_LINETERM __T("\r\n") + #endif +#endif +#if !defined INI_FILETYPE + #error Missing definition for INI_FILETYPE. +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +enum quote_option { + QUOTE_NONE, + QUOTE_ENQUOTE, + QUOTE_DEQUOTE, +}; + +#if defined PORTABLE_STRNICMP +int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n) +{ + while (n-- != 0 && (*s1 || *s2)) { + register int c1, c2; + c1 = *s1++; + if ('a' <= c1 && c1 <= 'z') + c1 += ('A' - 'a'); + c2 = *s2++; + if ('a' <= c2 && c2 <= 'z') + c2 += ('A' - 'a'); + if (c1 != c2) + return c1 - c2; + } /* while */ + return 0; +} +#endif /* PORTABLE_STRNICMP */ + +static TCHAR *skipleading(const TCHAR *str) +{ + assert(str != NULL); + while ('\0' < *str && *str <= ' ') + str++; + return (TCHAR *)str; +} + +static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base) +{ + assert(str != NULL); + assert(base != NULL); + while (str > base && '\0' < *(str-1) && *(str-1) <= ' ') + str--; + return (TCHAR *)str; +} + +static TCHAR *striptrailing(TCHAR *str) +{ + TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); + assert(ptr != NULL); + *ptr = '\0'; + return str; +} + +static TCHAR *save_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option) +{ + size_t d, s; + + assert(maxlen>0); + assert(dest <= source || dest >= source + maxlen); + if (option == QUOTE_ENQUOTE && maxlen < 3) + option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ + + switch (option) { + case QUOTE_NONE: + for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++) + dest[d] = source[d]; + assert(d < maxlen); + dest[d] = '\0'; + break; + case QUOTE_ENQUOTE: + d = 0; + dest[d++] = '"'; + for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { + if (source[s] == '"') { + if (d >= maxlen - 3) + break; /* no space to store the escape character plus the one that follows it */ + dest[d++] = '\\'; + } /* if */ + dest[d] = source[s]; + } /* for */ + dest[d++] = '"'; + dest[d] = '\0'; + break; + case QUOTE_DEQUOTE: + for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { + if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') + s++; + dest[d] = source[s]; + } /* for */ + dest[d] = '\0'; + break; + default: + assert(0); + } /* switch */ + + return dest; +} + +static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes) +{ + int isstring; + TCHAR *ep; + + assert(string != NULL); + assert(quotes != NULL); + + /* Remove a trailing comment */ + isstring = 0; + for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { + if (*ep == '"') { + if (*(ep + 1) == '"') + ep++; /* skip "" (both quotes) */ + else + isstring = !isstring; /* single quote, toggle isstring */ + } else if (*ep == '\\' && *(ep + 1) == '"') { + ep++; /* skip \" (both quotes */ + } /* if */ + } /* for */ + assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); + *ep = '\0'; /* terminate at a comment */ + striptrailing(string); + /* Remove double quotes surrounding a value */ + *quotes = QUOTE_NONE; + if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') { + string++; + *--ep = '\0'; + *quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ + } /* if */ + return string; +} + +static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, + int idxSection, int idxKey, TCHAR *Buffer, int BufferSize, + INI_FILEPOS *mark) +{ + TCHAR *sp, *ep; + int len, idx; + enum quote_option quotes; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + + assert(fp != NULL); + /* Move through file 1 line at a time until a section is matched or EOF. If + * parameter Section is NULL, only look at keys above the first section. If + * idxSection is postive, copy the relevant section name. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0 || idxSection >= 0) { + assert(Section != NULL); + idx = -1; + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) + return 0; + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); + if (idxSection >= 0) { + if (idx == idxSection) { + assert(ep != NULL); + assert(*ep == ']'); + *ep = '\0'; + save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more section found */ + } /* if */ + } /* if */ + + /* Now that the section has been found, find the entry. + * Stop searching upon leaving the section's area. + */ + assert(Key != NULL || idxKey >= 0); + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + idx = -1; + do { + if (mark != NULL) + ini_tell(fp, mark); /* optionally keep the mark to the start of the line */ + if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') + return 0; + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + } while (*sp == ';' || *sp == '#' || ep == NULL + || ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); + if (idxKey >= 0) { + if (idx == idxKey) { + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + *ep = '\0'; + striptrailing(sp); + save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more key found (in this section) */ + } /* if */ + + /* Copy up to BufferSize chars to buffer */ + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + sp = skipleading(ep + 1); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + save_strncpy(Buffer, sp, BufferSize, quotes); + return 1; +} + +/** ini_gets() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default string in the event of a failed read + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, + TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || Key == NULL) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + save_strncpy(Buffer, (DefValue != NULL) ? DefValue : __T(""), BufferSize, QUOTE_NONE); + return (int)_tcslen(Buffer); +} + +/** ini_getl() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue + : ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16) + : _tcstol(LocalBuffer, NULL, 10)); +} + +#if defined INI_REAL +/** ini_getf() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue : ini_atof(LocalBuffer); +} +#endif + +/** ini_getbool() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default value in the event of a failed read; it should + * zero (0) or one (1). + * \param Filename the name and full path of the .ini file to read from + * + * A true boolean is found if one of the following is matched: + * - A string starting with 'y' or 'Y' + * - A string starting with 't' or 'T' + * - A string starting with '1' + * + * A false boolean is found if one of the following is matched: + * - A string starting with 'n' or 'N' + * - A string starting with 'f' or 'F' + * - A string starting with '0' + * + * \return the true/false flag as interpreted at Key + */ +int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[2] = __T(""); + int ret; + + ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + LocalBuffer[0] = (TCHAR)_totupper((int)LocalBuffer[0]); + if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T') + ret = 1; + else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F') + ret = 0; + else + ret = DefValue; + + return(ret); +} + +/** ini_getsection() + * \param idx the zero-based sequence number of the section to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + +/** ini_getkey() + * \param Section the name of the section to browse through, or NULL to + * browse through the keys outside any section + * \param idx the zero-based sequence number of the key to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + + +#if !defined INI_NOBROWSE +/** ini_browse() + * \param Callback a pointer to a function that will be called for every + * setting in the INI file. + * \param UserData arbitrary data, which the function passes on the the + * \c Callback function + * \param Filename the name and full path of the .ini file to read from + * + * \return 1 on success, 0 on failure (INI file not found) + * + * \note The \c Callback function must return 1 to continue + * browsing through the INI file, or 0 to stop. Even when the + * callback stops the browsing, this function will return 1 + * (for success). + */ +int ini_browse(INI_CALLBACK Callback, const void *UserData, const TCHAR *Filename) +{ + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int lenSec, lenKey; + enum quote_option quotes; + INI_FILETYPE fp; + + if (Callback == NULL) + return 0; + if (!ini_openread(Filename, &fp)) + return 0; + + LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */ + lenSec = (int)_tcslen(LocalBuffer) + 1; + for ( ;; ) { + TCHAR *sp, *ep; + if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp)) + break; + sp = skipleading(LocalBuffer + lenSec); + /* ignore empty strings and comments */ + if (*sp == '\0' || *sp == ';' || *sp == '#') + continue; + /* see whether we reached a new section */ + ep = _tcsrchr(sp, ']'); + if (*sp == '[' && ep != NULL) { + *ep = '\0'; + save_strncpy(LocalBuffer, sp + 1, INI_BUFFERSIZE, QUOTE_NONE); + lenSec = (int)_tcslen(LocalBuffer) + 1; + continue; + } /* if */ + /* not a new section, test for a key/value pair */ + ep = _tcschr(sp, '='); /* test for the equal sign or colon */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + if (ep == NULL) + continue; /* invalid line, ignore */ + *ep++ = '\0'; /* split the key from the value */ + striptrailing(sp); + save_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE); + lenKey = (int)_tcslen(LocalBuffer + lenSec) + 1; + /* clean up the value */ + sp = skipleading(ep); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + save_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes); + /* call the callback */ + if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData)) + break; + } /* for */ + + (void)ini_close(&fp); + return 1; +} +#endif /* INI_NOBROWSE */ + +#if ! defined INI_READONLY +static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength) +{ + TCHAR *p; + + save_strncpy(dest, source, maxlength, QUOTE_NONE); + p = _tcsrchr(dest, '\0'); + assert(p != NULL); + *(p - 1) = '~'; +} + +static enum quote_option check_enquote(const TCHAR *Value) +{ + const TCHAR *p; + + /* run through the value, if it has trailing spaces, or '"', ';' or '#' + * characters, enquote it + */ + assert(Value != NULL); + for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) + /* nothing */; + return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; +} + +static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp) +{ + if (Section != NULL && _tcslen(Section) > 0) { + TCHAR *p; + LocalBuffer[0] = '['; + save_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = ']'; + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); + } /* if */ +} + +static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp) +{ + TCHAR *p; + enum quote_option option = check_enquote(Value); + save_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = '='; + save_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); +} + +static int cache_accum(const TCHAR *string, int *size, int max) +{ + int len = (int)_tcslen(string); + if (*size + len >= max) + return 0; + *size += len; + return 1; +} + +static int cache_flush(TCHAR *buffer, int *size, + INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark) +{ + int terminator_len = (int)_tcslen(INI_LINETERM); + int pos = 0; + + (void)ini_seek(rfp, mark); + assert(buffer != NULL); + buffer[0] = '\0'; + assert(size != NULL); + assert(*size <= INI_BUFFERSIZE); + while (pos < *size) { + (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp); + while (pos < *size && buffer[pos] != '\0') + pos++; /* cannot use _tcslen() because buffer may not be zero-terminated */ + } /* while */ + if (buffer[0] != '\0') { + assert(pos > 0 && pos <= INI_BUFFERSIZE); + if (pos == INI_BUFFERSIZE) + pos--; + buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */ + (void)ini_write(buffer, wfp); + } + ini_tell(rfp, mark); /* update mark */ + *size = 0; + /* return whether the buffer ended with a line termination */ + return (pos > terminator_len) && (_tcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0); +} + +static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer) +{ + (void)ini_close(rfp); + (void)ini_close(wfp); + (void)ini_remove(filename); + (void)ini_tempname(buffer, filename, INI_BUFFERSIZE); + (void)ini_rename(buffer, filename); + return 1; +} + +/** ini_puts() + * \param Section the name of the section to write the string in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value a pointer to the buffer the string, or NULL to erase the key + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + INI_FILEPOS mark; + INI_FILEPOS head, tail; + TCHAR *sp, *ep; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, flag, cachelen; + + assert(Filename != NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (Key != NULL && Value != NULL) { + if (!ini_openwrite(Filename, &wfp)) + return 0; + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + (void)ini_close(&wfp); + } /* if */ + return 1; + } /* if */ + + /* If parameters Key and Value are valid (so this is not an "erase" request) + * and the setting already exists, there are two short-cuts to avoid rewriting + * the INI file. + */ + if (Key != NULL && Value != NULL) { + ini_tell(&rfp, &mark); + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head); + if (match) { + /* if the current setting is identical to the one to write, there is + * nothing to do. + */ + if (_tcscmp(LocalBuffer,Value) == 0) { + (void)ini_close(&rfp); + return 1; + } /* if */ + /* if the new setting has the same length as the current setting, and the + * glue file permits file read/write access, we can modify in place. + */ + #if defined ini_openrewrite + /* we already have the start of the (raw) line, get the end too */ + ini_tell(&rfp, &tail); + /* create new buffer (without writing it to file) */ + writekey(LocalBuffer, Key, Value, NULL); + if (_tcslen(LocalBuffer) == (size_t)(tail - head)) { + /* length matches, close the file & re-open for read/write, then + * write at the correct position + */ + (void)ini_close(&rfp); + if (!ini_openrewrite(Filename, &wfp)) + return 0; + (void)ini_seek(&wfp, &head); + (void)ini_write(LocalBuffer, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + #endif + } /* if */ + /* key not found, or different value & length -> proceed (but rewind the + * input file first) + */ + (void)ini_seek(&rfp, &mark); + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) { + (void)ini_close(&rfp); + return 0; + } /* if */ + (void)ini_tell(&rfp, &mark); + cachelen = 0; + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for and this section must be removed + */ + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); + if (!match || Key != NULL) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } while (!match); + } /* if */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + /* when deleting a section, the section head that was just found has not been + * copied to the output file, but because this line was not "accumulated" in + * the cache, the position in the input file was reset to the point just + * before the section; this must now be skipped (again) + */ + if (Key == NULL) { + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + (void)ini_tell(&rfp, &mark); + } /* if */ + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry so make one */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); + if ((Key != NULL && match) || *sp == '[') + break; /* found the key, or found a new section */ + /* copy other keys in the section */ + if (Key == NULL) { + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + } else { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } /* for */ + /* the key was found, or we just dropped on the next section (meaning that it + * wasn't found); in both cases we need to write the key, but in the latter + * case, we also need to write the line starting the new section after writing + * the key + */ + flag = (*sp == '['); + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key != NULL && Value != NULL) + writekey(LocalBuffer, Key, Value, &wfp); + /* cache_flush() reset the "read pointer" to the start of the line with the + * previous key or the new section; read it again (because writekey() destroyed + * the buffer) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + if (flag) { + /* the new section heading needs to be copied to the output file */ + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } else { + /* forget the old key line */ + (void)ini_tell(&rfp, &mark); + } /* if */ + /* Copy the rest of the INI file */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* while */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ +} + +/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ +#define ABS(v) ((v) < 0 ? -(v) : (v)) + +static void strreverse(TCHAR *str) +{ + int i, j; + for (i = 0, j = (int)_tcslen(str) - 1; i < j; i++, j--) { + TCHAR t = str[i]; + str[i] = str[j]; + str[j] = t; + } /* for */ +} + +static void long2str(long value, TCHAR *str) +{ + int i = 0; + long sign = value; + + /* generate digits in reverse order */ + do { + int n = (int)(value % 10); /* get next lowest digit */ + str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */ + } while (value /= 10); /* delete the lowest digit */ + if (sign < 0) + str[i++] = '-'; + str[i] = '\0'; + + strreverse(str); +} + +/** ini_putl() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[32]; + long2str(Value, LocalBuffer); + return ini_puts(Section, Key, LocalBuffer, Filename); +} + +#if defined INI_REAL +/** ini_putf() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + ini_ftoa(LocalBuffer, Value); + return ini_puts(Section, Key, LocalBuffer, Filename); +} +#endif /* INI_REAL */ +#endif /* !INI_READONLY */ diff --git a/amx-deps/src/amx/minIni.h b/amx-deps/src/amx/minIni.h new file mode 100644 index 0000000..6ce11de --- /dev/null +++ b/amx-deps/src/amx/minIni.h @@ -0,0 +1,152 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * Copyright (c) CompuPhase, 2008-2012 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.h 5181 2015-01-21 09:44:28Z thiadmer $ + */ +#ifndef MININI_H +#define MININI_H + +#include "minGlue.h" + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY + #include + #define mTCHAR TCHAR +#else + /* force TCHAR to be "char", but only for minIni */ + #define mTCHAR char +#endif + +#if !defined INI_BUFFERSIZE + #define INI_BUFFERSIZE 512 +#endif + +#if defined __cplusplus + extern "C" { +#endif + +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); + +#if defined INI_REAL +INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename); +#endif + +#if !defined INI_READONLY +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); +#if defined INI_REAL +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); +#endif +#endif /* INI_READONLY */ + +#if !defined INI_NOBROWSE +typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const void *UserData); +int ini_browse(INI_CALLBACK Callback, const void *UserData, const mTCHAR *Filename); +#endif /* INI_NOBROWSE */ + +#if defined __cplusplus + } +#endif + + +#if defined __cplusplus + +#if defined __WXWINDOWS__ + #include "wxMinIni.h" +#else + #include + + /* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */ + class minIni + { + public: + minIni(const std::string& filename) : iniFilename(filename) + { } + + bool getbool(const std::string& Section, const std::string& Key, bool DefValue=false) const + { return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; } + + long getl(const std::string& Section, const std::string& Key, long DefValue=0) const + { return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } + + int geti(const std::string& Section, const std::string& Key, int DefValue=0) const + { return static_cast(this->getl(Section, Key, long(DefValue))); } + + std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const + { + char buffer[INI_BUFFERSIZE]; + ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + std::string getsection(int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + std::string getkey(const std::string& Section, int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + +#if defined INI_REAL + INI_REAL getf(const std::string& Section, const std::string& Key, INI_REAL DefValue=0) const + { return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } +#endif + +#if ! defined INI_READONLY + bool put(const std::string& Section, const std::string& Key, long Value) const + { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, int Value) const + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, bool Value) const + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, const std::string& Value) const + { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, const char* Value) const + { return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + +#if defined INI_REAL + bool put(const std::string& Section, const std::string& Key, INI_REAL Value) const + { return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } +#endif + + bool del(const std::string& Section, const std::string& Key) const + { return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; } + + bool del(const std::string& Section) const + { return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; } +#endif + + private: + std::string iniFilename; + }; + +#endif /* __WXWINDOWS__ */ +#endif /* __cplusplus */ + +#endif /* MININI_H */ diff --git a/amx-deps/src/amx/osdefs.h b/amx-deps/src/amx/osdefs.h index ef23ceb..ae9162a 100644 --- a/amx-deps/src/amx/osdefs.h +++ b/amx-deps/src/amx/osdefs.h @@ -1,148 +1,100 @@ -/* - * Platform - * __MSDOS__ set when compiling for DOS (not Windows) - * _Windows set when compiling for any version of Microsoft Windows - * __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode) - * __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows) - * __ECOS__ set if Pawn was included with the eCos with configtool - * __LINUX__ set when compiling for Linux - * - * Copyright 1998-2007, ITB CompuPhase, The Netherlands. - * No usage restrictions, no warranties. - */ - -#ifndef _OSDEFS_H -#define _OSDEFS_H - -/* Every compiler uses different "default" macros to indicate the mode - * it is in. Throughout the source, we use the Borland C++ macros, so - * the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to - * those of Borland C++. - */ -#if defined(__WATCOMC__) -# if defined(__WINDOWS__) || defined(__NT__) -# define _Windows 1 -# endif -# ifdef __386__ -# define __32BIT__ 1 -# endif -# if defined(_Windows) && defined(__32BIT__) -# define __WIN32__ 1 -# endif -#elif defined(_MSC_VER) -# if defined(_WINDOWS) || defined(_WIN32) -# define _Windows 1 -# endif -# ifdef _WIN32 -# define __WIN32__ 1 -# define __32BIT__ 1 -# endif -#endif - -#if defined __linux || defined __linux__ -# define __LINUX__ -#endif -/* To be able to eventually set __ECOS__, we have to find a symbol - * defined in a common place (so including the header file won't break - * anything for other platforms). includes - * and in this later file we can find CYGPKG_PAWN - * if the Pawn package was included with configtool and so we know - * that we are compiling for eCos. - */ -#include -#if defined CYGPKG_PAWN -# define __ECOS__ 1 -# define HAVE_ALLOCA_H 0 -#endif - - -#if defined __FreeBSD__ -# include -#elif defined __LINUX__ -# include -#elif defined __ECOS__ -# include -# define BIG_ENDIAN 4321 -# define LITTLE_ENDIAN 1234 -# if (CYG_BYTEORDER == CYG_LSBFIRST) -# define BYTE_ORDER LITTLE_ENDIAN -# else -# define BYTE_ORDER BIG_ENDIAN -# endif - /* - * eCos option management. - */ -# include -# if CYGPKG_PAWN_AMX_ANSIONLY==1 -# define AMX_ANSIONLY -# endif -# define PAWN_CELL_SIZE CYGPKG_PAWN_AMX_CELLSIZE -# if CYGPKG_PAWN_CORE_RANDOM==0 -# define AMX_NORANDOM -# endif -# if CYGPKG_PAWN_CORE_PROPERTY==0 -# define AMX_NOPROPLIST -# endif -# if CYGPKG_PAWN_AMX_CONS_FIXEDPOINT==1 -# define FIXEDPOINT -# endif -# if CYGPKG_PAWN_AMX_CONS_FLOATPOINT==1 -# define FLOATPOINT -# endif -#endif - -/* Linux now has these */ -#if !defined BIG_ENDIAN - #define BIG_ENDIAN 4321 -#endif -#if !defined LITTLE_ENDIAN - #define LITTLE_ENDIAN 1234 -#endif - -/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */ -#if !defined BYTE_ORDER - #if defined UCLINUX - #define BYTE_ORDER BIG_ENDIAN - #else - #define BYTE_ORDER LITTLE_ENDIAN - #endif -#endif - -#if defined __MSDOS__ || defined __WIN32__ || defined _Windows - #define DIRSEP_CHAR '\\' -#elif defined macintosh - #define DIRSEP_CHAR ':' -#else - #define DIRSEP_CHAR '/' /* directory separator character */ -#endif - -/* _MAX_PATH is sometimes called differently and it may be in limits.h or - * stdlib.h instead of stdio.h. - */ -#if !defined _MAX_PATH - /* not defined, perhaps stdio.h was not included */ - #if !defined PATH_MAX - #include - #endif - #if !defined _MAX_PATH && !defined PATH_MAX - /* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */ - #include - #endif - #if !defined _MAX_PATH && !defined PATH_MAX - /* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */ - #include - #endif - /* if _MAX_PATH is undefined, try common alternative names */ - #if !defined _MAX_PATH - #if defined MAX_PATH - #define _MAX_PATH MAX_PATH - #elif defined _POSIX_PATH_MAX - #define _MAX_PATH _POSIX_PATH_MAX - #else - /* everything failed, actually we have a problem here... */ - #define _MAX_PATH 1024 - #endif - #endif -#endif - -#endif /* _OSDEFS_H */ +/* __MSDOS__ set when compiling for DOS (not Windows) + * _Windows set when compiling for any version of Microsoft Windows + * __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode) + * __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows) + * + * Copyright 1998-2005, ITB CompuPhase, The Netherlands. + * info@compuphase.com. + */ + +#ifndef _OSDEFS_H +#define _OSDEFS_H + +/* Every compiler uses different "default" macros to indicate the mode + * it is in. Throughout the source, we use the Borland C++ macros, so + * the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to + * those of Borland C++. + */ +#if defined(__WATCOMC__) +# if defined(__WINDOWS__) || defined(__NT__) +# define _Windows 1 +# endif +# ifdef __386__ +# define __32BIT__ 1 +# endif +# if defined(_Windows) && defined(__32BIT__) +# define __WIN32__ 1 +# endif +#elif defined(_MSC_VER) +# if defined(_WINDOWS) || defined(_WIN32) +# define _Windows 1 +# endif +# ifdef _WIN32 +# define __WIN32__ 1 +# define __32BIT__ 1 +# endif +#endif + +#if defined __FreeBSD__ + #include +#elif defined __APPLE__ + #include +#elif defined LINUX + #include +#endif + +/* Linux NOW has these */ +#if !defined BIG_ENDIAN + #define BIG_ENDIAN 4321 +#endif +#if !defined LITTLE_ENDIAN + #define LITTLE_ENDIAN 1234 +#endif + +/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */ +#if !defined BYTE_ORDER + #if defined UCLINUX + #define BYTE_ORDER BIG_ENDIAN + #else + #define BYTE_ORDER LITTLE_ENDIAN + #endif +#endif + +#if defined __MSDOS__ || defined __WIN32__ || defined _Windows + #define DIRSEP_CHAR '\\' +#elif defined macintosh + #define DIRSEP_CHAR ':' +#else + #define DIRSEP_CHAR '/' /* directory separator character */ +#endif + +/* _MAX_PATH is sometimes called differently and it may be in limits.h or + * stdlib.h instead of stdio.h. + */ +#if !defined _MAX_PATH + /* not defined, perhaps stdio.h was not included */ + #if !defined PATH_MAX + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */ + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */ + #include + #endif + /* if _MAX_PATH is undefined, try common alternative names */ + #if !defined _MAX_PATH + #if defined MAX_PATH + #define _MAX_PATH MAX_PATH + #elif defined _POSIX_PATH_MAX + #define _MAX_PATH _POSIX_PATH_MAX + #else + /* everything failed, actually we have a problem here... */ + #define _MAX_PATH 1024 + #endif + #endif +#endif + +#endif /* _OSDEFS_H */ diff --git a/amx-deps/src/amx/pawndbg b/amx-deps/src/amx/pawndbg deleted file mode 100644 index cf01342..0000000 Binary files a/amx-deps/src/amx/pawndbg and /dev/null differ diff --git a/amx-deps/src/amx/pawndbg.c b/amx-deps/src/amx/pawndbg.c index d83ae8b..76ada77 100644 --- a/amx-deps/src/amx/pawndbg.c +++ b/amx-deps/src/amx/pawndbg.c @@ -1,2862 +1,2866 @@ -/* Pawn debugger - * - * Simple (minimalistic) debugger that supports source-level debugging with - * several interfaces: - * - a console interface with terminal support: VT100/ANSI terminal (or the - * xterm terminal emulator), Win32 pseudo-terminal and curses (for the Apple - * Macintosh) - * - hooks to use other pseudo-terminals, such as the Win32 GUI terminal and - * the GraphApp terminal, both of which support Unicode - * - a GDB-style "streaming" interface, to hook GUI shells to the debugger (in - * a similar way as how KDbg and DDD do this with GDB) - * - a dual-screen mode where the abstract machine uses a different (pseudo) - * terminal than the debugger --especially useful when combined with a GUI - * shell - * - ability to do remote debugging over an RS232 connection - * - * - * Copyright (c) ITB CompuPhase, 1998-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: pawndbg.c 3936 2008-03-06 14:18:27Z thiadmer $ - * - * - * Command line options: - * -rs232=1 set remote debugging over a serial line (port number - * may be 1,2,... for Windows; 0,1,... for Linux) - * -term=x,y set terminal size - * -term=off force terminal off (if environments where it is on by - * default) - * -term=hide hide the window with the terminal, useful when using - * a "dual terminal" debugger from an IDE - * -transfer transfer file on start-up to the remote host (this - * requires remote debugging to be set first) - * -quit terminate debugger immediately, used when the debugger - * needs only to transfer a file: - * pawndbg myprog.amx -rs232=1 -transfer -quit - */ -#include -#include -#include -#include -#include -#include -#include -#include -#if defined READLINE - #define __READLINE_IMPORT__ 1 - #define __P(protos) protos - #include - #include -#endif -#include "osdefs.h" /* for _MAX_PATH and other macros */ -#include "amx.h" -#include "amxdbg.h" - -#if defined __WIN32__ || defined __MSDOS__ || defined __WATCOMC__ - #include - #if defined __WIN32__ || defined __WATCOMC__ - #if !defined __WIN32__ - #define __WIN32__ 1 - #endif - #include - #if !defined amx_Init && !defined NO_WIN32_CONSOLE && !defined AMX_TERMINAL - #define WIN32_CONSOLE - #endif - #endif -#elif !defined macintosh - #include "../linux/getch.h" - #include - #include - #include -#endif - -#if !defined AMX_NODYNALOAD && defined ENABLE_BINRELOC && (defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) - #include /* from BinReloc, see www.autopackage.org */ -#endif - -extern int chdir(const char *path); /* position of this function in header files depends on the compiler */ - - -#define MAXSTACKTRACE 128 -#define MAXLINELENGTH 128 -#define MAX_DIMS 3 /* number of array dimensions */ -#define TABSIZE 8 -#define EATLINE 5 /* the number of characters for the line number */ -#define STD_COLUMNS 80 /* number of characters that fit on a line */ -#if defined amx_Init - #define STD_LINES 29 /* number of lines that fit on a screen */ -#else - #define STD_LINES 24 /* number of lines that fit on a screen */ -#endif -#define WATCHLINES 4 /* default number of watches displayed */ -#define LISTLINES (STD_LINES - WATCHLINES - 10) /* default code lines */ - -#if (defined AMX_TERMINAL || defined amx_Init) && !defined DBG_STREAMTERM - /* required functions are implemented elsewhere */ - int amx_printf(char *,...); - int amx_putchar(int); - int amx_fflush(void); - int amx_getch(void); - char *amx_gets(char *,int); - int amx_termctl(int,int); - void amx_clrscr(void); - void amx_clreol(void); - void amx_gotoxy(int x,int y); - void amx_wherexy(int *x,int *y); - unsigned int amx_setattr(int foregr,int backgr,int highlight); - void amx_console(int columns, int lines, int flags); - void amx_viewsize(int *width,int *height); - #if defined amx_Init - #define STR_PROMPT "dbg\xbb " - #define CHR_HLINE '\x97' - #else - #define STR_PROMPT "dbg> " - #define CHR_HLINE '-' - #endif - #define CHR_VLINE '|' -#elif defined USE_CURSES || defined HAVE_CURSES_H - /* Use the "curses" library to implement the console */ - const int _False = 0; /* to avoid compiler warnings */ - #define amx_printf printw - #define amx_putchar(c) addch(c) - #define amx_fflush() (0) - #define amx_getch() getch() - #define amx_gets(s,n) getnstr(s,n) - #define amx_clrscr() (void)(0) - #define amx_clreol() (void)(0) - #define amx_gotoxy(x,y) (void)(0) - #define amx_wherexy(x,y) (*(x)=*(y)=0) - #define amx_setattr(c,b,h) (_False) - #define amx_termctl(c,v) (_False) - #define amx_console(c,l,f) (void)(0) - #define STR_PROMPT "dbg> " - #define CHR_HLINE '-' - #define CHR_VLINE '|' -#elif defined VT100 || defined __LINUX__ || defined ANSITERM - /* ANSI/VT100 terminal, or shell emulating "xterm" */ - #if !defined VT100 && !defined ANSITERM && defined __LINUX__ - #define VT100 - #endif - #define amx_printf printf - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) - int amx_termctl(int,int); - void amx_clrscr(void); - void amx_clreol(void); - void amx_gotoxy(int x,int y); - void amx_wherexy(int *x,int *y); - unsigned int amx_setattr(int foregr,int backgr,int highlight); - void amx_console(int columns, int lines, int flags); - void amx_viewsize(int *width,int *height); - #define STR_PROMPT "dbg> " - #define CHR_HLINE '-' - #define CHR_VLINE '|' -#elif defined WIN32_CONSOLE - /* Win32 console */ - #define amx_printf printf - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) - int amx_termctl(int,int); - void amx_clrscr(void); - void amx_clreol(void); - void amx_gotoxy(int x,int y); - void amx_wherexy(int *x,int *y); - unsigned int amx_setattr(int foregr,int backgr,int highlight); - void amx_console(int columns, int lines, int flags); - void amx_viewsize(int *width,int *height); - #define STR_PROMPT "dbg> " - #define CHR_HLINE '\xc4' - #define CHR_VLINE '\xb3' -#else - /* assume a streaming terminal; limited features (no colour, no cursor - * control) - */ - #define amx_printf printf - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) - #define amx_clrscr() (void)(0) - #define amx_clreol() (void)(0) - #define amx_gotoxy(x,y) ((void)(x),(void)(y),(void)(0)) - #define amx_wherexy(x,y) (*(x)=*(y)=0) - #define amx_setattr(c,b,h) ((void)(c),(void)(b),(void)(h),(0)) - #define amx_termctl(c,v) ((void)(c),(void)(v),(0)) - #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f),(void)(0)) - #define amx_viewsize (*(x)=80,*(y)=25) - #define STR_PROMPT "dbg> " - #define CHR_HLINE '-' - #define CHR_VLINE '|' -#endif -#define CHR_CURLINE '*' -#if defined VT100 - #define CHR_HLINE_VT100 'q' // in alternate font - #define CHR_VLINE_VT100 'x' -#endif - -enum { - BP_NONE, - BP_CODE, - BP_DATA, /* ??? not implemented */ - /* --- */ - BP_TYPES -}; - -enum { - DISP_DEFAULT = 0x10, - DISP_STRING = 0x20, - DISP_BIN = 0x30, /* ??? not implemented */ - DISP_HEX = 0x40, - DISP_BOOL = 0x50, - DISP_FIXED = 0x60, - DISP_FLOAT = 0x70, -}; -#define DISP_MASK 0x0f - -typedef struct tagBREAKPOINT { - struct tagBREAKPOINT *next; - int type; /* one of the BP_xxx types */ - ucell addr; /* address (in code or data segment) */ - const char *name; /* name of the symbol (function) */ - int number; /* sequential breakpoint number (to refer to the breakpoint) */ -} BREAKPOINT; - -typedef struct tagNAMELIST { - struct tagNAMELIST *next; - char *name; - int number; -} NAMELIST; - -enum { - REMOTE_NONE, /* this means "not remote" */ - REMOTE_RS232, - REMOTE_UDP, /* ??? not implemented */ - /* --- */ - REMOTE_TYPES -}; - -enum { - STEPPING, /* step into functions */ - STEPOVER, /* step over functions */ - STEPOUT, /* run until the function returns */ - RUNNING, /* just run */ -}; -static int runmode; /* running or tracing */ - -static char amx_filename[_MAX_PATH]; -static const char *curfilename; /* pointer to the name of the "active" file */ -static char **cursource; -static int autolist=1; -static int screencolumns=STD_COLUMNS; -static int screenlines=STD_LINES; -static int listlines=LISTLINES; -static int watchlines=WATCHLINES; -static BREAKPOINT breakpoints={ NULL }; -static NAMELIST watches={ NULL }; -static int curtopline; /* current line that is on top in the list */ -static int recentline=-1; -static int terminal=0; -static int remote=REMOTE_NONE; -static char chr_hline=CHR_HLINE; -static char chr_vline=CHR_VLINE; -#if defined __WIN32__ - HANDLE hCom=INVALID_HANDLE_VALUE; -#elif !defined __MSDOS__ - int fdCom=-1; - struct termios oldtio, newtio; -#endif -static char remote_pendingbuf[30]; -static int remote_pendingsize=0; -static jmp_buf restart_buf; - - -static void draw_hline(int forceinit) -{ -static char hline_str[256] = ""; /* number of columns in a window should - * never exceed 255 */ - - /* initialize the string, if not yet done */ - if (forceinit || strlen(hline_str)==0) { - memset(hline_str,chr_hline,sizeof hline_str); - hline_str[screencolumns]='\0'; - } /* if */ - - #if defined VT100 - if (terminal>0) - amx_printf("\016"); /* SO code to select the graphic set */ - #endif - - amx_printf(hline_str); - - #if defined VT100 - if (terminal>0) - amx_printf("\017"); /* SI code to select the standard set */ - #endif - - amx_fflush(); -} - - -static int csrsave_x, csrsave_y; -#if !defined NDEBUG - static int csrsave_flag=0; -#endif - -static void term_csrsave(void) -{ - assert(terminal==1); - #if !defined NDEBUG - assert(csrsave_flag==0); - csrsave_flag++; - #endif - amx_wherexy(&csrsave_x,&csrsave_y); -} - -static void term_csrrestore(void) -{ - assert(terminal==1); - #if !defined NDEBUG - assert(csrsave_flag==1); - csrsave_flag--; - #endif - amx_gotoxy(csrsave_x,csrsave_y); -} - -static void term_refresh(int recalc) -{ - assert(terminal==1); - - if (recalc) { - listlines=(screenlines-watchlines)/2; - if (listlines0) { - SMALL_RECT ScrollRectangle; - COORD Destination; - CHAR_INFO Fill; - int screenlines,screencolumns; - - amx_viewsize(&screencolumns,&screenlines); - ScrollRectangle.Left=0; - ScrollRectangle.Top=(short)(top-1); - ScrollRectangle.Right=(short)(screencolumns-1); - ScrollRectangle.Bottom=(short)(bottom-1); - Destination.X=0; - Destination.Y=(short)(top-1-lines); - Fill.Char.UnicodeChar=' '; - Fill.Attributes=0x07; - ScrollConsoleScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE), - &ScrollRectangle,&ScrollRectangle, - Destination,&Fill); - } /* if */ - #else - (void)top; - (void)bottom; - (void)lines; - #endif -} - -static int term_switch(int number) -{ - if (terminal>0) - return amx_termctl(2,number); - return 0; -} - -static void source_free(char **source) -{ - int i; - - assert(source!=NULL); - for (i=0; source[i]!=NULL; i++) - free(source[i]); - free(source); -} - -static char **source_load(const char *filename) -{ - char **source; - FILE *fp; - char line[256]; - int lines,i; - - /* open the file, number of characters */ - assert(filename!=NULL); - if ((fp=fopen(filename,"rt"))==NULL) - return NULL; - lines=0; - while (fgets(line,sizeof(line),fp)!=NULL) - lines++; - - /* allocate memory, reload the file */ - if ((source=(char **)malloc((lines+1)*sizeof(char *)))==NULL) { - fclose(fp); - return NULL; - } /* if */ - for (i=0; i<=lines; i++) /* initialize, so that source_free() works */ - source[i]=NULL; - rewind(fp); - i=0; - while (fgets(line,sizeof(line),fp)!=NULL) { - assert(i0) { - term_csrsave(); - amx_gotoxy(1,watchlines+2); - numlines=listlines; /* override user setting */ - } /* if */ - - if (startline<0) - startline=0; - lastline=startline+numlines; - curtopline=startline; /* save line that is currently displayed at the top */ - - /* seek to line number from the start (to avoid displaying something - * beyond the file) - */ - for (result=0; cursource[result]!=NULL && result0) - amx_clreol(); - if ((int)strlen(cursource[startline])>screencolumns-EATLINE) { - c1=cursource[startline][screencolumns-EATLINE-1]; - c2=cursource[startline][screencolumns-EATLINE]; - cursource[startline][screencolumns-EATLINE-1] = '\n'; - cursource[startline][screencolumns-EATLINE] = '\0'; - } /* if */ - (void)amx_setattr(-1,-1,startline==curline); - if (terminal<=0) { - if (startline==curline) - amx_printf("%3d%c %s",startline+1,CHR_CURLINE,cursource[startline]); - else - amx_printf("%3d %s",startline+1,cursource[startline]); - } else { - /* see if there is a breakpoint on the line */ - if (amxdbg!=NULL && dbg_GetLineAddress(amxdbg,startline,curfilename,&address)==AMX_ERR_NONE) { - BREAKPOINT *bp; - for (bp=breakpoints.next; bp!=NULL; bp=bp->next) { - assert(bp->type!=BP_NONE); - if (bp->type==BP_CODE && bp->addr==address) - (void)amx_setattr(-1,0x1,1); - } /* for */ - } /* if */ - amx_printf("%4d",startline+1); - #if defined VT100 - if (terminal>0) - amx_printf("\016"); /* SO code to select the graphic set */ - #endif - amx_printf("%c",chr_vline); - #if defined VT100 - if (terminal>0) - amx_printf("\017"); /* SI code to select the standard set */ - #endif - (void)amx_setattr(-1,0x0,startline==curline); - amx_printf("%s",cursource[startline]); - } /* if */ - if (c1!='\0') { - cursource[startline][screencolumns-EATLINE-1] = c1; - cursource[startline][screencolumns-EATLINE] = c2; - } /* if */ - startline++; - } /* while */ - } /* if */ - - if (terminal>0) { - (void)amx_setattr(-1,-1,0); - while (startline=curtopline+listlines) - return topline; - return curtopline; -} - -static char *skipwhitespace(const char *str) -{ - while (*str==' ' || *str=='\t') - str++; - return (char*)str; -} - -static char *skipvalue(const char *str) -{ - while (isdigit(*str)) - str++; - return skipwhitespace(str); -} - -static const char *skippath(const char *str) -{ - const char *p1,*p2; - - /* DOS/Windows pathnames */ - if ((p1=strrchr(str,'\\'))!=NULL) - p1++; - else - p1=str; - if (p1==str && p1[1]==':') - p1=str+2; - /* Unix pathnames */ - if ((p2=strrchr(str,'/'))!=NULL) - p2++; - else - p2=str; - return p1>p2 ? p1 : p2; -} - -static void scroll_cmdwin(int lines) -{ - if (terminal>0) { - int csrx,csry; - amx_wherexy(&csrx,&csry); - if (csry==screenlines) { - term_scroll(watchlines+listlines+3,screenlines-1,lines); - amx_gotoxy(csrx,csry-1); - } /* if */ - } /* if */ -} - -#if !defined NO_REMOTE -/* For the remote debugging capabilities, the "device" is the apparatus/PC on - * which the script runs and the "host" is the PC on which the debugger runs. - * Both the device and the host load the script. The device typically does not - * load the debugging info. The host would not really need to load the opcodes, - * but its taks becomes a lot simpler when it has locally an image of what runs - * on the device. - * - * Handshake - * - * The first step, after opening the connection, is the handshake. For that, - * the host must send a packet with the single character "!". Since the device - * may not be "on-line" immediately and packets may get lost, the host should - * repeat sending these characters until the device reponds. - * - * When the device starts up and loads a script, it should check whether this - * script contains debugging information (even if it does not load the debugging - * information). If debugging information is present, it should check for the - * reception of packets with the character "!". Upon reception, it replies with - * a return packed with the character "@". The device should have a time-out on - * the polling loop for receivng the "!" packets, because the script should run - * normally when no debugger is attached. - * - * Running - * - * After entering in debug mode, the device will send a packed with the - * character "@" followed by the new instruction pointer address (CIP) at every - * "BREAK" instruction. The device then waits for a response from the host (the - * script is halted). To continue running, the host simply sends a packet with - * the single character "!". - * - * If, instead of allowing the device to continue running the script, you want - * to halt execution (e.g. because a breakpoint is reached), the first command - * that the host should send is the string "?R\n", where \n stands for a - * newline. Upon reception, the device must reply with a more complete state of - * the abstract machine: the stack address, the frame pointer and the heap top. - * - * With the few registers sent over, the state of the abstract machine (on the - * device) is now known to the host, but it does not know the contents of the - * data memory on the device: the values of global and local variables. It was - * estimated too costly to transfer all of this data memory at every "stop". - * Instead, the host must query for the range of memory that it wants. It does - * so by sending a packet with the contents: - * - * ?Maddress,size\n - * - * where "address" and "size" are hexadecimal numbers. The size is in cells; - * the address does not have to be cell-aligned, but it typically is. The - * device responds to this command by sending a packet with an "@" character - * followed by one or more hexadecimal values, separated with commas and - * terminated with a newline. - * - * Other (suggested) commands are: - * - * ?M address,size - * ?R (registers) send the status of FRM, STK and HEA - * - * ?G name\n (get) retrieve file - * ?P size,name\n (put) send over new script (or other file) - * ?B value\n (baud) set baud rate (restarts the connection) - * ?L\n (list) retrieve a list of files on the device - * - * ?U\n (unhook) close the debugger down - * ?U*\n unhook the debugger and restart (do this after a transfer of an updated script) - * - * When sending over scripts (or perhaps other files), the reply of the - * ?P command carries the block size that the debugger should use to send - * the data (like all numbers in the debugger interface, this value is in - * hexadecimal). For example, if the remote host replies with @100, the debugger - * should transfer the file in blocks of 256 bytes. The file itself is then - * sent as binary data (and in blocks). After each block, the debugger must - * wait for the reply. - * - * Before sending the block, the debugger sends a "start code". This code is - * either an ACK (ASCII 6) or a NAK (ASCII 21). If the debugger sends an ACK, - * the block that follows is the next sequential block of data for the file. If - * it sends a NAK, the block that follows is a repeated send of the preceding block. - * The debugger should resend a block on a checksum mismatch. After sending a - * block, the remote host replies. The checksum of the block is in the value - * that follows the @ sign. Due to the way that the checksum is computed, the - * checksum is never zero. The debugger can now compare the checksum with the - * one it calculated itself. On a mismatch, the debugger can then send a NAK - * and resend the block. - * - * The checksum is the "Internet checksum", but as an 8-bit variant. This - * checksum is often called the "one's complement", because it wraps the carry - * around on overflow. - * - * - * Notes - * - * The rationale for the debugger communication protocol is performance, and - * especially attempting to avoid that the communication link becomes the bottleneck. Therefore, the - * device and the host send over as little as possible until it is known that - * the device should be halted. In the case of a serial link, data is sent byte - * by byte. When the device is halted (on a breakpoint) and - * waiting for user interaction, performance is no longer as important. - * - * There is a special case in the handshaking stage when the device is already - * running in debug mode (because an earlier session was aborted). In that case, - * it will respond with "@" and the new instruction pointer (CIP) address. - */ - -static int send_rs232(const char *buffer, int len) -{ - unsigned long size; - - #if defined __WIN32__ - WriteFile(hCom,buffer,len,&size,NULL); - #else - size=write(fdCom,buffer,len); - #endif - assert((unsigned long)len==size); - return size; -} - -static int getresponse_rs232(char *buffer, int buffersize, long retries) -{ - int len=0; - unsigned long size; - - //assert(hCom!=INVALID_HANDLE_VALUE); - do { - - /* read character by character, so that when we see the '\n' we stop - * reading and keep the rest of the waiting characters in the queue - */ - #if defined __WIN32__ - ReadFile(hCom,buffer+len,1,&size,NULL); - #else - size=read(fdCom,buffer+len,1); - #endif - len+=size; - - /* throw away dummy input characters */ - while (buffer[0]!='@' && len>0) { - int idx; - for (idx=0; idx0); - - return len; -} - -static int settimestamp_rs232(unsigned long sec1970) -{ - char str[30]; - - #if defined __WIN32__ - assert(hCom!=INVALID_HANDLE_VALUE); - #else - assert(fdCom>=0); - #endif - sprintf(str,"?T%lx\n",sec1970); - send_rs232(str,strlen(str)); - getresponse_rs232(str,sizeof str,10); - return strlen(str)>0 && atoi(str+1)>0; -} - -static int remote_rs232(int port,int baud) -{ - #if defined __WIN32__ - DCB dcb; - COMMTIMEOUTS commtimeouts; - #endif - unsigned long size,cip; - char buffer[40]; - - /* optionally issue a "close-down" request for the remote host */ - #if defined __WIN32__ - if (baud==0 && hCom!=INVALID_HANDLE_VALUE) { - #else - if (baud==0 && fdCom>=0) { - #endif - sprintf(buffer,"?U\n"); - #if defined __WIN32__ - WriteFile(hCom,buffer,strlen(buffer),&size,NULL); - #else - write(fdCom,buffer,strlen(buffer)); - #endif - /* do not wait for a reply */ - } /* if */ - - /* set up the connection */ - #if defined __WIN32__ - if (hCom!=INVALID_HANDLE_VALUE) - CloseHandle(hCom); - if (baud==0) - return 0; - sprintf(buffer,"com%d:",port); - hCom=CreateFile(buffer,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); - if (hCom==INVALID_HANDLE_VALUE) - return 0; - GetCommState(hCom,&dcb); - dcb.BaudRate=baud; - dcb.ByteSize=8; - dcb.StopBits=ONESTOPBIT; - dcb.Parity=NOPARITY; - dcb.fBinary=TRUE; - dcb.fDtrControl=DTR_CONTROL_DISABLE; - dcb.fOutX=FALSE; - dcb.fInX=FALSE; - dcb.fNull=FALSE; - dcb.fRtsControl=RTS_CONTROL_DISABLE; - SetCommState(hCom,&dcb); - SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY); - commtimeouts.ReadIntervalTimeout =0x7fffffff; - commtimeouts.ReadTotalTimeoutMultiplier =0; - commtimeouts.ReadTotalTimeoutConstant =1; - commtimeouts.WriteTotalTimeoutMultiplier=0; - commtimeouts.WriteTotalTimeoutConstant =0; - SetCommTimeouts(hCom,&commtimeouts); - #else - if (fdCom>=0) { - tcflush(fdCom,TCOFLUSH); - tcflush(fdCom,TCIFLUSH); - tcsetattr(fdCom,TCSANOW,&oldtio); - close(fdCom); - } /* if */ - if (baud==0) - return 0; - sprintf(buffer,"/dev/ttyS%d",port); - fdCom = open(buffer,O_RDWR|O_NOCTTY|O_NONBLOCK); - if (fdCom<0) - return 0; - /* clear input & output buffers, then switch to "blocking mode" */ - tcflush(fdCom,TCOFLUSH); - tcflush(fdCom,TCIFLUSH); - fcntl(fdCom,F_SETFL,fcntl(fdCom,F_GETFL) & ~O_NONBLOCK); - - tcgetattr(fdCom,&oldtio); /* save current port settings */ - bzero(&newtio,sizeof newtio); - newtio.c_cflag=CS8 | CLOCAL | CREAD; - - switch (baud) { - case 1152000: newtio.c_cflag |= B1152000; break; - case 576000: newtio.c_cflag |= B576000; break; - case 230400: newtio.c_cflag |= B230400; break; - case 115200: newtio.c_cflag |= B115200; break; - case 57600: newtio.c_cflag |= B57600; break; - case 38400: newtio.c_cflag |= B38400; break; - case 19200: newtio.c_cflag |= B19200; break; - case 9600: newtio.c_cflag |= B9600; break; - default: return 0; - } /* switch */ - newtio.c_iflag=IGNPAR | IGNBRK | IXON | IXOFF; - newtio.c_oflag=0; - - /* set input mode (non-canonical, no echo,...) */ - newtio.c_lflag=0; - - cfmakeraw(&newtio); - newtio.c_cc[VTIME]=1; /* inter-character timer used */ - newtio.c_cc[VMIN] =0; /* blocking read until 0 chars received */ - tcflush(fdCom,TCIFLUSH); - tcsetattr(fdCom,TCSANOW,&newtio); - #endif - - /* handshake, send token and wait for a reply */ - do { - #if defined __WIN32__ - WriteFile(hCom,"!",1,&size,NULL); - Sleep(500); - do { - ReadFile(hCom,buffer,1,&size,NULL); - } while (size>0 && buffer[0]!='@'); - Sleep(500); - /* read remaining buffer (if any) */ - ReadFile(hCom,buffer+1,sizeof buffer - 1,&size,NULL); - size++; /* add size of the handshake character */ - #else - write(fdCom,"!",1); - usleep(500*1000); - do { - size=read(fdCom,buffer,1); - } while (size>0 && buffer[0]!='@'); - usleep(500*1000); - /* read remaining buffer (if any) */ - size=read(fdCom,buffer+1,sizeof buffer - 1); - size++; /* add size of the handshake character */ - #endif - } while (size==0 || buffer[0]!='@'); - if (sscanf(buffer,"@%lx",&cip)>0) { - /* if the script is already running, give a "go" command */ - #if defined __WIN32__ - WriteFile(hCom,"!",1,&size,NULL); - #else - write(fdCom,"!",1); - #endif - } else if (size>1 && size=sizeof buffer - 1) { - amx_printf("%s",buffer); - offs=0; - state=SCAN; - } /* if */ - /* read a buffer, see if we can find the start condition */ - do { - if (remote_pendingsize>0) { - assert(remote_pendingsize=size) { - amx_printf("%s",buffer); - } else { - memmove(buffer,ptr,offs); - state=START; - } /* if */ - } /* if */ - if (state==START) { - for (ptr=buffer; (unsigned long)(ptr-buffer)<(size+offs) && *ptr!='\n'; ptr++) - /* nothing */; - if (*ptr=='\n') { - state=FINISH; - if (strlen(++ptr)>0) - amx_printf("%s",ptr); - } /* if */ - } /* if */ - } /* while */ - amx_fflush(); - /* we found a line starting with '@' and ending '\n'; now check the validity - * of the line - */ - if (sscanf(buffer,"@%lx",&cip)==1) { - amx->cip=(cell)cip; - return 1; - } else { - /* unknown buffer format; print and continue */ - amx_printf("%s",buffer); - } /* if */ - } /* for */ -} - -static void remote_resume_rs232(void) -{ - #if defined __WIN32__ - unsigned long size; - WriteFile(hCom,"!",1,&size,NULL); - #else - write(fdCom,"!",1); - #endif -} - -static void remote_sync_rs232(AMX *amx) -{ - char buffer[128]; - long frm,stk,hea; - #if defined __WIN32__ - unsigned long size; - #endif - - for ( ;; ) { - #if defined __WIN32__ - WriteFile(hCom,"?R\n",3,&size,NULL); - #else - write(fdCom,"?R\n",3); - #endif - - buffer[0]='\0'; - getresponse_rs232(buffer,sizeof buffer,10); - if (sscanf(buffer,"@%lx,%lx,%lx",&frm,&stk,&hea)==3) { - amx->frm=(cell)frm; - amx->stk=(cell)stk; - amx->hea=(cell)hea; - return; - } /* if */ - } /* for */ -} - -static void remote_read_rs232(AMX *amx,cell vaddr,int number) -{ - char buffer[128]; - char *ptr; - int len; - cell val; - cell *cptr; - - while (number>0) { - sprintf(buffer,"?M%lx,%x\n",(long)vaddr,(number>10) ? 10 : number); - len=strlen(buffer); - send_rs232(buffer,len); - getresponse_rs232(buffer,sizeof buffer,100); - ptr=buffer+1; /* skip '@' */ - while (number>0 && (ptr-buffer)0) { - num=(number>10) ? 10 : number; - number-=num; - sprintf(buffer,"?W%lx",(long)vaddr); - while (num>0) { - if (amx_GetAddr(amx,vaddr,&cptr)!=AMX_ERR_NONE) - return; - strcat(buffer,","); - sprintf(buffer+strlen(buffer),"%x",*cptr); - num--; - vaddr+=sizeof(cell); - } /* while */ - strcat(buffer,"\n"); - len=strlen(buffer); - send_rs232(buffer,len); - getresponse_rs232(buffer,sizeof buffer,100); - if (strtol(buffer+1,NULL,16)==0) - return; - } /* while */ -} - -static int remote_transfer_rs232(const char *filename) -{ - #define ACK ((unsigned char)6) - #define NAK ((unsigned char)21) - unsigned char *buffer; - char str[128]; - FILE *fp; - size_t bytes,block; - unsigned long size,chksum; - int len,err; - - #if defined __WIN32__ - if (hCom==INVALID_HANDLE_VALUE) - return 0; - #else - if (fdCom<0) - return 0; - #endif - - if ((fp=fopen(filename,"rb"))==NULL) - return 0; - /* determine the file size */ - fseek(fp,0,SEEK_END); - size=ftell(fp); - fseek(fp,0,SEEK_SET); - - /* set up */ - sprintf(str,"?P %lx,%s\n",size,skippath(filename)); - len=strlen(str); - send_rs232(str,len); - getresponse_rs232(str,sizeof str,100); - if (sscanf(str,"@%x",&block)!=1) - block=0; - /* allocate 1 byte more, for the ACK/NAK prefix */ - if (block==0 || (buffer=malloc((block+1)*sizeof(char)))==NULL) { - fclose(fp); - return 0; - } /* if */ - - /* file transfer acknowledged, transfer data per block */ - amx_printf("Transferring "); - amx_fflush(); - while (size>0 && (bytes=fread(buffer+1,1,block,fp))!=0) { - buffer[0]=ACK; - /* calculate the checksum */ - chksum=1; - for (len=1; len<=(int)bytes; len++) - chksum+=buffer[len]; - while (chksum>0xff) - chksum=(chksum&0xff)+(chksum>>8); - do { - /* send block */ - send_rs232((const char*)buffer,bytes+1); /* also send the ACK/NAK prefix */ - getresponse_rs232(str,sizeof str,100); - err=(str[0]!='\0') ? strtol(str+1,NULL,16) : 0; - assert(err>=0 && err<=255); - if (err==0) { - free(buffer); - fclose(fp); - return 0; - } /* if */ - buffer[0]=NAK; /* preset for failure (if err!=chksum => failure) */ - } while (err!=(int)chksum); - size-=block; - if (sizenext!=NULL) { - next=root->next; - root->next=next->next; /* unlink */ - free(next->name); /* then free */ - free(next); - } /* while */ -} - -static NAMELIST *namelist_find(NAMELIST *root,int number) -{ - NAMELIST *name; - - for (name=root->next; name!=NULL && name->number!=number; name=name->next) - /* nothing */; - return name; -} - -static int namelist_delete(NAMELIST *root,NAMELIST *item) -{ - NAMELIST *cur; - - /* find the item */ - assert(root!=NULL); - cur=root; - while (cur->next!=NULL && cur->next!=item) - cur=cur->next; - assert(cur!=NULL); - if (cur->next==NULL) - return 0; /* item not found */ - - cur->next=item->next; /* unlink */ - free(item->name); /* then free */ - free(item); - - return 1; -} - -static NAMELIST *namelist_add(NAMELIST *root,char *name,int number) -{ - NAMELIST *cur, *pred; - - /* allocate a structure */ - cur=malloc(sizeof(NAMELIST)); - if (cur==NULL) - return NULL; - cur->name=strdup(name); - cur->number=number; - if (cur->name==NULL) { - free(cur); - return NULL; - } /* if */ - - /* link it in the list */ - assert(root!=NULL); - for (pred=root; pred->next!=NULL && pred->next->numbernext) - /* nothing */; - cur->next=pred->next; - pred->next=cur; - - return cur; -} - -static int namelist_count(NAMELIST *root) -{ - int count = 0; - NAMELIST *name; - - for (name=root->next; name!=NULL; name=name->next) - count++; - return count; -} - -static int get_symbolvalue(AMX *amx,AMX_DBG_SYMBOL *sym,int index,cell *value) -{ - cell *vptr; - cell base=sym->address; - if (sym->vclass & DISP_MASK) - base+=amx->frm; /* addresses of local vars are relative to the frame */ - if (sym->ident==iREFERENCE || sym->ident==iREFARRAY) { /* a reference */ - amx_GetAddr(amx,base,&vptr); - base=*vptr; - } /* if */ - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_read_rs232(amx,(cell)(base+index*sizeof(cell)),1); - #endif - if (amx_GetAddr(amx,(cell)(base+index*sizeof(cell)),&vptr)!=AMX_ERR_NONE) - return 0; - *value=*vptr; - return 1; -} - -static int set_symbolvalue(AMX *amx,const AMX_DBG_SYMBOL *sym,int index,cell value) -{ - cell *vptr; - cell base=sym->address; - if (sym->vclass & DISP_MASK) - base+=amx->frm; /* addresses of local vars are relative to the frame */ - if (sym->ident==iREFERENCE || sym->ident==iREFARRAY) { /* a reference */ - amx_GetAddr(amx,base,&vptr); - base=*vptr; - } /* if */ - if (amx_GetAddr(amx,(cell)(base+index*sizeof(cell)),&vptr)!=AMX_ERR_NONE) - return 0; - *vptr=value; - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_write_rs232(amx,(cell)(base+index*sizeof(cell)),1); - #endif - return 1; -} - -static char *get_string(AMX *amx,AMX_DBG_SYMBOL *sym,int maxlength) -{ -static char string[MAXLINELENGTH]; - char *ptr; - cell *addr; - cell base; - int length,num; - - assert(sym->ident==iARRAY || sym->ident==iREFARRAY); - assert(sym->dim==1); - assert(maxlengthaddress; - if (sym->vclass) - base+=amx->frm; /* addresses of local vars are relative to the frame */ - if (sym->ident==iREFARRAY) { /* a reference */ - amx_GetAddr(amx,base,&addr); - base=*addr; - } /* if */ - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_read_rs232(amx,base,MAXLINELENGTH); - #endif - if (amx_GetAddr(amx,base,&addr)==AMX_ERR_NONE) { - amx_StrLen(addr,&length); - - /* allocate a temporary buffer */ - ptr=malloc(length+1); - if (ptr!=NULL) { - amx_GetString(ptr,addr,0,maxlength); - num=length; - if (num>=maxlength) { - num=maxlength-1; - if (num>3) - num-=3; /* make space for the ... terminator */ - } /* if */ - assert(num>=0); - strncpy(string,ptr,num); - string[num]='\0'; - if (numvclass & ~DISP_MASK)==0) { - const char *tagname; - if (dbg_GetTagName(amxdbg,sym->tag,&tagname)==AMX_ERR_NONE) { - if (stricmp(tagname,"bool")==0) - sym->vclass |= DISP_BOOL; - else if (stricmp(tagname,"fixed")==0) - sym->vclass |= DISP_FIXED; - else if (stricmp(tagname,"float")==0) - sym->vclass |= DISP_FLOAT; - } /* if */ - if ((sym->vclass & ~DISP_MASK)==0 && (sym->ident==iARRAY || sym->ident==iREFARRAY) && sym->dim==1) { - /* untagged array with a single dimension, walk through all elements - * and check whether this could be a string - */ - unsigned char *ptr=(unsigned char*)get_string(amx,sym,MAXLINELENGTH-1); - int i; - for (i=0; i=128) - break; /* non-ASCII character */ - if (i==0 && !isalpha(ptr[i])) - break; /* want a letter at the start */ - } /* for */ - if (i>0 && ivclass |= DISP_STRING; - } /* if */ - } /* if */ - - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - int dim; - dbg_GetArrayDim(amxdbg, sym, &symdim); - /* check whether any of the indices are out of range */ - assert(symdim!=NULL); - for (dim=0; dim0 && (ucell)index[dim]>=symdim[dim].size) - break; - if (dimident==iARRAY || sym->ident==iREFARRAY) && idxlevel==0) { - if ((sym->vclass & ~DISP_MASK)==DISP_STRING) { - amx_printf("\"%s\"", get_string(amx,sym,40)); - } else if (sym->dim==1) { - ucell len,i; - assert(symdim!=NULL); /* set in the previous block */ - len=symdim[0].size; - if (len>5) - len=5; - else if (len==0) - len=1; /* unknown array length, assume at least 1 element */ - amx_printf("{"); - for (i=0; i0) - amx_printf(","); - if (get_symbolvalue(amx,sym,(int)i,&value)) - printvalue(value,(sym->vclass & ~DISP_MASK)); - else - amx_printf("?"); - } /* for */ - if (lenident!=iARRAY && sym->ident!=iREFARRAY && idxlevel>0) { - /* index used on a non-array */ - amx_printf("(invalid index, not an array)"); - } else { - /* simple variable, or indexed array element */ - int base=0; - int dim; - assert(idxlevel>0 || index[0]==0); /* index should be zero if non-array */ - for (dim=0; dimdim==idxlevel) - printvalue(value,(sym->vclass & ~DISP_MASK)); - else if (sym->dim!=idxlevel) - amx_printf("(invalid number of dimensions)"); - else - amx_printf("?"); - } /* if */ -} - -static void watch_init(void) -{ - namelist_init(&watches); -} - -static void watch_list(AMX *amx,AMX_DBG *amxdbg) -{ - int num,dim,idx[MAX_DIMS]; - char *indexptr; - char name[sNAMEMAX+20]; /* extra space for an array index */ - NAMELIST *watch; - const AMX_DBG_SYMBOL *sym; - - if (terminal>0) { - term_csrsave(); - amx_gotoxy(1,1); - printf("\n"); /* clear "tab state" if the terminal driver forgot */ - amx_gotoxy(1,1); /* and set the cursor position again */ - } /* if */ - - num=0; - for (watch=watches.next; watch!=NULL; watch=watch->next) { - assert(watch->name!=NULL && strlen(watch->name)>0); - strcpy(name,watch->name); - indexptr=strchr(name,'['); - dim=0; - memset(idx,0,sizeof idx); - while (indexptr!=NULL && dimname); - /* find the symbol with the given range with the smallest scope */ - if (dbg_GetVariable(amxdbg,watch->name,amx->cip,&sym)==AMX_ERR_NONE) - display_variable(amx,amxdbg,(AMX_DBG_SYMBOL *)sym,idx,dim); - else - amx_printf("(unknown symbol)"); - if (terminal>0) - amx_clreol(); - amx_printf("\n"); - } /* for */ - - if (terminal>0) { - if (num==0) - amx_printf("(no watches)"); - for ( ; num0) - draw_hline(0); - } /* if */ -} - -static int watch_set(int number, char *name) -{ - NAMELIST *cur; - - /* find a free number */ - if (number<0) { - int changed; - number=0; - do { - changed=0; - for (cur=watches.next; cur!=NULL; cur=cur->next) { - if (cur->number==number) { - number++; - changed=1; - } /* if */ - } /* for */ - } while (changed); - } /* if */ - - /* add the watch */ - return namelist_add(&watches,name,number)!=NULL; -} - -static int watch_clear(int number) -{ - NAMELIST *name=namelist_find(&watches,number); - if (name!=NULL) { - namelist_delete(&watches,name); - return 1; - } /* if */ - return 0; -} - -static int watch_count(void) -{ - return namelist_count(&watches); -} - -static void break_init(void) -{ - BREAKPOINT *next; - - while (breakpoints.next!=NULL) { - next=breakpoints.next; - breakpoints.next=next->next; /* unlink */ - free(next); /* then free */ - } /* while */ -} - -static int break_clear(int number) -{ - BREAKPOINT *cur; - - /* find the breakpoint */ - cur=&breakpoints; - while (cur->next!=NULL && cur->next->number!=number) - cur=cur->next; - if (cur->next==NULL) - return 0; /* breakpoint not found */ - - cur->next=cur->next->next; /* unlink */ - free(cur->next); /* then free */ - return 1; -} - -static int break_set(AMX_DBG *amxdbg,const char *symaddr,int flags) -{ - BREAKPOINT *cur,*newbp, bp; - int number,changed,err; - - /* find type */ - memset(&bp,0,sizeof(BREAKPOINT)); - symaddr=(const char*)skipwhitespace(symaddr); - if (isdigit(*symaddr)) { - bp.type=BP_CODE; - if ((flags & 1)!=0) - bp.number=0; /* for temporary breakpoint, use number 0 */ - err=dbg_GetLineAddress(amxdbg,strtol(symaddr,NULL,10)-1,curfilename,&bp.addr); - } else { - bp.type=BP_CODE; - err=dbg_GetFunctionAddress(amxdbg,symaddr,curfilename,&bp.addr); - dbg_LookupFunction(amxdbg, bp.addr, &bp.name); - } /* if */ - if (err!=AMX_ERR_NONE) - return -1; - - /* walk through the list, see if the breakpoint already exists */ - for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { - if (cur->addr==bp.addr) { - if (flags & 2) - break_clear(cur->number); /* toggle the breakpoint */ - return AMX_ERR_NONE; - } /* if */ - } /* for */ - - /* find an unused breakpoint number */ - number=1; - do { - changed=0; - for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { - if (cur->number==number) { - number++; - changed=1; - } /* if */ - } /* for */ - } while (changed); - - /* allocate a new breakpoint, add the entry parsed earlier to the list */ - newbp=malloc(sizeof(BREAKPOINT)); - if (newbp==0) - return -1; - memcpy(newbp,&bp,sizeof(BREAKPOINT)); - for (cur=&breakpoints; cur->next!=NULL && cur->next->numbernext) - /* nothing */; - assert(cur!=NULL); - newbp->next=cur->next; - cur->next=newbp; - newbp->number=number; - - return number; -} - -static void break_list(AMX_DBG *amxdbg) -{ - BREAKPOINT *cur; - - for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { - assert(cur->type!=BP_NONE); - scroll_cmdwin(1); - amx_printf("%2d ",cur->number); - if (cur->type==BP_CODE) { - long line; - const char *filename; - dbg_LookupLine(amxdbg,cur->addr,&line); - amx_printf("line: %ld",line); - if (cur->name!=NULL) { - amx_printf("\tfunc: %s",cur->name); - } else { - dbg_LookupFile(amxdbg,cur->addr,&filename); - if (filename!=NULL) - amx_printf("\tfile: %s",skippath(filename)); - } /* if */ - } /* if */ - amx_printf("\n"); - } /* for */ -} - -static int break_check(AMX *amx) -{ - BREAKPOINT *cur; - ucell cip; - - /* when the "break" statement comes, the instruction pointer is already - * incremented; we must adjust for this - */ - cip=(ucell)amx->cip-sizeof(cell); - for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { - if (cur->type==BP_CODE) { - if (cur->addr==cip) { - int number=cur->number; - if (number==0) - break_clear(number); - return number; - } /* if */ - } /* if */ - } /* for */ - return -1; -} - -#if defined READLINE -/* Read a string, and return a pointer to it. Returns NULL on EOF. */ -char *rl_gets(char *str,int length) -{ - char *line_read=readline(STR_PROMPT); - if (line_read==NULL) - return NULL; - - /* copy the line (with possible truncation) */ - strncpy(str,line_read,length); - if ((int)strlen(line_read)>length) - str[length-1]='\0'; - #if !defined __WIN32__ - /* readline() allocates memory internally, which it asks you to deallocate - * with free(). This is an acceptable design with a statically linked - * library, and perhaps even with shared libraries in Unix/Linux, but it - * is problematic with DLLs under Microsoft Windows. In Microsoft Windows, - * the standard C library was never part of the OS, so each compiler - * vendor provided its own. If your DLL allocates memory and your program - * (that uses the DLL) frees it, chances are that the malloc() and free() - * implementations assume a different heap lay-out (because they originate - * from different vendors, or from different versions of the compiler, or - * even because of different compilation options/flags). - * Now if malloc() and free() disagree, trouble is never far. - * To illustruate the problem: I downloaded a readline Win32 port (a DLL), - * but calling free() on the returned pointer consistently crashes the - * program. I must have a different compiler than the one used to compile - * the DLL, or a different version of the compiler, or different compiler - * options, or a different MSVCRT.DLL. - * As a temporary solution, I have chosen to "leak" memory (instead of - * crashing). - */ - free(line_read); - #endif - - /* If the line has any text in it, save it on the history. */ - if (strlen(str)>0) - add_history(line_read); - - return str; -} -#else -char *rl_gets(char *str,int length) -{ - #if defined __WIN32__ - unsigned long num; - INPUT_RECORD rec; - CONSOLE_CURSOR_INFO csrinfo; - HANDLE stdinput=GetStdHandle(STD_INPUT_HANDLE); - HANDLE stdoutput=GetStdHandle(STD_OUTPUT_HANDLE); - #endif - - amx_printf("%s",STR_PROMPT); - amx_fflush(); - #if defined __WIN32__ - if (terminal>0) { - /* first wait for a function key */ - GetConsoleCursorInfo(stdoutput,&csrinfo); - csrinfo.bVisible=FALSE; - SetConsoleCursorInfo(stdoutput,&csrinfo); - term_statusbar(1); - assert(length>5); - for ( ;; ) { - while (PeekConsoleInput(stdinput,&rec,1,&num) && num==0) - /* nothing */; - if (num==0) - break; - /* analyze the event, keep only the keyboard events */ - switch (rec.EventType) { - case KEY_EVENT: - if (!rec.Event.KeyEvent.bKeyDown) { - ReadConsoleInput(stdinput,&rec,1,&num); - continue; - } /* if */ - str[0]='\0'; - switch (rec.Event.KeyEvent.wVirtualKeyCode) { - case VK_F1: - strcpy(str,"\033[11~"); /* \e[11~ on VT220, \eOP on VT100 */ - break; - case VK_F2: - strcpy(str,"\033[12~"); /* \e[12~ on VT220, \eOQ on VT100 */ - break; - case VK_F3: - strcpy(str,"\033[13~"); /* \e[13~ on VT220, \eOR on VT100 */ - break; - case VK_F4: - strcpy(str,"\033[14~"); /* \e[14~ on VT220, \eOS on VT100 */ - break; - case VK_F5: - strcpy(str,"\033[15~"); - break; - case VK_F6: - strcpy(str,"\033[17~"); - break; - case VK_F7: - strcpy(str,"\033[18~"); - break; - case VK_F8: - strcpy(str,"\033[19~"); - break; - case VK_F9: - strcpy(str,"\033[20~"); - break; - case VK_F10: - strcpy(str,"\033[21~"); - break; - case VK_F11: - strcpy(str,"\033[23~"); - break; - case VK_F12: - strcpy(str,"\033[24~"); - break; - case VK_PRIOR: /* PgUp */ - strcpy(str,"\033[5~"); - break; - case VK_NEXT: /* PgDn */ - strcpy(str,"\033[6~"); - break; -#if 0//??? - case VK_UP: /* Arrow up */ - strcpy(str,"\033[A"); - break; - case VK_DOWN: /* Arrow down */ - strcpy(str,"\033[B"); - break; - case VK_LEFT: /* Arrow left */ - strcpy(str,"\033[C"); - break; - case VK_RIGHT:/* Arrow right */ - strcpy(str,"\033[D"); - break; -#endif - } /* switch */ - if (str[0]!='\0') { - ReadConsoleInput(stdinput,&rec,1,&num); /* remove from queue */ - csrinfo.bVisible=TRUE; - SetConsoleCursorInfo(stdoutput,&csrinfo); - term_statusbar(0); - return str; - } /* if */ - break; - case MOUSE_EVENT: - ReadConsoleInput(stdinput,&rec,1,&num); - if (rec.Event.MouseEvent.dwEventFlags==0) { - /* mouse click or release */ - int y=rec.Event.MouseEvent.dwMousePosition.Y; - if (terminal>0) { - int line=y-watchlines-1; - if (line>=0 && line0) { - term_csrsave(); - amx_clrscr(); - if (strlen(command)==0 || strcmp(command,"?")==0) - amx_printf("At the prompt, you can type debug commands. For example, the word \"step\" is a\n" - "command to execute a single line in the source code. The commands that you will\n" - "use most frequently may be abbreviated to a single letter: instead of the full\n" - "word \"step\", you can also type the letter \"s\" followed by the enter key.\n\n" - "Available commands:\n"); - else - amx_printf("Options for command \"%s\":\n",command); - } /* if */ - - if (stricmp(command,"break")==0) { - amx_printf("\tBREAK\t\tlist all breakpoints\n" - "\tBREAK n\t\tset a breakpoint at line \"n\"\n" - "\tBREAK func\tset a breakpoint at function with name \"func\"\n"); - } else if (stricmp(command,"cbreak")==0) { - amx_printf("\tCBREAK n\tremove breakpoint number \"n\"\n" - "\tCBREAK *\tremove all breakpoints\n"); - } else if (stricmp(command,"cw")==0 || stricmp(command,"cwatch")==0) { - amx_printf("\tCWATCH may be abbreviated to CW\n\n" - "\tCWATCH n\tremove watch number \"n\"\n" - "\tCWATCH *\tremove all watches\n"); - } else if (stricmp(command,"d")==0 || stricmp(command,"disp")==0) { - amx_printf("\tDISP may be abbreviated to D\n\n" - "\tDISP\t\tdisplay all variables that are currently in scope\n" - "\tDISP var\tdisplay the value of variable \"var\"\n" - "\tDISP var[i]\tdisplay the value of array element \"var[i]\"\n"); - } else if (stricmp(command,"file")==0) { - amx_printf("\tFILE\t\tlist all files that this program is composed off\n" - "\tFILE name\tset the current file to \"name\"\n"); - } else if (stricmp(command,"g")==0 || stricmp(command,"go")==0) { - amx_printf("\tGO may be abbreviated to G\n\n" - "\tGO\t\trun until the next breakpoint or program termination\n" - "\tGO n\t\trun until line number \"n\"\n" - "\tGO func\t\trun until the current function returns (\"step out\")\n"); - } else if (stricmp(command,"l")==0 || stricmp(command,"list")==0) { - amx_printf("\tLIST may be abbreviated to L\n\n" - "\tLIST\t\tdisplay source code lines around the current line\n" - "\tLIST n\t\tdisplay lines, starting from line \"n\"\n" - "\tLIST n m\tdisplay \"m\" lines, starting from line \"n\" (not\n" - "\t\t\tsupported in terminal emulation modes)\n" - "\tLIST UP\tdisplay preceding lines\n" - "\tLIST DOWN\tdisplay following lines\n" - "\tLIST FUNCS\tdisplay all functions\n" - "\tLIST ON\t\tautomatically list source code lines after each step\n" - "\tLIST OFF\tturn off automatic list (not supported in terminal\n" - "\t\t\temulation modes)\n" - "\tLIST STATES\tdisplay all automatons and the state that these are in\n"); - } else if (stricmp(command,"set")==0) { - amx_printf("\tSET var=value\t\tset variable \"var\" to the numeric value \"value\"\n" - "\tSET var[i]=value\tset array item \"var\" to a numeric value\n"); - } else if (stricmp(command,"term")==0) { - amx_printf("\tTERM OFF\tdisable VT100/Win32 terminal functions\n" - "\tTERM ON\t\tuse terminal capabilities, default console size\n" - "\tTERM x y\tset terminal size (x columns, y lines)\n"); -#if !defined NO_REMOTE - } else if (stricmp(command,"transfer")==0) { - amx_printf("\tTRANSFER\ttransfer the program to the remote host (for remote\n" - "\t\t\tdebugging)\n"); -#endif - } else if (stricmp(command,"type")==0) { - amx_printf("\tTYPE var STRING\tdisplay \"var\" as string\n" - "\tTYPE var STD\tset default display format (decimal integer)\n" - "\tTYPE var HEX\tset hexadecimal integer format\n" - "\tTYPE var FIXED\tset fixed point format (3 decimals)\n" - "\tTYPE var FLOAT\tset floating point format\n"); - } else if (stricmp(command,"watch")==0 || stricmp(command,"w")==0) { - amx_printf("\tWATCH may be abbreviated to W\n\n" - "\tWATCH var\tset a new watch at variable \"var\"\n" - "\tWATCH n var\tchange watch \"n\" to variable \"var\"\n"); - } else if (stricmp(command,"n")==0 || stricmp(command,"next")==0 - || stricmp(command,"quit")==0 - || stricmp(command,"s")==0 || stricmp(command,"step")==0) - { - amx_printf("\tno additional information\n"); - } else { - amx_printf("\tBREAK\t\tset breakpoint at line number or variable name\n" - "\tCBREAK\t\tremove breakpoint\n" - "\tCW(atch)\tremove a \"watchpoint\"\n" - "\tD(isp)\t\tdisplay the value of a variable, list variables\n" - "\tFILE\t\tswitch to a file\n" - "\tG(o)\t\trun program (until breakpoint)\n" - "\tL(ist)\t\tdisplay source file, automatons and functions\n" - "\tN(ext)\t\tRun until next line, step over functions\n" - "\tOUTPUT\t\tShow program output (if supported by terminal)\n" - "\tQUIT\t\texit debugger, terminate program\n" - "\tSET\t\tSet a variable to a value\n" - "\tS(tep)\t\tsingle step, step into functions\n" - "\tTERM\t\tset terminal type\n" -#if !defined NO_REMOTE - "\tTRANSFER\ttransfer program to remote host\n" -#endif - "\tTYPE\t\tset the \"display type\" of a symbol\n" - "\tW(atch)\t\tset a \"watchpoint\" on a variable\n" - "\n\tUse \"? \" to view more information on a command\n"); - } /* if */ - amx_fflush(); - if (terminal>0) { - amx_printf("\nPress a key to continue..."); - amx_fflush(); - amx_getch(); - term_refresh(0); - term_csrrestore(); - source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); - } /* if */ -} - -static int docommand(AMX *amx,AMX_DBG *amxdbg,int curline) -{ -static char lastcommand[32] = ""; - char line[MAXLINELENGTH], command[32]; - int result,i; - char *params; - - for ( ;; ) { - if (terminal>0) { - term_csrsave(); - amx_gotoxy(1,screenlines); - amx_clreol(); - } /* if */ - rl_gets(line,sizeof(line)); - if (terminal>0) - term_csrrestore(); - if (line[0]=='\033') { - assert(terminal==1); - if (strcmp(line+1,"[11~")==0 || strcmp(line+1,"OP")==0) { - strcpy(line,"?"); /* F1 == Help */ - } else if (strcmp(line+1,"[13~")==0 || strcmp(line+1,"OR")==0) { - strcpy(line,"QUIT"); /* F3 == List */ - } else if (strcmp(line+1,"[14~")==0 || strcmp(line+1,"OS")==0) { - strcpy(line,"Output"); /* F4 == Output */ - } else if (strcmp(line+1,"[15~")==0) { - strcpy(line,"G"); /* F5 == Go */ - } else if (strcmp(line+1,"[17~")==0) { - strcpy(line,"D"); /* F6 == Disp */ - } else if (strcmp(line+1,"[19~")==0) { - strcpy(line,"S"); /* F8 == Step */ - #if 0 //??? - } else if (strcmp(line+1,"[20~")==0) { - if (recentline<0) - recentline=curline; - sprintf(line,"BREAK %d",recentline+1); /* F9 == Breakpoint */ - //??? should toggle if a breakpoint is already set - #endif - } else if (strcmp(line+1,"[21~")==0) { - strcpy(line,"N"); /* F10 == Next */ - } else if (strcmp(line+1,"[5~")==0) { - strcpy(line,"L UP"); /* PgUp */ - } else if (strcmp(line+1,"[6~")==0) { - strcpy(line,"L DOWN"); /* PgDn */ - } else { - continue; - } /* if */ - } /* if */ - if ((params=strchr(line,'\n'))!=NULL) - *params='\0'; /* strip newline character */ - if ((params=strchr(line,'\r'))!=NULL) - *params='\0'; /* strip carriage return character too */ - if (strlen(line)==0) { - if (terminal==0) { - int csrx,csry; - amx_wherexy(&csrx,&csry); - amx_gotoxy(1,csry-1); - amx_clreol(); - } /* if */ - strcpy(line,lastcommand); - } /* if */ - lastcommand[0]='\0'; - - result=sscanf(line,"%8s",command); - if (result<=0) { - if (terminal>=0) - listcommands(NULL,amxdbg,curline); - continue; - } /* if */ - params=strchr(line,' '); - params=(params!=NULL) ? skipwhitespace(params) : ""; - - if (stricmp(command,"?")==0) { - result=sscanf(line,"%*s %30s",command); - listcommands(result ? command : NULL,amxdbg,curline); - } else if (stricmp(command,"quit")==0) { - if (terminal>0) { - amx_clrscr(); - term_close(); - } /* if */ - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_rs232(0,0); - #endif - exit(0); - } else if (stricmp(command,"g")==0 || stricmp(command,"go")==0) { - if (isdigit(*params)) - break_set(amxdbg,params,0x1); - recentline=-1; - if (stricmp(params,"func")==0) - return STEPOUT; - return RUNNING; - } else if (stricmp(command,"s")==0 || stricmp(command,"step")==0) { - strcpy(lastcommand,"s"); - recentline=-1; - return STEPPING; - } else if (stricmp(command,"n")==0 || stricmp(command,"next")==0) { - strcpy(lastcommand,"n"); - recentline=-1; - return STEPOVER; - } else if (stricmp(command,"l")==0 || stricmp(command,"list")==0) { - /* first check a few hard cases */ - if (stricmp(params,"funcs")==0) { - for (i=0; ihdr->symbols; i++) { - if (amxdbg->symboltbl[i]->ident == iFUNCTN) { - const char *filename; - scroll_cmdwin(1); - amx_printf("%s",amxdbg->symboltbl[i]->name); - if (dbg_LookupFile(amxdbg,amxdbg->symboltbl[i]->address,&filename)==AMX_ERR_NONE) - amx_printf("\t(%s)",skippath(filename)); - //??? also say what automaton this function is part of - amx_printf("\n"); - } /* if */ - } /* for */ - } else if (stricmp(params,"states")==0) { - const char *statename=NULL; - cell *cptr; - if (amxdbg->hdr->automatons==0) { - scroll_cmdwin(1); - amx_printf("\t(no states)\n"); - } /* if */ - for (i=0; ihdr->automatons; i++) { - scroll_cmdwin(1); - amx_printf("%s",strlen(amxdbg->automatontbl[i]->name)==0 ? "(anonymous)" : amxdbg->automatontbl[i]->name); - /* read the variable at the address */ - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_read_rs232(amx,amxdbg->automatontbl[i]->address,1); - #endif - if (amx_GetAddr(amx,amxdbg->automatontbl[i]->address,&cptr)==AMX_ERR_NONE) - dbg_GetStateName(amxdbg,(int)*cptr,&statename); - amx_printf("\t-> %s\n",(statename==NULL) ? "(none)" : statename); - } /* for */ - } else if (stricmp(params,"on")==0) { - autolist=listlines; - watch_list(amx,amxdbg); - source_list(amxdbg,curline-autolist/2,autolist,curline); - } else if (stricmp(params,"off")==0) { - if (terminal<=0) - autolist=1; - else - amx_printf("\tCommand not supported on terminals\n"); - } else { - int lnum,numlines; - lnum=curline-(listlines/2-1); /* preset */ - numlines=listlines; - if (stricmp(params,"up")==0) { - lnum=curtopline-listlines; - if (lnum<0) - lnum=0; - } else if (stricmp(params,"down")==0) { - lnum=curtopline+listlines; - if (lnum>source_lines()-3) - lnum=source_lines()-3; - } else { - sscanf(line,"%*s %d %d",&lnum,&numlines); - lnum--; /* if user filled in a line number, subtract 1 */ - } /* if */ - #if !defined VT100 - if (terminal>0) { - int csrx,csry; - amx_wherexy(&csrx,&csry); - if (csry>=screenlines) - term_refresh(0); - } /* if */ - #endif - source_list(amxdbg,lnum,numlines,curline); - } /* if */ - } else if (stricmp(command,"break")==0 || stricmp(command,"tbreak")==0) { - if (*params=='\0') { - break_list(amxdbg); - } else { - result=break_set(amxdbg,params,stricmp(command,"tbreak")==0); - if (result<0) - amx_printf("Invalid breakpoint\n"); - else if (terminal>0) - source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); - } /* if */ - } else if (stricmp(command,"cbreak")==0) { - if (*params=='*') { - /* clear all breakpoints */ - break_init(); - } else if (isdigit(*params)) { - if (!break_clear(atoi(params))) - amx_printf("\tUnknown breakpoint\n"); - } else { - amx_printf("\tInvalid command\n"); - } /* if */ - if (terminal>0) - source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); - } else if (stricmp(command,"disp")==0 || stricmp(command,"d")==0) { - int idx[MAX_DIMS],dim; - dim=0; - memset(idx,0,sizeof idx); - if (*params=='\0') { - /* display all variables that are in scope */ - for (i=0; ihdr->symbols; i++) { - if (amxdbg->symboltbl[i]->ident!=iFUNCTN - && amxdbg->symboltbl[i]->codestart<=(ucell)amx->cip - && amxdbg->symboltbl[i]->codeend>(ucell)amx->cip) - { - scroll_cmdwin(1); - amx_printf("%s\t%s\t",(amxdbg->symboltbl[i]->vclass & DISP_MASK)>0 ? "loc" : "glb",amxdbg->symboltbl[i]->name); - display_variable(amx,amxdbg,amxdbg->symboltbl[i],idx,0); - amx_printf("\n"); - } /* if */ - } /* for */ - } else { - const AMX_DBG_SYMBOL *sym; - char *indexptr=strchr(params,'['); - char *behindname=NULL; - assert(dim==0); - while (indexptr!=NULL && dimcip,&sym)==AMX_ERR_NONE) { - scroll_cmdwin(1); - if (behindname!=NULL) - *behindname='['; - amx_printf("%s\t%s\t",(sym->vclass & DISP_MASK)>0 ? "loc" : "glb",params/*sym->name*/); - display_variable(amx,amxdbg,(AMX_DBG_SYMBOL*)sym,idx,dim); - amx_printf("\n"); - } else { - amx_printf("\tSymbol not found, or not a variable\n"); - } /* if */ - } /* if */ - } else if (stricmp(command,"set")==0) { - char varname[32]; - long index,value; - const AMX_DBG_SYMBOL *sym; - if (sscanf(params," %[^[ ][%ld] = %ld",varname,&index,&value)!=3) { - index=0; - if (sscanf(params," %[^= ] = %ld",varname,&value)!=2) - varname[0]='\0'; - } /* if */ - if (varname[0]!='\0') { - /* find the symbol with the given range with the smallest scope */ - if (dbg_GetVariable(amxdbg,varname,amx->cip,(const AMX_DBG_SYMBOL**)&sym)==AMX_ERR_NONE) - set_symbolvalue(amx,sym,(int)index,(cell)value); - } /* if */ - } else if (stricmp(command,"file")==0) { - if (*params=='\0') { - /* browse through the file table */ - for (i=0; ihdr->files; i++) { - scroll_cmdwin(1); - amx_printf("%s\n",amxdbg->filetbl[i]->name); - } /* for */ - } else { - /* find the file */ - for (i=0; ihdr->files; i++) - if (stricmp(params,amxdbg->filetbl[i]->name)==0 || stricmp(params,skippath(amxdbg->filetbl[i]->name))==0) - break; - if (ihdr->files) { - if (curfilename!=amxdbg->filetbl[i]->name) { - curfilename=amxdbg->filetbl[i]->name; - curline=0; - if (cursource!=NULL) - source_free(cursource); - assert(amxdbg->filetbl[i]->name!=NULL); - cursource=source_load(amxdbg->filetbl[i]->name); - if (cursource==NULL) { - amx_printf("\tSource file not found (or insufficient memory)\n"); - continue; - } /* if */ - } /* if */ - } else { - amx_printf("\tunknown file\n"); - } /* if */ - } /* if */ - } else if (stricmp(command,"term")==0) { - int new_term=-1; - int columns=screencolumns; - int lines=screenlines+1; - if (stricmp(params,"off")==0) - new_term=0; - else if (stricmp(params,"on")==0) - new_term=1; - else if (sscanf(params,"%d %d",&columns,&lines)==2) - new_term=2; - if (new_term==-1) { - amx_printf("\tTerminal support is %s\n",terminal>0 ? "ON" : "OFF"); - } else if (terminal!=new_term) { - curtopline=0; - if (terminal>0) - term_close(); - if (new_term && amx_termctl(0,0)) { - term_open(columns,lines); - autolist=listlines; - } else if (new_term) { - amx_printf("\tTerminal not supported\n"); - } /* if */ - watch_list(amx,amxdbg); - source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); - } /* if */ - } else if (stricmp(command,"output")==0) { - if (term_switch(0)) /* switch back to user screen */ - amx_getch(); /* if it worked, wait for a key press */ - term_switch(1); /* switch to debugger screen */ - } else if (stricmp(command,"transfer")==0) { - #if defined NO_REMOTE - amx_printf("\tRemote file transfer is not supported.\n"); - #else - if (remote!=REMOTE_RS232) { - amx_printf("\tRemote file transfer is not supported.\n"); - } else { - if (remote_transfer_rs232(amx_filename)) { - /* restart the debugger */ - longjmp(restart_buf,1); - } else { - amx_printf("\tRemote file transfer failed.\n"); - } /* if */ - } /* if */ - #endif - } else if (stricmp(command,"type")==0) { - char symname[sNAMEMAX+1],*ptr; - int len; - for (ptr=params; *ptr!='\0' && *ptr!=' ' && *ptr!='\t'; ptr++) - /* nothing */; - len=(int)(ptr-params); - if (len==0 || len>sNAMEMAX) { - amx_printf("\tInvalid (or missing) symbol name\n"); - } else { - AMX_DBG_SYMBOL *sym; - strncpy(symname,params,len); - symname[len]='\0'; - params=skipwhitespace(ptr); - /* look up the symbol */ - if (dbg_GetVariable(amxdbg,symname,amx->cip,(const AMX_DBG_SYMBOL**)&sym)==AMX_ERR_NONE) { - assert(sym!=NULL); - if (stricmp(params,"std")==0) { - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_DEFAULT); - } else if (stricmp(params,"string")==0) { - /* check array with single dimension */ - if (!(sym->ident==iARRAY || sym->ident==iREFARRAY) || sym->dim!=1) - amx_printf("\t\"string\" display type is only valid for arrays with one dimension\n"); - else - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_STRING); - } else if (stricmp(params,"bin")==0) { - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_BIN); - } else if (stricmp(params,"hex")==0) { - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_HEX); - } else if (stricmp(params,"fixed")==0) { - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_FIXED); - } else if (stricmp(params,"float")==0) { - sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_FLOAT); - } else { - amx_printf("\tUnknown (or missing) display type\n"); - } /* if */ - watch_list(amx,amxdbg); - } else { - amx_printf("\tUnknown symbol \"%s\"\n",symname); - } /* if */ - } /* if */ - } else if (stricmp(command,"w")==0 || stricmp(command,"watch")==0) { - /* see whether this is a new watch, or whether one is replaced */ - if (isdigit(*params)) { - i=atoi(params); - params=skipvalue(params); - } else { - i = -1; - } /* if */ - if (strlen(params)==0) { - amx_printf("Missing variable name\n"); - continue; - } /* if */ - /* is there space for another watch? (only when using terminal support) */ - if (i<0 && terminal>0 && watch_count()>=WATCHLINES) { - int w=watch_count()+1; /* new number of watches */ - int l=listlines; - int c=screenlines-w-l-3; - if (c<=1) { - /* command window cannot shrink, shrink code listing instead */ - c=2; - l=screenlines-w-c-3; - if (l<5) { - /* code listing cannot shrink either, give a message */ - amx_printf("Too many watches, sorry\n"); - continue; - } /* if */ - } /* if */ - watchlines=w; - listlines=l; - term_refresh(0); /* reset */ - source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); - } /* if */ - result=watch_set(i,params); - if (result>=0) - watch_list(amx,amxdbg); - else - amx_printf("Invalid watch\n"); - } else if (stricmp(command,"cw")==0 || stricmp(command,"cwatch")==0) { - if (*params=='*') { - /* clear all watches */ - watch_init(); - } else if (isdigit(*params)) { - watch_clear(atoi(params)); - } else { - amx_printf("\tInvalid command syntax, use \"? cw\" for details\n"); - } /* if */ - if (terminal>0 && watchlines>WATCHLINES) { - int w,l,c; - w=watch_count(); /* new number of watches */ - if (wfrm>lastfrm) - runmode=STEPPING; - - /* when running, check the breakpoints */ - breaknr=-1; - if (runmode!=STEPPING && runmode!=STEPOVER) { - /* check breakpoint address */ - breaknr=break_check(amx); - if (breaknr<0) { - /* just print the address at the bottom line, so one can trace where the - * the program is - */ - if (terminal>0) { - term_csrsave(); - amx_gotoxy(1,screenlines); - amx_printf("cip: %08lx",(long)amx->cip); - term_csrrestore(); - } /* if */ - return AMX_ERR_NONE; - } /* if */ - runmode=STEPPING; - } /* for */ - - /* get more information from the remote target */ - #if !defined NO_REMOTE - if (remote!=REMOTE_NONE) - remote_sync_rs232(amx); - #endif - - /* get the debug information header */ - err = amx_GetUserData(amx, AMX_USERTAG('P','D','b','g'), (void**)&amxdbg); - if (err!=AMX_ERR_NONE) - return AMX_ERR_DEBUG; - - /* try to avoid halting on the same line twice */ - dbg_LookupLine(amxdbg,amx->cip,&line); - if (line==lastline) - return AMX_ERR_NONE; - lastline=line; - - /* check whether we are stepping through a sub-function */ - if (runmode==STEPOVER) { - assert(lastfrm!=0); - if (amx->frmcip,&filename); - - /* check breakpoints */ - msg=""; - i=0; - if (breaknr==0) { - msg="STOPPED at line %d\n"; - i=(int)line+1; - } else if (breaknr>0) { - msg="STOPPED at breakpoint %d\n"; - i=breaknr; - } /* if */ - term_switch(1); /* switch to the debugger console */ - if (msg!=NULL) - amx_printf(msg,i); /* print breakpoint number */ - assert(filename!=NULL); - if (curfilename!=filename) { - curfilename=filename; - if (cursource!=NULL) - source_free(cursource); - cursource=source_load(filename); - if (cursource==NULL) { - amx_printf("\nCritical error: source file not found (or insufficient memory)\n"); - exit(1); - } else if (terminal<=0) { - amx_printf("(%s)\n",curfilename); - } /* if */ - } /* if */ - #if !defined VT100 - if (terminal>0) { - int csrx,csry; - amx_wherexy(&csrx,&csry); - if (csry>=screenlines) - term_refresh(0); - } /* if */ - #endif - - assert(cursource[(int)line]!=NULL); - watch_list(amx,amxdbg); - source_list(amxdbg,gettopline((int)line,(int)line-autolist/2),autolist,(int)line); - runmode=docommand(amx,amxdbg,(int)line); - term_switch(0); /* switch back to the program output */ - if (runmode==STEPOVER) - lastfrm=amx->frm; /* step OVER functions (so save the stack frame) */ - - return AMX_ERR_NONE; -} - -#if !defined amx_Init - -extern int CreateConsoleThread(void); - -static void *loadprogram(AMX *amx,const char *filename) -{ - FILE *fp; - AMX_HEADER hdr; - void *program; - - if ((fp = fopen(filename,"rb")) != NULL) { - fread(&hdr, sizeof hdr, 1, fp); - amx_Align32((uint32_t *)&hdr.stp); - amx_Align32((uint32_t *)&hdr.size); - if ((program = malloc((int)hdr.stp)) != NULL) { - rewind(fp); - fread(program, 1, (int)hdr.size, fp); - fclose(fp); - memset(amx, 0, sizeof *amx); - if (amx_Init(amx,program) == AMX_ERR_NONE) - return program; - free(program); - } /* if */ - } /* if */ - return NULL; -} - -static int loaddbginfo(AMX *amx,AMX_DBG *amxdbg,const char *filename) -{ - FILE *fp; - int err; - - /* load the debug information from the file */ - if ((fp = fopen(filename,"rb")) == NULL) - return AMX_ERR_DEBUG; - err = dbg_LoadInfo(amxdbg,fp); - fclose(fp); - if (err != AMX_ERR_NONE) - return err; - - /* attach the debug information structure to the abstract machine */ - err = amx_SetUserData(amx, AMX_USERTAG('P','D','b','g'), amxdbg); - return err; -} - -int main(int argc,char *argv[]) -{ -extern AMX_NATIVE_INFO core_Natives[]; -extern AMX_NATIVE_INFO console_Natives[]; - - AMX amx; - AMX_DBG amxdbg; - cell ret; - int err,i; - void *program=NULL; - unsigned short flags; - char *ptr; - - #if !defined AMX_NODYNALOAD && defined ENABLE_BINRELOC && (defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) - /* see www.autopackage.org for the BinReloc module */ - if (br_init(NULL)) { - char *libroot=br_find_exe_dir(""); - setenv("AMXLIB",libroot,0); - free(libroot); - } /* if */ - #endif - - amx_printf("Pawn command line debugger\n\n"); - if (setjmp(restart_buf)) { - assert(program!=NULL); - amx_InternalDebugProc(NULL); /* clean up debug information */ - if (terminal>0) - amx_clrscr(); - terminal=0; - dbg_FreeInfo(&amxdbg); - free(program); - } /* if */ - - /* get filename */ - if (strlen(amx_filename)==0) { - if (argc >= 2) { - strcpy(amx_filename,argv[1]); - } else { - amx_printf("File: "); - amx_gets(amx_filename,sizeof amx_filename); - if ((ptr=strchr(amx_filename,'\n'))!=NULL) - *ptr='\0'; /* strip newline characters */ - } /* if */ - } /* if */ - if (strlen(amx_filename)==0) - return 1; - if ((program = loadprogram(&amx,amx_filename)) == NULL) { - /* try adding an extension */ - strcat(amx_filename, ".amx"); - if ((program = loadprogram(&amx,amx_filename)) == NULL) { - amx_printf("Cannot load the program file \"%s\"\n", amx_filename); - return 1; - } /* if */ - } /* if */ - /* switch the current directory to that of the debugged script */ - if ((ptr=strrchr(amx_filename,DIRSEP_CHAR))!=NULL) { - char dir[_MAX_PATH]; - int len=(int)(ptr-amx_filename); - if (len0) - amx_clrscr(); - #if !defined NO_REMOTE - if (remote==REMOTE_RS232) - remote_rs232(0,0); - #endif - if (err != AMX_ERR_NONE) - amx_printf("Run time error %d\n", err); - else - amx_printf("Normal termination, return value %ld\n", (long)ret); - - //??? option for restart - - dbg_FreeInfo(&amxdbg); - free(program); - - return 0; -} - -#endif /* !defined amx_Init */ +/* Pawn debugger + * + * Simple (minimalistic) debugger that supports source-level debugging with + * several interfaces: + * - a console interface with terminal support: VT100/ANSI terminal (or the + * xterm terminal emulator), Win32 pseudo-terminal and curses (for the Apple + * Macintosh) + * - hooks to use other pseudo-terminals, such as the Win32 GUI terminal and + * the GraphApp terminal, both of which support Unicode + * - a GDB-style "streaming" interface, to hook GUI shells to the debugger (in + * a similar way as how KDbg and DDD do this with GDB) + * - a dual-screen mode where the abstract machine uses a different (pseudo) + * terminal than the debugger --especially useful when combined with a GUI + * shell + * - ability to do remote debugging over an RS232 connection + * + * + * Copyright (c) ITB CompuPhase, 1998-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: pawndbg.c 3662 2006-11-07 08:44:33Z thiadmer $ + * + * + * Command line options: + * -rs232=1 set remote debugging over a serial line (port number + * may be 1,2,... for Windows; 0,1,... for Linux) + * -term=x,y set terminal size + * -term=off force terminal off (if environments where it is on by + * default) + * -term=hide hide the window with the terminal, useful when using + * a "dual terminal" debugger from an IDE + * -transfer transfer file on start-up to the remote host (this + * requires remote debugging to be set first) + * -quit terminate debugger immediately, used when the debugger + * needs only to transfer a file: + * pawndbg myprog.amx -rs232=1 -transfer -quit + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined READLINE + #define __READLINE_IMPORT__ 1 + #define __P(protos) protos + #include + #include +#endif +#include "osdefs.h" /* for _MAX_PATH */ +#include "amx.h" +#include "amxdbg.h" + +#if defined __WIN32 || defined _WIN32 || defined __WIN32__ || defined __MSDOS__ || defined __WATCOMC__ + #include + #if defined __WIN32 || defined _WIN32 || defined __WIN32__ || defined __WATCOMC__ + #if !defined __WIN32__ + #define __WIN32__ 1 + #endif + #include + #if !defined amx_Init && !defined NO_WIN32_CONSOLE && !defined AMX_TERMINAL + #define WIN32_CONSOLE + #endif + #endif +#elif !defined macintosh + #include "../linux/getch.h" + #include + #include + #include +#endif + +#if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) + #include /* from BinReloc, see www.autopackage.org */ +#endif + + +#define MAXSTACKTRACE 128 +#define MAXLINELENGTH 128 +#define MAX_DIMS 3 /* number of array dimensions */ +#define TABSIZE 8 +#define EATLINE 5 /* the number of characters for the line number */ +#define STD_COLUMNS 80 /* number of characters that fit on a line */ +#if defined amx_Init + #define STD_LINES 29 /* number of lines that fit on a screen */ +#else + #define STD_LINES 24 /* number of lines that fit on a screen */ +#endif +#define WATCHLINES 4 /* default number of watches displayed */ +#define LISTLINES (STD_LINES - WATCHLINES - 10) /* default code lines */ + +#if (defined AMX_TERMINAL || defined amx_Init) && !defined DBG_STREAMTERM + /* required functions are implemented elsewhere */ + int amx_printf(char *,...); + int amx_putchar(int); + int amx_fflush(void); + int amx_getch(void); + char *amx_gets(char *,int); + int amx_termctl(int,int); + void amx_clrscr(void); + void amx_clreol(void); + void amx_gotoxy(int x,int y); + void amx_wherexy(int *x,int *y); + unsigned int amx_setattr(int foregr,int backgr,int highlight); + void amx_console(int columns, int lines, int flags); + #if defined amx_Init + #define STR_PROMPT "dbg\xbb " + #define CHR_HLINE '\x97' + #else + #define STR_PROMPT "dbg> " + #define CHR_HLINE '-' + #endif + #define CHR_VLINE '|' +#elif defined VT100 || defined LINUX || defined ANSITERM + /* ANSI/VT100 terminal, or shell emulating "xterm" */ + #if !defined VT100 && !defined ANSITERM && defined LINUX + #define VT100 + #endif + #define amx_printf printf + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + int amx_termctl(int,int); + void amx_clrscr(void); + void amx_clreol(void); + void amx_gotoxy(int x,int y); + void amx_wherexy(int *x,int *y); + unsigned int amx_setattr(int foregr,int backgr,int highlight); + void amx_console(int columns, int lines, int flags); + #define STR_PROMPT "dbg> " + #define CHR_HLINE '-' + #define CHR_VLINE '|' +#elif defined WIN32_CONSOLE + /* Win32 console */ + #define amx_printf printf + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + int amx_termctl(int,int); + void amx_clrscr(void); + void amx_clreol(void); + void amx_gotoxy(int x,int y); + void amx_wherexy(int *x,int *y); + unsigned int amx_setattr(int foregr,int backgr,int highlight); + void amx_console(int columns, int lines, int flags); + #define STR_PROMPT "dbg> " + #define CHR_HLINE '\xc4' + #define CHR_VLINE '\xb3' +#elif defined USE_CURSES + /* Use the "curses" library to implement the console */ + const int _False = 0; /* to avoid compiler warnings */ + #define amx_printf printw + #define amx_putchar(c) addch(c) + #define amx_fflush() (0) + #define amx_getch() getch() + #define amx_gets(s,n) getnstr(s,n) + #define amx_clrscr() (void)(0) + #define amx_clreol() (void)(0) + #define amx_gotoxy(x,y) (void)(0) + #define amx_wherexy(x,y) (*(x)=*(y)=0) + #define amx_setattr(c,b,h) (_False) + #define amx_termctl(c,v) (_False) + #define amx_console(c,l,f) (void)(0) + #define STR_PROMPT "dbg> " + #define CHR_HLINE '-' + #define CHR_VLINE '|' +#else + /* assume a streaming terminal; limited features (no colour, no cursor + * control) + */ + #define amx_printf printf + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_clrscr() (void)(0) + #define amx_clreol() (void)(0) + #define amx_gotoxy(x,y) ((void)(x),(void)(y),(void)(0)) + #define amx_wherexy(x,y) (*(x)=*(y)=0) + #define amx_setattr(c,b,h) ((void)(c),(void)(b),(void)(h),(0)) + #define amx_termctl(c,v) ((void)(c),(void)(v),(0)) + #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f),(void)(0)) + #define STR_PROMPT "dbg> " + #define CHR_HLINE '-' + #define CHR_VLINE '|' +#endif +#define CHR_CURLINE '*' +#if defined VT100 + #define CHR_HLINE_VT100 'q' // in alternate font + #define CHR_VLINE_VT100 'x' +#endif + +enum { + BP_NONE, + BP_CODE, + BP_DATA, /* ??? not implemented */ + /* --- */ + BP_TYPES +}; + +enum { + DISP_DEFAULT = 0x10, + DISP_STRING = 0x20, + DISP_BIN = 0x30, /* ??? not implemented */ + DISP_HEX = 0x40, + DISP_BOOL = 0x50, + DISP_FIXED = 0x60, + DISP_FLOAT = 0x70, +}; +#define DISP_MASK 0x0f + +typedef struct tagBREAKPOINT { + struct tagBREAKPOINT *next; + int type; /* one of the BP_xxx types */ + ucell addr; /* address (in code or data segment) */ + const char *name; /* name of the symbol (function) */ + int number; /* sequential breakpoint number (to refer to the breakpoint) */ +} BREAKPOINT; + +typedef struct tagNAMELIST { + struct tagNAMELIST *next; + char *name; + int number; +} NAMELIST; + +enum { + REMOTE_NONE, /* this means "not remote" */ + REMOTE_RS232, + REMOTE_UDP, /* ??? not implemented */ + /* --- */ + REMOTE_TYPES +}; + +enum { + STEPPING, /* step into functions */ + STEPOVER, /* step over functions */ + STEPOUT, /* run until the function returns */ + RUNNING, /* just run */ +}; +static int runmode; /* running or tracing */ + +static char amx_filename[_MAX_PATH]; +static const char *curfilename; /* pointer to the name of the "active" file */ +static char **cursource; +static int autolist=1; +static int screencolumns=STD_COLUMNS; +static int screenlines=STD_LINES; +static int listlines=LISTLINES; +static int watchlines=WATCHLINES; +static BREAKPOINT breakpoints={ NULL }; +static NAMELIST watches={ NULL }; +static int curtopline; /* current line that is on top in the list */ +static int recentline=-1; +static int terminal=0; +static int remote=REMOTE_NONE; +static char chr_hline=CHR_HLINE; +static char chr_vline=CHR_VLINE; +#if defined __WIN32__ + HANDLE hCom=INVALID_HANDLE_VALUE; +#elif !defined __MSDOS__ + int fdCom=-1; + struct termios oldtio, newtio; +#endif +static char remote_pendingbuf[30]; +static int remote_pendingsize=0; +static jmp_buf restart_buf; + + +static void draw_hline(int forceinit) +{ +static char hline_str[256] = ""; /* number of columns in a window should + * never exceed 255 */ + + /* initialize the string, if not yet done */ + if (forceinit || strlen(hline_str)==0) { + memset(hline_str,chr_hline,sizeof hline_str); + hline_str[screencolumns]='\0'; + } /* if */ + + #if defined VT100 + if (terminal>0) + amx_printf("\016"); /* SO code to select the graphic set */ + #endif + + amx_printf(hline_str); + + #if defined VT100 + if (terminal>0) + amx_printf("\017"); /* SI code to select the standard set */ + #endif + + amx_fflush(); +} + +#if defined WIN32_CONSOLE +void win32_getscreensize(int *width,int *height) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + assert(width!=NULL && height!=NULL); + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi); + *width=(int)csbi.dwSize.X; + *height=(int)(csbi.srWindow.Bottom-csbi.srWindow.Top+1); +} +#endif + +static int csrsave_x, csrsave_y; +#if !defined NDEBUG + static int csrsave_flag=0; +#endif + +static void term_csrsave(void) +{ + assert(terminal==1); + #if !defined NDEBUG + assert(csrsave_flag==0); + csrsave_flag++; + #endif + amx_wherexy(&csrsave_x,&csrsave_y); +} + +static void term_csrrestore(void) +{ + assert(terminal==1); + #if !defined NDEBUG + assert(csrsave_flag==1); + csrsave_flag--; + #endif + amx_gotoxy(csrsave_x,csrsave_y); +} + +static void term_refresh(int recalc) +{ + assert(terminal==1); + + if (recalc) { + listlines=(screenlines-watchlines)/2; + if (listlines0) { + SMALL_RECT ScrollRectangle; + COORD Destination; + CHAR_INFO Fill; + int screenlines,screencolumns; + + win32_getscreensize(&screencolumns,&screenlines); + ScrollRectangle.Left=0; + ScrollRectangle.Top=(short)(top-1); + ScrollRectangle.Right=(short)(screencolumns-1); + ScrollRectangle.Bottom=(short)(bottom-1); + Destination.X=0; + Destination.Y=(short)(top-1-lines); + Fill.Char.UnicodeChar=' '; + Fill.Attributes=0x07; + ScrollConsoleScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE), + &ScrollRectangle,&ScrollRectangle, + Destination,&Fill); + } /* if */ + #else + (void)top; + (void)bottom; + (void)lines; + #endif +} + +static int term_switch(int number) +{ + if (terminal>0) + return amx_termctl(2,number); + return 0; +} + +static void source_free(char **source) +{ + int i; + + assert(source!=NULL); + for (i=0; source[i]!=NULL; i++) + free(source[i]); + free(source); +} + +static char **source_load(const char *filename) +{ + char **source; + FILE *fp; + char line[256]; + int lines,i; + + /* open the file, number of characters */ + assert(filename!=NULL); + if ((fp=fopen(filename,"rt"))==NULL) + return NULL; + lines=0; + while (fgets(line,sizeof(line),fp)!=NULL) + lines++; + + /* allocate memory, reload the file */ + if ((source=(char **)malloc((lines+1)*sizeof(char *)))==NULL) { + fclose(fp); + return NULL; + } /* if */ + for (i=0; i<=lines; i++) /* initialize, so that source_free() works */ + source[i]=NULL; + rewind(fp); + i=0; + while (fgets(line,sizeof(line),fp)!=NULL) { + assert(i0) { + term_csrsave(); + amx_gotoxy(1,watchlines+2); + numlines=listlines; /* override user setting */ + } /* if */ + + if (startline<0) + startline=0; + lastline=startline+numlines; + curtopline=startline; /* save line that is currently displayed at the top */ + + /* seek to line number from the start (to avoid displaying something + * beyond the file) + */ + for (result=0; cursource[result]!=NULL && result0) + amx_clreol(); + if ((int)strlen(cursource[startline])>screencolumns-EATLINE) { + c1=cursource[startline][screencolumns-EATLINE-1]; + c2=cursource[startline][screencolumns-EATLINE]; + cursource[startline][screencolumns-EATLINE-1] = '\n'; + cursource[startline][screencolumns-EATLINE] = '\0'; + } /* if */ + (void)amx_setattr(-1,-1,startline==curline); + if (terminal<=0) { + if (startline==curline) + amx_printf("%3d%c %s",startline+1,CHR_CURLINE,cursource[startline]); + else + amx_printf("%3d %s",startline+1,cursource[startline]); + } else { + /* see if there is a breakpoint on the line */ + if (amxdbg!=NULL && dbg_GetLineAddress(amxdbg,startline,curfilename,&address)==AMX_ERR_NONE) { + BREAKPOINT *bp; + for (bp=breakpoints.next; bp!=NULL; bp=bp->next) { + assert(bp->type!=BP_NONE); + if (bp->type==BP_CODE && bp->addr==address) + (void)amx_setattr(-1,0x1,1); + } /* for */ + } /* if */ + amx_printf("%4d",startline+1); + #if defined VT100 + if (terminal>0) + amx_printf("\016"); /* SO code to select the graphic set */ + #endif + amx_printf("%c",chr_vline); + #if defined VT100 + if (terminal>0) + amx_printf("\017"); /* SI code to select the standard set */ + #endif + (void)amx_setattr(-1,0x0,startline==curline); + amx_printf("%s",cursource[startline]); + } /* if */ + if (c1!='\0') { + cursource[startline][screencolumns-EATLINE-1] = c1; + cursource[startline][screencolumns-EATLINE] = c2; + } /* if */ + startline++; + } /* while */ + } /* if */ + + if (terminal>0) { + (void)amx_setattr(-1,-1,0); + while (startline=curtopline+listlines) + return topline; + return curtopline; +} + +static char *skipwhitespace(const char *str) +{ + while (*str==' ' || *str=='\t') + str++; + return (char*)str; +} + +static char *skipvalue(const char *str) +{ + while (isdigit(*str)) + str++; + return skipwhitespace(str); +} + +static const char *skippath(const char *str) +{ + const char *p1,*p2; + + /* DOS/Windows pathnames */ + if ((p1=strrchr(str,'\\'))!=NULL) + p1++; + else + p1=str; + if (p1==str && p1[1]==':') + p1=str+2; + /* Unix pathnames */ + if ((p2=strrchr(str,'/'))!=NULL) + p2++; + else + p2=str; + return p1>p2 ? p1 : p2; +} + +static void scroll_cmdwin(int lines) +{ + if (terminal>0) { + int csrx,csry; + amx_wherexy(&csrx,&csry); + if (csry==screenlines) { + term_scroll(watchlines+listlines+3,screenlines-1,lines); + amx_gotoxy(csrx,csry-1); + } /* if */ + } /* if */ +} + +#if !defined NO_REMOTE +/* For the remote debugging capabilities, the "device" is the apparatus/PC on + * which the script runs and the "host" is the PC on which the debugger runs. + * Both the device and the host load the script. The device typically does not + * load the debugging info. The host would not really need to load the opcodes, + * but its taks becomes a lot simpler when it has locally an image of what runs + * on the device. + * + * Handshake + * + * The first step, after opening the connection, is the handshake. For that, + * the host must send a packet with the single character "!". Since the device + * may not be "on-line" immediately and packets may get lost, the host should + * repeat sending these characters until the device reponds. + * + * When the device starts up and loads a script, it should check whether this + * script contains debugging information (even if it does not load the debugging + * information). If debugging information is present, it should check for the + * reception of packets with the character "!". Upon reception, it replies with + * a return packed with the character "@". The device should have a time-out on + * the polling loop for receivng the "!" packets, because the script should run + * normally when no debugger is attached. + * + * Running + * + * After entering in debug mode, the device will send a packed with the + * character "@" followed by the new instruction pointer address (CIP) at every + * "BREAK" instruction. The device then waits for a response from the host (the + * script is halted). To continue running, the host simply sends a packet with + * the single character "!". + * + * If, instead of allowing the device to continue running the script, you want + * to halt execution (e.g. because a breakpoint is reached), the first command + * that the host should send is the string "?R\n", where \n stands for a + * newline. Upon reception, the device must reply with a more complete state of + * the abstract machine: the stack address, the frame pointer and the heap top. + * + * With the few registers sent over, the state of the abstract machine (on the + * device) is now known to the host, but it does not know the contents of the + * data memory on the device: the values of global and local variables. It was + * estimated too costly to transfer all of this data memory at every "stop". + * Instead, the host must query for the range of memory that it wants. It does + * so by sending a packet with the contents: + * + * ?Maddress,size\n + * + * where "address" and "size" are hexadecimal numbers. The size is in cells; + * the address does not have to be cell-aligned, but it typically is. The + * device responds to this command by sending a packet with an "@" character + * followed by one or more hexadecimal values, separated with commas and + * terminated with a newline. + * + * Other (suggested) commands are: + * + * ?M address,size + * ?R (registers) send the status of FRM, STK and HEA + * + * ?G name\n (get) retrieve file + * ?P size,name\n (put) send over new script (or other file) + * ?B value\n (baud) set baud rate (restarts the connection) + * ?L\n (list) retrieve a list of files on the device + * + * ?U\n (unhook) close the debugger down + * ?U*\n unhook the debugger and restart (do this after a transfer of an updated script) + * + * When sending over scripts (or perhaps other files), the reply of the + * ?P command carries the block size that the debugger should use to send + * the data (like all numbers in the debugger interface, this value is in + * hexadecimal). For example, if the remote host replies with @100, the debugger + * should transfer the file in blocks of 256 bytes. The file itself is then + * sent as binary data (and in blocks). After each block, the debugger must + * wait for the reply. + * + * Before sending the block, the debugger sends a "start code". This code is + * either an ACK (ASCII 6) or a NAK (ASCII 21). If the debugger sends an ACK, + * the block that follows is the next sequential block of data for the file. If + * it sends a NAK, the block that follows is a repeated send of the preceding block. + * The debugger should resend a block on a checksum mismatch. After sending a + * block, the remote host replies. The checksum of the block is in the value + * that follows the @ sign. Due to the way that the checksum is computed, the + * checksum is never zero. The debugger can now compare the checksum with the + * one it calculated itself. On a mismatch, the debugger can then send a NAK + * and resend the block. + * + * The checksum is the "Internet checksum", but as an 8-bit variant. This + * checksum is often called the "one's complement", because it wraps the carry + * around on overflow. + * + * + * Notes + * + * The rationale for the debugger communication protocol is performance, and + * especially attempting to avoid that the communication link becomes the bottleneck. Therefore, the + * device and the host send over as little as possible until it is known that + * the device should be halted. In the case of a serial link, data is sent byte + * by byte. When the device is halted (on a breakpoint) and + * waiting for user interaction, performance is no longer as important. + * + * There is a special case in the handshaking stage when the device is already + * running in debug mode (because an earlier session was aborted). In that case, + * it will respond with "@" and the new instruction pointer (CIP) address. + */ + +static int send_rs232(const char *buffer, int len) +{ + unsigned long size; + + #if defined __WIN32__ + WriteFile(hCom,buffer,len,&size,NULL); + #else + size=write(fdCom,buffer,len); + #endif + assert((unsigned long)len==size); + return size; +} + +static int getresponse_rs232(char *buffer, int buffersize, long retries) +{ + int len=0; + unsigned long size; + + do { + + #if defined __WIN32__ + ReadFile(hCom,buffer+len,buffersize-len,&size,NULL); + #else + size=read(fdCom,buffer+len,buffersize-len); + #endif + len+=size; + + /* throw away dummy input characters */ + while (buffer[0]!='@' && len>0) { + int idx; + for (idx=0; idx=0); + + return len; +} + +static int settimestamp_rs232(unsigned long sec1970) +{ + char str[30]; + + #if defined __WIN32__ + assert(hCom!=INVALID_HANDLE_VALUE); + #else + assert(fdCom>=0); + #endif + sprintf(str,"?T%lx\n",sec1970); + send_rs232(str,strlen(str)); + getresponse_rs232(str,sizeof str,10); + return strlen(str)>0 && atoi(str+1)>0; +} + +static int remote_rs232(int port,int baud) +{ + #if defined __WIN32__ + DCB dcb; + COMMTIMEOUTS commtimeouts; + #endif + unsigned long size,cip; + char buffer[40]; + + /* optionally issue a "close-down" request for the remote host */ + #if defined __WIN32__ + if (baud==0 && hCom!=INVALID_HANDLE_VALUE) { + #else + if (baud==0 && fdCom>=0) { + #endif + sprintf(buffer,"?U\n"); + #if defined __WIN32__ + WriteFile(hCom,buffer,strlen(buffer),&size,NULL); + #else + write(fdCom,buffer,strlen(buffer)); + #endif + /* do not wait for a reply */ + } /* if */ + + /* set up the connection */ + #if defined __WIN32__ + if (hCom!=INVALID_HANDLE_VALUE) + CloseHandle(hCom); + if (baud==0) + return 0; + sprintf(buffer,"com%d:",port); + hCom=CreateFile(buffer,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + if (hCom==INVALID_HANDLE_VALUE) + return 0; + GetCommState(hCom,&dcb); + dcb.BaudRate=baud; + dcb.ByteSize=8; + dcb.StopBits=ONESTOPBIT; + dcb.Parity=NOPARITY; + dcb.fBinary=TRUE; + dcb.fDtrControl=DTR_CONTROL_DISABLE; + dcb.fOutX=FALSE; + dcb.fInX=FALSE; + dcb.fNull=FALSE; + dcb.fRtsControl=RTS_CONTROL_DISABLE; + SetCommState(hCom,&dcb); + SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY); + commtimeouts.ReadIntervalTimeout =0x7fffffff; + commtimeouts.ReadTotalTimeoutMultiplier =0; + commtimeouts.ReadTotalTimeoutConstant =1; + commtimeouts.WriteTotalTimeoutMultiplier=0; + commtimeouts.WriteTotalTimeoutConstant =0; + SetCommTimeouts(hCom,&commtimeouts); + #else + if (fdCom>=0) { + tcflush(fdCom,TCOFLUSH); + tcflush(fdCom,TCIFLUSH); + tcsetattr(fdCom,TCSANOW,&oldtio); + close(fdCom); + } /* if */ + if (baud==0) + return 0; + sprintf(buffer,"/dev/ttyS%d",port); + fdCom = open(buffer,O_RDWR|O_NOCTTY|O_NONBLOCK); + if (fdCom<0) + return 0; + /* clear input & output buffers, then switch to "blocking mode" */ + tcflush(fdCom,TCOFLUSH); + tcflush(fdCom,TCIFLUSH); + fcntl(fdCom,F_SETFL,fcntl(fdCom,F_GETFL) & ~O_NONBLOCK); + + tcgetattr(fdCom,&oldtio); /* save current port settings */ + bzero(&newtio,sizeof newtio); + newtio.c_cflag=CS8 | CLOCAL | CREAD; + + switch (baud) { + case 1152000: newtio.c_cflag |= B1152000; break; + case 576000: newtio.c_cflag |= B576000; break; + case 230400: newtio.c_cflag |= B230400; break; + case 115200: newtio.c_cflag |= B115200; break; + case 57600: newtio.c_cflag |= B57600; break; + case 38400: newtio.c_cflag |= B38400; break; + case 19200: newtio.c_cflag |= B19200; break; + case 9600: newtio.c_cflag |= B9600; break; + default: return 0; + } /* switch */ + newtio.c_iflag=IGNPAR | IGNBRK | IXON | IXOFF; + newtio.c_oflag=0; + + /* set input mode (non-canonical, no echo,...) */ + newtio.c_lflag=0; + + cfmakeraw(&newtio); + newtio.c_cc[VTIME]=1; /* inter-character timer used */ + newtio.c_cc[VMIN] =0; /* blocking read until 0 chars received */ + tcflush(fdCom,TCIFLUSH); + tcsetattr(fdCom,TCSANOW,&newtio); + #endif + + /* handshake, send token and wait for a reply */ + do { + #if defined __WIN32__ + WriteFile(hCom,"!",1,&size,NULL); + Sleep(100); + do { + ReadFile(hCom,buffer,1,&size,NULL); + } while (size>0 && buffer[0]!='@'); + Sleep(100); + /* read remaining buffer (if any) */ + ReadFile(hCom,buffer+1,sizeof buffer - 1,&size,NULL); + size++; /* add size of the handshake character */ + #else + write(fdCom,"!",1); + usleep(100*1000); + do { + size=read(fdCom,buffer,1); + } while (size>0 && buffer[0]!='@'); + usleep(100*1000); + /* read remaining buffer (if any) */ + size=read(fdCom,buffer+1,sizeof buffer - 1); + size++; /* add size of the handshake character */ + #endif + } while (size==0 || buffer[0]!='@'); + if (sscanf(buffer,"@%lx",&cip)>0) { + /* if the script is already running, give a "go" command */ + #if defined __WIN32__ + WriteFile(hCom,"!",1,&size,NULL); + #else + write(fdCom,"!",1); + #endif + } else if (size>1 && size=sizeof buffer - 1) { + amx_printf("%s",buffer); + offs=0; + state=SCAN; + } /* if */ + /* read a buffer, see if we can find the start condition */ + do { + if (remote_pendingsize>0) { + assert(remote_pendingsize=size) { + amx_printf("%s",buffer); + } else { + memmove(buffer,ptr,offs); + state=START; + } /* if */ + } /* if */ + if (state==START) { + for (ptr=buffer; (unsigned long)(ptr-buffer)<(size+offs) && *ptr!='\n'; ptr++) + /* nothing */; + if (*ptr=='\n') { + state=FINISH; + if (strlen(++ptr)>0) + amx_printf("%s",ptr); + } /* if */ + } /* if */ + } /* while */ + amx_fflush(); + /* we found a line starting with '@' and ending '\n'; now check the validity + * of the line + */ + if (sscanf(buffer,"@%lx",&cip)==1) { + amx->cip=(cell)cip; + return 1; + } else { + /* unknown buffer format; print and continue */ + amx_printf("%s",buffer); + } /* if */ + } /* for */ +} + +static void remote_resume_rs232(void) +{ + #if defined __WIN32__ + unsigned long size; + WriteFile(hCom,"!",1,&size,NULL); + #else + write(fdCom,"!",1); + #endif +} + +static void remote_sync_rs232(AMX *amx) +{ + char buffer[128]; + long frm,stk,hea; + #if defined __WIN32__ + unsigned long size; + #endif + + for ( ;; ) { + #if defined __WIN32__ + WriteFile(hCom,"?R\n",3,&size,NULL); + #else + write(fdCom,"?R\n",3); + #endif + + buffer[0]='\0'; + getresponse_rs232(buffer,sizeof buffer,10); + if (sscanf(buffer,"@%lx,%lx,%lx",&frm,&stk,&hea)==3) { + amx->frm=(cell)frm; + amx->stk=(cell)stk; + amx->hea=(cell)hea; + return; + } /* if */ + } /* for */ +} + +static void remote_read_rs232(AMX *amx,cell vaddr,int number) +{ + char buffer[128]; + char *ptr; + int len; + cell val; + cell *cptr; + + while (number>0) { + sprintf(buffer,"?M%lx,%x\n",(long)vaddr,(number>10) ? 10 : number); + len=strlen(buffer); + send_rs232(buffer,len); + getresponse_rs232(buffer,sizeof buffer,100); + ptr=buffer+1; /* skip '@' */ + while (number>0 && (ptr-buffer)0) { + num=(number>10) ? 10 : number; + number-=num; + sprintf(buffer,"?W%lx",(long)vaddr); + while (num>0) { + if (amx_GetAddr(amx,vaddr,&cptr)!=AMX_ERR_NONE) + return; + strcat(buffer,","); + sprintf(buffer+strlen(buffer),"%x",*cptr); + num--; + vaddr+=sizeof(cell); + } /* while */ + strcat(buffer,"\n"); + len=strlen(buffer); + send_rs232(buffer,len); + getresponse_rs232(buffer,sizeof buffer,100); + if (strtol(buffer+1,NULL,16)==0) + return; + } /* while */ +} + +static int remote_transfer_rs232(const char *filename) +{ + #define ACK ((unsigned char)6) + #define NAK ((unsigned char)21) + unsigned char *buffer; + char str[128]; + FILE *fp; + size_t bytes,block; + unsigned long size,chksum; + int len,err; + + #if defined __WIN32__ + if (hCom==INVALID_HANDLE_VALUE) + return 0; + #else + if (fdCom<0) + return 0; + #endif + + if ((fp=fopen(filename,"rb"))==NULL) + return 0; + /* determine the file size */ + fseek(fp,0,SEEK_END); + size=ftell(fp); + fseek(fp,0,SEEK_SET); + + /* set up */ + sprintf(str,"?P %lx,%s\n",size,skippath(filename)); + len=strlen(str); + send_rs232(str,len); + getresponse_rs232(str,sizeof str,100); + if (sscanf(str,"@%x",&block)!=1) + block=0; + /* allocate 1 byte more, for the ACK/NAK prefix */ + if (block==0 || (buffer=malloc((block+1)*sizeof(char)))==NULL) { + fclose(fp); + return 0; + } /* if */ + + /* file transfer acknowledged, transfer data per block */ + amx_printf("Transferring "); + amx_fflush(); + while (size>0 && (bytes=fread(buffer+1,1,block,fp))!=0) { + buffer[0]=ACK; + /* calculate the checksum */ + chksum=1; + for (len=1; len<=(int)bytes; len++) + chksum+=buffer[len]; + while (chksum>0xff) + chksum=(chksum&0xff)+(chksum>>8); + do { + /* send block */ + send_rs232((char*)buffer,bytes+1); /* also send the ACK/NAK prefix */ + getresponse_rs232(str,sizeof str,100); + err=(str[0]!='\0') ? strtol(str+1,NULL,16) : 0; + assert(err>=0 && err<=255); + if (err==0) { + free(buffer); + fclose(fp); + return 0; + } /* if */ + buffer[0]=NAK; /* preset for failure (if err!=chksum => failure) */ + } while (err!=(int)chksum); + size-=block; + if (sizenext!=NULL) { + next=root->next; + root->next=next->next; /* unlink */ + free(next->name); /* then free */ + free(next); + } /* while */ +} + +static NAMELIST *namelist_find(NAMELIST *root,int number) +{ + NAMELIST *name; + + for (name=root->next; name!=NULL && name->number!=number; name=name->next) + /* nothing */; + return name; +} + +static int namelist_delete(NAMELIST *root,NAMELIST *item) +{ + NAMELIST *cur; + + /* find the item */ + assert(root!=NULL); + cur=root; + while (cur->next!=NULL && cur->next!=item) + cur=cur->next; + assert(cur!=NULL); + if (cur->next==NULL) + return 0; /* item not found */ + + cur->next=item->next; /* unlink */ + free(item->name); /* then free */ + free(item); + + return 1; +} + +static NAMELIST *namelist_add(NAMELIST *root,char *name,int number) +{ + NAMELIST *cur, *pred; + + /* allocate a structure */ + cur=malloc(sizeof(NAMELIST)); + if (cur==NULL) + return NULL; + cur->name=strdup(name); + cur->number=number; + if (cur->name==NULL) { + free(cur); + return NULL; + } /* if */ + + /* link it in the list */ + assert(root!=NULL); + for (pred=root; pred->next!=NULL && pred->next->numbernext) + /* nothing */; + cur->next=pred->next; + pred->next=cur; + + return cur; +} + +static int namelist_count(NAMELIST *root) +{ + int count = 0; + NAMELIST *name; + + for (name=root->next; name!=NULL; name=name->next) + count++; + return count; +} + +static int get_symbolvalue(AMX *amx,AMX_DBG_SYMBOL *sym,int index,cell *value) +{ + cell *vptr; + cell base=sym->address; + if (sym->vclass & DISP_MASK) + base+=amx->frm; /* addresses of local vars are relative to the frame */ + if (sym->ident==iREFERENCE || sym->ident==iREFARRAY) { /* a reference */ + amx_GetAddr(amx,base,&vptr); + base=*vptr; + } /* if */ + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_read_rs232(amx,(cell)(base+index*sizeof(cell)),1); + #endif + if (amx_GetAddr(amx,(cell)(base+index*sizeof(cell)),&vptr)!=AMX_ERR_NONE) + return 0; + *value=*vptr; + return 1; +} + +static int set_symbolvalue(AMX *amx,const AMX_DBG_SYMBOL *sym,int index,cell value) +{ + cell *vptr; + cell base=sym->address; + if (sym->vclass & DISP_MASK) + base+=amx->frm; /* addresses of local vars are relative to the frame */ + if (sym->ident==iREFERENCE || sym->ident==iREFARRAY) { /* a reference */ + amx_GetAddr(amx,base,&vptr); + base=*vptr; + } /* if */ + if (amx_GetAddr(amx,(cell)(base+index*sizeof(cell)),&vptr)!=AMX_ERR_NONE) + return 0; + *vptr=value; + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_write_rs232(amx,(cell)(base+index*sizeof(cell)),1); + #endif + return 1; +} + +static char *get_string(AMX *amx,AMX_DBG_SYMBOL *sym,int maxlength) +{ +static char string[MAXLINELENGTH]; + char *ptr; + cell *addr; + cell base; + int length,num; + + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + assert(sym->dim==1); + assert(maxlengthaddress; + if (sym->vclass) + base+=amx->frm; /* addresses of local vars are relative to the frame */ + if (sym->ident==iREFARRAY) { /* a reference */ + amx_GetAddr(amx,base,&addr); + base=*addr; + } /* if */ + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_read_rs232(amx,base,MAXLINELENGTH); + #endif + if (amx_GetAddr(amx,base,&addr)==AMX_ERR_NONE) { + amx_StrLen(addr,&length); + + /* allocate a temporary buffer */ + ptr=malloc(length+1); + if (ptr!=NULL) { + amx_GetString(ptr,addr,0,maxlength); + num=length; + if (num>=maxlength) { + num=maxlength-1; + if (num>3) + num-=3; /* make space for the ... terminator */ + } /* if */ + assert(num>=0); + strncpy(string,ptr,num); + string[num]='\0'; + if (numvclass & ~DISP_MASK)==0) { + const char *tagname; + if (dbg_GetTagName(amxdbg,sym->tag,&tagname)==AMX_ERR_NONE) { + if (stricmp(tagname,"bool")==0) + sym->vclass |= DISP_BOOL; + else if (stricmp(tagname,"fixed")==0) + sym->vclass |= DISP_FIXED; + else if (stricmp(tagname,"float")==0) + sym->vclass |= DISP_FLOAT; + } /* if */ + if ((sym->vclass & ~DISP_MASK)==0 && (sym->ident==iARRAY || sym->ident==iREFARRAY) && sym->dim==1) { + /* untagged array with a single dimension, walk through all elements + * and check whether this could be a string + */ + unsigned char *ptr=(unsigned char*)get_string(amx,sym,MAXLINELENGTH-1); + int i; + for (i=0; i=128) + break; /* non-ASCII character */ + if (i==0 && !isalpha(ptr[i])) + break; /* want a letter at the start */ + } /* for */ + if (i>0 && ivclass |= DISP_STRING; + } /* if */ + } /* if */ + + if ((sym->ident==iARRAY || sym->ident==iREFARRAY)) { + int dim; + dbg_GetArrayDim(amxdbg, sym, &symdim); + /* check whether any of the indices are out of range */ + for (dim=0; dim0 && (ucell)index[dim]>=symdim[dim].size) + break; + if (dimident==iARRAY || sym->ident==iREFARRAY) && idxlevel==0) { + if ((sym->vclass & ~DISP_MASK)==DISP_STRING) { + amx_printf("\"%s\"", get_string(amx,sym,40)); + } else if (sym->dim==1) { + ucell len,i; + len=symdim[0].size; + if (len>5) + len=5; + else if (len==0) + len=1; /* unknown array length, assume at least 1 element */ + amx_printf("{"); + for (i=0; i0) + amx_printf(","); + if (get_symbolvalue(amx,sym,(int)i,&value)) + printvalue(value,(sym->vclass & ~DISP_MASK)); + else + amx_printf("?"); + } /* for */ + if (lenident!=iARRAY && sym->ident!=iREFARRAY && idxlevel>0) { + /* index used on a non-array */ + amx_printf("(invalid index, not an array)"); + } else { + /* simple variable, or indexed array element */ + int base=0; + int dim; + assert(idxlevel>0 || index[0]==0); /* index should be zero if non-array */ + for (dim=0; dimdim==idxlevel) + printvalue(value,(sym->vclass & ~DISP_MASK)); + else if (sym->dim!=idxlevel) + amx_printf("(invalid number of dimensions)"); + else + amx_printf("?"); + } /* if */ +} + +static void watch_init(void) +{ + namelist_init(&watches); +} + +static void watch_list(AMX *amx,AMX_DBG *amxdbg) +{ + int num,dim,idx[MAX_DIMS]; + char *indexptr; + char name[sNAMEMAX+20]; /* extra space for an array index */ + NAMELIST *watch; + const AMX_DBG_SYMBOL *sym; + + if (terminal>0) { + term_csrsave(); + amx_gotoxy(1,1); + printf("\n"); /* clear "tab state" if the terminal driver forgot */ + amx_gotoxy(1,1); /* and set the cursor position again */ + } /* if */ + + num=0; + for (watch=watches.next; watch!=NULL; watch=watch->next) { + assert(watch->name!=NULL && strlen(watch->name)>0); + strcpy(name,watch->name); + indexptr=strchr(name,'['); + dim=0; + memset(idx,0,sizeof idx); + while (indexptr!=NULL && dimname); + /* find the symbol with the given range with the smallest scope */ + if (dbg_GetVariable(amxdbg,watch->name,amx->cip,&sym)==AMX_ERR_NONE) + display_variable(amx,amxdbg,(AMX_DBG_SYMBOL *)sym,idx,dim); + else + amx_printf("(unknown symbol)"); + if (terminal>0) + amx_clreol(); + amx_printf("\n"); + } /* for */ + + if (terminal>0) { + if (num==0) + amx_printf("(no watches)"); + for ( ; num0) + draw_hline(0); + } /* if */ +} + +static int watch_set(int number, char *name) +{ + NAMELIST *cur; + + /* find a free number */ + if (number<0) { + int changed; + number=0; + do { + changed=0; + for (cur=watches.next; cur!=NULL; cur=cur->next) { + if (cur->number==number) { + number++; + changed=1; + } /* if */ + } /* for */ + } while (changed); + } /* if */ + + /* add the watch */ + return namelist_add(&watches,name,number)!=NULL; +} + +static int watch_clear(int number) +{ + NAMELIST *name=namelist_find(&watches,number); + if (name!=NULL) { + namelist_delete(&watches,name); + return 1; + } /* if */ + return 0; +} + +static int watch_count(void) +{ + return namelist_count(&watches); +} + +static void break_init(void) +{ + BREAKPOINT *next; + + while (breakpoints.next!=NULL) { + next=breakpoints.next; + breakpoints.next=next->next; /* unlink */ + free(next); /* then free */ + } /* while */ +} + +static int break_clear(int number) +{ + BREAKPOINT *cur; + + /* find the breakpoint */ + cur=&breakpoints; + while (cur->next!=NULL && cur->next->number!=number) + cur=cur->next; + if (cur->next==NULL) + return 0; /* breakpoint not found */ + + cur->next=cur->next->next; /* unlink */ + free(cur->next); /* then free */ + return 1; +} + +static int break_set(AMX_DBG *amxdbg,const char *symaddr,int flags) +{ + BREAKPOINT *cur,*newbp, bp; + int number,changed,err; + + /* find type */ + memset(&bp,0,sizeof(BREAKPOINT)); + symaddr=(const char*)skipwhitespace(symaddr); + if (isdigit(*symaddr)) { + bp.type=BP_CODE; + if ((flags & 1)!=0) + bp.number=0; /* for temporary breakpoint, use number 0 */ + err=dbg_GetLineAddress(amxdbg,strtol(symaddr,NULL,10)-1,curfilename,&bp.addr); + } else { + bp.type=BP_CODE; + err=dbg_GetFunctionAddress(amxdbg,symaddr,curfilename,&bp.addr); + dbg_LookupFunction(amxdbg, bp.addr, &bp.name); + } /* if */ + if (err!=AMX_ERR_NONE) + return -1; + + /* walk through the list, see if the breakpoint already exists */ + for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { + if (cur->addr==bp.addr) { + if (flags & 2) + break_clear(cur->number); /* toggle the breakpoint */ + return AMX_ERR_NONE; + } /* if */ + } /* for */ + + /* find an unused breakpoint number */ + number=1; + do { + changed=0; + for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { + if (cur->number==number) { + number++; + changed=1; + } /* if */ + } /* for */ + } while (changed); + + /* allocate a new breakpoint, add the entry parsed earlier to the list */ + newbp=malloc(sizeof(BREAKPOINT)); + if (newbp==0) + return -1; + memcpy(newbp,&bp,sizeof(BREAKPOINT)); + for (cur=&breakpoints; cur->next!=NULL && cur->next->numbernext) + /* nothing */; + assert(cur!=NULL); + newbp->next=cur->next; + cur->next=newbp; + newbp->number=number; + + return number; +} + +static void break_list(AMX_DBG *amxdbg) +{ + BREAKPOINT *cur; + + for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { + assert(cur->type!=BP_NONE); + scroll_cmdwin(1); + amx_printf("%2d ",cur->number); + if (cur->type==BP_CODE) { + long line; + const char *filename; + dbg_LookupLine(amxdbg,cur->addr,&line); + amx_printf("line: %ld",line); + if (cur->name!=NULL) { + amx_printf("\tfunc: %s",cur->name); + } else { + dbg_LookupFile(amxdbg,cur->addr,&filename); + if (filename!=NULL) + amx_printf("\tfile: %s",skippath(filename)); + } /* if */ + } /* if */ + amx_printf("\n"); + } /* for */ +} + +static int break_check(AMX *amx) +{ + BREAKPOINT *cur; + ucell cip; + + /* when the "break" statement comes, the instruction pointer is already + * incremented; we must adjust for this + */ + cip=(ucell)amx->cip-sizeof(cell); + for (cur=breakpoints.next; cur!=NULL; cur=cur->next) { + if (cur->type==BP_CODE) { + if (cur->addr==cip) { + int number=cur->number; + if (number==0) + break_clear(number); + return number; + } /* if */ + } /* if */ + } /* for */ + return -1; +} + +#if defined READLINE +/* Read a string, and return a pointer to it. Returns NULL on EOF. */ +char *rl_gets(char *str,int length) +{ + char *line_read=readline(STR_PROMPT); + if (line_read==NULL) + return NULL; + + /* copy the line (with possible truncation) */ + strncpy(str,line_read,length); + if ((int)strlen(line_read)>length) + str[length-1]='\0'; + #if !defined __WIN32__ + /* readline() allocates memory internally, which it asks you to deallocate + * with free(). This is an acceptable design with a statically linked + * library, and perhaps even with shared libraries in Unix/Linux, but it + * is problematic with DLLs under Microsoft Windows. In Microsoft Windows, + * the standard C library was never part of the OS, so each compiler + * vendor provided its own. If your DLL allocates memory and your program + * (that uses the DLL) frees it, chances are that the malloc() and free() + * implementations assume a different heap lay-out (because they originate + * from different vendors, or from different versions of the compiler, or + * even because of different compilation options/flags). + * Now if malloc() and free() disagree, trouble is never far. + * To illustruate the problem: I downloaded a readline Win32 port (a DLL), + * but calling free() on the returned pointer consistently crashes the + * program. I must have a different compiler than the one used to compile + * the DLL, or a different version of the compiler, or different compiler + * options, or a different MSVCRT.DLL. + * As a temporary solution, I have chosen to "leak" memory (instead of + * crashing). + */ + free(line_read); + #endif + + /* If the line has any text in it, save it on the history. */ + if (strlen(str)>0) + add_history(line_read); + + return str; +} +#else +char *rl_gets(char *str,int length) +{ + #if defined __WIN32__ + unsigned long num; + INPUT_RECORD rec; + CONSOLE_CURSOR_INFO csrinfo; + HANDLE stdinput=GetStdHandle(STD_INPUT_HANDLE); + HANDLE stdoutput=GetStdHandle(STD_OUTPUT_HANDLE); + #endif + + amx_printf("%s",STR_PROMPT); + amx_fflush(); + #if defined __WIN32__ + if (terminal>0) { + /* first wait for a function key */ + GetConsoleCursorInfo(stdoutput,&csrinfo); + csrinfo.bVisible=FALSE; + SetConsoleCursorInfo(stdoutput,&csrinfo); + term_statusbar(1); + assert(length>5); + for ( ;; ) { + while (PeekConsoleInput(stdinput,&rec,1,&num) && num==0) + /* nothing */; + if (num==0) + break; + /* analyze the event, keep only the keyboard events */ + switch (rec.EventType) { + case KEY_EVENT: + if (!rec.Event.KeyEvent.bKeyDown) { + ReadConsoleInput(stdinput,&rec,1,&num); + continue; + } /* if */ + str[0]='\0'; + switch (rec.Event.KeyEvent.wVirtualKeyCode) { + case VK_F1: + strcpy(str,"\033[11~"); /* \e[11~ on VT220, \eOP on VT100 */ + break; + case VK_F2: + strcpy(str,"\033[12~"); /* \e[12~ on VT220, \eOQ on VT100 */ + break; + case VK_F3: + strcpy(str,"\033[13~"); /* \e[13~ on VT220, \eOR on VT100 */ + break; + case VK_F4: + strcpy(str,"\033[14~"); /* \e[14~ on VT220, \eOS on VT100 */ + break; + case VK_F5: + strcpy(str,"\033[15~"); + break; + case VK_F6: + strcpy(str,"\033[17~"); + break; + case VK_F7: + strcpy(str,"\033[18~"); + break; + case VK_F8: + strcpy(str,"\033[19~"); + break; + case VK_F9: + strcpy(str,"\033[20~"); + break; + case VK_F10: + strcpy(str,"\033[21~"); + break; + case VK_F11: + strcpy(str,"\033[23~"); + break; + case VK_F12: + strcpy(str,"\033[24~"); + break; + case VK_PRIOR: /* PgUp */ + strcpy(str,"\033[5~"); + break; + case VK_NEXT: /* PgDn */ + strcpy(str,"\033[6~"); + break; +#if 0//??? + case VK_UP: /* Arrow up */ + strcpy(str,"\033[A"); + break; + case VK_DOWN: /* Arrow down */ + strcpy(str,"\033[B"); + break; + case VK_LEFT: /* Arrow left */ + strcpy(str,"\033[C"); + break; + case VK_RIGHT:/* Arrow right */ + strcpy(str,"\033[D"); + break; +#endif + } /* switch */ + if (str[0]!='\0') { + ReadConsoleInput(stdinput,&rec,1,&num); /* remove from queue */ + csrinfo.bVisible=TRUE; + SetConsoleCursorInfo(stdoutput,&csrinfo); + term_statusbar(0); + return str; + } /* if */ + break; + case MOUSE_EVENT: + ReadConsoleInput(stdinput,&rec,1,&num); + if (rec.Event.MouseEvent.dwEventFlags==0) { + /* mouse click or release */ + int y=rec.Event.MouseEvent.dwMousePosition.Y; + if (terminal>0) { + int line=y-watchlines-1; + if (line>=0 && line0) { + term_csrsave(); + amx_clrscr(); + if (strlen(command)==0 || strcmp(command,"?")==0) + amx_printf("At the prompt, you can type debug commands. For example, the word \"step\" is a\n" + "command to execute a single line in the source code. The commands that you will\n" + "use most frequently may be abbreviated to a single letter: instead of the full\n" + "word \"step\", you can also type the letter \"s\" followed by the enter key.\n\n" + "Available commands:\n"); + else + amx_printf("Options for command \"%s\":\n",command); + } /* if */ + + if (stricmp(command,"break")==0) { + amx_printf("\tBREAK\t\tlist all breakpoints\n" + "\tBREAK n\t\tset a breakpoint at line \"n\"\n" + "\tBREAK func\tset a breakpoint at function with name \"func\"\n"); + } else if (stricmp(command,"cbreak")==0) { + amx_printf("\tCBREAK n\tremove breakpoint number \"n\"\n" + "\tCBREAK *\tremove all breakpoints\n"); + } else if (stricmp(command,"cw")==0 || stricmp(command,"cwatch")==0) { + amx_printf("\tCWATCH may be abbreviated to CW\n\n" + "\tCWATCH n\tremove watch number \"n\"\n" + "\tCWATCH *\tremove all watches\n"); + } else if (stricmp(command,"d")==0 || stricmp(command,"disp")==0) { + amx_printf("\tDISP may be abbreviated to D\n\n" + "\tDISP\t\tdisplay all variables that are currently in scope\n" + "\tDISP var\tdisplay the value of variable \"var\"\n" + "\tDISP var[i]\tdisplay the value of array element \"var[i]\"\n"); + } else if (stricmp(command,"file")==0) { + amx_printf("\tFILE\t\tlist all files that this program is composed off\n" + "\tFILE name\tset the current file to \"name\"\n"); + } else if (stricmp(command,"g")==0 || stricmp(command,"go")==0) { + amx_printf("\tGO may be abbreviated to G\n\n" + "\tGO\t\trun until the next breakpoint or program termination\n" + "\tGO n\t\trun until line number \"n\"\n" + "\tGO func\t\trun until the current function returns (\"step out\")\n"); + } else if (stricmp(command,"l")==0 || stricmp(command,"list")==0) { + amx_printf("\tLIST may be abbreviated to L\n\n" + "\tLIST\t\tdisplay source code lines around the current line\n" + "\tLIST n\t\tdisplay lines, starting from line \"n\"\n" + "\tLIST n m\tdisplay \"m\" lines, starting from line \"n\" (not\n" + "\t\t\tsupported in terminal emulation modes)\n" + "\tLIST UP\tdisplay preceding lines\n" + "\tLIST DOWN\tdisplay following lines\n" + "\tLIST FUNCS\tdisplay all functions\n" + "\tLIST ON\t\tautomatically list source code lines after each step\n" + "\tLIST OFF\tturn off automatic list (not supported in terminal\n" + "\t\t\temulation modes)\n" + "\tLIST STATES\tdisplay all automatons and the state that these are in\n"); + } else if (stricmp(command,"set")==0) { + amx_printf("\tSET var=value\t\tset variable \"var\" to the numeric value \"value\"\n" + "\tSET var[i]=value\tset array item \"var\" to a numeric value\n"); + } else if (stricmp(command,"term")==0) { + amx_printf("\tTERM OFF\tdisable VT100/Win32 terminal functions\n" + "\tTERM ON\t\tuse terminal capabilities, default console size\n" + "\tTERM x y\tset terminal size (x columns, y lines)\n"); +#if !defined NO_REMOTE + } else if (stricmp(command,"transfer")==0) { + amx_printf("\tTRANSFER\ttransfer the program to the remote host (for remote\n" + "\t\t\tdebugging)\n"); +#endif + } else if (stricmp(command,"type")==0) { + amx_printf("\tTYPE var STRING\tdisplay \"var\" as string\n" + "\tTYPE var STD\tset default display format (decimal integer)\n" + "\tTYPE var HEX\tset hexadecimal integer format\n" + "\tTYPE var FIXED\tset fixed point format (3 decimals)\n" + "\tTYPE var FLOAT\tset floating point format\n"); + } else if (stricmp(command,"watch")==0 || stricmp(command,"w")==0) { + amx_printf("\tWATCH may be abbreviated to W\n\n" + "\tWATCH var\tset a new watch at variable \"var\"\n" + "\tWATCH n var\tchange watch \"n\" to variable \"var\"\n"); + } else if (stricmp(command,"n")==0 || stricmp(command,"next")==0 + || stricmp(command,"quit")==0 + || stricmp(command,"s")==0 || stricmp(command,"step")==0) + { + amx_printf("\tno additional information\n"); + } else { + amx_printf("\tBREAK\t\tset breakpoint at line number or variable name\n" + "\tCBREAK\t\tremove breakpoint\n" + "\tCW(atch)\tremove a \"watchpoint\"\n" + "\tD(isp)\t\tdisplay the value of a variable, list variables\n" + "\tFILE\t\tswitch to a file\n" + "\tG(o)\t\trun program (until breakpoint)\n" + "\tL(ist)\t\tdisplay source file, automatons and functions\n" + "\tN(ext)\t\tRun until next line, step over functions\n" + "\tOUTPUT\t\tShow program output (if supported by terminal)\n" + "\tQUIT\t\texit debugger, terminate program\n" + "\tSET\t\tSet a variable to a value\n" + "\tS(tep)\t\tsingle step, step into functions\n" + "\tTERM\t\tset terminal type\n" +#if !defined NO_REMOTE + "\tTRANSFER\ttransfer program to remote host\n" +#endif + "\tTYPE\t\tset the \"display type\" of a symbol\n" + "\tW(atch)\t\tset a \"watchpoint\" on a variable\n" + "\n\tUse \"? \" to view more information on a command\n"); + } /* if */ + amx_fflush(); + if (terminal>0) { + amx_printf("\nPress a key to continue..."); + amx_fflush(); + amx_getch(); + term_refresh(0); + term_csrrestore(); + source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); + } /* if */ +} + +static int docommand(AMX *amx,AMX_DBG *amxdbg,int curline) +{ +static char lastcommand[32] = ""; + char line[MAXLINELENGTH], command[32]; + int result,i; + char *params; + + for ( ;; ) { + if (terminal>0) { + term_csrsave(); + amx_gotoxy(1,screenlines); + amx_clreol(); + } /* if */ + rl_gets(line,sizeof(line)); + if (terminal>0) + term_csrrestore(); + if (line[0]=='\033') { + assert(terminal==1); + if (strcmp(line+1,"[11~")==0 || strcmp(line+1,"OP")==0) { + strcpy(line,"?"); /* F1 == Help */ + } else if (strcmp(line+1,"[13~")==0 || strcmp(line+1,"OR")==0) { + strcpy(line,"QUIT"); /* F3 == List */ + } else if (strcmp(line+1,"[14~")==0 || strcmp(line+1,"OS")==0) { + strcpy(line,"Output"); /* F4 == Output */ + } else if (strcmp(line+1,"[15~")==0) { + strcpy(line,"G"); /* F5 == Go */ + } else if (strcmp(line+1,"[17~")==0) { + strcpy(line,"D"); /* F6 == Disp */ + } else if (strcmp(line+1,"[19~")==0) { + strcpy(line,"S"); /* F8 == Step */ + #if 0 //??? + } else if (strcmp(line+1,"[20~")==0) { + if (recentline<0) + recentline=curline; + sprintf(line,"BREAK %d",recentline+1); /* F9 == Breakpoint */ + //??? should toggle if a breakpoint is already set + #endif + } else if (strcmp(line+1,"[21~")==0) { + strcpy(line,"N"); /* F10 == Next */ + } else if (strcmp(line+1,"[5~")==0) { + strcpy(line,"L UP"); /* PgUp */ + } else if (strcmp(line+1,"[6~")==0) { + strcpy(line,"L DOWN"); /* PgDn */ + } else { + continue; + } /* if */ + } /* if */ + if ((params=strchr(line,'\n'))!=NULL) + *params='\0'; /* strip newline character */ + if ((params=strchr(line,'\r'))!=NULL) + *params='\0'; /* strip carriage return character too */ + if (strlen(line)==0) { + if (terminal==0) { + int csrx,csry; + amx_wherexy(&csrx,&csry); + amx_gotoxy(1,csry-1); + amx_clreol(); + } /* if */ + strcpy(line,lastcommand); + } /* if */ + lastcommand[0]='\0'; + + result=sscanf(line,"%8s",command); + if (result<=0) { + if (terminal>=0) + listcommands(NULL,amxdbg,curline); + continue; + } /* if */ + params=strchr(line,' '); + params=(params!=NULL) ? skipwhitespace(params) : ""; + + if (stricmp(command,"?")==0) { + result=sscanf(line,"%*s %30s",command); + listcommands(result ? command : NULL,amxdbg,curline); + } else if (stricmp(command,"quit")==0) { + if (terminal>0) { + amx_clrscr(); + term_close(); + } /* if */ + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_rs232(0,0); + #endif + exit(0); + } else if (stricmp(command,"g")==0 || stricmp(command,"go")==0) { + if (isdigit(*params)) + break_set(amxdbg,params,0x1); + recentline=-1; + if (stricmp(params,"func")==0) + return STEPOUT; + return RUNNING; + } else if (stricmp(command,"s")==0 || stricmp(command,"step")==0) { + strcpy(lastcommand,"s"); + recentline=-1; + return STEPPING; + } else if (stricmp(command,"n")==0 || stricmp(command,"next")==0) { + strcpy(lastcommand,"n"); + recentline=-1; + return STEPOVER; + } else if (stricmp(command,"l")==0 || stricmp(command,"list")==0) { + /* first check a few hard cases */ + if (stricmp(params,"funcs")==0) { + for (i=0; ihdr->symbols; i++) { + if (amxdbg->symboltbl[i]->ident == iFUNCTN) { + const char *filename; + scroll_cmdwin(1); + amx_printf("%s",amxdbg->symboltbl[i]->name); + if (dbg_LookupFile(amxdbg,amxdbg->symboltbl[i]->address,&filename)==AMX_ERR_NONE) + amx_printf("\t(%s)",skippath(filename)); + //??? also say what automaton this function is part of + amx_printf("\n"); + } /* if */ + } /* for */ + } else if (stricmp(params,"states")==0) { + const char *statename; + cell *cptr; + if (amxdbg->hdr->automatons==0) { + scroll_cmdwin(1); + amx_printf("\t(no states)\n"); + } /* if */ + for (i=0; ihdr->automatons; i++) { + scroll_cmdwin(1); + amx_printf("%s",strlen(amxdbg->automatontbl[i]->name)==0 ? "(anonymous)" : amxdbg->automatontbl[i]->name); + /* read the variable at the address */ + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_read_rs232(amx,amxdbg->automatontbl[i]->address,1); + #endif + if (amx_GetAddr(amx,amxdbg->automatontbl[i]->address,&cptr)==AMX_ERR_NONE) + dbg_GetStateName(amxdbg,(int)*cptr,&statename); + amx_printf("\t-> %s\n",(statename==NULL) ? "(none)" : statename); + } /* for */ + } else if (stricmp(params,"on")==0) { + autolist=listlines; + watch_list(amx,amxdbg); + source_list(amxdbg,curline-autolist/2,autolist,curline); + } else if (stricmp(params,"off")==0) { + if (terminal<=0) + autolist=1; + else + amx_printf("\tCommand not supported on terminals\n"); + } else { + int lnum,numlines; + lnum=curline-(listlines/2-1); /* preset */ + numlines=listlines; + if (stricmp(params,"up")==0) { + lnum=curtopline-listlines; + if (lnum<0) + lnum=0; + } else if (stricmp(params,"down")==0) { + lnum=curtopline+listlines; + if (lnum>source_lines()-3) + lnum=source_lines()-3; + } else { + sscanf(line,"%*s %d %d",&lnum,&numlines); + lnum--; /* if user filled in a line number, subtract 1 */ + } /* if */ + #if !defined VT100 + if (terminal>0) { + int csrx,csry; + amx_wherexy(&csrx,&csry); + if (csry>=screenlines) + term_refresh(0); + } /* if */ + #endif + source_list(amxdbg,lnum,numlines,curline); + } /* if */ + } else if (stricmp(command,"break")==0 || stricmp(command,"tbreak")==0) { + if (*params=='\0') { + break_list(amxdbg); + } else { + result=break_set(amxdbg,params,stricmp(command,"tbreak")==0); + if (result<0) + amx_printf("Invalid breakpoint\n"); + else if (terminal>0) + source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); + } /* if */ + } else if (stricmp(command,"cbreak")==0) { + if (*params=='*') { + /* clear all breakpoints */ + break_init(); + } else if (isdigit(*params)) { + if (!break_clear(atoi(params))) + amx_printf("\tUnknown breakpoint\n"); + } else { + amx_printf("\tInvalid command\n"); + } /* if */ + if (terminal>0) + source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); + } else if (stricmp(command,"disp")==0 || stricmp(command,"d")==0) { + int idx[MAX_DIMS],dim; + dim=0; + memset(idx,0,sizeof idx); + if (*params=='\0') { + /* display all variables that are in scope */ + for (i=0; ihdr->symbols; i++) { + if (amxdbg->symboltbl[i]->ident!=iFUNCTN + && amxdbg->symboltbl[i]->codestart<=(ucell)amx->cip + && amxdbg->symboltbl[i]->codeend>(ucell)amx->cip) + { + scroll_cmdwin(1); + amx_printf("%s\t%s\t",(amxdbg->symboltbl[i]->vclass & DISP_MASK)>0 ? "loc" : "glb",amxdbg->symboltbl[i]->name); + display_variable(amx,amxdbg,amxdbg->symboltbl[i],idx,0); + amx_printf("\n"); + } /* if */ + } /* for */ + } else { + const AMX_DBG_SYMBOL *sym; + char *indexptr=strchr(params,'['); + char *behindname=NULL; + assert(dim==0); + while (indexptr!=NULL && dimcip,&sym)==AMX_ERR_NONE) { + scroll_cmdwin(1); + if (behindname!=NULL) + *behindname='['; + amx_printf("%s\t%s\t",(sym->vclass & DISP_MASK)>0 ? "loc" : "glb",params/*sym->name*/); + display_variable(amx,amxdbg,(AMX_DBG_SYMBOL*)sym,idx,dim); + amx_printf("\n"); + } else { + amx_printf("\tSymbol not found, or not a variable\n"); + } /* if */ + } /* if */ + } else if (stricmp(command,"set")==0) { + char varname[32]; + long index,value; + const AMX_DBG_SYMBOL *sym; + if (sscanf(params," %[^[ ][%ld] = %ld",varname,&index,&value)!=3) { + index=0; + if (sscanf(params," %[^= ] = %ld",varname,&value)!=2) + varname[0]='\0'; + } /* if */ + if (varname[0]!='\0') { + /* find the symbol with the given range with the smallest scope */ + if (dbg_GetVariable(amxdbg,varname,amx->cip,(const AMX_DBG_SYMBOL**)&sym)==AMX_ERR_NONE) + set_symbolvalue(amx,sym,(int)index,(cell)value); + } /* if */ + } else if (stricmp(command,"file")==0) { + if (*params=='\0') { + /* browse through the file table */ + for (i=0; ihdr->files; i++) { + scroll_cmdwin(1); + amx_printf("%s\n",amxdbg->filetbl[i]->name); + } /* for */ + } else { + /* find the file */ + for (i=0; ihdr->files; i++) + if (stricmp(params,amxdbg->filetbl[i]->name)==0 || stricmp(params,skippath(amxdbg->filetbl[i]->name))==0) + break; + if (ihdr->files) { + if (curfilename!=amxdbg->filetbl[i]->name) { + curfilename=amxdbg->filetbl[i]->name; + curline=0; + if (cursource!=NULL) + source_free(cursource); + assert(amxdbg->filetbl[i]->name!=NULL); + cursource=source_load(amxdbg->filetbl[i]->name); + if (cursource==NULL) { + amx_printf("\tSource file not found (or insufficient memory)\n"); + continue; + } /* if */ + } /* if */ + } else { + amx_printf("\tunknown file\n"); + } /* if */ + } /* if */ + } else if (stricmp(command,"term")==0) { + int new_term=-1; + int columns=screencolumns; + int lines=screenlines+1; + if (stricmp(params,"off")==0) + new_term=0; + else if (stricmp(params,"on")==0) + new_term=1; + else if (sscanf(params,"%d %d",&columns,&lines)==2) + new_term=2; + if (new_term==-1) { + amx_printf("\tTerminal support is %s\n",terminal>0 ? "ON" : "OFF"); + } else if (terminal!=new_term) { + curtopline=0; + if (terminal>0) + term_close(); + if (new_term && amx_termctl(0,0)) { + term_open(columns,lines); + autolist=listlines; + } else if (new_term) { + amx_printf("\tTerminal not supported\n"); + } /* if */ + watch_list(amx,amxdbg); + source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); + } /* if */ + } else if (stricmp(command,"output")==0) { + if (term_switch(0)) /* switch back to user screen */ + amx_getch(); /* if it worked, wait for a key press */ + term_switch(1); /* switch to debugger screen */ + } else if (stricmp(command,"transfer")==0) { + #if defined NO_REMOTE + amx_printf("\tRemote file transfer is not supported.\n"); + #else + if (remote!=REMOTE_RS232) { + amx_printf("\tRemote file transfer is not supported.\n"); + } else { + if (remote_transfer_rs232(amx_filename)) { + /* restart the debugger */ + longjmp(restart_buf,1); + } else { + amx_printf("\tRemote file transfer failed.\n"); + } /* if */ + } /* if */ + #endif + } else if (stricmp(command,"type")==0) { + char symname[sNAMEMAX+1],*ptr; + int len; + for (ptr=params; *ptr!='\0' && *ptr!=' ' && *ptr!='\t'; ptr++) + /* nothing */; + len=(int)(ptr-params); + if (len==0 || len>sNAMEMAX) { + amx_printf("\tInvalid (or missing) symbol name\n"); + } else { + AMX_DBG_SYMBOL *sym; + strncpy(symname,params,len); + symname[len]='\0'; + params=skipwhitespace(ptr); + /* look up the symbol */ + if (dbg_GetVariable(amxdbg,symname,amx->cip,(const AMX_DBG_SYMBOL**)&sym)==AMX_ERR_NONE) { + assert(sym!=NULL); + if (stricmp(params,"std")==0) { + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_DEFAULT); + } else if (stricmp(params,"string")==0) { + /* check array with single dimension */ + if (!(sym->ident==iARRAY || sym->ident==iREFARRAY) || sym->dim!=1) + amx_printf("\t\"string\" display type is only valid for arrays with one dimension\n"); + else + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_STRING); + } else if (stricmp(params,"bin")==0) { + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_BIN); + } else if (stricmp(params,"hex")==0) { + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_HEX); + } else if (stricmp(params,"fixed")==0) { + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_FIXED); + } else if (stricmp(params,"float")==0) { + sym->vclass = (char)((sym->vclass & DISP_MASK) | DISP_FLOAT); + } else { + amx_printf("\tUnknown (or missing) display type\n"); + } /* if */ + watch_list(amx,amxdbg); + } else { + amx_printf("\tUnknown symbol \"%s\"\n",symname); + } /* if */ + } /* if */ + } else if (stricmp(command,"w")==0 || stricmp(command,"watch")==0) { + /* see whether this is a new watch, or whether one is replaced */ + if (isdigit(*params)) { + i=atoi(params); + params=skipvalue(params); + } else { + i = -1; + } /* if */ + if (strlen(params)==0) { + amx_printf("Missing variable name\n"); + continue; + } /* if */ + /* is there space for another watch? (only when using terminal support) */ + if (i<0 && terminal>0 && watch_count()>=WATCHLINES) { + int w=watch_count()+1; /* new number of watches */ + int l=listlines; + int c=screenlines-w-l-3; + if (c<=1) { + /* command window cannot shrink, shrink code listing instead */ + c=2; + l=screenlines-w-c-3; + if (l<5) { + /* code listing cannot shrink either, give a message */ + amx_printf("Too many watches, sorry\n"); + continue; + } /* if */ + } /* if */ + watchlines=w; + listlines=l; + term_refresh(0); /* reset */ + source_list(amxdbg,gettopline(curline,curline-autolist/2),autolist,curline); + } /* if */ + result=watch_set(i,params); + if (result>=0) + watch_list(amx,amxdbg); + else + amx_printf("Invalid watch\n"); + } else if (stricmp(command,"cw")==0 || stricmp(command,"cwatch")==0) { + if (*params=='*') { + /* clear all watches */ + watch_init(); + } else if (isdigit(*params)) { + watch_clear(atoi(params)); + } else { + amx_printf("\tInvalid command syntax, use \"? cw\" for details\n"); + } /* if */ + if (terminal>0 && watchlines>WATCHLINES) { + int w,l,c; + w=watch_count(); /* new number of watches */ + if (wfrm>lastfrm) + runmode=STEPPING; + + /* when running, check the breakpoints */ + breaknr=-1; + if (runmode!=STEPPING && runmode!=STEPOVER) { + /* check breakpoint address */ + breaknr=break_check(amx); + if (breaknr<0) { + /* just print the address at the bottom line, so one can trace where the + * the program is + */ + if (terminal>0) { + term_csrsave(); + amx_gotoxy(1,screenlines); + amx_printf("cip: %08lx",(long)amx->cip); + term_csrrestore(); + } /* if */ + return AMX_ERR_NONE; + } /* if */ + runmode=STEPPING; + } /* for */ + + /* get more information from the remote target */ + #if !defined NO_REMOTE + if (remote!=REMOTE_NONE) + remote_sync_rs232(amx); + #endif + + /* get the debug information header */ + err = amx_GetUserData(amx, AMX_USERTAG('P','D','b','g'), (void**)&amxdbg); + if (err!=AMX_ERR_NONE) + return AMX_ERR_DEBUG; + + /* try to avoid halting on the same line twice */ + dbg_LookupLine(amxdbg,amx->cip,&line); + if (line==lastline) + return AMX_ERR_NONE; + lastline=line; + + /* check whether we are stepping through a sub-function */ + if (runmode==STEPOVER) { + assert(lastfrm!=0); + if (amx->frmcip,&filename); + + /* check breakpoints */ + msg=NULL; + if (breaknr==0) { + msg="STOPPED at line %d\n"; + i=(int)line+1; + } else if (breaknr>0) { + msg="STOPPED at breakpoint %d\n"; + i=breaknr; + } /* if */ + term_switch(1); /* switch to the debugger console */ + if (msg!=NULL) + amx_printf(msg,i); /* print breakpoint number */ + assert(filename!=NULL); + if (curfilename!=filename) { + curfilename=filename; + if (cursource!=NULL) + source_free(cursource); + cursource=source_load(filename); + if (cursource==NULL) { + amx_printf("\nCritical error: source file not found (or insufficient memory)\n"); + exit(1); + } else if (terminal<=0) { + amx_printf("(%s)\n",curfilename); + } /* if */ + } /* if */ + #if !defined VT100 + if (terminal>0) { + int csrx,csry; + amx_wherexy(&csrx,&csry); + if (csry>=screenlines) + term_refresh(0); + } /* if */ + #endif + + assert(cursource[(int)line]!=NULL); + watch_list(amx,amxdbg); + source_list(amxdbg,gettopline((int)line,(int)line-autolist/2),autolist,(int)line); + runmode=docommand(amx,amxdbg,(int)line); + term_switch(0); /* switch back to the program output */ + if (runmode==STEPOVER) + lastfrm=amx->frm; /* step OVER functions (so save the stack frame) */ + + return AMX_ERR_NONE; +} + +#if !defined amx_Init + +extern int CreateConsoleThread(void); + +static void *loadprogram(AMX *amx,const char *filename) +{ + FILE *fp; + AMX_HEADER hdr; + void *program; + + if ((fp = fopen(filename,"rb")) != NULL) { + fread(&hdr, sizeof hdr, 1, fp); + amx_Align32((uint32_t *)&hdr.stp); + amx_Align32((uint32_t *)&hdr.size); + if ((program = malloc((int)hdr.stp)) != NULL) { + rewind(fp); + fread(program, 1, (int)hdr.size, fp); + fclose(fp); + memset(amx, 0, sizeof *amx); + if (amx_Init(amx,program) == AMX_ERR_NONE) + return program; + free(program); + } /* if */ + } /* if */ + return NULL; +} + +static int loaddbginfo(AMX *amx,AMX_DBG *amxdbg,const char *filename) +{ + FILE *fp; + int err; + + /* load the debug information from the file */ + if ((fp = fopen(filename,"rb")) == NULL) + return AMX_ERR_DEBUG; + err = dbg_LoadInfo(amxdbg,fp); + fclose(fp); + if (err != AMX_ERR_NONE) + return err; + + /* attach the debug information structure to the abstract machine */ + err = amx_SetUserData(amx, AMX_USERTAG('P','D','b','g'), amxdbg); + return err; +} + +int main(int argc,char *argv[]) +{ +extern AMX_NATIVE_INFO core_Natives[]; +extern AMX_NATIVE_INFO console_Natives[]; + + AMX amx; + AMX_DBG amxdbg; + cell ret; + int err,i; + void *program=NULL; + unsigned short flags; + char *ptr; + + #if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) + /* see www.autopackage.org for the BinReloc module */ + if (br_init(NULL)) { + char *libroot=br_find_exe_dir(""); + setenv("AMXLIB",libroot,0); + free(libroot); + } /* if */ + #endif + + amx_printf("Pawn command line debugger\n\n"); + if (setjmp(restart_buf)) { + assert(program!=NULL); + amx_InternalDebugProc(NULL); /* clean up debug information */ + if (terminal>0) + amx_clrscr(); + terminal=0; + dbg_FreeInfo(&amxdbg); + free(program); + } /* if */ + + /* get filename */ + if (strlen(amx_filename)==0) { + if (argc >= 2) { + strcpy(amx_filename,argv[1]); + } else { + amx_printf("File: "); + amx_gets(amx_filename,sizeof amx_filename); + if ((ptr=strchr(amx_filename,'\n'))!=NULL) + *ptr='\0'; /* strip newline characters */ + } /* if */ + } /* if */ + if (strlen(amx_filename)==0) + return 1; + if ((program = loadprogram(&amx,amx_filename)) == NULL) { + /* try adding an extension */ + strcat(amx_filename, ".amx"); + if ((program = loadprogram(&amx,amx_filename)) == NULL) { + amx_printf("Cannot load the program file \"%s\"\n", amx_filename); + return 1; + } /* if */ + } /* if */ + /* switch the current directory to that of the debugged script */ + if ((ptr=strrchr(amx_filename,DIRSEP_CHAR))!=NULL) { + extern int chdir(const char *path); + char dir[_MAX_PATH]; + int len=(int)(ptr-amx_filename); + if (len0) + amx_clrscr(); + #if !defined NO_REMOTE + if (remote==REMOTE_RS232) + remote_rs232(0,0); + #endif + if (err != AMX_ERR_NONE) + amx_printf("Run time error %d\n", err); + else + amx_printf("Normal termination, return value %ld\n", (long)ret); + + //??? option for restart + + dbg_FreeInfo(&amxdbg); + free(program); + + return 0; +} + +#endif /* !defined amx_Init */ diff --git a/amx-deps/src/amx/pawnrun b/amx-deps/src/amx/pawnrun deleted file mode 100644 index 7968b56..0000000 Binary files a/amx-deps/src/amx/pawnrun and /dev/null differ diff --git a/amx-deps/src/amx/pawnrun.c b/amx-deps/src/amx/pawnrun.c index 3af7646..a345c4f 100644 --- a/amx-deps/src/amx/pawnrun.c +++ b/amx-deps/src/amx/pawnrun.c @@ -1,465 +1,398 @@ -/* Simple "run-time" for the Pawn Abstract Machine, with optional support - * for debugging information and overlays. - * - * Copyright (c) ITB CompuPhase, 1997-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: pawnrun.c 3935 2008-03-06 13:18:13Z thiadmer $ - */ -#include -#include -#include -#include -#include /* for memset() (on some compilers) */ -#include "osdefs.h" /* for _MAX_PATH */ -#include "amx.h" - -#include -#if !defined CLOCKS_PER_SEC /* some (older) compilers do not have it */ - #define CLOCKS_PER_SEC CLK_TCK -#endif - -#if !defined AMX_NODYNALOAD && defined ENABLE_BINRELOC && (defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) - #include /* from BinReloc, see www.autopackage.org */ -#endif - -#if defined AMXOVL - #include "amxpool.h" -#endif -#if defined AMXDBG - #include "amxdbg.h" -#endif -static char g_filename[_MAX_PATH]; /* for loading the debug or information - * or for loading overlays */ - -/* These initialization functions are part of the "extension modules" - * (libraries with native functions) that this run-time uses. More - * extension modules may be dynamically linked (depending on whether - * support for dynamic linking is enabled). - */ -extern int AMXEXPORT amx_ConsoleInit(AMX *amx); -extern int AMXEXPORT amx_CoreInit(AMX *amx); - -AMX *global_amx; -int AMXAPI prun_Monitor(AMX *amx); - -static int abortflagged = 0; -void sigabort(int sig) -{ - /* install the debug hook procedure if this was not done already */ - amx_SetDebugHook(global_amx,prun_Monitor); - abortflagged=1; - signal(sig,sigabort); /* re-install the signal handler */ -} - -typedef struct tagSTACKINFO { - long maxstack, maxheap; -} STACKINFO; - - -/* prun_Monitor() - * A simple debug hook, that allows the user to break out of a program - * and that monitors stack usage. Note that the stack usage can only be - * properly monitored when the debug hook is installed from the start - * of the script to the end. - */ -int AMXAPI prun_Monitor(AMX *amx) -{ - int err; - STACKINFO *stackinfo; - - /* record the heap and stack usage */ - err = amx_GetUserData(amx, AMX_USERTAG('S','t','c','k'), (void**)&stackinfo); - if (err == AMX_ERR_NONE) { - if (amx->stp - amx->stk > stackinfo->maxstack) - stackinfo->maxstack = amx->stp - amx->stk; - if (amx->hea - amx->hlw > stackinfo->maxheap) - stackinfo->maxstack = amx->stp - amx->stk; - } /* if */ - - /* check whether an "abort" was requested */ - return abortflagged ? AMX_ERR_EXIT : AMX_ERR_NONE; -} - -#if defined AMXOVL -/* prun_Overlay() - * Helper function to load overlays - */ -int AMXAPI prun_Overlay(AMX *amx, int index) -{ - AMX_HEADER *hdr; - AMX_OVERLAYINFO *tbl; - FILE *ovl; - - assert(amx != NULL); - hdr = (AMX_HEADER*)amx->base; - assert((size_t)index < (hdr->nametable - hdr->overlays) / sizeof(AMX_OVERLAYINFO)); - tbl = (AMX_OVERLAYINFO*)(amx->base + hdr->overlays) + index; - amx->codesize = tbl->size; - amx->code = amx_poolfind(index); - if (amx->code == NULL) { - if ((amx->code = amx_poolalloc(tbl->size, index)) == NULL) - return AMX_ERR_OVERLAY; /* failure allocating memory for the overlay */ - ovl = fopen(g_filename, "rb"); - assert(ovl != NULL); - fseek(ovl, (int)hdr->cod + tbl->offset, SEEK_SET); - fread(amx->code, 1, tbl->size, ovl); - fclose(ovl); - } /* if */ - return AMX_ERR_NONE; -} -#endif - -/* aux_LoadProgram() - * Load a compiled Pawn script into memory and initialize the abstract machine. - * This function is extracted out of AMXAUX.C. - */ -int AMXAPI aux_LoadProgram(AMX *amx, char *filename) -{ - FILE *fp; - AMX_HEADER hdr; - int result; - int32_t size; - unsigned char *datablock; - #define OVLPOOLSIZE 4096 - - /* open the file, read and check the header */ - if ((fp = fopen(filename, "rb")) == NULL) - return AMX_ERR_NOTFOUND; - fread(&hdr, sizeof hdr, 1, fp); - amx_Align16(&hdr.magic); - amx_Align16((uint16_t *)&hdr.flags); - amx_Align32((uint32_t *)&hdr.size); - amx_Align32((uint32_t *)&hdr.cod); - amx_Align32((uint32_t *)&hdr.dat); - amx_Align32((uint32_t *)&hdr.hea); - amx_Align32((uint32_t *)&hdr.stp); - if (hdr.magic != AMX_MAGIC) { - fclose(fp); - return AMX_ERR_FORMAT; - } /* if */ - - if ((hdr.flags & AMX_FLAG_OVERLAY) != 0) { - /* allocate the block for the data + stack/heap, plus the complete file - * header, plus the overlay pool - */ - #if defined AMXOVL - size = (hdr.stp - hdr.dat) + hdr.cod + OVLPOOLSIZE; - #else - return AMX_ERR_OVERLAY; - #endif - } else { - size = hdr.stp; - } /* if */ - if ((datablock = malloc(size)) == NULL) { - fclose(fp); - return AMX_ERR_MEMORY; - } /* if */ - - /* save the filename, for optionally reading the debug information (we could - * also have read it here immediately); for reading overlays, we also need - * the filename (and in this case, note that amx_Init() already browses - * through all overlays) - */ - strcpy(g_filename, filename); - - /* read in the file, in two parts; first the header and then the data section */ - rewind(fp); - if ((hdr.flags & AMX_FLAG_OVERLAY) != 0) { - #if defined AMXOVL - /* read the entire header */ - fread(datablock, 1, hdr.cod, fp); - /* read the data section, put it behind the header in the block */ - fseek(fp, hdr.dat, SEEK_SET); - fread(datablock + hdr.cod, 1, hdr.hea - hdr.dat, fp); - /* initialize the overlay pool */ - amx_poolinit(datablock + (hdr.stp - hdr.dat) + hdr.cod, OVLPOOLSIZE); - #endif - } else { - fread(datablock, 1, (size_t)hdr.size, fp); - } /* if */ - fclose(fp); - - /* initialize the abstract machine */ - memset(amx, 0, sizeof *amx); - #if defined AMXOVL - if ((hdr.flags & AMX_FLAG_OVERLAY) != 0) { - amx->data = datablock + hdr.cod; - amx->overlay = prun_Overlay; - } /* if */ - #endif - result = amx_Init(amx, datablock); - - /* free the memory block on error, if it was allocated here */ - if (result != AMX_ERR_NONE) { - free(datablock); - amx->base = NULL; /* avoid a double free */ - } /* if */ - - return result; -} - -/* aux_FreeProgram() - * Clean up a program and free memory. - * This function is extracted out of AMXAUX.C. - */ -int AMXAPI aux_FreeProgram(AMX *amx) -{ - if (amx->base!=NULL) { - amx_Cleanup(amx); - free(amx->base); - memset(amx,0,sizeof(AMX)); - } /* if */ - return AMX_ERR_NONE; -} - -/* aux_StrError() - * Convert an error code to a "readable" string. - * This function is extracted out of AMXAUX.C. - */ -char * AMXAPI aux_StrError(int errnum) -{ -static char *messages[] = { - /* AMX_ERR_NONE */ "(none)", - /* AMX_ERR_EXIT */ "Forced exit", - /* AMX_ERR_ASSERT */ "Assertion failed", - /* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)", - /* AMX_ERR_BOUNDS */ "Array index out of bounds", - /* AMX_ERR_MEMACCESS */ "Invalid memory access", - /* AMX_ERR_INVINSTR */ "Invalid instruction", - /* AMX_ERR_STACKLOW */ "Stack underflow", - /* AMX_ERR_HEAPLOW */ "Heap underflow", - /* AMX_ERR_CALLBACK */ "No (valid) native function callback", - /* AMX_ERR_NATIVE */ "Native function failed", - /* AMX_ERR_DIVIDE */ "Divide by zero", - /* AMX_ERR_SLEEP */ "(sleep mode)", - /* AMX_ERR_INVSTATE */ "Invalid state", - /* 14 */ "(reserved)", - /* 15 */ "(reserved)", - /* AMX_ERR_MEMORY */ "Out of memory", - /* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format", - /* AMX_ERR_VERSION */ "File is for a newer version of the AMX", - /* AMX_ERR_NOTFOUND */ "File or function is not found", - /* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)", - /* AMX_ERR_DEBUG */ "Debugger cannot run", - /* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)", - /* AMX_ERR_USERDATA */ "Unable to set user data field (table full)", - /* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT", - /* AMX_ERR_PARAMS */ "Parameter error", - /* AMX_ERR_DOMAIN */ "Domain error, expression result does not fit in range", - /* AMX_ERR_GENERAL */ "General error (unknown or unspecific error)", - /* AMX_ERR_OVERLAY */ "Overlays are unsupported (JIT) or uninitialized", - }; - if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0]) - return "(unknown)"; - return messages[errnum]; -} - -void ExitOnError(AMX *amx, int error) -{ - if (error != AMX_ERR_NONE) { - #if defined AMXDBG - FILE *fp; - AMX_DBG amxdbg; - long line; - const char *filename; - #endif - - printf("Run time error %d: \"%s\" on address %ld\n", - error, aux_StrError(error), (long)amx->cip); - - /* optionally use the debug library to find the line number (if debug info. - * is available) - */ - #if defined AMXDBG - /* load the debug info. */ - if ((fp=fopen(g_filename,"rb")) != NULL && dbg_LoadInfo(&amxdbg,fp) == AMX_ERR_NONE) { - dbg_LookupFile(&amxdbg, amx->cip, &filename); - dbg_LookupLine(&amxdbg, amx->cip, &line); - printf("File: %s, line: %ld\n", filename, line); - dbg_FreeInfo(&amxdbg); - fclose(fp); - } /* if */ - #endif - exit(1); - } /* if */ -} - -void PrintUsage(char *program) -{ - printf("Usage: %s [options]\n\n" - "Options:\n" - "\t-stack\tto monitor stack usage\n" - "\t...\tother options are passed to the script\n" - , program); - exit(1); -} - - -int main(int argc,char *argv[]) -{ - AMX amx; - cell ret = 0; - int err, i; - clock_t start,end; - STACKINFO stackinfo = { 0 }; - AMX_IDLE idlefunc; - - if (argc < 2) - PrintUsage(argv[0]); /* function "usage" aborts the program */ - - #if !defined AMX_NODYNALOAD && defined ENABLE_BINRELOC && (defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) - /* see www.autopackage.org for the BinReloc module */ - if (br_init(NULL)) { - char *libroot=br_find_exe_dir(""); - setenv("AMXLIB",libroot,0); - free(libroot); - } /* if */ - #endif - - /* Load the program and initialize the abstract machine. */ - err = aux_LoadProgram(&amx, argv[1]); - if (err != AMX_ERR_NONE) { - /* try adding an extension */ - char filename[_MAX_PATH]; - strcpy(filename, argv[1]); - strcat(filename, ".amx"); - err = aux_LoadProgram(&amx, filename); - if (err != AMX_ERR_NONE) - PrintUsage(argv[0]); - } /* if */ - - /* To install the debug hook "just-in-time", the signal function needs - * a pointer to the abstract machine(s) to abort. There are various ways - * to implement this; here I have done so with a simple global variable. - */ - global_amx = &amx; - signal(SIGINT, sigabort); - - /* Initialize two core extension modules (more extension modules may be - * loaded & initialized automatically as DLLs or shared libraries. - */ - amx_ConsoleInit(&amx); - err = amx_CoreInit(&amx); - ExitOnError(&amx, err); - - /* save the idle function, if set by any of the extension modules */ - if (amx_GetUserData(&amx, AMX_USERTAG('I','d','l','e'), (void**)&idlefunc) != AMX_ERR_NONE) - idlefunc = NULL; - - for (i = 2; i < argc; i++) { - if (strcmp(argv[i],"-stack") == 0) { - uint16_t flags; - amx_Flags(&amx, &flags); - if ((flags & AMX_FLAG_NOCHECKS) != 0) - printf("This script was compiled with debug information removed.\n" - "Stack monitoring is disfunctional\n\n"); - /* Set "user data" with which the debug monitor can monitor stack usage - * per abstract machine (in this example, though, there is only one abstract - * machine, so a global variable would have sufficed). - */ - memset(&stackinfo, 0, sizeof stackinfo); - err = amx_SetUserData(&amx, AMX_USERTAG('S','t','c','k'), &stackinfo); - ExitOnError(&amx, err); - /* Install the debug hook, so that we can start monitoring the stack/heap - * usage right from the beginning of the script. - */ - amx_SetDebugHook(&amx, prun_Monitor); - } /* if */ - } /* for */ - - start=clock(); - - /* Run the compiled script and time it. The "sleep" instruction causes the - * abstract machine to return in a "restartable" state (it restarts from - * the point that it stopped. This allows for a kind of light-weight - * cooperative multi-tasking. As native functions (or the debug hook) can - * also force an abstract machine to "sleep", you can also use it to create - * "latent functions": functions that allow the host application to continue - * processing, and/or other abstract machines to run, while they wait for - * some resource. - */ - err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN); - while (err == AMX_ERR_SLEEP) { - if (idlefunc != NULL) { - /* If the abstract machine was put to sleep, we can handle events during - * that time. To save the "restart point", we must make a copy of the AMX - * (keeping the stack, frame, instruction pointer and other vital - * registers), but without cloning the entire abstract machine. - * There should be some criterion on when the abstract machine must be - * "woken up". In this example run-time, the parameter of the sleep - * instruction is taken to be a delay in milliseconds. In your own host - * application, you can choose to wait on a resource/semaphore or other. - */ - AMX nested_amx = amx; - clock_t stamp = clock(); - while (((clock() - stamp)*1000)/CLOCKS_PER_SEC < amx.pri - && (err = idlefunc(&nested_amx,amx_Exec)) == AMX_ERR_NONE) - /* nothing */; - ExitOnError(&nested_amx, err); - } /* if */ - err = amx_Exec(&amx, &ret, AMX_EXEC_CONT); - } /* while */ - if (idlefunc == NULL || err != AMX_ERR_INDEX) - ExitOnError(&amx, err); /* event-driven programs may not have main() */ - - /* For event-driven programs, we also need to loop over the idle/monitor - * function that some extension module installed (this could be the console - * module, for example). We did this if the main program was put to "sleep", - * but we do that here too. - */ - if (idlefunc != NULL) { - while ((err = idlefunc(&amx,amx_Exec)) == AMX_ERR_NONE) - /* nothing */; - ExitOnError(&amx, err); - } /* if */ - - end=clock(); - - /* Free the compiled script and resources. This also unloads and DLLs or - * shared libraries that were registered automatically by amx_Init(). - */ - aux_FreeProgram(&amx); - - /* Print the return code of the compiled script (often not very useful), - * its run time, and its stack usage. - */ - if (ret!=0) - printf("\nReturn value: %ld\n", (long)ret); - - printf("\nRun time: %.2f seconds\n",(double)(end-start)/CLOCKS_PER_SEC); - if (stackinfo.maxstack != 0 || stackinfo.maxheap != 0) { - printf("Stack usage: %ld cells (%ld bytes)\n", - stackinfo.maxstack / sizeof(cell), stackinfo.maxstack); - printf("Heap usage: %ld cells (%ld bytes)\n", - stackinfo.maxheap / sizeof(cell), stackinfo.maxheap); - } /* if */ - - #if defined AMX_TERMINAL - /* This is likely a graphical terminal, which should not be closed - * automatically - */ - { - extern int amx_termctl(int,int); - while (amx_termctl(4,0)) - /* nothing */; - } - #endif - - return 0; -} +/* Simple "run-time" for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: pawnrun.c 3615 2006-07-27 07:49:08Z thiadmer $ + */ + +#include +#include +#include +#include +#include /* for memset() (on some compilers) */ +#include "osdefs.h" /* for _MAX_PATH */ +#include "amx.h" + +#include +#if !defined CLOCKS_PER_SEC /* some (older) compilers do not have it */ + #define CLOCKS_PER_SEC CLK_TCK +#endif + +#if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) + #include /* from BinReloc, see www.autopackage.org */ +#endif + +#if defined AMXDBG + #include "amxdbg.h" + static char g_filename[_MAX_PATH];/* for loading the debug information */ +#endif + + +/* These initialization functions are part of the "extension modules" + * (libraries with native functions) that this run-time uses. More + * extension modules may be dynamically linked (depending on whether + * support for dynamic linking is enabled). + */ +extern int AMXEXPORT amx_ConsoleInit(AMX *amx); +extern int AMXEXPORT amx_CoreInit(AMX *amx); + +AMX *global_amx; +int AMXAPI srun_Monitor(AMX *amx); + +static int abortflagged = 0; +void sigabort(int sig) +{ + /* install the debug hook procedure if this was not done already */ + amx_SetDebugHook(global_amx, srun_Monitor); + abortflagged=1; + signal(sig,sigabort); /* re-install the signal handler */ +} + +typedef struct tagSTACKINFO { + long maxstack, maxheap; +} STACKINFO; + + +/* srun_Monitor() + * A simple debug hook, that allows the user to break out of a program + * and that monitors stack usage. Note that the stack usage can only be + * properly monitored when the debug hook is installed from the start + * of the script to the end. + */ +int AMXAPI srun_Monitor(AMX *amx) +{ + int err; + STACKINFO *stackinfo; + + /* record the heap and stack usage */ + err = amx_GetUserData(amx, AMX_USERTAG('S','t','c','k'), (void**)&stackinfo); + if (err == AMX_ERR_NONE) { + if (amx->stp - amx->stk > stackinfo->maxstack) + stackinfo->maxstack = amx->stp - amx->stk; + if (amx->hea - amx->hlw > stackinfo->maxheap) + stackinfo->maxstack = amx->stp - amx->stk; + } /* if */ + + /* check whether an "abort" was requested */ + return abortflagged ? AMX_ERR_EXIT : AMX_ERR_NONE; +} + +/* aux_LoadProgram() + * Load a compiled Pawn script into memory and initialize the abstract machine. + * This function is extracted out of AMXAUX.C. + */ +int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock) +{ + FILE *fp; + AMX_HEADER hdr; + int result, didalloc; + + /* open the file, read and check the header */ + if ((fp = fopen(filename, "rb")) == NULL) + return AMX_ERR_NOTFOUND; + fread(&hdr, sizeof hdr, 1, fp); + amx_Align16(&hdr.magic); + amx_Align32((uint32_t *)&hdr.size); + amx_Align32((uint32_t *)&hdr.stp); + if (hdr.magic != AMX_MAGIC) { + fclose(fp); + return AMX_ERR_FORMAT; + } /* if */ + + /* allocate the memblock if it is NULL */ + didalloc = 0; + if (memblock == NULL) { + if ((memblock = malloc(hdr.stp)) == NULL) { + fclose(fp); + return AMX_ERR_MEMORY; + } /* if */ + didalloc = 1; + /* after amx_Init(), amx->base points to the memory block */ + } /* if */ + + /* read in the file */ + rewind(fp); + fread(memblock, 1, (size_t)hdr.size, fp); + fclose(fp); + + /* initialize the abstract machine */ + memset(amx, 0, sizeof *amx); + result = amx_Init(amx, memblock); + + /* free the memory block on error, if it was allocated here */ + if (result != AMX_ERR_NONE && didalloc) { + free(memblock); + amx->base = NULL; /* avoid a double free */ + } /* if */ + + /* save the filename, for optionally reading the debug information (we could + * also have read it here immediately + */ + #if defined AMXDBG + strcpy(g_filename, filename); + #endif + + return result; +} + +/* aux_FreeProgram() + * Clean up a program and free memory. + * This function is extracted out of AMXAUX.C. + */ +int AMXAPI aux_FreeProgram(AMX *amx) +{ + if (amx->base!=NULL) { + amx_Cleanup(amx); + free(amx->base); + memset(amx,0,sizeof(AMX)); + } /* if */ + return AMX_ERR_NONE; +} + +/* aux_StrError() + * Convert an error code to a "readable" string. + * This function is extracted out of AMXAUX.C. + */ +char * AMXAPI aux_StrError(int errnum) +{ +static char *messages[] = { + /* AMX_ERR_NONE */ "(none)", + /* AMX_ERR_EXIT */ "Forced exit", + /* AMX_ERR_ASSERT */ "Assertion failed", + /* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)", + /* AMX_ERR_BOUNDS */ "Array index out of bounds", + /* AMX_ERR_MEMACCESS */ "Invalid memory access", + /* AMX_ERR_INVINSTR */ "Invalid instruction", + /* AMX_ERR_STACKLOW */ "Stack underflow", + /* AMX_ERR_HEAPLOW */ "Heap underflow", + /* AMX_ERR_CALLBACK */ "No (valid) native function callback", + /* AMX_ERR_NATIVE */ "Native function failed", + /* AMX_ERR_DIVIDE */ "Divide by zero", + /* AMX_ERR_SLEEP */ "(sleep mode)", + /* 13 */ "(reserved)", + /* 14 */ "(reserved)", + /* 15 */ "(reserved)", + /* AMX_ERR_MEMORY */ "Out of memory", + /* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format", + /* AMX_ERR_VERSION */ "File is for a newer version of the AMX", + /* AMX_ERR_NOTFOUND */ "File or function is not found", + /* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)", + /* AMX_ERR_DEBUG */ "Debugger cannot run", + /* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)", + /* AMX_ERR_USERDATA */ "Unable to set user data field (table full)", + /* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT", + /* AMX_ERR_PARAMS */ "Parameter error", + }; + if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0]) + return "(unknown)"; + return messages[errnum]; +} + +void ExitOnError(AMX *amx, int error) +{ + if (error != AMX_ERR_NONE) { + #if defined AMXDBG + FILE *fp; + AMX_DBG amxdbg; + long line; + const char *filename; + #endif + + printf("Run time error %d: \"%s\" on address %ld\n", + error, aux_StrError(error), (long)amx->cip); + + /* optionally use the debug library to find the line number (if debug info. + * is available) + */ + #if defined AMXDBG + /* load the debug info. */ + if ((fp=fopen(g_filename,"r")) != NULL && dbg_LoadInfo(&amxdbg,fp) == AMX_ERR_NONE) { + dbg_LookupFile(&amxdbg, amx->cip, &filename); + dbg_LookupLine(&amxdbg, amx->cip, &line); + printf("File: %s, line: %ld\n", filename, line); + dbg_FreeInfo(&amxdbg); + fclose(fp); + } /* if */ + #endif + exit(1); + } /* if */ +} + +void PrintUsage(char *program) +{ + printf("Usage: %s [options]\n\n" + "Options:\n" + "\t-stack\tto monitor stack usage\n" + "\t...\tother options are passed to the script\n" + , program); + exit(1); +} + + +int main(int argc,char *argv[]) +{ + AMX amx; + cell ret = 0; + int err, i; + clock_t start,end; + STACKINFO stackinfo = { 0 }; + AMX_IDLE idlefunc; + + if (argc < 2) + PrintUsage(argv[0]); /* function "usage" aborts the program */ + + #if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__) + /* see www.autopackage.org for the BinReloc module */ + if (br_init(NULL)) { + char *libroot=br_find_exe_dir(""); + setenv("AMXLIB",libroot,0); + free(libroot); + } /* if */ + #endif + + /* Load the program and initialize the abstract machine. */ + err = aux_LoadProgram(&amx, argv[1], NULL); + if (err != AMX_ERR_NONE) { + /* try adding an extension */ + char filename[_MAX_PATH]; + strcpy(filename, argv[1]); + strcat(filename, ".amx"); + err = aux_LoadProgram(&amx, filename, NULL); + if (err != AMX_ERR_NONE) + PrintUsage(argv[0]); + } /* if */ + + /* To install the debug hook "just-in-time", the signal function needs + * a pointer to the abstract machine(s) to abort. There are various ways + * to implement this; here I have done so with a simple global variable. + */ + global_amx = &amx; + signal(SIGINT, sigabort); + + /* Initialize two core extension modules (more extension modules may be + * loaded & initialized automatically as DLLs or shared libraries. + */ + amx_ConsoleInit(&amx); + err = amx_CoreInit(&amx); + ExitOnError(&amx, err); + + /* save the idle function, if set by any of the extension modules */ + if (amx_GetUserData(&amx, AMX_USERTAG('I','d','l','e'), (void**)&idlefunc) != AMX_ERR_NONE) + idlefunc = NULL; + + for (i = 2; i < argc; i++) { + if (strcmp(argv[i],"-stack") == 0) { + uint16_t flags; + amx_Flags(&amx, &flags); + if ((flags & AMX_FLAG_NOCHECKS) != 0) + printf("This script was compiled with debug information removed.\n" + "Stack monitoring is disfunctional\n\n"); + /* Set "user data" with which the debug monitor can monitor stack usage + * per abstract machine (in this example, though, there is only one abstract + * machine, so a global variable would have sufficed). + */ + memset(&stackinfo, 0, sizeof stackinfo); + err = amx_SetUserData(&amx, AMX_USERTAG('S','t','c','k'), &stackinfo); + ExitOnError(&amx, err); + /* Install the debug hook, so that we can start monitoring the stack/heap + * usage right from the beginning of the script. + */ + amx_SetDebugHook(&amx, srun_Monitor); + } /* if */ + } /* for */ + + start=clock(); + + /* Run the compiled script and time it. The "sleep" instruction causes the + * abstract machine to return in a "restartable" state (it restarts from + * the point that it stopped. This allows for a kind of light-weight + * cooperative multi-tasking. As native functions (or the debug hook) can + * also force an abstract machine to "sleep", you can also use it to create + * "latent functions": functions that allow the host application to continue + * processing, and/or other abstract machines to run, while they wait for + * some resource. + */ + err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN); + while (err == AMX_ERR_SLEEP) { + if (idlefunc != NULL) { + /* If the abstract machine was put to sleep, we can handle events during + * that time. To save the "restart point", we must make a copy of the AMX + * (keeping the stack, frame, instruction pointer and other vital + * registers), but without cloning the entire abstract machine. + * There should be some criterion on when the abstract machine must be + * "woken up". In this example run-time, the parameter of the sleep + * instruction is taken to be a delay in milliseconds. In your own host + * application, you can choose to wait on a resource/semaphore or other. + */ + AMX nested_amx = amx; + clock_t stamp = clock(); + while (((clock() - stamp)*1000)/CLOCKS_PER_SEC < amx.pri + && (err = idlefunc(&nested_amx,amx_Exec)) == AMX_ERR_NONE) + /* nothing */; + ExitOnError(&nested_amx, err); + } /* if */ + err = amx_Exec(&amx, &ret, AMX_EXEC_CONT); + } /* while */ + ExitOnError(&amx, err); + + /* For event-driven programs, we also need to loop over the idle/monitor + * function that some extension module installed (this could be the console + * module, for example). We did this if the main program was put to "sleep", + * but we do that here too. + */ + if (idlefunc != NULL) { + while ((err = idlefunc(&amx,amx_Exec)) == AMX_ERR_NONE) + /* nothing */; + ExitOnError(&amx, err); + } /* if */ + + end=clock(); + + /* Free the compiled script and resources. This also unloads and DLLs or + * shared libraries that were registered automatically by amx_Init(). + */ + aux_FreeProgram(&amx); + + /* Print the return code of the compiled script (often not very useful), + * its run time, and its stack usage. + */ + if (ret!=0) + printf("\nReturn value: %ld\n", (long)ret); + + printf("\nRun time: %.2f seconds\n",(double)(end-start)/CLOCKS_PER_SEC); + if (stackinfo.maxstack != 0 || stackinfo.maxheap != 0) { + printf("Stack usage: %ld cells (%ld bytes)\n", + stackinfo.maxstack / sizeof(cell), stackinfo.maxstack); + printf("Heap usage: %ld cells (%ld bytes)\n", + stackinfo.maxheap / sizeof(cell), stackinfo.maxheap); + } /* if */ + + #if defined AMX_TERMINAL + /* This is likely a graphical terminal, which should not be closed + * automatically + */ + { + extern int amx_termctl(int,int); + while (amx_termctl(4,0)) + /* nothing */; + } + #endif + + return 0; +} diff --git a/amx-deps/src/amx/premake5.lua b/amx-deps/src/amx/premake5.lua new file mode 100644 index 0000000..b68744f --- /dev/null +++ b/amx-deps/src/amx/premake5.lua @@ -0,0 +1,42 @@ +local amxfiles = { + "amx.c", + "amxaux.c", + "amxcons.c", + "amxcore.c", + "amxfile.c", + "amxstring.c", + "amxtime.c", + "amxfloat.c", +} + +project "amx" + language "C++" + kind "StaticLib" + + defines { + -- From original project, but causes crashes? + -- "AMX_DONT_RELOCATE" + "FLOATPOINT", + } + + filter "system:windows" + -- "__WIN32__" needed for amx + defines { "__WIN32__" } + + vpaths { + ["Headers/*"] = {"**.h", "../linux/**.h"}, + ["Sources/*"] = amxfiles, + } + + files { + amxfiles, + } + + filter "system:linux" + files { "../linux/getch.c" } + + filter "system:linux" + includedirs { "../linux" } + + filter "system:windows" + links { "winmm" } \ No newline at end of file diff --git a/amx-deps/src/amx/term_ga.c b/amx-deps/src/amx/term_ga.c index 63e6707..332616a 100644 --- a/amx-deps/src/amx/term_ga.c +++ b/amx-deps/src/amx/term_ga.c @@ -1,517 +1,517 @@ -/* Simple terminal using GraphApp - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: term_ga.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#include -#include -#include -#include -#include -#include -#include "term_ga.h" - -#define NUM_COLUMNS 80 -#define NUM_LINES 30 -#define KEYQUEUE_SIZE 32 -#define BUFFERSIZE 2048 - -static App *app = NULL; -static Window *win = NULL; -static Font *font = NULL; -static TCHAR *lines = NULL; -static unsigned long keyqueue[KEYQUEUE_SIZE]; -static int keyq_start,keyq_end; -static int csrx = 0, csry = 0; -static int autowrap = 0; -static unsigned char attrib = 0x07; - -static -void window_redraw(Window *w, Graphics *g) -{ - Rect r; - Point p; - int l, h; - int length; - char *buffer; - - assert(lines != NULL); - /* Each wide char is encoded in at most 6 UTF8 characters. For the Base Plane, - * 3 UTF8 characters per Unicode character is enough - */ - buffer = malloc((6 * NUM_COLUMNS + 1) * sizeof(char)); - if (buffer != NULL) { - r = get_window_area(w); - - set_rgb(g, rgb(240,240,240)); //??? attrib - fill_rect(g, r); - - set_rgb(g, rgb(0,0,0)); //??? attrib - set_font(g, font); - set_text_direction(g, LR_TB); - p = pt(0,0); - h = font_height(font); - for (l = 0; l < NUM_LINES; l++) { - #if defined _UNICODE - /* convert the line to UTF8 */ - int c; - char *ptr; - for (c = 0, ptr = buffer; c < NUM_COLUMNS; c++) - amx_UTF8Put(ptr, &ptr, 6, lines + l * NUM_COLUMNS + c); - *ptr = '\0'; - length = (int)(ptr - buffer); - #else - /* assume line is ASCII */ - memcpy(buffer, lines + l * NUM_COLUMNS, NUM_COLUMNS); - buffer[NUM_COLUMNS] = '\0'; - length = NUM_COLUMNS; - #endif - - /* draw the line */ - draw_utf8(g, p, buffer, length); - p.y += h; - if (p.y > r.height) - break; - } /* if */ - - free(buffer); - } /* if */ -} - -/* dx = in columns, dy = in lines */ -void scroll_window(int dx, int dy) -{ - Graphics *g; - Rect r; - Point p; - - /* a negative value scrolls up */ - assert(lines != NULL); - if (dy < 0) { - assert(-dy < NUM_LINES); - memmove(lines,lines-dy*NUM_COLUMNS,(NUM_LINES+dy)*NUM_COLUMNS*sizeof(TCHAR)); - memset(lines+(NUM_LINES+dy)*NUM_COLUMNS*sizeof(TCHAR), __T(' '), -dy*NUM_COLUMNS); - } else if (dy > 0) { - assert(dy < NUM_LINES); - memmove(lines+dy*NUM_COLUMNS,lines,(NUM_LINES-dy)*NUM_COLUMNS*sizeof(TCHAR)); - memset(lines, __T(' '), dy*NUM_COLUMNS); - } /* if */ - csry += dy; - if (csry < 0) - csry = 0; - if (csry >= NUM_LINES) - csry=NUM_LINES - 1; - - assert(font != NULL); - dx *= font_width(font, "x", 1); - dy *= font_height(font); - - g = get_window_graphics(win); - r = get_window_area(win); - p = pt(r.x + dx, r.y + dy); - copy_rect(g, p, g, r); - if (dy > 0) { - /* moving window contents downwards */ - redraw_rect(win, rect(0,0,r.width,dy)); - } else if (dy < 0) { - /* moving window contents upwards */ - redraw_rect(win, rect(0,r.height+dy,r.width,0-dy)); - } /* if */ - if (dx > 0) { - /* moving window contents to the right */ - redraw_rect(win, rect(0,0,dx,r.height)); - } else if (dx < 0) { - /* moving window contents to the left */ - redraw_rect(win, rect(r.width+dx,0,0-dx,r.height)); - } /* if */ - del_graphics(g); -} - -static -void refresh_screen(int top, int bottom) -{ - Rect r; - int h; - - if (top != bottom) { - assert(win != NULL); - r = get_window_area(win); - assert(font != NULL); - h = font_height(font); - redraw_rect(win, rect(0, top * h, r.width, (bottom - top) * h)); - } /* if */ - - //??? set to draw a caret -} - -#if 0 -static -void window_fkey_action(Window *w, unsigned long key) -{ -} -#endif - -static -void window_key_action(Window *w, unsigned long key) -{ - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - /* translate the Enter key from '\n' (GraphApp convention) to '\r' - * (AMXCONS convention when running under Microsoft Windows) - */ - if (key == '\n') - key = '\r'; - #endif - - if ((keyq_end+1)%KEYQUEUE_SIZE==keyq_start) { - beep(app); - } else { - keyqueue[keyq_end]=key; - keyq_end=(keyq_end+1)%KEYQUEUE_SIZE; - } /* if */ -} - -static -void window_close(Window *w) -{ - /* we cannot delete the window on this notification (GraphApp refers to it - * on return from the function), so we just mark the global variable - * as invalid - */ - win = NULL; -} - -int createconsole(int argc, char *argv[]) -{ - if (win != NULL) - return 1; - - if (app != NULL) /* delete existing partial data structures */ - deleteconsole(); - - lines = malloc(NUM_LINES*NUM_COLUMNS*sizeof(TCHAR)); - if (lines == NULL) - return 0; - memset(lines, __T(' '), NUM_LINES * NUM_COLUMNS); - - app = new_app(argc, argv); - if (app == NULL) { - deleteconsole(); - return 0; - } /* if */ - - font = new_font(app, "unifont", PLAIN | PORTABLE_FONT, 16); - if (font == NULL) { - deleteconsole(); - return 0; - } /* if */ - - win = new_window(app, - rect(0,0, - NUM_COLUMNS*font_width(font,"x",1), - NUM_LINES*font_height(font)), - "Pawn console", - TITLEBAR|CLOSEBOX|MAXIMIZE|MINIMIZE|CENTRED); - on_window_redraw(win, window_redraw); - on_window_close (win, window_close); - on_window_key_down(win, window_key_action); /* normal keys (including CR) */ - show_window(win); - - /* handle any pending events */ - while (do_event(app)) - /* nothing */; - - csrx = 0; - csry = 0; - autowrap = 0; - attrib = 0x07; - - return 1; -} - -int deleteconsole(void) -{ - if (font != NULL) { - del_font(font); - font = NULL; - } /* if */ - if (app != NULL) { - del_app(app); - app = NULL; - } /* if */ - if (lines != NULL) { - free(lines); - lines = NULL; - } /* if */ - win = NULL; - return 1; -} - -int amx_putstr(const TCHAR *format) -{ - if (createconsole(0, NULL)) { - int pos, i; - - pos=csry * NUM_COLUMNS + csrx; - assert(lines!=NULL); - for (i=0; string[i]!=__T('\0'); i++) { - if (csry=NUM_LINES) - scroll_window(0, -1); - pos=csry * NUM_COLUMNS + csrx; - } else if (string[i]==__T('\b')) { - if (csrx>0) { - csrx--; - pos--; - lines[pos]=__T(' '); - //??? lines[pos+1]=attrib; - } /* if */ - } else { - lines[pos]=string[i]; - //??? lines[pos+1]=attrib; - pos++; - csrx++; - if (csrx>=NUM_COLUMNS && autowrap) { - csrx=0; - csry++; - if (csry>=NUM_LINES) - scroll_window(0, -1); - pos=csry * NUM_COLUMNS + csrx; - } /* if */ - } /* if */ - } /* if */ - } /* for */ - refresh_screen(csry,csry+1); - } /* if */ - return 0; -} - -int amx_printf(const TCHAR *format,...) -{ - int cnt; - TCHAR buffer[BUFFERSIZE]; - va_list argptr; - - va_start(argptr,format); - cnt=_vstprintf(buffer,format,argptr); - va_end(argptr); - amx_putstr(buffer); - return cnt; -} - -int amx_putchar(int c) -{ - if (createconsole(0, NULL)) { - if (csry=NUM_LINES) - scroll_window(0, -1); - } else if (c==__T('\b')) { - if (csrx>0) { - csrx--; - pos--; - lines[pos]=__T(' '); - //??? lines[pos+1]=attrib; - } /* if */ - } else { - lines[pos]=(TCHAR)c; - //??? lines[pos+1]=attrib; - csrx++; - if (csrx>=NUM_COLUMNS && autowrap) { - csrx=0; - csry++; - if (csry>=NUM_LINES) - scroll_window(0, -1); - } /* if */ - } /* if */ - refresh_screen(csry,csry+1); - } /* if */ - } /* if */ - return 1; -} - -int amx_fflush(void) -{ - return 1; -} - -int amx_kbhit(void) -{ - if (createconsole(0, NULL)) - return keyq_start!=keyq_end; - return 0; -} - -int amx_getch(void) -{ - int c=-1; - - if (createconsole(0, NULL)) { - while (keyq_start==keyq_end && app!=NULL) { - wait_event(app); - do_event(app); - } /* while */ - c=(int)keyqueue[keyq_start]; - keyq_start=(keyq_start+1)%KEYQUEUE_SIZE; - } /* if */ - return c; -} - -TCHAR *amx_gets(TCHAR *string,int size) -{ - int c=-1,num=0; - - if (createconsole(0, NULL)) { - while (num+10) - string[--num]=__T(' '); - } else { - string[num++]=(TCHAR)c; - } /* if */ - amx_putchar(c); /* echo the character read */ - } /* while */ - if (num0 && x<=NUM_COLUMNS) - csrx=x-1; - if (y>0 && y<=NUM_LINES) - csry=y-1; - refresh_screen(0, 0); /* only to set the cursor at the correct location */ - } /* if */ -} - -void amx_wherexy(int *x,int *y) -{ - if (createconsole(0, NULL)) { - if (x!=NULL) - *x=csrx+1; - if (y!=NULL) - *y=csry+1; - } /* if */ -} - -unsigned int amx_setattr(int foregr,int backgr,int highlight) -{ - int prev=0; - if (createconsole(0, NULL)) { - int f,b,h; - - f=attrib & 0x07; - b=(attrib >> 4) & 0x0f; - h=(attrib & 0x08) ? 1 : 0; - prev=(b << 8) | f | (h << 15); - if (foregr>=0 && foregr<8) - f=foregr; - if (backgr>=0 && backgr<8) - b=backgr; - if (highlight>=0) - h=highlight!=0; - attrib=(TCHAR)((b << 4) | f | (h << 3)); - } /* if */ - return prev; -} - -void amx_console(int columns, int lines, int flags) -{ - createconsole(0, NULL); /* columns and lines are currently not supported */ -} +/* Simple terminal using GraphApp + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: term_ga.c 3519 2006-02-17 17:57:04Z thiadmer $ + */ + +#include +#include +#include +#include +#include +#include +#include "term_ga.h" + +#define NUM_COLUMNS 80 +#define NUM_LINES 30 +#define KEYQUEUE_SIZE 32 +#define BUFFERSIZE 2048 + +static App *app = NULL; +static Window *win = NULL; +static Font *font = NULL; +static TCHAR *lines = NULL; +static unsigned long keyqueue[KEYQUEUE_SIZE]; +static int keyq_start,keyq_end; +static int csrx = 0, csry = 0; +static int autowrap = 0; +static unsigned char attrib = 0x07; + +static +void window_redraw(Window *w, Graphics *g) +{ + Rect r; + Point p; + int l, h; + int length; + char *buffer; + + assert(lines != NULL); + /* Each wide char is encoded in at most 6 UTF8 characters. For the Base Plane, + * 3 UTF8 characters per Unicode character is enough + */ + buffer = malloc((6 * NUM_COLUMNS + 1) * sizeof(char)); + if (buffer != NULL) { + r = get_window_area(w); + + set_rgb(g, rgb(240,240,240)); //??? attrib + fill_rect(g, r); + + set_rgb(g, rgb(0,0,0)); //??? attrib + set_font(g, font); + set_text_direction(g, LR_TB); + p = pt(0,0); + h = font_height(font); + for (l = 0; l < NUM_LINES; l++) { + #if defined _UNICODE + /* convert the line to UTF8 */ + int c; + char *ptr; + for (c = 0, ptr = buffer; c < NUM_COLUMNS; c++) + amx_UTF8Put(ptr, &ptr, 6, lines + l * NUM_COLUMNS + c); + *ptr = '\0'; + length = (int)(ptr - buffer); + #else + /* assume line is ASCII */ + memcpy(buffer, lines + l * NUM_COLUMNS, NUM_COLUMNS); + buffer[NUM_COLUMNS] = '\0'; + length = NUM_COLUMNS; + #endif + + /* draw the line */ + draw_utf8(g, p, buffer, length); + p.y += h; + if (p.y > r.height) + break; + } /* if */ + + free(buffer); + } /* if */ +} + +/* dx = in columns, dy = in lines */ +void scroll_window(int dx, int dy) +{ + Graphics *g; + Rect r; + Point p; + + /* a negative value scrolls up */ + assert(lines != NULL); + if (dy < 0) { + assert(-dy < NUM_LINES); + memmove(lines,lines-dy*NUM_COLUMNS,(NUM_LINES+dy)*NUM_COLUMNS*sizeof(TCHAR)); + memset(lines+(NUM_LINES+dy)*NUM_COLUMNS*sizeof(TCHAR), __T(' '), -dy*NUM_COLUMNS); + } else if (dy > 0) { + assert(dy < NUM_LINES); + memmove(lines+dy*NUM_COLUMNS,lines,(NUM_LINES-dy)*NUM_COLUMNS*sizeof(TCHAR)); + memset(lines, __T(' '), dy*NUM_COLUMNS); + } /* if */ + csry += dy; + if (csry < 0) + csry = 0; + if (csry >= NUM_LINES) + csry=NUM_LINES - 1; + + assert(font != NULL); + dx *= font_width(font, "x", 1); + dy *= font_height(font); + + g = get_window_graphics(win); + r = get_window_area(win); + p = pt(r.x + dx, r.y + dy); + copy_rect(g, p, g, r); + if (dy > 0) { + /* moving window contents downwards */ + redraw_rect(win, rect(0,0,r.width,dy)); + } else if (dy < 0) { + /* moving window contents upwards */ + redraw_rect(win, rect(0,r.height+dy,r.width,0-dy)); + } /* if */ + if (dx > 0) { + /* moving window contents to the right */ + redraw_rect(win, rect(0,0,dx,r.height)); + } else if (dx < 0) { + /* moving window contents to the left */ + redraw_rect(win, rect(r.width+dx,0,0-dx,r.height)); + } /* if */ + del_graphics(g); +} + +static +void refresh_screen(int top, int bottom) +{ + Rect r; + int h; + + if (top != bottom) { + assert(win != NULL); + r = get_window_area(win); + assert(font != NULL); + h = font_height(font); + redraw_rect(win, rect(0, top * h, r.width, (bottom - top) * h)); + } /* if */ + + //??? set to draw a caret +} + +#if 0 +static +void window_fkey_action(Window *w, unsigned long key) +{ +} +#endif + +static +void window_key_action(Window *w, unsigned long key) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + /* translate the Enter key from '\n' (GraphApp convention) to '\r' + * (AMXCONS convention when running under Microsoft Windows) + */ + if (key == '\n') + key = '\r'; + #endif + + if ((keyq_end+1)%KEYQUEUE_SIZE==keyq_start) { + beep(app); + } else { + keyqueue[keyq_end]=key; + keyq_end=(keyq_end+1)%KEYQUEUE_SIZE; + } /* if */ +} + +static +void window_close(Window *w) +{ + /* we cannot delete the window on this notification (GraphApp refers to it + * on return from the function), so we just mark the global variable + * as invalid + */ + win = NULL; +} + +int createconsole(int argc, char *argv[]) +{ + if (win != NULL) + return 1; + + if (app != NULL) /* delete existing partial data structures */ + deleteconsole(); + + lines = malloc(NUM_LINES*NUM_COLUMNS*sizeof(TCHAR)); + if (lines == NULL) + return 0; + memset(lines, __T(' '), NUM_LINES * NUM_COLUMNS); + + app = new_app(argc, argv); + if (app == NULL) { + deleteconsole(); + return 0; + } /* if */ + + font = new_font(app, "unifont", PLAIN | PORTABLE_FONT, 16); + if (font == NULL) { + deleteconsole(); + return 0; + } /* if */ + + win = new_window(app, + rect(0,0, + NUM_COLUMNS*font_width(font,"x",1), + NUM_LINES*font_height(font)), + "Pawn console", + TITLEBAR|CLOSEBOX|MAXIMIZE|MINIMIZE|CENTRED); + on_window_redraw(win, window_redraw); + on_window_close (win, window_close); + on_window_key_down(win, window_key_action); /* normal keys (including CR) */ + show_window(win); + + /* handle any pending events */ + while (do_event(app)) + /* nothing */; + + csrx = 0; + csry = 0; + autowrap = 0; + attrib = 0x07; + + return 1; +} + +int deleteconsole(void) +{ + if (font != NULL) { + del_font(font); + font = NULL; + } /* if */ + if (app != NULL) { + del_app(app); + app = NULL; + } /* if */ + if (lines != NULL) { + free(lines); + lines = NULL; + } /* if */ + win = NULL; + return 1; +} + +int amx_putstr(const TCHAR *format) +{ + if (createconsole(0, NULL)) { + int pos, i; + + pos=csry * NUM_COLUMNS + csrx; + assert(lines!=NULL); + for (i=0; string[i]!=__T('\0'); i++) { + if (csry=NUM_LINES) + scroll_window(0, -1); + pos=csry * NUM_COLUMNS + csrx; + } else if (string[i]==__T('\b')) { + if (csrx>0) { + csrx--; + pos--; + lines[pos]=__T(' '); + //??? lines[pos+1]=attrib; + } /* if */ + } else { + lines[pos]=string[i]; + //??? lines[pos+1]=attrib; + pos++; + csrx++; + if (csrx>=NUM_COLUMNS && autowrap) { + csrx=0; + csry++; + if (csry>=NUM_LINES) + scroll_window(0, -1); + pos=csry * NUM_COLUMNS + csrx; + } /* if */ + } /* if */ + } /* if */ + } /* for */ + refresh_screen(csry,csry+1); + } /* if */ + return 0; +} + +int amx_printf(const TCHAR *format,...) +{ + int cnt; + TCHAR buffer[BUFFERSIZE]; + va_list argptr; + + va_start(argptr,format); + cnt=_vstprintf(buffer,format,argptr); + va_end(argptr); + amx_putstr(buffer); + return cnt; +} + +int amx_putchar(int c) +{ + if (createconsole(0, NULL)) { + if (csry=NUM_LINES) + scroll_window(0, -1); + } else if (c==__T('\b')) { + if (csrx>0) { + csrx--; + pos--; + lines[pos]=__T(' '); + //??? lines[pos+1]=attrib; + } /* if */ + } else { + lines[pos]=(TCHAR)c; + //??? lines[pos+1]=attrib; + csrx++; + if (csrx>=NUM_COLUMNS && autowrap) { + csrx=0; + csry++; + if (csry>=NUM_LINES) + scroll_window(0, -1); + } /* if */ + } /* if */ + refresh_screen(csry,csry+1); + } /* if */ + } /* if */ + return 1; +} + +int amx_fflush(void) +{ + return 1; +} + +int amx_kbhit(void) +{ + if (createconsole(0, NULL)) + return keyq_start!=keyq_end; + return 0; +} + +int amx_getch(void) +{ + int c=-1; + + if (createconsole(0, NULL)) { + while (keyq_start==keyq_end && app!=NULL) { + wait_event(app); + do_event(app); + } /* while */ + c=(int)keyqueue[keyq_start]; + keyq_start=(keyq_start+1)%KEYQUEUE_SIZE; + } /* if */ + return c; +} + +TCHAR *amx_gets(TCHAR *string,int size) +{ + int c=-1,num=0; + + if (createconsole(0, NULL)) { + while (num+10) + string[--num]=__T(' '); + } else { + string[num++]=(TCHAR)c; + } /* if */ + amx_putchar(c); /* echo the character read */ + } /* while */ + if (num0 && x<=NUM_COLUMNS) + csrx=x-1; + if (y>0 && y<=NUM_LINES) + csry=y-1; + refresh_screen(0, 0); /* only to set the cursor at the correct location */ + } /* if */ +} + +void amx_wherexy(int *x,int *y) +{ + if (createconsole(0, NULL)) { + if (x!=NULL) + *x=csrx+1; + if (y!=NULL) + *y=csry+1; + } /* if */ +} + +unsigned int amx_setattr(int foregr,int backgr,int highlight) +{ + int prev=0; + if (createconsole(0, NULL)) { + int f,b,h; + + f=attrib & 0x07; + b=(attrib >> 4) & 0x0f; + h=(attrib & 0x08) ? 1 : 0; + prev=(b << 8) | f | (h << 15); + if (foregr>=0 && foregr<8) + f=foregr; + if (backgr>=0 && backgr<8) + b=backgr; + if (highlight>=0) + h=highlight!=0; + attrib=(TCHAR)((b << 4) | f | (h << 3)); + } /* if */ + return prev; +} + +void amx_console(int columns, int lines, int flags) +{ + createconsole(0, NULL); /* columns and lines are currently not supported */ +} diff --git a/amx-deps/src/amx/term_ga.h b/amx-deps/src/amx/term_ga.h index 5982cbd..2a451cd 100644 --- a/amx-deps/src/amx/term_ga.h +++ b/amx-deps/src/amx/term_ga.h @@ -1,76 +1,76 @@ -/* Simple terminal using GraphApp - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * This file may be freely used. No warranties of any kind. - * - * Version: $Id: term_ga.h 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#if !defined TERMGA_H_INCLUDED -#define TERMGA_H_INCLUDED - -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _stprintf sprintf -# define _vstprintf vsprintf -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -int createconsole(int argc, char *argv[]); -int deleteconsole(void); - -int amx_printf(const TCHAR*,...); -int amx_putstr(const TCHAR*); -int amx_putchar(int); -int amx_fflush(void); -int amx_kbhit(void); -int amx_getch(void); -TCHAR* amx_gets(TCHAR*,int); -int amx_termctl(int,int); -void amx_clrscr(void); -void amx_clreol(void); -void amx_gotoxy(int x,int y); -void amx_wherexy(int *x,int *y); -unsigned amx_setattr(int foregr,int backgr,int highlight); -void amx_console(int columns, int lines, int flags); - -#ifdef __cplusplus - } -#endif - -#endif /* TERMGA_H_INCLUDED */ +/* Simple terminal using GraphApp + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * This file may be freely used. No warranties of any kind. + * + * Version: $Id: term_ga.h 3519 2006-02-17 17:57:04Z thiadmer $ + */ + +#if !defined TERMGA_H_INCLUDED +#define TERMGA_H_INCLUDED + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _stprintf sprintf +# define _vstprintf vsprintf +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +int createconsole(int argc, char *argv[]); +int deleteconsole(void); + +int amx_printf(const TCHAR*,...); +int amx_putstr(const TCHAR*); +int amx_putchar(int); +int amx_fflush(void); +int amx_kbhit(void); +int amx_getch(void); +TCHAR* amx_gets(TCHAR*,int); +int amx_termctl(int,int); +void amx_clrscr(void); +void amx_clreol(void); +void amx_gotoxy(int x,int y); +void amx_wherexy(int *x,int *y); +unsigned amx_setattr(int foregr,int backgr,int highlight); +void amx_console(int columns, int lines, int flags); + +#ifdef __cplusplus + } +#endif + +#endif /* TERMGA_H_INCLUDED */ diff --git a/amx-deps/src/amx/termwin.c b/amx-deps/src/amx/termwin.c index ccf99b9..f645b2a 100644 --- a/amx-deps/src/amx/termwin.c +++ b/amx-deps/src/amx/termwin.c @@ -1,1097 +1,1073 @@ -/* Simple terminal for Microsoft Windows - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: termwin.c 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#if defined _UNICODE -# include -#else -# include -#endif - -#include -#include -#include -#include -#include -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - #include -#endif -#include "termwin.h" - -#define DEFWINLINES 30 -#define DEFBUFFERLINES DEFWINLINES -#define DEFCOLUMNS 80 -#define DEFFONTHEIGHT 14 -#define KEYQUEUE_SIZE 32 -#define BUFFERSIZE 2048 - -typedef struct tagCONSOLE { - struct tagCONSOLE *next; - HWND hwnd; - TCHAR *buffer; /* text buffer */ - int lines,columns; - int winlines; /* default # lines in the window */ - TCHAR attrib; /* text attribute */ - int csrx,csry; /* cursor position */ - short keyqueue[KEYQUEUE_SIZE]; - int keyq_start,keyq_end; - BOOL autowrap; - BOOL boldfont; - HFONT hfont; - int cwidth,cheight; /* character width and height */ -} CONSOLE; - -static CONSOLE consoleroot={ NULL }; -static HWND activeconsole=NULL; - -#if defined __WIN32__ || defined _WIN32 || defined WIN32 - #define EXPORT -#else - #define EXPORT _export -#endif - -long CALLBACK EXPORT ConsoleFunc(HWND hwnd,unsigned message,WPARAM wParam, - LPARAM lParam); - -static BOOL InitWindowClass(HINSTANCE hinst) -{ - static BOOL initok=FALSE; - if (!initok) { - WNDCLASS wc; - memset(&wc,0,sizeof wc); - wc.style=CS_GLOBALCLASS; - wc.lpfnWndProc=(WNDPROC)ConsoleFunc; - wc.hInstance=hinst; - wc.hCursor=LoadCursor(NULL, IDC_ARROW); - wc.hIcon=LoadIcon(GetModuleHandle(NULL), __T("AppIcon")); - wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); - wc.lpszClassName=__T("TermWin:Console"); - initok=RegisterClass(&wc); - } /* if */ - return initok; -} - -static CONSOLE *Hwnd2Console(HWND hwnd) -{ - CONSOLE *con; - for (con=consoleroot.next; con!=NULL && con->hwnd!=hwnd; con=con->next) - /* nothing */; - return con; -} - -static void DoDeleteConsole(CONSOLE *con) -{ - CONSOLE *prev; - - assert(con!=NULL); - - /* unlink first, to avoid a recursive delete when destroying the window */ - for (prev=&consoleroot; prev->next!=NULL && prev->next!=con; prev=prev->next) - /* nothing */; - if (prev->next==con) - prev->next=con->next; - - /* free memory and resources */ - if (con->hwnd!=NULL && IsWindow(con->hwnd)) - DestroyWindow(con->hwnd); - if (con->hfont!=0) - DeleteObject(con->hfont); - if (con->buffer!=NULL) - free(con->buffer); - free(con); -} - -HWND CreateConsole(HINSTANCE hinst,HWND hwndParent,int columns,int lines,int bufferlines,int fontsize, - DWORD style) -{ - CONSOLE *con, *prev; - - /* allocate a structure to hold the information */ - con=malloc(sizeof(CONSOLE)); - if (con==NULL) - return NULL; - memset(con,0,sizeof(CONSOLE)); - - /* link to the list */ - for (prev=&consoleroot; prev->next!=NULL; prev=prev->next) - /* nothing */; - prev->next=con; - - /* fill in information */ - if (lines>=bufferlines) - lines=bufferlines; - con->lines=bufferlines; - con->columns=columns; - con->winlines=lines; - con->cheight=fontsize; - con->attrib=0xf0; /* black on white */ - con->autowrap=TRUE; - con->buffer=malloc(bufferlines*columns*2*sizeof(TCHAR)); - /* clear in the console - * just like the DOS console buffer, our console consists of character/ - * attribute pairs. - */ - if (con->buffer!=NULL) { - int i; - int size=bufferlines*columns*2; - for (i=0; ibuffer[i]=__T(' '); - con->buffer[i+1]=con->attrib; - } /* for */ - } /* if */ - - if (style==0) - style=WS_OVERLAPPEDWINDOW; - if (hinst==NULL) - hinst=GetModuleHandle(NULL); - - /* create the window */ - InitWindowClass(hinst); - con->hwnd=CreateWindow(__T("TermWin:Console"),__T("Pawn console"),style, - CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, - hwndParent,NULL,hinst,NULL); - - /* check whether all is ok */ - if (IsWindow(con->hwnd) && con->buffer!=NULL) { - ShowWindow(con->hwnd,SW_SHOWNORMAL); - return con->hwnd; - } /* if */ - /* when we arrive here, something was initialized correctly and something else was not */ - DoDeleteConsole(con); - return NULL; -} - -BOOL DeleteConsole(HWND hconsole) -{ - CONSOLE *con=Hwnd2Console(hconsole); - if (con!=NULL) - DoDeleteConsole(con); - return con!=NULL; -} - -BOOL SetActiveConsole(HWND hconsole) -{ - CONSOLE *con=Hwnd2Console(hconsole); - if (con!=NULL) - activeconsole=con->hwnd; - return con!=NULL; -} - -HWND GetConsoleByIndex(int index) -{ - CONSOLE *con; - - for (con=consoleroot.next; con!=NULL && index>0; con=con->next) - index--; - if (con!=NULL) - return con->hwnd; - return NULL; -} - -static CONSOLE *ActiveConsole(void) -{ - CONSOLE *con; - - for (con=consoleroot.next; con!=NULL && con->hwnd!=activeconsole; con=con->next) - /* nothing */; - if (con==NULL) { /* active console "disappeared", switch to first console */ - /* create a console if there are none left */ - if (consoleroot.next==NULL) - CreateConsole(NULL,HWND_DESKTOP,DEFCOLUMNS,DEFWINLINES,DEFBUFFERLINES,DEFFONTHEIGHT,0); - con=consoleroot.next; - activeconsole= (con!=NULL) ? con->hwnd : NULL; - } /* if */ - /* if "con" still is NULL here, then the following holds: - * 1. the "active console" (if there was one) disappeared - * 2. there are no consoles left at all - * 3. a new console could not be created - */ - return con; -} - -#if defined __WIN32__ || defined _WIN32 || defined WIN32 -static void ConsoleThreadProc(void *dummy) -{ - MSG msg; - - (void)dummy; - - /* initialize the screen */ - amx_console(DEFCOLUMNS,DEFWINLINES,0); - - /* message loop to process user input */ - while (amx_termctl(4,0)) { - if (GetMessage(&msg,NULL,0,0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } /* if */ - } /* while */ -} - -BOOL CreateConsoleThread(void) -{ - AllocConsole(); - if ((HANDLE)_beginthread(ConsoleThreadProc, 0, NULL)==INVALID_HANDLE_VALUE) - return FALSE; - return TRUE; -} -#endif - -static void SetConsoleFont(CONSOLE *con,int height) -{ -static TCHAR *ConsoleFonts[] = { - #if defined _UNICODE - __T("Monotype.com"), - #endif - __T("Andante"), - __T("ProggyClean"), - __T("Bitstream Vera Sans Mono"), - __T("Lucida Console"), - __T("Monaco"), - __T("Andale Mono"), - __T("Courier New"), - __T("FixedSys"), - NULL }; - HDC hdc; - SIZE size; - HFONT hfontOrg; - int weight,index; - - assert(con!=NULL); - - /* remove the existing font (if any) */ - if (con->hfont!=0) - DeleteObject(con->hfont); - - /* make a new font */ - weight= con->boldfont ? FW_BOLD : FW_NORMAL; - index=0; - do { - con->hfont=CreateFont(height, 0, 0, 0, weight, FALSE, 0, 0, ANSI_CHARSET, - OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, - FIXED_PITCH|FF_DONTCARE, ConsoleFonts[index]); - } while (con->hfont==NULL && ConsoleFonts[++index]!=NULL); - /* get character size */ - hdc=GetDC(con->hwnd); - hfontOrg=SelectObject(hdc,con->hfont); - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - GetTextExtentPoint32(hdc,__T("x"),1,&size); - #else - GetTextExtentPoint(hdc,__T("x"),1,&size); - #endif - SelectObject(hdc,hfontOrg); - ReleaseDC(con->hwnd,hdc); - con->cwidth=(int)size.cx; - con->cheight=(int)size.cy; -} - -static void ClampToScreen(RECT *rc) -{ - int cx=GetSystemMetrics(SM_CXSCREEN); - int cy=GetSystemMetrics(SM_CYSCREEN); - - if (rc->left<0) - OffsetRect(rc,-rc->left,0); - if (rc->top<0) - OffsetRect(rc,0,-rc->top); - if (rc->right>cx) { - /* first try to move left */ - OffsetRect(rc,cx-rc->right,0); - /* also check not to exceed the left edge */ - if (rc->left<0) - rc->left=0; - } /* if */ - if (rc->bottom>cy) { - /* first try to move up */ - OffsetRect(rc,0,cy-rc->bottom); - /* also check not to exceed the top edge */ - if (rc->top<0) - rc->top=0; - } /* if */ -} - -static void RefreshCaretPos(CONSOLE *con) -{ - assert(con!=NULL); - assert(IsWindow(con->hwnd)); - //??? should scroll so that the caret is visible - SetCaretPos(con->cwidth*con->csrx-GetScrollPos(con->hwnd,SB_HORZ), - con->cheight*(con->csry+1)-2-GetScrollPos(con->hwnd,SB_VERT)); -} - -static void RefreshScreen(CONSOLE *con,int startline,int endline) -{ - RECT rect; - assert(con!=NULL); - assert(IsWindow(con->hwnd)); - GetClientRect(con->hwnd,&rect); - rect.top=startline*con->cheight; - rect.bottom=endline*con->cheight; - InvalidateRect(con->hwnd,NULL/*&rect*/,FALSE); - RefreshCaretPos(con); -} - -static void ScrollScreen(CONSOLE *con,int dx,int dy) -{ - int x,y,linesize,pos; - - assert(con!=NULL); - assert(con->buffer!=NULL); - - /* vertical scrolling */ - if (dy!=0) { - linesize=con->columns*2; - /* a positive value scrolls up */ - if (dy>=0) { - for (y=0; ylines-1; y++) - memcpy(con->buffer+y*linesize,con->buffer+(y+1)*linesize,linesize*sizeof(TCHAR)); - pos=((con->lines-1)*con->columns)*2; - for (x=0; xcolumns*2; x+=2) { - con->buffer[pos+x]=__T(' '); - con->buffer[pos+x+1]=con->attrib; - } /* for */ - } else { - for (y=con->lines-1; y>0; y++) - memcpy(con->buffer+y*linesize,con->buffer+(y-1)*linesize,linesize*sizeof(TCHAR)); - pos=((con->lines-1)*con->columns)*2; - for (x=0; xcolumns*2; x+=2) { - con->buffer[pos+x]=__T(' '); - con->buffer[pos+x+1]=con->attrib; - } /* for */ - } /* if */ - con->csry-=dy; - if (con->csry<0) - con->csry=0; - if (con->csry>=con->lines) - con->csry=con->lines-1; - } /* if */ - - /* horizontal scrolling */ - /* ??? to be implemented */ - - RefreshScreen(con,0,con->lines); -} - -static void SetTextAttribute(HDC hdc,TCHAR attrib) -{ - int fore=attrib & 0x07; - int back=(attrib >> 4) & 0x0f; - int highlight=(attrib & 0x08) ? 1 : 0; - - switch (fore) { - case 0: /* black */ - SetTextColor(hdc,highlight ? RGB(128,128,128) : RGB(0,0,0)); - break; - case 1: /* red */ - SetTextColor(hdc,highlight ? RGB(255,0,0) : RGB(128,0,0)); - break; - case 2: /* green */ - SetTextColor(hdc,highlight ? RGB(0,255,0) : RGB(0,128,0)); - break; - case 3: /* yellow */ - SetTextColor(hdc,highlight ? RGB(255,255,0) : RGB(128,128,0)); - break; - case 4: /* blue */ - SetTextColor(hdc,highlight ? RGB(0,0,255) : RGB(0,0,128)); - break; - case 5: /* magenta */ - SetTextColor(hdc,highlight ? RGB(255,0,255) : RGB(128,0,128)); - break; - case 6: /* cyan */ - SetTextColor(hdc,highlight ? RGB(0,255,255) : RGB(0,128,128)); - break; - case 7: /* white */ - SetTextColor(hdc,highlight ? RGB(255,255,255) : RGB(192,192,192)); - break; - } /* switch */ - - switch (back) { - case 0: /* black */ - SetBkColor(hdc,RGB(0,0,0)); - break; - case 1: /* red */ - SetBkColor(hdc,RGB(128,0,0)); - break; - case 2: /* green */ - SetBkColor(hdc,RGB(0,128,0)); - break; - case 3: /* yellow */ - SetBkColor(hdc,RGB(128,128,0)); - break; - case 4: /* blue */ - SetBkColor(hdc,RGB(0,0,128)); - break; - case 5: /* magenta */ - SetBkColor(hdc,RGB(128,0,128)); - break; - case 6: /* cyan */ - SetBkColor(hdc,RGB(0,128,128)); - break; - case 7: /* white */ - SetBkColor(hdc,RGB(192,192,192)); - break; - } /* switch */ -} - -long CALLBACK EXPORT ConsoleFunc(HWND hwnd,unsigned message,WPARAM wParam, - LPARAM lParam) -{ - CONSOLE *con; - PAINTSTRUCT ps; - RECT rect; - - switch (message) { - case WM_CHAR: - if ((con=Hwnd2Console(hwnd))!=NULL) { - /* store in a key queue */ - if ((con->keyq_end+1)%KEYQUEUE_SIZE==con->keyq_start) { - MessageBeep(MB_OK); - break; - } /* if */ - con->keyqueue[con->keyq_end]=(short)wParam; - con->keyq_end=(con->keyq_end+1)%KEYQUEUE_SIZE; - } /* if */ - break; - - case WM_CREATE: - /* The "hwnd" member of the CONSOLE structure has not yet been set, which - * means that Hwnd2Console() cannot work on the real "hwnd". There should - * at every instant be only one CONSOLE structure with a NULL handle, - * however. - */ - if ((con=Hwnd2Console(NULL))!=NULL) { - con->hwnd=hwnd; - SetConsoleFont(con,con->cheight); - GetWindowRect(hwnd, &rect); - SetRect(&rect,rect.left,rect.top, - rect.left+con->cwidth*con->columns, - rect.top+con->cheight*con->winlines); - AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); - if (con->winlineslines) - rect.right+=GetSystemMetrics(SM_CXVSCROLL); - ClampToScreen(&rect); - SetWindowPos(hwnd,NULL,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,0); - } /* if */ - break; - - case WM_DESTROY: - if ((con=Hwnd2Console(hwnd))!=NULL) - DoDeleteConsole(con); - /* if there are no consoles left, abort the program */ - if (consoleroot.next==NULL) - ExitProcess(0); - break; - - case WM_GETMINMAXINFO: - if ((con=Hwnd2Console(hwnd))!=NULL) { - MINMAXINFO FAR *lpmmi=(MINMAXINFO FAR*)lParam; - int rx,ry,hsize,vsize; - GetClientRect(hwnd,&rect); - rx= (rect.right < con->columns*con->cwidth) ? con->columns*con->cwidth-rect.right : 0; - ry= (rect.bottom < con->lines*con->cheight) ? con->lines*con->cheight-rect.bottom : 0; - hsize= (ry>0) ? GetSystemMetrics(SM_CXVSCROLL) : 0; - vsize= (rx>0) ? GetSystemMetrics(SM_CYHSCROLL) : 0; - SetRect(&rect,0,0,con->cwidth*con->columns+hsize,con->cheight*con->lines+vsize); - AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); - lpmmi->ptMaxTrackSize.x=rect.right-rect.left; - lpmmi->ptMaxTrackSize.y=rect.bottom-rect.top; - lpmmi->ptMaxSize=lpmmi->ptMaxTrackSize; - } /* if */ - break; - - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - if ((con=Hwnd2Console(hwnd))!=NULL) { - TCHAR str[20]; - int i; - str[0]=__T('\0'); - switch (LOWORD(wParam)) { - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - case VK_F10: - case VK_F11: - case VK_F12: - if (LOWORD(wParam)<=VK_F5) - _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F1+11); - else if (LOWORD(wParam)==VK_F10) - _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F6+17); - else - _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F11+23); - break; - case VK_ADD: - case VK_SUBTRACT: - /* check Ctrl key */ - if ((GetKeyState(VK_CONTROL) & 0x8000)!=0) { - POINT pt; - int newheight=con->cheight; - int oldheight=newheight; - int incr= (LOWORD(wParam)==VK_SUBTRACT) ? -1 : 1; - do { - newheight+=incr; - /* make a new font, re-create a caret and redraw everything */ - SetConsoleFont(con,newheight); - } while (newheight>5 && (oldheight==con->cheight || con->hfont==NULL)); - if (con->hfont==NULL) /* reset to original on failure */ - SetConsoleFont(con,oldheight); - GetClientRect(hwnd,&rect); - DestroyCaret(); - CreateCaret(hwnd,NULL,con->cwidth,2); - RefreshCaretPos(con); - /* redraw the window */ - InvalidateRect(hwnd,NULL,TRUE); - /* resize the window */ - SetRect(&rect,0,0,con->cwidth*con->columns,con->cheight*con->winlines); - AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); - pt.x=pt.y=0; - ClientToScreen(hwnd,&pt); - OffsetRect(&rect,pt.x,pt.y); - ClampToScreen(&rect); - SetWindowPos(hwnd,NULL,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top, - SWP_NOZORDER); - } /* if */ - break; - case VK_UP: - _tcscpy(str,__T("\033[A")); - break; - case VK_DOWN: - _tcscpy(str,__T("\033[B")); - break; - case VK_RIGHT: - _tcscpy(str,__T("\033[C")); - break; - case VK_LEFT: - _tcscpy(str,__T("\033[D")); - break; - case VK_HOME: - _tcscpy(str,__T("\033[1~")); - break; - case VK_END: - _tcscpy(str,__T("\033[4~")); - break; - case VK_INSERT: - _tcscpy(str,__T("\033[2~")); - break; - case VK_DELETE: - _tcscpy(str,__T("\033[3~")); - break; - case VK_PRIOR: /* PageUp */ - _tcscpy(str,__T("\033[5~")); - break; - case VK_NEXT: /* PageDown */ - _tcscpy(str,__T("\033[6~")); - break; - default: - return DefWindowProc(hwnd,message,wParam,lParam); - } /* switch */ - for (i=0; str[i]!=__T('\0'); i++) { - if ((con->keyq_end+1)%KEYQUEUE_SIZE!=con->keyq_start) { - con->keyqueue[con->keyq_end]=(short)str[i]; - con->keyq_end=(con->keyq_end+1)%KEYQUEUE_SIZE; - } /* if */ - } /* for */ - } /* if */ - break; - - case WM_KILLFOCUS: - HideCaret(hwnd); - DestroyCaret(); - break; - case WM_SETFOCUS: - if ((con=Hwnd2Console(hwnd))!=NULL) { - CreateCaret(hwnd,NULL,con->cwidth,2); - RefreshCaretPos(con); - ShowCaret(hwnd); - } /* if */ - break; - - case WM_LBUTTONDOWN: - SetFocus(hwnd); - break; - - case WM_PAINT: - HideCaret(hwnd); - BeginPaint(hwnd, &ps); - if ((con=Hwnd2Console(hwnd))!=NULL && con->buffer!=NULL) { - TCHAR *string; - string=malloc(con->columns*sizeof(TCHAR)); - if (string!=NULL) { - int l,c,bpos,start; - TCHAR attr; - HFONT hfontOrg; - int scrollx=GetScrollPos(hwnd,SB_HORZ); - int scrolly=GetScrollPos(hwnd,SB_VERT); - GetClientRect(hwnd,&rect); - hfontOrg=SelectObject(ps.hdc,con->hfont); - SetBkMode(ps.hdc,OPAQUE); - for (l=0; llines; l++) { - bpos=l*con->columns*2; - c=0; - while (ccolumns) { - /* find stretches with the same attribute */ - attr=con->buffer[bpos+1]; - start=c; - while (ccolumns && con->buffer[bpos+1]==attr) { - assert(c-start>=0); - assert(c-startcolumns); - string[c-start]=con->buffer[bpos]; - c++; - bpos+=2; - } /* if */ - SetTextAttribute(ps.hdc,attr); - TextOut(ps.hdc,start*con->cwidth-scrollx,l*con->cheight-scrolly,string,c-start); - } /* while */ - } /* for */ - SelectObject(ps.hdc,hfontOrg); - free(string); - } /* if */ - } /* if */ - EndPaint(hwnd, &ps); - ShowCaret(hwnd); - break; - - case WM_SIZE: - if ((con=Hwnd2Console(hwnd))!=NULL) { - int rx,ry; - /* add/remove/recalculate scroll bars */ - GetClientRect(hwnd,&rect); - rx= (rect.right < con->columns*con->cwidth) ? con->columns*con->cwidth-rect.right : 0; - ry= (rect.bottom < con->lines*con->cheight) ? con->lines*con->cheight-rect.bottom : 0; - /* adjust scrolling position, if necessary */ - if (GetScrollPos(hwnd,SB_HORZ)>=rx) { - SetScrollPos(hwnd,SB_HORZ,rx,FALSE); - InvalidateRect(hwnd,NULL,FALSE); - } /* if */ - if (GetScrollPos(hwnd,SB_VERT)>=ry) { - SetScrollPos(hwnd,SB_VERT,ry,FALSE); - InvalidateRect(hwnd,NULL,FALSE); - } /* if */ - SetScrollRange(hwnd,SB_HORZ,0,rx,TRUE); - SetScrollRange(hwnd,SB_VERT,0,ry,TRUE); - } /* if */ - break; - - case WM_HSCROLL: - if ((con=Hwnd2Console(hwnd))!=NULL) { - int scrollx=GetScrollPos(hwnd,SB_HORZ); - int oldpos=scrollx; - int min,max; - GetScrollRange(hwnd,SB_HORZ,&min,&max); - switch (LOWORD(wParam)) { - case SB_TOP: - scrollx=min; - break; - case SB_BOTTOM: - scrollx=max; - break; - case SB_LINELEFT: - scrollx=(scrollx>min) ? scrollx-1 : min; - break; - case SB_LINERIGHT: - scrollx=(scrollxmin) ? scrollx-50 : min; - break; - case SB_PAGERIGHT: - scrollx=(scrollxmin) ? scrolly-1 : min; - break; - case SB_LINERIGHT: - scrolly=(scrollymin) ? scrolly-50 : min; - break; - case SB_PAGERIGHT: - scrolly=(scrollycsry; - - pos=(con->csry*con->columns+con->csrx)*2; - assert(con->buffer!=NULL); - for (i=0; string[i]!=__T('\0'); i++) { - if (con->csrylines && con->csrxcolumns) { - if (string[i]==__T('\r')) { - con->csrx=0; - pos=(con->csry*con->columns+con->csrx)*2; - } else if (string[i]==__T('\n')) { - con->csrx=0; - con->csry++; - if (con->csry>=con->lines) - ScrollScreen(con,0,1); - pos=(con->csry*con->columns+con->csrx)*2; - } else if (string[i]==__T('\b')) { - if (con->csrx>0) { - con->csrx--; - pos-=2; - con->buffer[pos]=__T(' '); - con->buffer[pos+1]=con->attrib; - } /* if */ - } else { - con->buffer[pos]=string[i]; - con->buffer[pos+1]=con->attrib; - pos+=2; - con->csrx+=1; - if (con->csrx>=con->columns && con->autowrap) { - con->csrx=0; - con->csry++; - if (con->csry>=con->lines) - ScrollScreen(con,0,1); - pos=(con->csry*con->columns+con->csrx)*2; - } /* if */ - } /* if */ - } /* if */ - } /* for */ - RefreshScreen(con,top,con->csry+1); - } /* if */ - return 0; -} - -int amx_printf(const TCHAR *format,...) -{ - int cnt; - TCHAR buffer[BUFFERSIZE]; - va_list argptr; - - va_start(argptr,format); - cnt=_vstprintf(buffer,format,argptr); - va_end(argptr); - amx_putstr(buffer); - return cnt; -} - -int amx_putchar(int c) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - if (con->csrylines && con->csrxcolumns) { - int pos=(con->csry*con->columns+con->csrx)*2; - assert(con->buffer!=NULL); - if (c==__T('\r')) { - con->csrx=0; - } else if (c==__T('\n')) { - con->csrx=0; - con->csry++; - if (con->csry>=con->lines) - ScrollScreen(con,0,1); - } else if (c==__T('\b')) { - if (con->csrx>0) { - con->csrx--; - pos-=2; - con->buffer[pos]=__T(' '); - con->buffer[pos+1]=con->attrib; - } /* if */ - } else if (c==__T('\t')) { - while (con->csrx % 8!=0 && con->csrxcolumns) { - con->buffer[pos]=' '; - con->buffer[pos+1]=con->attrib; - con->csrx+=1; - if (con->csrx>=con->columns && con->autowrap) { - con->csrx=0; - con->csry++; - if (con->csry>=con->lines) - ScrollScreen(con,0,1); - } /* if */ - } /* while */ - } else { - con->buffer[pos]=(TCHAR)c; - con->buffer[pos+1]=con->attrib; - con->csrx+=1; - if (con->csrx>=con->columns && con->autowrap) { - con->csrx=0; - con->csry++; - if (con->csry>=con->lines) - ScrollScreen(con,0,1); - } /* if */ - } /* if */ - RefreshScreen(con,con->csry,con->csry+1); - } /* if */ - } /* if */ - return 1; -} - -int amx_fflush(void) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL && IsWindow(con->hwnd)) - UpdateWindow(con->hwnd); - return 1; -} - -static void ProcessMessages(void) -{ - MSG msg; - - while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } /* while */ -} - -int amx_kbhit(void) -{ - CONSOLE *con; - - ProcessMessages(); - if ((con=ActiveConsole())!=NULL) - return con->keyq_start!=con->keyq_end; - return 0; -} - -int amx_getch(void) -{ - CONSOLE *con; - int c=-1; - - if ((con=ActiveConsole())!=NULL) { - while (con->keyq_start==con->keyq_end) - ProcessMessages(); - c=con->keyqueue[con->keyq_start]; - con->keyq_start=(con->keyq_start+1)%KEYQUEUE_SIZE; - } /* if */ - return c; -} - -TCHAR *amx_gets(TCHAR *string,int size) -{ - int c=-1,num=0; - - if (ActiveConsole()!=NULL) { - while (num+10) - string[--num]=__T(' '); - } else { - string[num++]=(TCHAR)c; - } /* if */ - amx_putchar(c); /* echo the character read */ - } /* while */ - if (numautowrap=(BOOL)value; - return 1; - } /* if */ - return 0; - } /* case */ - - case 2: { - HWND hconsole=GetConsoleByIndex(value); - while (hconsole==NULL) { - CreateConsole(NULL,HWND_DESKTOP,DEFCOLUMNS,DEFWINLINES,DEFBUFFERLINES,DEFFONTHEIGHT,0); - hconsole=GetConsoleByIndex(value); - } /* while */ - return SetActiveConsole(hconsole); - } /* case */ - - case 3: { - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - con->boldfont=(BOOL)value; - SetConsoleFont(con,con->cheight); - return 1; - } /* if */ - return 0; - } /* case */ - - case 4: { - MSG msg; - while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } /* while */ - return (GetConsoleByIndex(value)!=NULL); - } /* case */ - - default: - return 0; - } /* switch */ -} - -void amx_clrscr(void) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - int i; - int size=con->lines*con->columns*2; - assert(con->buffer!=NULL); - for (i=0; ibuffer[i]=__T(' '); - con->buffer[i+1]=con->attrib; - } /* for */ - con->csrx=con->csry=0; - RefreshScreen(con,0,con->lines); - } /* if */ -} - -void amx_clreol(void) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - int i; - int size=(con->columns-con->csrx)*2; - int pos=(con->csry*con->columns+con->csrx)*2; - assert(con->buffer!=NULL); - for (i=0; ibuffer[pos+i]=__T(' '); - con->buffer[pos+i+1]=con->attrib; - } /* for */ - RefreshScreen(con,con->csry,con->csry+1); - } /* if */ -} - -void amx_gotoxy(int x,int y) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - if (x>0 && x<=con->columns) - con->csrx=x-1; - if (y>0 && y<=con->lines) - con->csry=y-1; - RefreshScreen(con,0,0); - } /* if */ -} - -void amx_wherexy(int *x,int *y) -{ - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - if (x!=NULL) - *x=con->csrx+1; - if (y!=NULL) - *y=con->csry+1; - } /* if */ -} - -unsigned int amx_setattr(int foregr,int backgr,int highlight) -{ - int prev=0; - CONSOLE *con; - if ((con=ActiveConsole())!=NULL) { - int f,b,h; - - f=con->attrib & 0x07; - b=(con->attrib >> 4) & 0x0f; - h=(con->attrib & 0x08) ? 1 : 0; - prev=(b << 8) | f | (h << 15); - if (foregr>=0 && foregr<8) - f=foregr; - if (backgr>=0 && backgr<8) - b=backgr; - if (highlight>=0) - h=highlight!=0; - con->attrib=(TCHAR)((b << 4) | f | (h << 3)); - } /* if */ - return prev; -} - -void amx_console(int columns, int lines, int flags) -{ - HWND hwnd=CreateConsole(NULL,HWND_DESKTOP,columns,lines,DEFBUFFERLINES,DEFFONTHEIGHT,0); - SetActiveConsole(hwnd); - (void)flags; -} +/* Simple terminal for Microsoft Windows + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: termwin.c 3648 2006-10-12 11:24:50Z thiadmer $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#if defined _UNICODE +# include +#else +# include +#endif + +#include +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + #include +#endif +#include "termwin.h" + +#define DEFWINLINES 30 +#define DEFBUFFERLINES DEFWINLINES +#define DEFCOLUMNS 80 +#define DEFFONTHEIGHT 14 +#define KEYQUEUE_SIZE 32 +#define BUFFERSIZE 2048 + +typedef struct tagCONSOLE { + struct tagCONSOLE *next; + HWND hwnd; + TCHAR *buffer; /* text buffer */ + int lines,columns; + int winlines; /* default # lines in the window */ + TCHAR attrib; /* text attribute */ + int csrx,csry; /* cursor position */ + short keyqueue[KEYQUEUE_SIZE]; + int keyq_start,keyq_end; + BOOL autowrap; + BOOL boldfont; + HFONT hfont; + int cwidth,cheight; /* character width and height */ +} CONSOLE; + +static CONSOLE consoleroot={ NULL }; +static HWND activeconsole=NULL; + +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + #define EXPORT +#else + #define EXPORT _export +#endif + +long CALLBACK EXPORT ConsoleFunc(HWND hwnd,unsigned message,WPARAM wParam, + LPARAM lParam); + +static BOOL InitWindowClass(HINSTANCE hinst) +{ + static BOOL initok=FALSE; + if (!initok) { + WNDCLASS wc; + memset(&wc,0,sizeof wc); + wc.style=CS_GLOBALCLASS; + wc.lpfnWndProc=(WNDPROC)ConsoleFunc; + wc.hInstance=hinst; + wc.hCursor=LoadCursor(NULL, IDC_ARROW); + wc.hIcon=LoadIcon(GetModuleHandle(NULL), "AppIcon"); + wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); + wc.lpszClassName=__T("TermWin:Console"); + initok=RegisterClass(&wc); + } /* if */ + return initok; +} + +static CONSOLE *Hwnd2Console(HWND hwnd) +{ + CONSOLE *con; + for (con=consoleroot.next; con!=NULL && con->hwnd!=hwnd; con=con->next) + /* nothing */; + return con; +} + +static void DoDeleteConsole(CONSOLE *con) +{ + CONSOLE *prev; + + assert(con!=NULL); + + /* unlink first, to avoid a recursive delete when destroying the window */ + for (prev=&consoleroot; prev->next!=NULL && prev->next!=con; prev=prev->next) + /* nothing */; + if (prev->next==con) + prev->next=con->next; + + /* free memory and resources */ + if (con->hwnd!=NULL && IsWindow(con->hwnd)) + DestroyWindow(con->hwnd); + if (con->hfont!=0) + DeleteObject(con->hfont); + if (con->buffer!=NULL) + free(con->buffer); + free(con); +} + +HWND CreateConsole(HINSTANCE hinst,HWND hwndParent,int columns,int lines,int bufferlines,int fontsize, + DWORD style) +{ + CONSOLE *con, *prev; + + /* allocate a structure to hold the information */ + con=malloc(sizeof(CONSOLE)); + if (con==NULL) + return NULL; + memset(con,0,sizeof(CONSOLE)); + + /* link to the list */ + for (prev=&consoleroot; prev->next!=NULL; prev=prev->next) + /* nothing */; + prev->next=con; + + /* fill in information */ + if (lines>=bufferlines) + lines=bufferlines; + con->lines=bufferlines; + con->columns=columns; + con->winlines=lines; + con->cheight=fontsize; + con->attrib=0xf0; /* black on white */ + con->autowrap=TRUE; + con->buffer=malloc(bufferlines*columns*2*sizeof(TCHAR)); + /* clear in the console + * just like the DOS console buffer, our console consists of character/ + * attribute pairs. + */ + if (con->buffer!=NULL) { + int i; + int size=bufferlines*columns*2; + for (i=0; ibuffer[i]=__T(' '); + con->buffer[i+1]=con->attrib; + } /* for */ + } /* if */ + + if (style==0) + style=WS_OVERLAPPEDWINDOW; + if (hinst==NULL) + hinst=GetModuleHandle(NULL); + + /* create the window */ + InitWindowClass(hinst); + con->hwnd=CreateWindow(__T("TermWin:Console"),__T("Pawn console"),style, + CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, + hwndParent,NULL,hinst,NULL); + + /* check whether all is ok */ + if (IsWindow(con->hwnd) && con->buffer!=NULL) { + ShowWindow(con->hwnd,SW_SHOWNORMAL); + return con->hwnd; + } /* if */ + /* when we arrive here, something was initialized correctly and something else was not */ + DoDeleteConsole(con); + return NULL; +} + +BOOL DeleteConsole(HWND hconsole) +{ + CONSOLE *con=Hwnd2Console(hconsole); + if (con!=NULL) + DoDeleteConsole(con); + return con!=NULL; +} + +BOOL SetActiveConsole(HWND hconsole) +{ + CONSOLE *con=Hwnd2Console(hconsole); + if (con!=NULL) + activeconsole=con->hwnd; + return con!=NULL; +} + +HWND GetConsoleByIndex(int index) +{ + CONSOLE *con; + + for (con=consoleroot.next; con!=NULL && index>0; con=con->next) + index--; + if (con!=NULL) + return con->hwnd; + return NULL; +} + +static CONSOLE *ActiveConsole(void) +{ + CONSOLE *con; + + for (con=consoleroot.next; con!=NULL && con->hwnd!=activeconsole; con=con->next) + /* nothing */; + if (con==NULL) { /* active console "disappeared", switch to first console */ + /* create a console if there are none left */ + if (consoleroot.next==NULL) + CreateConsole(NULL,HWND_DESKTOP,DEFCOLUMNS,DEFWINLINES,DEFBUFFERLINES,DEFFONTHEIGHT,0); + con=consoleroot.next; + activeconsole= (con!=NULL) ? con->hwnd : NULL; + } /* if */ + /* if "con" still is NULL here, then the following holds: + * 1. the "active console" (if there was one) disappeared + * 2. there are no consoles left at all + * 3. a new console could not be created + */ + return con; +} + +#if defined __WIN32__ || defined _WIN32 || defined WIN32 +static void ConsoleThreadProc(void *dummy) +{ + MSG msg; + + (void)dummy; + + /* initialize the screen */ + amx_console(DEFCOLUMNS,DEFWINLINES,0); + + /* message loop to process user input */ + while (amx_termctl(4,0)) { + if (GetMessage(&msg,NULL,0,0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } /* if */ + } /* while */ +} + +BOOL CreateConsoleThread(void) +{ + AllocConsole(); + if ((HANDLE)_beginthread(ConsoleThreadProc, 0, NULL)==INVALID_HANDLE_VALUE) + return FALSE; + return TRUE; +} +#endif + +static void SetConsoleFont(CONSOLE *con,int height) +{ +static TCHAR *ConsoleFonts[] = { + #if defined _UNICODE + __T("Monotype.com"), + #endif + __T("Andante"), + __T("ProggyClean"), + __T("Bitstream Vera Sans Mono"), + __T("Lucida Console"), + __T("Monaco"), + __T("Andale Mono"), + __T("Courier New"), + __T("FixedSys"), + NULL }; + HDC hdc; + SIZE size; + HFONT hfontOrg; + int weight,index; + + assert(con!=NULL); + + /* remove the existing font (if any) */ + if (con->hfont!=0) + DeleteObject(con->hfont); + + /* make a new font */ + weight= con->boldfont ? FW_BOLD : FW_NORMAL; + index=0; + do { + con->hfont=CreateFont(height, 0, 0, 0, weight, FALSE, 0, 0, ANSI_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, + FIXED_PITCH|FF_DONTCARE, ConsoleFonts[index]); + } while (con->hfont==NULL && ConsoleFonts[++index]!=NULL); + /* get character size */ + hdc=GetDC(con->hwnd); + hfontOrg=SelectObject(hdc,con->hfont); + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + GetTextExtentPoint32(hdc,__T("x"),1,&size); + #else + GetTextExtentPoint(hdc,__T("x"),1,&size); + #endif + SelectObject(hdc,hfontOrg); + ReleaseDC(con->hwnd,hdc); + con->cwidth=(int)size.cx; + con->cheight=(int)size.cy; +} + +static void ClampToScreen(RECT *rc) +{ + int cx=GetSystemMetrics(SM_CXSCREEN); + int cy=GetSystemMetrics(SM_CYSCREEN); + + if (rc->left<0) + OffsetRect(rc,-rc->left,0); + if (rc->top<0) + OffsetRect(rc,0,-rc->top); + if (rc->right>cx) { + /* first try to move left */ + OffsetRect(rc,cx-rc->right,0); + /* also check not to exceed the left edge */ + if (rc->left<0) + rc->left=0; + } /* if */ + if (rc->bottom>cy) { + /* first try to move up */ + OffsetRect(rc,0,cy-rc->bottom); + /* also check not to exceed the top edge */ + if (rc->top<0) + rc->top=0; + } /* if */ +} + +static void RefreshCaretPos(CONSOLE *con) +{ + assert(con!=NULL); + assert(IsWindow(con->hwnd)); + //??? should scroll so that the caret is visible + SetCaretPos(con->cwidth*con->csrx-GetScrollPos(con->hwnd,SB_HORZ), + con->cheight*(con->csry+1)-2-GetScrollPos(con->hwnd,SB_VERT)); +} + +static void RefreshScreen(CONSOLE *con,int startline,int endline) +{ + RECT rect; + assert(con!=NULL); + assert(IsWindow(con->hwnd)); + GetClientRect(con->hwnd,&rect); + rect.top=startline*con->cheight; + rect.bottom=endline*con->cheight; + InvalidateRect(con->hwnd,&rect,FALSE); + RefreshCaretPos(con); +} + +static void ScrollScreen(CONSOLE *con,int dx,int dy) +{ + int x,y,linesize,pos; + + assert(con!=NULL); + assert(con->buffer!=NULL); + + /* vertical scrolling */ + if (dy!=0) { + linesize=con->columns*2; + /* a positive value scrolls up */ + if (dy>=0) { + for (y=0; ylines-1; y++) + memcpy(con->buffer+y*linesize,con->buffer+(y+1)*linesize,linesize*sizeof(TCHAR)); + pos=((con->lines-1)*con->columns)*2; + for (x=0; xcolumns*2; x+=2) { + con->buffer[pos+x]=__T(' '); + con->buffer[pos+x+1]=con->attrib; + } /* for */ + } else { + for (y=con->lines-1; y>0; y++) + memcpy(con->buffer+y*linesize,con->buffer+(y-1)*linesize,linesize*sizeof(TCHAR)); + pos=((con->lines-1)*con->columns)*2; + for (x=0; xcolumns*2; x+=2) { + con->buffer[pos+x]=__T(' '); + con->buffer[pos+x+1]=con->attrib; + } /* for */ + } /* if */ + con->csry-=dy; + if (con->csry<0) + con->csry=0; + if (con->csry>=con->lines) + con->csry=con->lines-1; + } /* if */ + + /* horizontal scrolling */ + /* ??? to be implemented */ + + RefreshScreen(con,0,con->lines); +} + +static void SetTextAttribute(HDC hdc,TCHAR attrib) +{ + int fore=attrib & 0x07; + int back=(attrib >> 4) & 0x0f; + int highlight=(attrib & 0x08) ? 1 : 0; + + switch (fore) { + case 0: /* black */ + SetTextColor(hdc,highlight ? RGB(128,128,128) : RGB(0,0,0)); + break; + case 1: /* red */ + SetTextColor(hdc,highlight ? RGB(255,0,0) : RGB(128,0,0)); + break; + case 2: /* green */ + SetTextColor(hdc,highlight ? RGB(0,255,0) : RGB(0,128,0)); + break; + case 3: /* yellow */ + SetTextColor(hdc,highlight ? RGB(255,255,0) : RGB(128,128,0)); + break; + case 4: /* blue */ + SetTextColor(hdc,highlight ? RGB(0,0,255) : RGB(0,0,128)); + break; + case 5: /* magenta */ + SetTextColor(hdc,highlight ? RGB(255,0,255) : RGB(128,0,128)); + break; + case 6: /* cyan */ + SetTextColor(hdc,highlight ? RGB(0,255,255) : RGB(0,128,128)); + break; + case 7: /* white */ + SetTextColor(hdc,highlight ? RGB(255,255,255) : RGB(192,192,192)); + break; + } /* switch */ + + switch (back) { + case 0: /* black */ + SetBkColor(hdc,RGB(0,0,0)); + break; + case 1: /* red */ + SetBkColor(hdc,RGB(128,0,0)); + break; + case 2: /* green */ + SetBkColor(hdc,RGB(0,128,0)); + break; + case 3: /* yellow */ + SetBkColor(hdc,RGB(128,128,0)); + break; + case 4: /* blue */ + SetBkColor(hdc,RGB(0,0,128)); + break; + case 5: /* magenta */ + SetBkColor(hdc,RGB(128,0,128)); + break; + case 6: /* cyan */ + SetBkColor(hdc,RGB(0,128,128)); + break; + case 7: /* white */ + SetBkColor(hdc,RGB(192,192,192)); + break; + } /* switch */ +} + +long CALLBACK EXPORT ConsoleFunc(HWND hwnd,unsigned message,WPARAM wParam, + LPARAM lParam) +{ + CONSOLE *con; + PAINTSTRUCT ps; + RECT rect; + + switch (message) { + case WM_CHAR: + if ((con=Hwnd2Console(hwnd))!=NULL) { + /* store in a key queue */ + if ((con->keyq_end+1)%KEYQUEUE_SIZE==con->keyq_start) { + MessageBeep(MB_OK); + break; + } /* if */ + con->keyqueue[con->keyq_end]=(short)wParam; + con->keyq_end=(con->keyq_end+1)%KEYQUEUE_SIZE; + } /* if */ + break; + + case WM_CREATE: + /* The "hwnd" member of the CONSOLE structure has not yet been set, which + * means that Hwnd2Console() cannot work on the real "hwnd". There should + * at every instant be only one CONSOLE structure with a NULL handle, + * however. + */ + if ((con=Hwnd2Console(NULL))!=NULL) { + con->hwnd=hwnd; + SetConsoleFont(con,con->cheight); + GetWindowRect(hwnd, &rect); + SetRect(&rect,rect.left,rect.top, + rect.left+con->cwidth*con->columns, + rect.top+con->cheight*con->winlines); + AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); + if (con->winlineslines) + rect.right+=GetSystemMetrics(SM_CXVSCROLL); + ClampToScreen(&rect); + SetWindowPos(hwnd,NULL,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top, + SWP_NOZORDER); + } /* if */ + break; + + case WM_DESTROY: + if ((con=Hwnd2Console(hwnd))!=NULL) + DoDeleteConsole(con); + break; + + case WM_GETMINMAXINFO: + if ((con=Hwnd2Console(hwnd))!=NULL) { + MINMAXINFO FAR *lpmmi=(MINMAXINFO FAR*)lParam; + int rx,ry,hsize,vsize; + GetClientRect(hwnd,&rect); + rx= (rect.right < con->columns*con->cwidth) ? con->columns*con->cwidth-rect.right : 0; + ry= (rect.bottom < con->lines*con->cheight) ? con->lines*con->cheight-rect.bottom : 0; + hsize= (ry>0) ? GetSystemMetrics(SM_CXVSCROLL) : 0; + vsize= (rx>0) ? GetSystemMetrics(SM_CYHSCROLL) : 0; + SetRect(&rect,0,0,con->cwidth*con->columns+hsize,con->cheight*con->lines+vsize); + AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); + lpmmi->ptMaxTrackSize.x=rect.right-rect.left; + lpmmi->ptMaxTrackSize.y=rect.bottom-rect.top; + lpmmi->ptMaxSize=lpmmi->ptMaxTrackSize; + } /* if */ + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ((con=Hwnd2Console(hwnd))!=NULL) { + TCHAR str[20]; + int i; + str[0]=__T('\0'); + switch (LOWORD(wParam)) { + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + if (LOWORD(wParam)<=VK_F5) + _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F1+11); + else if (LOWORD(wParam)==VK_F10) + _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F6+17); + else + _stprintf(str,__T("\033[%d~\n"),LOWORD(wParam)-VK_F11+23); + break; + case VK_ADD: + case VK_SUBTRACT: + /* check Ctrl key */ + if ((GetKeyState(VK_CONTROL) & 0x8000)!=0) { + POINT pt; + int newheight=con->cheight; + int oldheight=newheight; + int incr= (LOWORD(wParam)==VK_SUBTRACT) ? -1 : 1; + do { + newheight+=incr; + /* make a new font, re-create a caret and redraw everything */ + SetConsoleFont(con,newheight); + } while (newheight>5 && (oldheight==con->cheight || con->hfont==NULL)); + if (con->hfont==NULL) /* reset to original on failure */ + SetConsoleFont(con,oldheight); + GetClientRect(hwnd,&rect); + DestroyCaret(); + CreateCaret(hwnd,NULL,con->cwidth,2); + RefreshCaretPos(con); + /* redraw the window */ + InvalidateRect(hwnd,NULL,TRUE); + /* resize the window */ + SetRect(&rect,0,0,con->cwidth*con->columns,con->cheight*con->winlines); + AdjustWindowRect(&rect,GetWindowLong(hwnd,GWL_STYLE),FALSE); + pt.x=pt.y=0; + ClientToScreen(hwnd,&pt); + OffsetRect(&rect,pt.x,pt.y); + ClampToScreen(&rect); + SetWindowPos(hwnd,NULL,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top, + SWP_NOZORDER); + } /* if */ + break; + case VK_UP: + _tcscpy(str,__T("\033[A")); + break; + case VK_DOWN: + _tcscpy(str,__T("\033[B")); + break; + case VK_RIGHT: + _tcscpy(str,__T("\033[C")); + break; + case VK_LEFT: + _tcscpy(str,__T("\033[D")); + break; + case VK_HOME: + _tcscpy(str,__T("\033[1~")); + break; + case VK_END: + _tcscpy(str,__T("\033[4~")); + break; + case VK_INSERT: + _tcscpy(str,__T("\033[2~")); + break; + case VK_DELETE: + _tcscpy(str,__T("\033[3~")); + break; + case VK_PRIOR: /* PageUp */ + _tcscpy(str,__T("\033[5~")); + break; + case VK_NEXT: /* PageDown */ + _tcscpy(str,__T("\033[6~")); + break; + default: + return DefWindowProc(hwnd,message,wParam,lParam); + } /* switch */ + for (i=0; str[i]!=__T('\0'); i++) { + if ((con->keyq_end+1)%KEYQUEUE_SIZE!=con->keyq_start) { + con->keyqueue[con->keyq_end]=(short)str[i]; + con->keyq_end=(con->keyq_end+1)%KEYQUEUE_SIZE; + } /* if */ + } /* for */ + } /* if */ + break; + + case WM_KILLFOCUS: + HideCaret(hwnd); + DestroyCaret(); + break; + case WM_SETFOCUS: + if ((con=Hwnd2Console(hwnd))!=NULL) { + CreateCaret(hwnd,NULL,con->cwidth,2); + RefreshCaretPos(con); + ShowCaret(hwnd); + } /* if */ + break; + + case WM_LBUTTONDOWN: + SetFocus(hwnd); + break; + + case WM_PAINT: + HideCaret(hwnd); + BeginPaint(hwnd, &ps); + if ((con=Hwnd2Console(hwnd))!=NULL && con->buffer!=NULL) { + TCHAR *string; + string=malloc(con->columns*sizeof(TCHAR)); + if (string!=NULL) { + int l,c,bpos,start; + TCHAR attr; + HFONT hfontOrg; + int scrollx=GetScrollPos(hwnd,SB_HORZ); + int scrolly=GetScrollPos(hwnd,SB_VERT); + GetClientRect(hwnd,&rect); + hfontOrg=SelectObject(ps.hdc,con->hfont); + SetBkMode(ps.hdc,OPAQUE); + for (l=0; llines; l++) { + bpos=l*con->columns*2; + c=0; + while (ccolumns) { + /* find stretches with the same attribute */ + attr=con->buffer[bpos+1]; + start=c; + while (ccolumns && con->buffer[bpos+1]==attr) { + assert(c-start>=0); + assert(c-startcolumns); + string[c-start]=con->buffer[bpos]; + c++; + bpos+=2; + } /* if */ + SetTextAttribute(ps.hdc,attr); + TextOut(ps.hdc,start*con->cwidth-scrollx,l*con->cheight-scrolly,string,c-start); + } /* while */ + } /* for */ + SelectObject(ps.hdc,hfontOrg); + free(string); + } /* if */ + } /* if */ + EndPaint(hwnd, &ps); + ShowCaret(hwnd); + break; + + case WM_SIZE: + if ((con=Hwnd2Console(hwnd))!=NULL) { + int rx,ry; + /* add/remove/recalculate scroll bars */ + GetClientRect(hwnd,&rect); + rx= (rect.right < con->columns*con->cwidth) ? con->columns*con->cwidth-rect.right : 0; + ry= (rect.bottom < con->lines*con->cheight) ? con->lines*con->cheight-rect.bottom : 0; + /* adjust scrolling position, if necessary */ + if (GetScrollPos(hwnd,SB_HORZ)>=rx) { + SetScrollPos(hwnd,SB_HORZ,rx,FALSE); + InvalidateRect(hwnd,NULL,FALSE); + } /* if */ + if (GetScrollPos(hwnd,SB_VERT)>=ry) { + SetScrollPos(hwnd,SB_VERT,ry,FALSE); + InvalidateRect(hwnd,NULL,FALSE); + } /* if */ + SetScrollRange(hwnd,SB_HORZ,0,rx,TRUE); + SetScrollRange(hwnd,SB_VERT,0,ry,TRUE); + } /* if */ + break; + + case WM_HSCROLL: + if ((con=Hwnd2Console(hwnd))!=NULL) { + int scrollx=GetScrollPos(hwnd,SB_HORZ); + int oldpos=scrollx; + int min,max; + GetScrollRange(hwnd,SB_HORZ,&min,&max); + switch (LOWORD(wParam)) { + case SB_TOP: + scrollx=min; + break; + case SB_BOTTOM: + scrollx=max; + break; + case SB_LINELEFT: + scrollx=(scrollx>min) ? scrollx-1 : min; + break; + case SB_LINERIGHT: + scrollx=(scrollxmin) ? scrollx-50 : min; + break; + case SB_PAGERIGHT: + scrollx=(scrollxmin) ? scrolly-1 : min; + break; + case SB_LINERIGHT: + scrolly=(scrollymin) ? scrolly-50 : min; + break; + case SB_PAGERIGHT: + scrolly=(scrollycsry*con->columns+con->csrx)*2; + assert(con->buffer!=NULL); + for (i=0; string[i]!=__T('\0'); i++) { + if (con->csrylines && con->csrxcolumns) { + if (string[i]==__T('\r')) { + con->csrx=0; + pos=(con->csry*con->columns+con->csrx)*2; + } else if (string[i]==__T('\n')) { + con->csrx=0; + con->csry++; + if (con->csry>=con->lines) + ScrollScreen(con,0,1); + pos=(con->csry*con->columns+con->csrx)*2; + } else if (string[i]==__T('\b')) { + if (con->csrx>0) { + con->csrx--; + pos-=2; + con->buffer[pos]=__T(' '); + con->buffer[pos+1]=con->attrib; + } /* if */ + } else { + con->buffer[pos]=string[i]; + con->buffer[pos+1]=con->attrib; + pos+=2; + con->csrx+=1; + if (con->csrx>=con->columns && con->autowrap) { + con->csrx=0; + con->csry++; + if (con->csry>=con->lines) + ScrollScreen(con,0,1); + pos=(con->csry*con->columns+con->csrx)*2; + } /* if */ + } /* if */ + } /* if */ + } /* for */ + RefreshScreen(con,con->csry,con->csry+1); + } /* if */ + return 0; +} + +int amx_printf(const TCHAR *format,...) +{ + int cnt; + TCHAR buffer[BUFFERSIZE]; + va_list argptr; + + va_start(argptr,format); + cnt=_vstprintf(buffer,format,argptr); + va_end(argptr); + amx_putstr(buffer); + return cnt; +} + +int amx_putchar(int c) +{ + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + if (con->csrylines && con->csrxcolumns) { + int pos=(con->csry*con->columns+con->csrx)*2; + assert(con->buffer!=NULL); + if (c==__T('\r')) { + con->csrx=0; + } else if (c==__T('\n')) { + con->csrx=0; + con->csry++; + if (con->csry>=con->lines) + ScrollScreen(con,0,1); + } else if (c==__T('\b')) { + if (con->csrx>0) { + con->csrx--; + pos-=2; + con->buffer[pos]=__T(' '); + con->buffer[pos+1]=con->attrib; + } /* if */ + } else { + con->buffer[pos]=(TCHAR)c; + con->buffer[pos+1]=con->attrib; + con->csrx+=1; + if (con->csrx>=con->columns && con->autowrap) { + con->csrx=0; + con->csry++; + if (con->csry>=con->lines) + ScrollScreen(con,0,1); + } /* if */ + } /* if */ + RefreshScreen(con,con->csry,con->csry+1); + } /* if */ + } /* if */ + return 1; +} + +int amx_fflush(void) +{ + return 1; +} + +int amx_kbhit(void) +{ + CONSOLE *con; + + if ((con=ActiveConsole())!=NULL) + return con->keyq_start!=con->keyq_end; + return 0; +} + +int amx_getch(void) +{ + CONSOLE *con; + int c=-1; + + if ((con=ActiveConsole())!=NULL) { + MSG msg; + while (con->keyq_start==con->keyq_end) { + while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } /* while */ + } /* while */ + c=con->keyqueue[con->keyq_start]; + con->keyq_start=(con->keyq_start+1)%KEYQUEUE_SIZE; + } /* if */ + return c; +} + +TCHAR *amx_gets(TCHAR *string,int size) +{ + int c=-1,num=0; + + if (ActiveConsole()!=NULL) { + while (num+10) + string[--num]=__T(' '); + } else { + string[num++]=(TCHAR)c; + } /* if */ + amx_putchar(c); /* echo the character read */ + } /* while */ + if (numautowrap=(BOOL)value; + return 1; + } /* if */ + return 0; + } /* case */ + + case 2: { + HWND hconsole=GetConsoleByIndex(value); + while (hconsole==NULL) { + CreateConsole(NULL,HWND_DESKTOP,DEFCOLUMNS,DEFWINLINES,DEFBUFFERLINES,DEFFONTHEIGHT,0); + hconsole=GetConsoleByIndex(value); + } /* while */ + return SetActiveConsole(hconsole); + } /* case */ + + case 3: { + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + con->boldfont=(BOOL)value; + SetConsoleFont(con,con->cheight); + return 1; + } /* if */ + return 0; + } /* case */ + + case 4: { + MSG msg; + while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } /* while */ + return (GetConsoleByIndex(value)!=NULL); + } /* case */ + + default: + return 0; + } /* switch */ +} + +void amx_clrscr(void) +{ + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + int i; + int size=con->lines*con->columns*2; + assert(con->buffer!=NULL); + for (i=0; ibuffer[i]=__T(' '); + con->buffer[i+1]=con->attrib; + } /* for */ + con->csrx=con->csry=0; + RefreshScreen(con,0,con->lines); + } /* if */ +} + +void amx_clreol(void) +{ + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + int i; + int size=(con->columns-con->csrx)*2; + int pos=(con->csry*con->columns+con->csrx)*2; + assert(con->buffer!=NULL); + for (i=0; ibuffer[pos+i]=__T(' '); + con->buffer[pos+i+1]=con->attrib; + } /* for */ + RefreshScreen(con,con->csry,con->csry+1); + } /* if */ +} + +void amx_gotoxy(int x,int y) +{ + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + if (x>0 && x<=con->columns) + con->csrx=x-1; + if (y>0 && y<=con->lines) + con->csry=y-1; + RefreshScreen(con,0,0); + } /* if */ +} + +void amx_wherexy(int *x,int *y) +{ + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + if (x!=NULL) + *x=con->csrx+1; + if (y!=NULL) + *y=con->csry+1; + } /* if */ +} + +unsigned int amx_setattr(int foregr,int backgr,int highlight) +{ + int prev=0; + CONSOLE *con; + if ((con=ActiveConsole())!=NULL) { + int f,b,h; + + f=con->attrib & 0x07; + b=(con->attrib >> 4) & 0x0f; + h=(con->attrib & 0x08) ? 1 : 0; + prev=(b << 8) | f | (h << 15); + if (foregr>=0 && foregr<8) + f=foregr; + if (backgr>=0 && backgr<8) + b=backgr; + if (highlight>=0) + h=highlight!=0; + con->attrib=(TCHAR)((b << 4) | f | (h << 3)); + } /* if */ + return prev; +} + +void amx_console(int columns, int lines, int flags) +{ + HWND hwnd=CreateConsole(NULL,HWND_DESKTOP,columns,lines,DEFBUFFERLINES,DEFFONTHEIGHT,0); + SetActiveConsole(hwnd); + (void)flags; +} diff --git a/amx-deps/src/amx/termwin.h b/amx-deps/src/amx/termwin.h index 08b8a5a..0bf3045 100644 --- a/amx-deps/src/amx/termwin.h +++ b/amx-deps/src/amx/termwin.h @@ -1,80 +1,80 @@ -/* Simple terminal for Microsoft Windows - * - * Copyright (c) ITB CompuPhase, 2004-2008 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: termwin.h 3902 2008-01-23 17:40:01Z thiadmer $ - */ - -#if !defined TERMWIN_H_INCLUDED -#define TERMWIN_H_INCLUDED - -#if defined _UNICODE || defined __UNICODE__ || defined UNICODE -# if !defined UNICODE /* for Windows */ -# define UNICODE -# endif -# if !defined _UNICODE /* for C library */ -# define _UNICODE -# endif -#endif - -#if !defined WM_NULL -# include -#endif -#if defined _UNICODE -# include -#elif !defined __T - typedef char TCHAR; -# define __T(string) string -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _stprintf sprintf -# define _vstprintf vsprintf -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -HWND CreateConsole(HINSTANCE hinst,HWND hwndParent,int columns,int lines,int bufferlines,int fontsize,DWORD style); -BOOL SetActiveConsole(HWND hconsole); -BOOL DeleteConsole(HWND hconsole); -HWND GetConsoleByIndex(int index); - -int amx_printf(const TCHAR*,...); -int amx_putstr(const TCHAR*); -int amx_putchar(int); -int amx_fflush(void); -int amx_kbhit(void); -int amx_getch(void); -TCHAR* amx_gets(TCHAR*,int); -int amx_termctl(int,int); -void amx_clrscr(void); -void amx_clreol(void); -void amx_gotoxy(int x,int y); -void amx_wherexy(int *x,int *y); -unsigned amx_setattr(int foregr,int backgr,int highlight); -void amx_console(int columns, int lines, int flags); - -#ifdef __cplusplus - } -#endif - -#endif /* TERMWIN_H_INCLUDED */ +/* Simple terminal for Microsoft Windows + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: termwin.h 3519 2006-02-17 17:57:04Z thiadmer $ + */ + +#if !defined TERMWIN_H_INCLUDED +#define TERMWIN_H_INCLUDED + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#if !defined WM_NULL +# include +#endif +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _stprintf sprintf +# define _vstprintf vsprintf +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +HWND CreateConsole(HINSTANCE hinst,HWND hwndParent,int columns,int lines,int bufferlines,int fontsize,DWORD style); +BOOL SetActiveConsole(HWND hconsole); +BOOL DeleteConsole(HWND hconsole); +HWND GetConsoleByIndex(int index); + +int amx_printf(const TCHAR*,...); +int amx_putstr(const TCHAR*); +int amx_putchar(int); +int amx_fflush(void); +int amx_kbhit(void); +int amx_getch(void); +TCHAR* amx_gets(TCHAR*,int); +int amx_termctl(int,int); +void amx_clrscr(void); +void amx_clreol(void); +void amx_gotoxy(int x,int y); +void amx_wherexy(int *x,int *y); +unsigned amx_setattr(int foregr,int backgr,int highlight); +void amx_console(int columns, int lines, int flags); + +#ifdef __cplusplus + } +#endif + +#endif /* TERMWIN_H_INCLUDED */ diff --git a/amx-deps/src/king.sln b/amx-deps/src/king.sln deleted file mode 100644 index 9f048a1..0000000 --- a/amx-deps/src/king.sln +++ /dev/null @@ -1,24 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "king", "ml_base.vcxproj", "{132A627F-058D-46F8-BCBB-63B6C8D2CC1A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {132A627F-058D-46F8-BCBB-63B6C8D2CC1A}.Debug|Win32.ActiveCfg = Debug|Win32 - {132A627F-058D-46F8-BCBB-63B6C8D2CC1A}.Debug|Win32.Build.0 = Debug|Win32 - {132A627F-058D-46F8-BCBB-63B6C8D2CC1A}.Release|Win32.ActiveCfg = Release|Win32 - {132A627F-058D-46F8-BCBB-63B6C8D2CC1A}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A4F37C2B-A037-433B-AB5E-35D34219EBF2} - EndGlobalSection -EndGlobal diff --git a/amx-deps/src/lib/lua5.1.so b/amx-deps/src/lib/lua5.1.so new file mode 100644 index 0000000..1cb475f Binary files /dev/null and b/amx-deps/src/lib/lua5.1.so differ diff --git a/amx-deps/src/linux/getch.c b/amx-deps/src/linux/getch.c index 6dd3959..5ffe9bb 100644 --- a/amx-deps/src/linux/getch.c +++ b/amx-deps/src/linux/getch.c @@ -18,7 +18,6 @@ getch (void) { struct termios save_termios; struct termios ios; - unsigned char b; int c = 0; if (!isatty (STDIN_FILENO)) @@ -35,9 +34,7 @@ getch (void) if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) return EOF; - if (read (STDIN_FILENO, &b, 1) == 1) - c = (int)(0xff & b); - else + if (read (STDIN_FILENO, &c, 1) != 1) c = EOF; tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); diff --git a/amx-deps/src/linux/sclinux.h b/amx-deps/src/linux/sclinux.h index ff34ddc..79c0fad 100644 --- a/amx-deps/src/linux/sclinux.h +++ b/amx-deps/src/linux/sclinux.h @@ -11,12 +11,10 @@ * terminal tty's in 'raw' mode, if we want to be able to fetch a single character. This also means that everything must * be put back correctly when the function ends. See GETCH.C for an implementation. * - * For interactive use of PawnRun/PawnDbg if would be much better to use GNU's readline package: the user would be able to + * For interactive use of SRUN/SDBG if would be much better to use GNU's readline package: the user would be able to * have a complete emacs/vi like line editing system. */ -#if !defined getch && !defined kbhit - #include "getch.h" -#endif +#include "getch.h" #define stricmp(a,b) strcasecmp(a,b) #define strnicmp(a,b,c) strncasecmp(a,b,c) @@ -27,6 +25,10 @@ #define DIRECTORY_SEP_CHAR '/' #define DIRECTORY_SEP_STR "/" +#if defined HAVE_ENDIAN_H +# include +#endif + /* * SC assumes that a computer is Little Endian unless told otherwise. It uses * (and defines) the macros BYTE_ORDER and BIG_ENDIAN. @@ -42,6 +44,12 @@ # define __BIG_ENDIAN BIG_ENDIAN #endif +#if defined __APPLE__ +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +#endif + #if !defined __BYTE_ORDER # error "Can't figure computer byte order (__BYTE_ORDER macro not found)" #endif diff --git a/amx-deps/src/ml_base.vcxproj b/amx-deps/src/ml_base.vcxproj deleted file mode 100644 index 1645e02..0000000 --- a/amx-deps/src/ml_base.vcxproj +++ /dev/null @@ -1,189 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - king - {132A627F-058D-46F8-BCBB-63B6C8D2CC1A} - MFCDLLProj - 10.0 - - - - DynamicLibrary - v142 - false - MultiByte - true - - - DynamicLibrary - v142 - false - MultiByte - - - - - - - - - - - - - - - <_ProjectFileVersion>16.0.30028.132 - - - Debug\ - true - $(SolutionDir)..\..\..\..\..\modules\ - - - Release\ - false - $(SolutionDir)..\..\..\..\..\modules\ - - - - _DEBUG;%(PreprocessorDefinitions) - false - - - Disabled - .\include;%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_DEBUG;_USRDLL;__WIN32__;FLOATPOINT;%(PreprocessorDefinitions);HAVE_INTTYPES_H;HAVE_MALLOC_H;HAVE_STDINT_H;_CRT_SECURE_NO_WARNINGS - true - EnableFastChecks - MultiThreadedDebugDLL - true - - Level3 - EditAndContinue - stdcpp17 - - - _DEBUG;%(PreprocessorDefinitions) - 0x0809 - $(IntDir);%(AdditionalIncludeDirectories) - - - ./lib/lua5.1.lib;./lib/sqlite3.lib;winmm.lib;%(AdditionalDependencies) - MSVCRT;%(IgnoreSpecificDefaultLibraries) - - true - Windows - false - - $(OutDir)king.lib - MachineX86 - false - - - - - NDEBUG;%(PreprocessorDefinitions) - false - - - Speed - true - .\include;%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;NDEBUG;_USRDLL;__WIN32__;FLOATPOINT;%(PreprocessorDefinitions);HAVE_INTTYPES_H;HAVE_MALLOC_H;HAVE_STDINT_H;_CRT_SECURE_NO_WARNINGS - true - MultiThreadedDLL - true - - Level3 - ProgramDatabase - stdcpp17 - - - NDEBUG;%(PreprocessorDefinitions) - 0x0809 - $(IntDir);%(AdditionalIncludeDirectories) - - - ./lib/lua5.1.lib;./lib/sqlite3.lib;winmm.lib;%(AdditionalDependencies) - - true - Windows - true - true - false - - $(OutDir)king.lib - MachineX86 - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/amx-deps/src/ml_base.vcxproj.filters b/amx-deps/src/ml_base.vcxproj.filters deleted file mode 100644 index ee9e7d6..0000000 --- a/amx-deps/src/ml_base.vcxproj.filters +++ /dev/null @@ -1,173 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {e466f55e-c718-4d88-8397-c30d81176d7a} - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {b43b71f0-38ce-411a-96b3-9a948c9b07e1} - - - {340453f2-51bc-444e-84eb-42864614ba77} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files\amx - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files\amx - - - Header Files\amx - - - Header Files\amx - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files\include - - - Header Files - - - - - - \ No newline at end of file diff --git a/amx-deps/src/premake5.lua b/amx-deps/src/premake5.lua new file mode 100644 index 0000000..56b90bb --- /dev/null +++ b/amx-deps/src/premake5.lua @@ -0,0 +1,73 @@ +solution "king" + configurations { "Debug", "Release" } + platforms { "x86", "x64" } + location ( "Build" ) + targetdir "Bin/%{cfg.buildcfg}" + + cppdialect "C++17" + characterset "MBCS" + pic "On" + symbols "On" + + defines { + "_CRT_SECURE_NO_WARNINGS", + "HAVE_STDINT_H", + } + + filter "system:windows" + defines { "WINDOWS", "WIN32" } + + filter "configurations:Debug" + defines { "DEBUG" } + + filter "configurations:Release" + optimize "Speed" + + include "amx" + + project "ml_base" + language "C++" + kind "SharedLib" + targetname "king" + + includedirs { "include" } + libdirs { "lib" } + + vpaths { + ["Headers/*"] = {"*.h", "include/*.h", "linux/*.h"}, + ["Sources/*"] = {"**.cpp"}, + ["Resources/*"] = "king.rc", + + ["*"] = "premake5.lua", + } + + files { + "premake5.lua", + "**.cpp", + "*.h", + "include/*.h", + } + + filter "system:windows" + files { "king.rc" } + + filter "system:linux" + includedirs { "linux" } + + include "amx" + links "amx" + + filter {"system:linux", "platforms:x86" } + linkoptions { "-Wl,-rpath=mods/deathmatch" } + + filter {"system:linux", "platforms:x64" } + linkoptions { "-Wl,-rpath=x64" } + + filter "system:linux" + linkoptions { "-l:lua5.1.so" } + + filter "system:windows" + links { "lua5.1", "sqlite3" } + + filter {} + links "amx"