Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b07022379 | ||
|
|
a4fce0b3b1 | ||
|
|
79ad1f8d51 | ||
|
|
73aef11554 | ||
|
|
318916abc9 | ||
|
|
164a9d604a | ||
|
|
7696b115ae | ||
|
|
b8ecaf336c | ||
|
|
1ea8ebf84c | ||
|
|
9f70fe5373 | ||
|
|
9695ede7c2 | ||
|
|
a5cd72d5db | ||
|
|
f87e282981 | ||
|
|
4b15972543 | ||
|
|
d5753bb15a | ||
|
|
a92e5b3f7b | ||
|
|
72d2357560 | ||
|
|
d306b560b1 | ||
|
|
4d331d9c9f | ||
|
|
932f6d83f7 | ||
|
|
d57783aa3a | ||
|
|
1d79854d3b |
No files matched your search
@@ -4,7 +4,6 @@
|
||||
*.plg
|
||||
*.suo
|
||||
*.vcproj.*.*.user
|
||||
*.kate-swp
|
||||
Win32_LIB_ASM_Debug
|
||||
Win32_LIB_ASM_Release
|
||||
*.bsc
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e # Enable auto-exit for debugging :))
|
||||
|
||||
# Make Linux AppImage program data
|
||||
# https://docs.appimage.org/reference/appdir.html
|
||||
|
||||
# PWD: {repo_root}/build
|
||||
# See deployer.sh for usage
|
||||
|
||||
# Terminal stuff
|
||||
RESET="$(tput sgr0)" #"\e[0m" # Resets terminal colors
|
||||
|
||||
# Colors for various information types
|
||||
FAILURE="$(tput setaf 1)" #"\e[91m"
|
||||
SUCCESS="$(tput setaf 2)" #"\e[92m"
|
||||
NOTICE="$(tput setaf 3)" #"\e[93m"
|
||||
VERBOSE="$(tput setaf 4)" #"\e[94m"
|
||||
|
||||
verbosity=0 # By default don't print anything unless it's *ultra* important.
|
||||
quiet=0 # Also don't print anything if they supplied the quiet argument
|
||||
|
||||
# URL from which to download appimagetool if there isn't one available in your $PATH
|
||||
# at time of writing only aarch64, armhf, i686, and x86_64 versions are available via this method
|
||||
# in those cases, and honestly even outside of those cases, it's better to just put a working one in your $PATH yourself..
|
||||
url="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$(uname -m).AppImage"
|
||||
|
||||
# A whitelist of libraries to include into SRB2's AppImage if SRB2 is dynamically linked.
|
||||
# please don't add a newline at the end of this or i will cry :(
|
||||
libraryWhitelist="
|
||||
libbacktrace
|
||||
libnsl
|
||||
libSDL2
|
||||
libSDL2_mixer
|
||||
libgme
|
||||
libopenmpt
|
||||
libpng
|
||||
libfluidsynth
|
||||
libpulse
|
||||
libmodplug
|
||||
libvorbisfile
|
||||
libopusfile
|
||||
libFLAC
|
||||
libmad
|
||||
libmpg123
|
||||
libvorbis
|
||||
libpulsecommon
|
||||
libsndfile
|
||||
libsndio
|
||||
libogg
|
||||
libvorbisenc
|
||||
libjson
|
||||
libreadline
|
||||
libwrap
|
||||
libtinfo
|
||||
libtirpc
|
||||
libpng16"
|
||||
|
||||
# these kinda look like macros don't they
|
||||
function print { (( "$quiet" == 0 )) && echo "$@" || :; } # echo if not quiet
|
||||
function coloredText { printf "$1"; print "${@:2}"; printf "$RESET"; } # use print to inherit quiet
|
||||
function success { coloredText "$SUCCESS" "$@"; } # use coloredText to inherit quiet
|
||||
function important { coloredText "$NOTICE" "$@"; } # use coloredText to inherit quiet
|
||||
function verboseOnly { (( "$verbosity" >= $1 )) && (coloredText "$VERBOSE" "${@:2}") || :; } # use coloredText to inherit quiet
|
||||
|
||||
# Parses an argument with a single dash
|
||||
function parseSingleDashArg() {
|
||||
while read -n 1 char; do
|
||||
if [[ "$char" == '-' || "$char" == '' ]]; then
|
||||
:
|
||||
elif [[ "$char" == 'h' ]]; then
|
||||
helppassed=1
|
||||
elif [[ "$char" == 'q' ]]; then
|
||||
quiet=1
|
||||
elif [[ "$char" == 'v' ]]; then
|
||||
verbosity="$((verbosity + 1))"
|
||||
elif [[ "$char" == 'd' ]]; then
|
||||
dry=1
|
||||
elif [[ "$char" == 's' ]]; then
|
||||
small=1
|
||||
else
|
||||
echo "$0: invalid option -- '$1'"
|
||||
echo "Try '$0 --help' for more information."
|
||||
return 1
|
||||
fi
|
||||
done <<<"$1"
|
||||
}
|
||||
|
||||
# Simple help argument checker.
|
||||
currentarg=1; # Which argument is --help...
|
||||
|
||||
# For $arg in the argument array...
|
||||
for arg in "$@"; do
|
||||
# Is it a help argument?
|
||||
if [[ "$arg" == "--help" ]]; then
|
||||
helppassed=1; # Help is passed, set $helppassed!
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}" # Remove argument from list
|
||||
elif [[ "$arg" == "--quiet" ]]; then
|
||||
quiet=1
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}"
|
||||
elif [[ "$arg" == "--verbose" ]]; then
|
||||
verbosity="$((verbosity + 1))" # Increase verbosity!
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}"
|
||||
elif [[ "$arg" == "--dry" ]]; then
|
||||
dry=1; # Show parameters instead of using them.
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}"
|
||||
elif [[ "$arg" == "--small" ]]; then
|
||||
small=1; # Don't copy assets to save on bandwidth.
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}"
|
||||
elif [[ ! "$arg" == *'--'* && "$arg" == *'-'* ]]; then
|
||||
parseSingleDashArg "$arg"
|
||||
|
||||
if [[ "$?" == 0 ]]; then
|
||||
set -- "${@:1:$currentarg-1}" "${@:$currentarg+1}" # Remove argument from list
|
||||
fi
|
||||
elif [[ $arg == *'--'* ]]; then
|
||||
print "$0: invalid option -- '$1'"
|
||||
print "Try '$0 --help' for more information."
|
||||
return 1
|
||||
else
|
||||
currentarg="$((currentarg+1))" # Increment current argument number otherwise...
|
||||
fi
|
||||
done
|
||||
|
||||
important "SRB2Kart AppImage Packager v1.0 modified for kart by NepDisk."
|
||||
important "Orignal for SRB2 my mazmazz and Golden."
|
||||
|
||||
# Get root directory from the full path of this script.
|
||||
__ROOT_DIR="$3"
|
||||
|
||||
if [[ -z "$3" ]]; then
|
||||
# finds out where this script is and goes to that directory and goes up one for the root
|
||||
|
||||
scriptdir="$(dirname "${BASH_SOURCE[0]}")"
|
||||
scriptbase="$(basename "${BASH_SOURCE[0]}")"
|
||||
|
||||
__ROOT_DIR="$(cd "$scriptdir" && pwd -P)"
|
||||
fi
|
||||
|
||||
# If it starts with '.', put the current working directory behind it so things work.
|
||||
if [[ "$__ROOT_DIR" == '.' || "$__ROOT_DIR" == '..' ]]; then
|
||||
__ROOT_DIR="$PWD/$__ROOT_DIR"
|
||||
fi
|
||||
|
||||
# Set up defaults
|
||||
__PROGRAM_NAME="${PROGRAM_NAME:-"SRB2Kart Saturn"}"
|
||||
__PROGRAM_DESCRIPTION="${PROGRAM_DESCRIPTION:-"Modded version of SRB2kart with more features."}"
|
||||
__PROGRAM_FILENAME="${PROGRAM_FILENAME:-"lsdl2srb2kart"}"
|
||||
__PROGRAM_ASSETS="${PROGRAM_ASSETS:-"$__ROOT_DIR/assets/installer"}"
|
||||
|
||||
__BUILD_DIR="${1:-"$__ROOT_DIR/bin"}"
|
||||
__OUTPUT_FILENAME="${2:-"$__PROGRAM_FILENAME.AppImage"}"
|
||||
|
||||
__APPIMAGETOOL="${4:-"appimagetool"}"
|
||||
|
||||
if (! command -v "$__APPIMAGETOOL" &> /dev/null); then
|
||||
__APPIMAGETOOL=
|
||||
fi
|
||||
|
||||
# Stuff that prints and exits.
|
||||
|
||||
# Prints setup text using method given by arguments.
|
||||
# Example: printSetup "print" # print "Setup for AppImage...
|
||||
function printSetup {
|
||||
$@ "Setup for AppImage in BUILD_DIR: '$__BUILD_DIR'..."
|
||||
$@ "With the OUTPUT_FILENAME: '$__OUTPUT_FILENAME'"
|
||||
$@ "And SRB2 Repository Root ROOT_DIR: '$__ROOT_DIR'"
|
||||
$@ "Using APPIMAGETOOL: '$([ -z "$__APPIMAGETOOL" ] && echo '(download)' || echo $__APPIMAGETOOL)'"
|
||||
$@ ""
|
||||
$@ "This should be run from a Makefile or from the directory of the build. If it is not, then change to that directory or specify that directory as an argument."
|
||||
$@ "If ROOT_DIR isn't SRB2's repository root, specify that as an argument too."
|
||||
$@ "Make must have built the program before you run this script."
|
||||
$@ ""
|
||||
$@ "Make sure these Environment Variables are correct."
|
||||
$@ "If not, then enter: export PROGRAM_VARIABLE=value"
|
||||
$@ "PROGRAM_NAME: $__PROGRAM_NAME"
|
||||
$@ "PROGRAM_DESCRIPTION: $__PROGRAM_DESCRIPTION"
|
||||
$@ "PROGRAM_FILENAME: $__PROGRAM_FILENAME"
|
||||
$@ "PROGRAM_ASSETS: $__PROGRAM_ASSETS"
|
||||
}
|
||||
|
||||
if [[ "$helppassed" ]]; then
|
||||
print "Usage: $(basename "$0") [OPTION]... [BUILD_DIR] [OUTPUT_NAME] [ROOT_DIR] [APPIMAGETOOL]"
|
||||
print "Packages a SRB2 binary into an AppImage."
|
||||
print "OPTIONs may be included anywhere within the arguments."
|
||||
print ""
|
||||
print "Arguments:"
|
||||
print " -h, --help display this help and exit."
|
||||
print " -q, --quiet don't display any text."
|
||||
print " -v, --verbose make commands more verbose, will always print."
|
||||
print " -s, --small don't copy SRB2 assets to save on bandwidth."
|
||||
print " parameters regardless of --dry."
|
||||
print " -d, --dry print parameters and exit."
|
||||
print ""
|
||||
if (( "$verbosity" > 0 )) || [[ "$dry" ]]; then
|
||||
printSetup "print"
|
||||
else
|
||||
print "This should be run from a Makefile or from the directory of the build."
|
||||
print "Make must have built the program before you run this script."
|
||||
fi
|
||||
exit;
|
||||
fi
|
||||
|
||||
if (( "$verbosity" > 0 )) || [[ "$dry" ]]; then
|
||||
printSetup "verboseOnly" "0"
|
||||
verboseOnly 0 ""
|
||||
|
||||
if [[ "$dry" ]]; then
|
||||
set -n # Enable debug mode and verbose for dry run.
|
||||
fi
|
||||
fi
|
||||
|
||||
# End stuff that prints and exits.
|
||||
|
||||
if (( "$verbosity" >= 4 )); then
|
||||
set -v # Really verbose? Print *everything*!
|
||||
fi
|
||||
|
||||
# Define AppDir structure
|
||||
mkdir -p "$__BUILD_DIR/AppDir/usr/bin"
|
||||
mkdir -p "$__BUILD_DIR/AppDir/lib"
|
||||
mkdir -p "$__BUILD_DIR/AppDir/usr/share/applications"
|
||||
mkdir -p "$__BUILD_DIR/AppDir/usr/share/icons/hicolor/256x256/apps"
|
||||
|
||||
# Copy program data
|
||||
if [[ ! $small ]]; then
|
||||
verboseOnly 1 "Packaging program assets..."
|
||||
cp -r "$__PROGRAM_ASSETS"/* "$__BUILD_DIR/AppDir/usr/bin/"
|
||||
cp -r "$__BUILD_DIR/$__PROGRAM_FILENAME" "$__BUILD_DIR/AppDir/usr/bin"
|
||||
else
|
||||
verboseOnly 1 "Skipping program asset packing, --small or -s passed..."
|
||||
fi
|
||||
|
||||
verboseOnly 1 "Packaging SRB2..."
|
||||
verboseOnly 1 "Assuming executable name $__PROGRAM_FILENAME"
|
||||
cp -r "$__BUILD_DIR/$__PROGRAM_FILENAME" "$__BUILD_DIR/AppDir/usr/bin"
|
||||
|
||||
# Copy required dependencies, but only if the program is dynamically linked.
|
||||
verboseOnly 1 "Testing if this build is dynamically linked..."
|
||||
|
||||
set +e # Disable auto-exit for ldd.
|
||||
|
||||
srb2Libraries="$(ldd "$__BUILD_DIR/$__PROGRAM_FILENAME")"
|
||||
exitcode="$?"
|
||||
|
||||
set -e # Enable it again!
|
||||
|
||||
if (( $exitcode == 0 )); then
|
||||
verboseOnly 1 "This build *is* dynamically linked! Packaging dependencies."
|
||||
|
||||
# read the dynamic libraries of srb2, trim off beginning whitespace, and get the third field seperated by spaces
|
||||
# this gives us a newline-seperated list of libraries with their full paths.
|
||||
srb2Libraries="$(echo "$srb2Libraries" | cut -f 2 | cut -d ' ' -f 3)"
|
||||
|
||||
# remove any extraneous newlines around the string
|
||||
libraryWhitelist="$(echo $libraryWhitelist | head -c -1 | tr ' ' '\n')"
|
||||
|
||||
# replace e.g. 'abc' with '(/abc\b)', and replace newlines with '|'.
|
||||
# this converts it into a grep regular expression to check for any whitelisted library names.
|
||||
libraryWhitelist="$(printf "%s" "$libraryWhitelist" | sed 's|.*|(/\0\\b)|' | tr '\n' '|')"
|
||||
|
||||
# grab only the entries that match the regex
|
||||
__LDD_LIST="$(printf "%s" "$srb2Libraries" | grep -E "$libraryWhitelist")"
|
||||
|
||||
# use echo to convert the newline-seperated list into an argument list
|
||||
__LDD_LIST="$(echo $__LDD_LIST)"
|
||||
|
||||
# read into an array because i can't be bothered iterating the lines of a string today
|
||||
# there's so many other things in this file that'd need to be adjusted to be POSIX-compliant anyway
|
||||
IFS=' ' read -r -a paths <<< "$__LDD_LIST"
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
if [ -f "$path" ]; then
|
||||
verboseOnly 2 "Packaging dependency $(basename "$path")..."
|
||||
cp "$path" $__BUILD_DIR/AppDir/lib/
|
||||
else
|
||||
verboseOnly 2 "Dependency $(basename "$path") not found"
|
||||
fi
|
||||
done
|
||||
else
|
||||
verboseOnly 1 "This SRB2 build is statically linked, skipping dependency packing."
|
||||
fi
|
||||
|
||||
cd "$__BUILD_DIR/AppDir"
|
||||
|
||||
# Copy icons
|
||||
verboseOnly 1 "Packaging resources..."
|
||||
cp "$__ROOT_DIR/srb2.png" "./usr/share/icons/hicolor/256x256/apps/$__PROGRAM_FILENAME.png"
|
||||
ln -sf "./usr/share/icons/hicolor/256x256/apps/$__PROGRAM_FILENAME.png" "./.DirIcon"
|
||||
ln -sf "./usr/share/icons/hicolor/256x256/apps/$__PROGRAM_FILENAME.png" "./$__PROGRAM_FILENAME.png"
|
||||
|
||||
# Make desktop descriptor
|
||||
cat > "./usr/share/applications/$__PROGRAM_FILENAME.desktop" <<EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=$__PROGRAM_NAME
|
||||
Comment=$__PROGRAM_DESCRIPTION
|
||||
Icon=$__PROGRAM_FILENAME
|
||||
Exec=AppRun %F
|
||||
Categories=Game;
|
||||
EOF
|
||||
|
||||
ln -sf "./usr/share/applications/$__PROGRAM_FILENAME.desktop" "./$__PROGRAM_FILENAME.desktop"
|
||||
|
||||
# Make entry point
|
||||
echo -e '#!'"$(dirname "$SHELL")/sh" > ./AppRun
|
||||
echo -e 'HERE="$(dirname "$(readlink -f "${0}")")"' >> ./AppRun
|
||||
echo -e 'SRB2WADDIR=$HERE/usr/bin LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HERE/lib exec $HERE/usr/bin/'"$__PROGRAM_FILENAME"' "$@"' >> ./AppRun
|
||||
chmod +x ./AppRun
|
||||
|
||||
cd .. # now in $__BUILD_DIR
|
||||
|
||||
# Package AppImage
|
||||
if [ -z "$__APPIMAGETOOL" ]; then
|
||||
verboseOnly 1 "No valid appimagetool path given or detected, downloading appimagetool..."
|
||||
|
||||
# Print notice for internet connection.
|
||||
important "If the command hangs here, check your internet connection, or download appimagetool and add it to your \$PATH and try compiling again."
|
||||
|
||||
(( "$verbosity" >= 3 )) && wget "$url" || wget -q "$url"
|
||||
|
||||
APPIMAGETOOL="./$(basename "$url")"
|
||||
chmod a+x "$APPIMAGETOOL"
|
||||
else
|
||||
verboseOnly 1 "appimagetool path given or detected, no download required."
|
||||
APPIMAGETOOL="$__APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
verboseOnly 1 "Packing AppImage $__OUTPUT_FILENAME"
|
||||
|
||||
if (( verbosity >= 3 )); then
|
||||
"$APPIMAGETOOL" ./AppDir "$__OUTPUT_FILENAME"
|
||||
elif (( verbosity >= 2 )); then
|
||||
"$APPIMAGETOOL" ./AppDir "$__OUTPUT_FILENAME" >/dev/null
|
||||
else
|
||||
"$APPIMAGETOOL" ./AppDir "$__OUTPUT_FILENAME" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
success "AppImage ready!"
|
||||
verboseOnly 1 "Cleaning up files..."
|
||||
rm -r ./AppDir
|
||||
|
||||
if [ -z "$__APPIMAGETOOL" ]; then
|
||||
rm "$APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
cd "$__ROOT_DIR" # snap back to reality
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
|
||||
# Version change is fine.
|
||||
project(SRB2
|
||||
|
||||
-42
@@ -28,10 +28,6 @@ exist.
|
||||
Returns the values of the given HUD item's `xoffset`/`yoffset` cvars.
|
||||
Available for any HUD item with offset cvars.
|
||||
|
||||
## patch = v.cachePatchRotated(name, rollangle)
|
||||
Like v.cachePatch, it caches a new patch with the corresponding name. Unlike v.cachePatch, however,
|
||||
a rollangle can be supplied to the function so that it returns a rotated patch instead.
|
||||
|
||||
## x, y, flags = v.getDrawInfo(item)
|
||||
|
||||
Returns the X, Y and flags where the given HUD item will be drawn for the current displayplayer.
|
||||
@@ -134,41 +130,3 @@ if rawget(_G, "S_StopSoundByNum") ~= nil then
|
||||
... -- Can use it
|
||||
end
|
||||
```
|
||||
## addHook("ServerJoin", function())
|
||||
|
||||
ServerJoin hook called when client joins server (starting a listen server also calls it for host).
|
||||
Generally it was made to make loading custom config files less hacky, but can be used for anything else too.
|
||||
|
||||
## player.viewrollangle field
|
||||
|
||||
Returns player's current view roll angle for the Screen Tilting feature, useful for HUD elements.
|
||||
|
||||
## G_SetPlayerGamepadIndicatorColor(player, skincolor)
|
||||
|
||||
Set a custom color for Supported Gamepads with RGB LED functionality.
|
||||
To be used with Displayplayers.
|
||||
Only takes Skincolors.
|
||||
Best to be used in a Loop to ensure the color wont get overwritten by the game.
|
||||
|
||||
## G_PlayerDeviceRumble(player, low_strength, high_strength, -optional- duration)
|
||||
|
||||
Add Gamepad Rumble support for things.
|
||||
To be used with Displayplayers.
|
||||
Duration is in milliseconds and is optional to set, default value is 84ms.
|
||||
|
||||
## P_CheckSightFast(mo1, mo2)
|
||||
|
||||
Exactly same as P_CheckSight but uses cheaper algorithm, useful for things like nametags. Doesn't work exactly
|
||||
like P_CheckSight so don't use it for anything gameplay-related.
|
||||
|
||||
# Other changes
|
||||
|
||||
## P_PlayRinglossSound(source, damager)
|
||||
|
||||
Add optional `damager` argument (should be `mobj_t`), which causes hurt sound also play for damager
|
||||
if `karthitemdialog` option is enabled
|
||||
|
||||
## K_PlayHitEmSound(mobj, victim)
|
||||
|
||||
Add optional `victim` argument (should be a player's `mobj_t`), which causes "hit em" sound to be
|
||||
delayed and played for victim too if `karthitemdialog` option is enabled
|
||||
@@ -1,8 +0,0 @@
|
||||
ifdef SILENT
|
||||
MAKEFLAGS+=--no-print-directory
|
||||
endif
|
||||
|
||||
all :
|
||||
|
||||
% ::
|
||||
@$(MAKE) -C src $(MAKECMDGOALS)
|
||||
@@ -76,7 +76,7 @@ Crashlogs now contain alot more informations. and even more if your build has de
|
||||
Also allows 64bit Windows builds to produce Crashlogs!
|
||||
|
||||
## MISC
|
||||
- Bird's Camera Tilting feature is no longer turned on by default
|
||||
- Bird's Camara Tilting feature is no longer turned on by default
|
||||
- Bird's warning on the title screen has been removed
|
||||
|
||||
## Bugs
|
||||
|
||||
@@ -1726,6 +1726,11 @@ HW3SOUND for 3D hardware sound support
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="src/r_draw.h" />
|
||||
<Unit filename="src/r_draw16.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compile="0" />
|
||||
<Option link="0" />
|
||||
</Unit>
|
||||
<Unit filename="src/r_draw8.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compile="0" />
|
||||
|
||||
@@ -773,6 +773,16 @@ Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit79]
|
||||
FileName=src\r_draw16.c
|
||||
Folder=R_Rend
|
||||
Compile=0
|
||||
CompileCpp=0
|
||||
Link=0
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit80]
|
||||
FileName=src\r_draw8.c
|
||||
Folder=R_Rend
|
||||
|
||||
@@ -1 +1 @@
|
||||
/lsdl2srb2kart
|
||||
/lsdlsrb2
|
||||
+32
-32
@@ -1,32 +1,32 @@
|
||||
@echo off
|
||||
set BRA=Unknown
|
||||
set REV=illegal
|
||||
|
||||
copy nul: /b +%1\comptime.c tmp.$$$ > nul
|
||||
move tmp.$$$ %1\comptime.c > nul
|
||||
|
||||
if exist .git goto gitrev
|
||||
if exist ..\.git goto gitrev
|
||||
if exist .svn goto svnrev
|
||||
goto filwri
|
||||
|
||||
:gitrev
|
||||
set GIT=%2
|
||||
if "%GIT%"=="" set GIT=git
|
||||
for /f "usebackq" %%s in (`%GIT% rev-parse --abbrev-ref HEAD`) do @set BRA=%%s
|
||||
for /f "usebackq" %%s in (`%GIT% rev-parse HEAD`) do @set REV=%%s
|
||||
set REV=%REV:~0,8%
|
||||
goto filwri
|
||||
|
||||
:svnrev
|
||||
set BRA=Subversion
|
||||
for /f "usebackq" %%s in (`svnversion .`) do @set REV=%%s
|
||||
set REV=r%REV%
|
||||
goto filwri
|
||||
|
||||
:filwri
|
||||
echo // Do not edit! This file was autogenerated > %1\comptime.h
|
||||
echo // by the %0 batch file >> %1\comptime.h
|
||||
echo // >> %1\comptime.h
|
||||
echo const char* compbranch = "%BRA%"; >> %1\comptime.h
|
||||
echo const char* comprevision = "%REV%"; >> %1\comptime.h
|
||||
@echo off
|
||||
set BRA=Unknown
|
||||
set REV=illegal
|
||||
|
||||
copy nul: /b +%1\comptime.c tmp.$$$ > nul
|
||||
move tmp.$$$ %1\comptime.c > nul
|
||||
|
||||
if exist .git goto gitrev
|
||||
if exist ..\.git goto gitrev
|
||||
if exist .svn goto svnrev
|
||||
goto filwri
|
||||
|
||||
:gitrev
|
||||
set GIT=%2
|
||||
if "%GIT%"=="" set GIT=git
|
||||
for /f "usebackq" %%s in (`%GIT% rev-parse --abbrev-ref HEAD`) do @set BRA=%%s
|
||||
for /f "usebackq" %%s in (`%GIT% rev-parse HEAD`) do @set REV=%%s
|
||||
set REV=%REV:~0,8%
|
||||
goto filwri
|
||||
|
||||
:svnrev
|
||||
set BRA=Subversion
|
||||
for /f "usebackq" %%s in (`svnversion .`) do @set REV=%%s
|
||||
set REV=r%REV%
|
||||
goto filwri
|
||||
|
||||
:filwri
|
||||
echo // Do not edit! This file was autogenerated > %1\comptime.h
|
||||
echo // by the %0 batch file >> %1\comptime.h
|
||||
echo // >> %1\comptime.h
|
||||
echo const char* compbranch = "%BRA%"; >> %1\comptime.h
|
||||
echo const char* comprevision = "%REV%"; >> %1\comptime.h
|
||||
+2
-1
@@ -17,7 +17,7 @@ EOF
|
||||
|
||||
versiongit() {
|
||||
gitbranch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
gitversion="$(git show --pretty=format:'%h %s' -s HEAD | sed 's/"/\\"/g')"
|
||||
gitversion="$(git rev-parse HEAD | cut -c -8)"
|
||||
version "$gitbranch" "$gitversion";
|
||||
exit 0
|
||||
}
|
||||
@@ -42,3 +42,4 @@ compversion() {
|
||||
|
||||
[ -f "$path/comptime.c" ] && compversion "$@"
|
||||
exit 2
|
||||
|
||||
+1
-5
@@ -31,8 +31,4 @@ and don't forget to build r_opengl.dll for srb2dd.
|
||||
|
||||
Updated 2/17/2024 (v1.6 Saturn)
|
||||
No i threw everthing into dll-binaries
|
||||
theres no reason at all to have stuff spread across multiple directories
|
||||
|
||||
Updated 3/19/2025 (v1.6; Neptune)
|
||||
We're using some C++ code now, so we need to have a few more DLLs.
|
||||
Otherwise, it's about the same as prior.
|
||||
theres no reason at all to have stuff spread across multiple directories
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,191 @@
|
||||
/* include/curl/curlbuild.h. Generated from curlbuild.h.in by configure. */
|
||||
#ifndef __CURL_CURLBUILD_H
|
||||
#define __CURL_CURLBUILD_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* ================================================================ */
|
||||
/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* NOTE 1:
|
||||
* -------
|
||||
*
|
||||
* Nothing in this file is intended to be modified or adjusted by the
|
||||
* curl library user nor by the curl library builder.
|
||||
*
|
||||
* If you think that something actually needs to be changed, adjusted
|
||||
* or fixed in this file, then, report it on the libcurl development
|
||||
* mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/
|
||||
*
|
||||
* This header file shall only export symbols which are 'curl' or 'CURL'
|
||||
* prefixed, otherwise public name space would be polluted.
|
||||
*
|
||||
* NOTE 2:
|
||||
* -------
|
||||
*
|
||||
* Right now you might be staring at file include/curl/curlbuild.h.in or
|
||||
* at file include/curl/curlbuild.h, this is due to the following reason:
|
||||
*
|
||||
* On systems capable of running the configure script, the configure process
|
||||
* will overwrite the distributed include/curl/curlbuild.h file with one that
|
||||
* is suitable and specific to the library being configured and built, which
|
||||
* is generated from the include/curl/curlbuild.h.in template file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* ================================================================ */
|
||||
/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */
|
||||
/* ================================================================ */
|
||||
|
||||
#ifdef CURL_SIZEOF_LONG
|
||||
# error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SIZEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_TYPEOF_CURL_OFF_T
|
||||
# error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_CURL_OFF_T
|
||||
# error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_CURL_OFF_TU
|
||||
# error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_OFF_T
|
||||
# error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SIZEOF_CURL_OFF_T
|
||||
# error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SUFFIX_CURL_OFF_T
|
||||
# error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SUFFIX_CURL_OFF_TU
|
||||
# error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined
|
||||
#endif
|
||||
|
||||
/* ================================================================ */
|
||||
/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */
|
||||
/* ================================================================ */
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file ws2tcpip.h must be included by the external interface. */
|
||||
#define CURL_PULL_WS2TCPIP_H 1
|
||||
#ifdef CURL_PULL_WS2TCPIP_H
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file sys/types.h must be included by the external interface. */
|
||||
#define CURL_PULL_SYS_TYPES_H 1
|
||||
#ifdef CURL_PULL_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file stdint.h must be included by the external interface. */
|
||||
#define CURL_PULL_STDINT_H 1
|
||||
#ifdef CURL_PULL_STDINT_H
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file inttypes.h must be included by the external interface. */
|
||||
#define CURL_PULL_INTTYPES_H 1
|
||||
#ifdef CURL_PULL_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file sys/socket.h must be included by the external interface. */
|
||||
/* #undef CURL_PULL_SYS_SOCKET_H */
|
||||
#ifdef CURL_PULL_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_LONG 4
|
||||
|
||||
/* Integral data type used for curl_socklen_t. */
|
||||
#define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
|
||||
|
||||
/* The size of `curl_socklen_t', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_CURL_SOCKLEN_T 4
|
||||
|
||||
/* Data type definition of curl_socklen_t. */
|
||||
typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;
|
||||
|
||||
/* Signed integral data type used for curl_off_t. */
|
||||
#define CURL_TYPEOF_CURL_OFF_T int64_t
|
||||
|
||||
/* Data type definition of curl_off_t. */
|
||||
typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
|
||||
|
||||
/* curl_off_t formatting string directive without "%" conversion specifier. */
|
||||
#define CURL_FORMAT_CURL_OFF_T "I64d"
|
||||
|
||||
/* unsigned curl_off_t formatting string without "%" conversion specifier. */
|
||||
#define CURL_FORMAT_CURL_OFF_TU "I64u"
|
||||
|
||||
/* curl_off_t formatting string directive with "%" conversion specifier. */
|
||||
#define CURL_FORMAT_OFF_T "%I64d"
|
||||
|
||||
/* The size of `curl_off_t', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_CURL_OFF_T 8
|
||||
|
||||
/* curl_off_t constant suffix. */
|
||||
#define CURL_SUFFIX_CURL_OFF_T LL
|
||||
|
||||
/* unsigned curl_off_t constant suffix. */
|
||||
#define CURL_SUFFIX_CURL_OFF_TU ULL
|
||||
|
||||
#endif /* __CURL_CURLBUILD_H */
|
||||
@@ -0,0 +1,261 @@
|
||||
#ifndef __CURL_CURLRULES_H
|
||||
#define __CURL_CURLRULES_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* ================================================================ */
|
||||
/* COMPILE TIME SANITY CHECKS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* NOTE 1:
|
||||
* -------
|
||||
*
|
||||
* All checks done in this file are intentionally placed in a public
|
||||
* header file which is pulled by curl/curl.h when an application is
|
||||
* being built using an already built libcurl library. Additionally
|
||||
* this file is also included and used when building the library.
|
||||
*
|
||||
* If compilation fails on this file it is certainly sure that the
|
||||
* problem is elsewhere. It could be a problem in the curlbuild.h
|
||||
* header file, or simply that you are using different compilation
|
||||
* settings than those used to build the library.
|
||||
*
|
||||
* Nothing in this file is intended to be modified or adjusted by the
|
||||
* curl library user nor by the curl library builder.
|
||||
*
|
||||
* Do not deactivate any check, these are done to make sure that the
|
||||
* library is properly built and used.
|
||||
*
|
||||
* You can find further help on the libcurl development mailing list:
|
||||
* http://cool.haxx.se/mailman/listinfo/curl-library/
|
||||
*
|
||||
* NOTE 2
|
||||
* ------
|
||||
*
|
||||
* Some of the following compile time checks are based on the fact
|
||||
* that the dimension of a constant array can not be a negative one.
|
||||
* In this way if the compile time verification fails, the compilation
|
||||
* will fail issuing an error. The error description wording is compiler
|
||||
* dependent but it will be quite similar to one of the following:
|
||||
*
|
||||
* "negative subscript or subscript is too large"
|
||||
* "array must have at least one element"
|
||||
* "-1 is an illegal array size"
|
||||
* "size of array is negative"
|
||||
*
|
||||
* If you are building an application which tries to use an already
|
||||
* built libcurl library and you are getting this kind of errors on
|
||||
* this file, it is a clear indication that there is a mismatch between
|
||||
* how the library was built and how you are trying to use it for your
|
||||
* application. Your already compiled or binary library provider is the
|
||||
* only one who can give you the details you need to properly use it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Verify that some macros are actually defined.
|
||||
*/
|
||||
|
||||
#ifndef CURL_SIZEOF_LONG
|
||||
# error "CURL_SIZEOF_LONG definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_LONG_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_TYPEOF_CURL_SOCKLEN_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SIZEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_SIZEOF_CURL_SOCKLEN_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_TYPEOF_CURL_OFF_T
|
||||
# error "CURL_TYPEOF_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_CURL_OFF_T
|
||||
# error "CURL_FORMAT_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_CURL_OFF_TU
|
||||
# error "CURL_FORMAT_CURL_OFF_TU definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_OFF_T
|
||||
# error "CURL_FORMAT_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SIZEOF_CURL_OFF_T
|
||||
# error "CURL_SIZEOF_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SUFFIX_CURL_OFF_T
|
||||
# error "CURL_SUFFIX_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SUFFIX_CURL_OFF_TU
|
||||
# error "CURL_SUFFIX_CURL_OFF_TU definition is missing!"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_is_missing
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros private to this header file.
|
||||
*/
|
||||
|
||||
#define CurlchkszEQ(t, s) sizeof(t) == s ? 1 : -1
|
||||
|
||||
#define CurlchkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for long
|
||||
* is the same as the one reported by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_01__
|
||||
[CurlchkszEQ(long, CURL_SIZEOF_LONG)];
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for
|
||||
* curl_off_t is actually the the same as the one reported
|
||||
* by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_02__
|
||||
[CurlchkszEQ(curl_off_t, CURL_SIZEOF_CURL_OFF_T)];
|
||||
|
||||
/*
|
||||
* Verify at compile time that the size of curl_off_t as reported
|
||||
* by sizeof() is greater or equal than the one reported for long
|
||||
* for the current compilation.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_03__
|
||||
[CurlchkszGE(curl_off_t, long)];
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for
|
||||
* curl_socklen_t is actually the the same as the one reported
|
||||
* by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_04__
|
||||
[CurlchkszEQ(curl_socklen_t, CURL_SIZEOF_CURL_SOCKLEN_T)];
|
||||
|
||||
/*
|
||||
* Verify at compile time that the size of curl_socklen_t as reported
|
||||
* by sizeof() is greater or equal than the one reported for int for
|
||||
* the current compilation.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_05__
|
||||
[CurlchkszGE(curl_socklen_t, int)];
|
||||
|
||||
/* ================================================================ */
|
||||
/* EXTERNALLY AND INTERNALLY VISIBLE DEFINITIONS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
|
||||
* these to be visible and exported by the external libcurl interface API,
|
||||
* while also making them visible to the library internals, simply including
|
||||
* setup.h, without actually needing to include curl.h internally.
|
||||
* If some day this section would grow big enough, all this should be moved
|
||||
* to its own header file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Figure out if we can use the ## preprocessor operator, which is supported
|
||||
* by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
|
||||
* or __cplusplus so we need to carefully check for them too.
|
||||
*/
|
||||
|
||||
#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
|
||||
defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
|
||||
defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
|
||||
defined(__ILEC400__)
|
||||
/* This compiler is believed to have an ISO compatible preprocessor */
|
||||
#define CURL_ISOCPP
|
||||
#else
|
||||
/* This compiler is believed NOT to have an ISO compatible preprocessor */
|
||||
#undef CURL_ISOCPP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros for minimum-width signed and unsigned curl_off_t integer constants.
|
||||
*/
|
||||
|
||||
#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
|
||||
# define __CURL_OFF_T_C_HLPR2(x) x
|
||||
# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
|
||||
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
|
||||
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
|
||||
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
|
||||
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
|
||||
#else
|
||||
# ifdef CURL_ISOCPP
|
||||
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
|
||||
# else
|
||||
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
|
||||
# endif
|
||||
# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
|
||||
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
|
||||
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get rid of macros private to this header file.
|
||||
*/
|
||||
|
||||
#undef CurlchkszEQ
|
||||
#undef CurlchkszGE
|
||||
|
||||
/*
|
||||
* Get rid of macros not intended to exist beyond this point.
|
||||
*/
|
||||
|
||||
#undef CURL_PULL_WS2TCPIP_H
|
||||
#undef CURL_PULL_SYS_TYPES_H
|
||||
#undef CURL_PULL_SYS_SOCKET_H
|
||||
#undef CURL_PULL_STDINT_H
|
||||
#undef CURL_PULL_INTTYPES_H
|
||||
|
||||
#undef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
#undef CURL_TYPEOF_CURL_OFF_T
|
||||
|
||||
#ifdef CURL_NO_OLDIES
|
||||
#undef CURL_FORMAT_OFF_T /* not required since 7.19.0 - obsoleted in 7.20.0 */
|
||||
#endif
|
||||
|
||||
#endif /* __CURL_CURLRULES_H */
|
||||
@@ -0,0 +1 @@
|
||||
/* not used */
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+94
-94
@@ -1,94 +1,94 @@
|
||||
# CMake project definition file.
|
||||
project(libgme)
|
||||
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
# When version is changed, also change the one in gme/gme.h to match
|
||||
set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
|
||||
|
||||
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
|
||||
# Of course, 2.4 might work, in which case you're welcome to drop
|
||||
# down the requirement, but I can't test that.
|
||||
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
|
||||
|
||||
# Default emulators to build (all of them! ;)
|
||||
if (NOT DEFINED USE_GME_AY)
|
||||
SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GBS)
|
||||
SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GYM)
|
||||
SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_HES)
|
||||
SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_KSS)
|
||||
SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSF)
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSFE)
|
||||
SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SAP)
|
||||
SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SPC)
|
||||
SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_VGM)
|
||||
SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation")
|
||||
endif()
|
||||
|
||||
if (USE_GME_NSFE AND NOT USE_GME_NSF)
|
||||
MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
|
||||
|
||||
# Check for GCC "visibility" support.
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
|
||||
set (ENABLE_VISIBILITY OFF)
|
||||
if (__LIBGME_TEST_VISIBILITY)
|
||||
# get the gcc version
|
||||
exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info)
|
||||
string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
|
||||
|
||||
# gcc <4.1 had poor support for symbol visibility
|
||||
if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set (ENABLE_VISIBILITY ON)
|
||||
add_definitions (-DLIBGME_VISIBILITY)
|
||||
|
||||
# GCC >= 4.2 also correctly supports making inline members have hidden
|
||||
# visibility by default.
|
||||
if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
||||
endif()
|
||||
endif()
|
||||
endif() # test visibility
|
||||
|
||||
# Cache this result
|
||||
set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
# Shared library defined here
|
||||
add_subdirectory(gme)
|
||||
|
||||
# EXCLUDE_FROM_ALL adds build rules but keeps it out of default build
|
||||
add_subdirectory(player EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(demo EXCLUDE_FROM_ALL)
|
||||
# CMake project definition file.
|
||||
project(libgme)
|
||||
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
# When version is changed, also change the one in gme/gme.h to match
|
||||
set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
|
||||
|
||||
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
|
||||
# Of course, 2.4 might work, in which case you're welcome to drop
|
||||
# down the requirement, but I can't test that.
|
||||
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
|
||||
|
||||
# Default emulators to build (all of them! ;)
|
||||
if (NOT DEFINED USE_GME_AY)
|
||||
SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GBS)
|
||||
SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GYM)
|
||||
SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_HES)
|
||||
SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_KSS)
|
||||
SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSF)
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSFE)
|
||||
SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SAP)
|
||||
SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SPC)
|
||||
SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_VGM)
|
||||
SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation")
|
||||
endif()
|
||||
|
||||
if (USE_GME_NSFE AND NOT USE_GME_NSF)
|
||||
MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
|
||||
|
||||
# Check for GCC "visibility" support.
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
|
||||
set (ENABLE_VISIBILITY OFF)
|
||||
if (__LIBGME_TEST_VISIBILITY)
|
||||
# get the gcc version
|
||||
exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info)
|
||||
string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
|
||||
|
||||
# gcc <4.1 had poor support for symbol visibility
|
||||
if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set (ENABLE_VISIBILITY ON)
|
||||
add_definitions (-DLIBGME_VISIBILITY)
|
||||
|
||||
# GCC >= 4.2 also correctly supports making inline members have hidden
|
||||
# visibility by default.
|
||||
if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
||||
endif()
|
||||
endif()
|
||||
endif() # test visibility
|
||||
|
||||
# Cache this result
|
||||
set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
# Shared library defined here
|
||||
add_subdirectory(gme)
|
||||
|
||||
# EXCLUDE_FROM_ALL adds build rules but keeps it out of default build
|
||||
add_subdirectory(player EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(demo EXCLUDE_FROM_ALL)
|
||||
@@ -1,5 +1,5 @@
|
||||
Game_Music_Emu Change Log
|
||||
-------------------------
|
||||
|
||||
Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
|
||||
for the accurate change log.
|
||||
Game_Music_Emu Change Log
|
||||
-------------------------
|
||||
|
||||
Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
|
||||
for the accurate change log.
|
||||
+194
-194
@@ -1,194 +1,194 @@
|
||||
Game_Music_Emu 0.6.0 Design
|
||||
---------------------------
|
||||
This might be slightly out-of-date at times, but will be a big help in
|
||||
understanding the library implementation.
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
The library is essentially a bunch of independent game music file
|
||||
emulators unified with a common interface.
|
||||
|
||||
Gme_File and Music_Emu provide a common interface to the emulators. The
|
||||
virtual functions are protected rather than public to allow pre- and
|
||||
post-processing of arguments and data in one place. This allows the
|
||||
emulator classes to assume that everything is set up properly when
|
||||
starting a track and playing samples.
|
||||
|
||||
All file input is done with the Data_Reader interface. Many derived
|
||||
classes are present, for the usual disk-based file and block of memory,
|
||||
to specialized adaptors for things like reading a subset of data or
|
||||
combining a block of memory with a Data_Reader to the remaining data.
|
||||
This makes the library much more flexible with regard to the source of
|
||||
game music file data. I still added a specialized load_mem() function to
|
||||
have the emulator keep a pointer to data already read in memory, for
|
||||
those formats whose files can be absolutely huge (GYM, some VGMs). This
|
||||
is important if for some reason the caller must load the data ahead of
|
||||
time, but doesn't want the emulator needlessly making a copy.
|
||||
|
||||
Since silence checking and fading are relatively complex, they are kept
|
||||
separate from basic file loading and track information, which are
|
||||
handled in the base class Gme_File. My original intent was to use
|
||||
Gme_File as the common base class for full emulators and track
|
||||
information-only readers, but implementing the C interface was much
|
||||
simpler if both derived from Music_Emu. User C++ code can still benefit
|
||||
from static checking by using Gme_File where only track information will
|
||||
be accessed.
|
||||
|
||||
Each emulator generally has three components: main emulator, CPU
|
||||
emulator, and sound chip emulator(s). Each component has minimal
|
||||
coupling, so use in a full emulator or stand alone is fairly easy. This
|
||||
modularity really helps reduce complexity. Blip_Buffer helps a lot with
|
||||
simplifying the APU interfaces and implementation.
|
||||
|
||||
The "classic" emulators derive from Classic_Emu, which handles
|
||||
Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
|
||||
output, allowing you to derive a custom buffer that could output each
|
||||
voice to a separate sound channel and do different processing on each.
|
||||
At some point I'm going to implement a better Effects_Buffer that allows
|
||||
individual control of every channel.
|
||||
|
||||
In implementing the C interface, I wanted a way to specify an emulator
|
||||
type that didn't require linking in all the emulators. For each emulator
|
||||
type there is a global object with pointers to functions to create the
|
||||
emulator or a track information reader. The emulator type is thus a
|
||||
pointer to this, which conveniently allows for a NULL value. The user
|
||||
referencing this emulator type object is what ultimately links the
|
||||
emulator in (unless new Foo_Emu is used in C++, of course). This type
|
||||
also serves as a useful substitute for RTTI on older C++ compilers.
|
||||
|
||||
Addendum: I have since added gme_type_list(), which causes all listed
|
||||
emulators to be linked in. To avoid this, I make the list itself
|
||||
editable in blargg_config.h. Having a built-in list allows
|
||||
gme_load_file() to take a path and give back an emulator with the file
|
||||
loaded, which is extremely useful for new users.
|
||||
|
||||
|
||||
Interface conventions
|
||||
----------------------
|
||||
If a function retains a pointer to or replaces the value of an object
|
||||
passed, it takes a pointer so that it will be clear in the caller's
|
||||
source code that care is required.
|
||||
|
||||
Multi-word names have an underscore '_' separator between individual
|
||||
words.
|
||||
|
||||
Functions are named with lowercase words. Functions which perform an
|
||||
action with side-effects are named with a verb phrase (i.e. load, move,
|
||||
run). Functions which return the value of a piece of state are named
|
||||
using a noun phrase (i.e. loaded, moved, running).
|
||||
|
||||
Classes are named with capitalized words. Only the first letter of an
|
||||
acronym is capitalized. Class names are nouns, sometimes suggestive of
|
||||
what they do (i.e. File_Scanner).
|
||||
|
||||
Structure, enumeration, and typedefs to these and built-in types are
|
||||
named using lowercase words with a _t suffix.
|
||||
|
||||
Macros are named with all-uppercase words.
|
||||
|
||||
Internal names which can't be hidden due to technical reasons have an
|
||||
underscore '_' suffix.
|
||||
|
||||
|
||||
Managing Complexity
|
||||
-------------------
|
||||
Complexity has been a factor in most library decisions. Many features
|
||||
have been passed by due to the complexity they would add. Once
|
||||
complexity goes past a certain level, it mentally grasping the library
|
||||
in its entirety, at which point more defects will occur and be hard to
|
||||
find.
|
||||
|
||||
I chose 16-bit signed samples because it seems to be the most common
|
||||
format. Supporting multiple formats would add too much complexity to be
|
||||
worth it. Other formats can be obtained via conversion.
|
||||
|
||||
I've kept interfaces fairly lean, leaving many possible features
|
||||
untapped but easy to add if necessary. For example the classic emulators
|
||||
could have volume and frequency equalization adjusted separately for
|
||||
each channel, since they each have an associated Blip_Synth.
|
||||
|
||||
Source files of 400 lines or less seem to be the best size to limit
|
||||
complexity. In a few cases there is no reasonable way to split longer
|
||||
files, or there is benefit from having the source together in one file.
|
||||
|
||||
|
||||
Preventing Bugs
|
||||
---------------
|
||||
I've done many things to reduce the opportunity for defects. A general
|
||||
principle is to write code so that defects will be as visible as
|
||||
possible. I've used several techniques to achieve this.
|
||||
|
||||
I put assertions at key points where defects seem likely or where
|
||||
corruption due to a defect is likely to be visible. I've also put
|
||||
assertions where violations of the interface are likely. In emulators
|
||||
where I am unsure of exact hardware operation in a particular case, I
|
||||
output a debug-only message noting that this has occurred; many times I
|
||||
haven't implemented a hardware feature because nothing uses it. I've
|
||||
made code brittle where there is no clear reason flexibility; code
|
||||
written to handle every possibility sacrifices quality and reliability
|
||||
to handle vaguely defined situations.
|
||||
|
||||
|
||||
Flexibility through indirection
|
||||
-------------------------------
|
||||
I've tried to allow the most flexibility of modules by using indirection
|
||||
to allow extension by the user. This keeps each module simpler and more
|
||||
focused on its unique task.
|
||||
|
||||
The classic emulators use Multi_Buffer, which potentially allows a
|
||||
separate Blip_Buffer for each channel. This keeps emulators free of
|
||||
typical code to allow output in mono, stereo, panning, etc.
|
||||
|
||||
All emulators use a reader object to access file data, allowing it to be
|
||||
stored in a regular file, compressed archive, memory, or generated
|
||||
on-the-fly. Again, the library can be kept free of the particulars of
|
||||
file access and changes required to support new formats.
|
||||
|
||||
|
||||
Emulators in general
|
||||
--------------------
|
||||
When I wrote the first NES sound emulator, I stored most of the state in
|
||||
an emulator-specific format, with significant redundancy. In the
|
||||
register write function I decoded everything into named variables. I
|
||||
became tired of the verbosity and wanted to more closely model the
|
||||
hardware, so I moved to a style of storing the last written value to
|
||||
each register, along with as little other state as possible, mostly the
|
||||
internal hardware registers. While this involves slightly more
|
||||
recalculation, in most cases the emulation code is of comparable size.
|
||||
It also makes state save/restore (for use in a full emulator) much
|
||||
simpler. Finally, it makes debugging easier since the hardware registers
|
||||
used in emulation are obvious.
|
||||
|
||||
|
||||
CPU Cores
|
||||
---------
|
||||
I've spent lots of time coming up with techniques to optimize the CPU
|
||||
cores. Some of the most important: execute multiple instructions during
|
||||
an emulation call, keep state in local variables to allow register
|
||||
assignment, optimize state representation for most common instructions,
|
||||
defer status flag calculation until actually needed, read program code
|
||||
directly without a call to the memory read function, always pre-fetch
|
||||
the operand byte before decoding instruction, and emulate instructions
|
||||
using common blocks of code.
|
||||
|
||||
I've successfully used Nes_Cpu in a fairly complete NES emulator, and
|
||||
I'd like to make all the CPU emulators suitable for use in emulators. It
|
||||
seems a waste for them to be used only for the small amount of emulation
|
||||
necessary for game music files.
|
||||
|
||||
I debugged the CPU cores by writing a test shell that ran them in
|
||||
parallel with other CPU cores and compared all memory accesses and
|
||||
processor states at each step. This provided good value at little cost.
|
||||
|
||||
The CPU mapping page size is adjustable to allow the best tradeoff
|
||||
between memory/cache usage and handler granularity. The interface allows
|
||||
code to be somewhat independent of the page size.
|
||||
|
||||
I optimize program memory accesses to direct reads rather than calls to
|
||||
the memory read function. My assumption is that it would be difficult to
|
||||
get useful code out of hardware I/O addresses, so no software will
|
||||
intentionally execute out of I/O space. Since the page size can be
|
||||
changed easily, most program memory mapping schemes can be accommodated.
|
||||
This greatly reduces memory access function calls.
|
||||
|
||||
Game_Music_Emu 0.6.0 Design
|
||||
---------------------------
|
||||
This might be slightly out-of-date at times, but will be a big help in
|
||||
understanding the library implementation.
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
The library is essentially a bunch of independent game music file
|
||||
emulators unified with a common interface.
|
||||
|
||||
Gme_File and Music_Emu provide a common interface to the emulators. The
|
||||
virtual functions are protected rather than public to allow pre- and
|
||||
post-processing of arguments and data in one place. This allows the
|
||||
emulator classes to assume that everything is set up properly when
|
||||
starting a track and playing samples.
|
||||
|
||||
All file input is done with the Data_Reader interface. Many derived
|
||||
classes are present, for the usual disk-based file and block of memory,
|
||||
to specialized adaptors for things like reading a subset of data or
|
||||
combining a block of memory with a Data_Reader to the remaining data.
|
||||
This makes the library much more flexible with regard to the source of
|
||||
game music file data. I still added a specialized load_mem() function to
|
||||
have the emulator keep a pointer to data already read in memory, for
|
||||
those formats whose files can be absolutely huge (GYM, some VGMs). This
|
||||
is important if for some reason the caller must load the data ahead of
|
||||
time, but doesn't want the emulator needlessly making a copy.
|
||||
|
||||
Since silence checking and fading are relatively complex, they are kept
|
||||
separate from basic file loading and track information, which are
|
||||
handled in the base class Gme_File. My original intent was to use
|
||||
Gme_File as the common base class for full emulators and track
|
||||
information-only readers, but implementing the C interface was much
|
||||
simpler if both derived from Music_Emu. User C++ code can still benefit
|
||||
from static checking by using Gme_File where only track information will
|
||||
be accessed.
|
||||
|
||||
Each emulator generally has three components: main emulator, CPU
|
||||
emulator, and sound chip emulator(s). Each component has minimal
|
||||
coupling, so use in a full emulator or stand alone is fairly easy. This
|
||||
modularity really helps reduce complexity. Blip_Buffer helps a lot with
|
||||
simplifying the APU interfaces and implementation.
|
||||
|
||||
The "classic" emulators derive from Classic_Emu, which handles
|
||||
Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
|
||||
output, allowing you to derive a custom buffer that could output each
|
||||
voice to a separate sound channel and do different processing on each.
|
||||
At some point I'm going to implement a better Effects_Buffer that allows
|
||||
individual control of every channel.
|
||||
|
||||
In implementing the C interface, I wanted a way to specify an emulator
|
||||
type that didn't require linking in all the emulators. For each emulator
|
||||
type there is a global object with pointers to functions to create the
|
||||
emulator or a track information reader. The emulator type is thus a
|
||||
pointer to this, which conveniently allows for a NULL value. The user
|
||||
referencing this emulator type object is what ultimately links the
|
||||
emulator in (unless new Foo_Emu is used in C++, of course). This type
|
||||
also serves as a useful substitute for RTTI on older C++ compilers.
|
||||
|
||||
Addendum: I have since added gme_type_list(), which causes all listed
|
||||
emulators to be linked in. To avoid this, I make the list itself
|
||||
editable in blargg_config.h. Having a built-in list allows
|
||||
gme_load_file() to take a path and give back an emulator with the file
|
||||
loaded, which is extremely useful for new users.
|
||||
|
||||
|
||||
Interface conventions
|
||||
----------------------
|
||||
If a function retains a pointer to or replaces the value of an object
|
||||
passed, it takes a pointer so that it will be clear in the caller's
|
||||
source code that care is required.
|
||||
|
||||
Multi-word names have an underscore '_' separator between individual
|
||||
words.
|
||||
|
||||
Functions are named with lowercase words. Functions which perform an
|
||||
action with side-effects are named with a verb phrase (i.e. load, move,
|
||||
run). Functions which return the value of a piece of state are named
|
||||
using a noun phrase (i.e. loaded, moved, running).
|
||||
|
||||
Classes are named with capitalized words. Only the first letter of an
|
||||
acronym is capitalized. Class names are nouns, sometimes suggestive of
|
||||
what they do (i.e. File_Scanner).
|
||||
|
||||
Structure, enumeration, and typedefs to these and built-in types are
|
||||
named using lowercase words with a _t suffix.
|
||||
|
||||
Macros are named with all-uppercase words.
|
||||
|
||||
Internal names which can't be hidden due to technical reasons have an
|
||||
underscore '_' suffix.
|
||||
|
||||
|
||||
Managing Complexity
|
||||
-------------------
|
||||
Complexity has been a factor in most library decisions. Many features
|
||||
have been passed by due to the complexity they would add. Once
|
||||
complexity goes past a certain level, it mentally grasping the library
|
||||
in its entirety, at which point more defects will occur and be hard to
|
||||
find.
|
||||
|
||||
I chose 16-bit signed samples because it seems to be the most common
|
||||
format. Supporting multiple formats would add too much complexity to be
|
||||
worth it. Other formats can be obtained via conversion.
|
||||
|
||||
I've kept interfaces fairly lean, leaving many possible features
|
||||
untapped but easy to add if necessary. For example the classic emulators
|
||||
could have volume and frequency equalization adjusted separately for
|
||||
each channel, since they each have an associated Blip_Synth.
|
||||
|
||||
Source files of 400 lines or less seem to be the best size to limit
|
||||
complexity. In a few cases there is no reasonable way to split longer
|
||||
files, or there is benefit from having the source together in one file.
|
||||
|
||||
|
||||
Preventing Bugs
|
||||
---------------
|
||||
I've done many things to reduce the opportunity for defects. A general
|
||||
principle is to write code so that defects will be as visible as
|
||||
possible. I've used several techniques to achieve this.
|
||||
|
||||
I put assertions at key points where defects seem likely or where
|
||||
corruption due to a defect is likely to be visible. I've also put
|
||||
assertions where violations of the interface are likely. In emulators
|
||||
where I am unsure of exact hardware operation in a particular case, I
|
||||
output a debug-only message noting that this has occurred; many times I
|
||||
haven't implemented a hardware feature because nothing uses it. I've
|
||||
made code brittle where there is no clear reason flexibility; code
|
||||
written to handle every possibility sacrifices quality and reliability
|
||||
to handle vaguely defined situations.
|
||||
|
||||
|
||||
Flexibility through indirection
|
||||
-------------------------------
|
||||
I've tried to allow the most flexibility of modules by using indirection
|
||||
to allow extension by the user. This keeps each module simpler and more
|
||||
focused on its unique task.
|
||||
|
||||
The classic emulators use Multi_Buffer, which potentially allows a
|
||||
separate Blip_Buffer for each channel. This keeps emulators free of
|
||||
typical code to allow output in mono, stereo, panning, etc.
|
||||
|
||||
All emulators use a reader object to access file data, allowing it to be
|
||||
stored in a regular file, compressed archive, memory, or generated
|
||||
on-the-fly. Again, the library can be kept free of the particulars of
|
||||
file access and changes required to support new formats.
|
||||
|
||||
|
||||
Emulators in general
|
||||
--------------------
|
||||
When I wrote the first NES sound emulator, I stored most of the state in
|
||||
an emulator-specific format, with significant redundancy. In the
|
||||
register write function I decoded everything into named variables. I
|
||||
became tired of the verbosity and wanted to more closely model the
|
||||
hardware, so I moved to a style of storing the last written value to
|
||||
each register, along with as little other state as possible, mostly the
|
||||
internal hardware registers. While this involves slightly more
|
||||
recalculation, in most cases the emulation code is of comparable size.
|
||||
It also makes state save/restore (for use in a full emulator) much
|
||||
simpler. Finally, it makes debugging easier since the hardware registers
|
||||
used in emulation are obvious.
|
||||
|
||||
|
||||
CPU Cores
|
||||
---------
|
||||
I've spent lots of time coming up with techniques to optimize the CPU
|
||||
cores. Some of the most important: execute multiple instructions during
|
||||
an emulation call, keep state in local variables to allow register
|
||||
assignment, optimize state representation for most common instructions,
|
||||
defer status flag calculation until actually needed, read program code
|
||||
directly without a call to the memory read function, always pre-fetch
|
||||
the operand byte before decoding instruction, and emulate instructions
|
||||
using common blocks of code.
|
||||
|
||||
I've successfully used Nes_Cpu in a fairly complete NES emulator, and
|
||||
I'd like to make all the CPU emulators suitable for use in emulators. It
|
||||
seems a waste for them to be used only for the small amount of emulation
|
||||
necessary for game music files.
|
||||
|
||||
I debugged the CPU cores by writing a test shell that ran them in
|
||||
parallel with other CPU cores and compared all memory accesses and
|
||||
processor states at each step. This provided good value at little cost.
|
||||
|
||||
The CPU mapping page size is adjustable to allow the best tradeoff
|
||||
between memory/cache usage and handler granularity. The interface allows
|
||||
code to be somewhat independent of the page size.
|
||||
|
||||
I optimize program memory accesses to direct reads rather than calls to
|
||||
the memory read function. My assumption is that it would be difficult to
|
||||
get useful code out of hardware I/O addresses, so no software will
|
||||
intentionally execute out of I/O space. Since the page size can be
|
||||
changed easily, most program memory mapping schemes can be accommodated.
|
||||
This greatly reduces memory access function calls.
|
||||
|
||||
+376
-376
@@ -1,376 +1,376 @@
|
||||
Game_Music_Emu 0.6.2
|
||||
--------------------
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Maintainer : Michael Pyne <mpyne@purinchu.net>
|
||||
Website : https://bitbucket.org/mpyne/game-music-emu/
|
||||
Source : https://bitbucket.org/mpyne/game-music-emu/
|
||||
License : GNU Lesser General Public License (LGPL), see LICENSE.txt
|
||||
|
||||
Contents
|
||||
--------
|
||||
* Overview
|
||||
* Error handling
|
||||
* Emulator types
|
||||
* M3U playlist support
|
||||
* Information fields
|
||||
* Track length
|
||||
* Loading file data
|
||||
* Sound parameters
|
||||
* VGM/GYM YM2413 & YM2612 FM sound
|
||||
* Modular construction
|
||||
* Obscure features
|
||||
* Solving problems
|
||||
* Thanks
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
This library can open game music files, play tracks, and read game and
|
||||
track information tags. To play a game music file, do the following:
|
||||
|
||||
* Open the file with gme_open_file()
|
||||
* Start a track with gme_start_track();
|
||||
* Generate samples as needed with gme_play()
|
||||
* Play samples through speaker using your operating system
|
||||
* Delete emulator when done with gme_delete()
|
||||
|
||||
Your code must arrange for the generated samples to be played through
|
||||
the computer's speaker using whatever method your operating system
|
||||
requires.
|
||||
|
||||
There are many additional features available; you can:
|
||||
|
||||
* Determine of the type of a music file without opening it with
|
||||
gme_identify_*()
|
||||
* Load just the file's information tags with gme_info_only
|
||||
* Load from a block of memory rather than a file with gme_load_data()
|
||||
* Arrange for a fade-out at a particular time with gme_set_fade
|
||||
* Find when a track has ended with gme_track_ended()
|
||||
* Seek to a new time in the track with gme_seek()
|
||||
* Load an extended m3u playlist with gme_load_m3u()
|
||||
* Get a list of the voices (channels) and mute them individually with
|
||||
gme_voice_names() and gme_mute_voice()
|
||||
* Change the playback tempo without affecting pitch with gme_set_tempo()
|
||||
* Adjust treble/bass equalization with gme_set_equalizer()
|
||||
* Associate your own data with an emulator and later get it back with
|
||||
gme_set_user_data()
|
||||
* Register a function of yours to be called back when the emulator is
|
||||
deleted with gme_set_user_cleanup()
|
||||
|
||||
Refer to gme.h for a comprehensive summary of features.
|
||||
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
Functions which can fail have a return type of gme_err_t, which is a
|
||||
pointer to an error string (const char*). If a function is successful it
|
||||
returns NULL. Errors that you can easily avoid are checked with debug
|
||||
assertions; gme_err_t return values are only used for genuine run-time
|
||||
errors that can't be easily predicted in advance (out of memory, I/O
|
||||
errors, incompatible file data). Your code should check all error
|
||||
values.
|
||||
|
||||
When loading a music file in the wrong emulator or trying to load a
|
||||
non-music file, gme_wrong_file_type is returned. You can check for this
|
||||
error in C++ like this:
|
||||
|
||||
gme_err_t err = gme_open_file( path, &emu );
|
||||
if ( err == gme_wrong_file_type )
|
||||
...
|
||||
|
||||
To check for minor problems, call gme_warning() to get a string
|
||||
describing the last warning. Your player should allow the user some way
|
||||
of knowing when this is the case, since these minor errors could affect
|
||||
playback. Without this information the user can't solve problems as
|
||||
well. When playing a track, gme_warning() returns minor playback-related
|
||||
problems (major playback problems end the track immediately and set the
|
||||
warning string).
|
||||
|
||||
|
||||
Emulator types
|
||||
--------------
|
||||
The library includes several game music emulators that each support a
|
||||
different file type. Each is identified by a gme_type_t constant defined
|
||||
in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use
|
||||
gme_open_file() or gme_open_data(), the library does the work of
|
||||
determining the file type and creating an appropriate emulator. If you
|
||||
want more control over this process, read on.
|
||||
|
||||
There are two basic ways to identify a game music file's type: look at
|
||||
its file extension, or read the header data. The library includes
|
||||
functions to help with both methods. The first is preferable because it
|
||||
is fast and the most common way to identify files. Sometimes the
|
||||
extension is lost or wrong, so the header must be read.
|
||||
|
||||
Use gme_identify_extension() to find the correct game music type based
|
||||
on a filename. To identify a file based on its extension and header
|
||||
contents, use gme_identify_file(). If you read the header data yourself,
|
||||
use gme_identify_header().
|
||||
|
||||
If you want to remove support for some music types to reduce your
|
||||
executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to
|
||||
support just NSF and GBS, use this:
|
||||
|
||||
#define GME_TYPE_LIST \
|
||||
gme_nsf_type,\
|
||||
gme_gbs_type
|
||||
|
||||
|
||||
M3U playlist support
|
||||
--------------------
|
||||
The library supports playlists in an extended m3u format with
|
||||
gme_load_m3u() to give track names and times to multi-song formats: AY,
|
||||
GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format
|
||||
itself is not well-defined so some m3u files won't work properly
|
||||
(particularly those provided with KSS files). Only m3u files referencing
|
||||
a single file are supported; your code must handle m3u files covering
|
||||
more than one game music file, though it can use the built-in m3u
|
||||
parsing provided by the library.
|
||||
|
||||
|
||||
Information fields
|
||||
------------------
|
||||
Support is provided for the various text fields and length information
|
||||
in a file with gme_track_info(). If you just need track information for
|
||||
a file (for example, building a playlist), use gme_new_info() in place
|
||||
of gme_new_emu(), load the file normally, then you can access the track
|
||||
count and info, but nothing else.
|
||||
|
||||
M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS
|
||||
-------------------------------------------------------
|
||||
Track Count | * * * * * * * * *
|
||||
|
|
||||
System | * * * * * * * * * *
|
||||
|
|
||||
Game | * * * * * * *
|
||||
|
|
||||
Song | * * * * * * *
|
||||
|
|
||||
Author | * * * * * * * *
|
||||
|
|
||||
Copyright | * * * * * * * *
|
||||
|
|
||||
Comment | * * * *
|
||||
|
|
||||
Dumper | * * * *
|
||||
|
|
||||
Length | * * * * * *
|
||||
|
|
||||
Intro Length| * * *
|
||||
|
|
||||
Loop Length | * * *
|
||||
|
||||
As listed above, the HES and KSS file formats don't include a track
|
||||
count, and tracks are often scattered over the 0-255 range, so an m3u
|
||||
playlist for these is a must.
|
||||
|
||||
Unavailable text fields are set to an empty string and times to -1. Your
|
||||
code should be prepared for any combination of available and unavailable
|
||||
fields, as a particular music file might not use all of the supported
|
||||
fields listed above.
|
||||
|
||||
Currently text fields are truncated to 255 characters. Obscure fields of
|
||||
some formats are not currently decoded; contact me if you want one
|
||||
added.
|
||||
|
||||
|
||||
Track length
|
||||
------------
|
||||
The library leaves it up to you as to when to stop playing a track. You
|
||||
can ask for available length information and then tell the library what
|
||||
time it should start fading the track with gme_set_fade(). By default it
|
||||
also continually checks for 6 or more seconds of silence to mark the end
|
||||
of a track. Here is a reasonable algorithm you can use to decide how
|
||||
long to play a track:
|
||||
|
||||
* If the track length is > 0, use it
|
||||
* If the loop length > 0, play for intro + loop * 2
|
||||
* Otherwise, default to 2.5 minutes (150000 msec)
|
||||
|
||||
If you want to play a track longer than normal, be sure the loop length
|
||||
isn't zero. See Music_Player.cpp around line 145 for example code.
|
||||
|
||||
By default, the library skips silence at the beginning of a track. It
|
||||
also continually checks for the end of a non-looping track by watching
|
||||
for 6 seconds of unbroken silence. When doing this is scans *ahead* by
|
||||
several seconds so it can report the end of the track after only one
|
||||
second of silence has actually played. This feature can be disabled with
|
||||
gme_ignore_silence().
|
||||
|
||||
|
||||
Loading file data
|
||||
-----------------
|
||||
The library allows file data to be loaded in many different ways. All
|
||||
load functions return an error which you should check. The following
|
||||
examples assume these variables:
|
||||
|
||||
Music_Emu* emu;
|
||||
gme_err_t error;
|
||||
|
||||
If you're letting the library determine a file's type, you can use
|
||||
either gme_open_file() or gme_open_data():
|
||||
|
||||
error = gme_open_file( pathname, &emu );
|
||||
error = gme_open_data( pointer, size, &emu );
|
||||
|
||||
If you're manually determining file type and using used gme_new_emu() to
|
||||
create an emulator, you can use the following methods of loading:
|
||||
|
||||
* From a block of memory:
|
||||
|
||||
error = gme_load_data( emu, pointer, size );
|
||||
|
||||
* Have library call your function to read data:
|
||||
|
||||
gme_err_t my_read( void* my_data, void* out, long count )
|
||||
{
|
||||
// code that reads 'count' bytes into 'out' buffer
|
||||
// and return 0 if no error
|
||||
}
|
||||
|
||||
error = gme_load_custom( emu, my_read, file_size, my_data );
|
||||
|
||||
|
||||
Sound parameters
|
||||
----------------
|
||||
All emulators support an arbitrary output sampling rate. A rate of 44100
|
||||
Hz should work well on most systems. Since band-limited synthesis is
|
||||
used, a sampling rate above 48000 Hz is not necessary and will actually
|
||||
reduce sound quality and performance.
|
||||
|
||||
All emulators also support adjustable gain, mainly for the purpose of
|
||||
getting consistent volume between different music formats and avoiding
|
||||
excessive modulation. The gain can only be set *before* setting the
|
||||
emulator's sampling rate, so it's not useful as a general volume
|
||||
control. The default gains of emulators are set so that they give
|
||||
generally similar volumes, though some soundtracks are significantly
|
||||
louder or quieter than normal.
|
||||
|
||||
Some emulators support adjustable treble and bass frequency equalization
|
||||
(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer().
|
||||
Parameters are specified using gme_equalizer_t eq = { treble_dB,
|
||||
bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives
|
||||
normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble
|
||||
for an extra crisp sound. Bass_freq sets the frequency where bass
|
||||
response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass,
|
||||
and 15000 Hz removes all bass. For example, the following makes the
|
||||
sound extra-crisp but lacking bass:
|
||||
|
||||
gme_equalizer_t eq = { 5.0, 1000 };
|
||||
gme_set_equalizer( music_emu, &eq );
|
||||
|
||||
Each emulator's equalization defaults to approximate the particular
|
||||
console's sound quality; this default can be determined by calling
|
||||
equalizer() just after creating the emulator. The Music_Emu::tv_eq
|
||||
profile gives sound as if coming from a TV speaker, and some emulators
|
||||
include other profiles for different versions of the system. For
|
||||
example, to use Famicom sound equalization with the NSF emulator, do the
|
||||
following:
|
||||
|
||||
music_emu->set_equalizer( Nsf_Emu::famicom_eq );
|
||||
|
||||
|
||||
VGM/GYM YM2413 & YM2612 FM sound
|
||||
--------------------------------
|
||||
The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
|
||||
chip emulator based on the Gens project. Because this has some
|
||||
inaccuracies, other YM2612 emulators can be used in its place by
|
||||
re-implementing the interface in YM2612_Emu.h. Available on my website
|
||||
is a modified version of MAME's YM2612 emulator, which sounds better in
|
||||
some ways and whose author is still making improvements.
|
||||
|
||||
VGM music files using the YM2413 FM sound chip are also supported, but a
|
||||
YM2413 emulator isn't included with the library due to technical
|
||||
reasons. I have put one of the available YM2413 emulators on my website
|
||||
that can be used directly.
|
||||
|
||||
|
||||
Modular construction
|
||||
--------------------
|
||||
The library is made of many fairly independent modules. If you're using
|
||||
only one music file emulator, you can eliminate many of the library
|
||||
sources from your program. Refer to the files list in readme.txt to get
|
||||
a general idea of what can be removed, and be sure to edit GME_TYPE_LIST
|
||||
(see "Emulator types" above). Post to the forum if you'd like me to put
|
||||
together a smaller version for a particular use, as this only takes me a
|
||||
few minutes to do.
|
||||
|
||||
If you want to use one of the individual sound chip emulators (or CPU
|
||||
cores) in your own console emulator, first check the libraries page on
|
||||
my website since I have released several of them as stand alone
|
||||
libraries with included documentation and examples on their use. If you
|
||||
don't find it as a standalone library, contact me and I'll consider
|
||||
separating it.
|
||||
|
||||
The "classic" sound chips use my Blip_Buffer library, which greatly
|
||||
simplifies their implementation and efficiently handles band-limited
|
||||
synthesis. It is also available as a stand alone library with
|
||||
documentation and many examples.
|
||||
|
||||
|
||||
Obscure features
|
||||
----------------
|
||||
The library's flexibility allows many possibilities. Contact me if you
|
||||
want help implementing ideas or removing limitations.
|
||||
|
||||
* Uses no global/static variables, allowing multiple instances of any
|
||||
emulator. This is useful in a music player if you want to allow
|
||||
simultaneous recording or scanning of other tracks while one is already
|
||||
playing. This will also be useful if your platform disallows global
|
||||
data.
|
||||
|
||||
* Emulators that support a custom sound buffer can have *every* voice
|
||||
routed to a different Blip_Buffer, allowing custom processing on each
|
||||
voice. For example you could record a Game Boy track as a 4-channel
|
||||
sound file.
|
||||
|
||||
* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive
|
||||
synthesis on "classic" emulators, which might help on some really old
|
||||
processors. This significantly lowers sound quality and prevents treble
|
||||
equalization. Try this if your platform's processor isn't fast enough
|
||||
for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces
|
||||
processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth
|
||||
the quality loss.
|
||||
|
||||
|
||||
Solving problems
|
||||
----------------
|
||||
If you're having problems, try the following:
|
||||
|
||||
* If you're getting garbled sound, try this simple siren generator in
|
||||
place of your call to play(). This will quickly tell whether the problem
|
||||
is in the library or in your code.
|
||||
|
||||
static void play_siren( long count, short* out )
|
||||
{
|
||||
static double a, a2;
|
||||
while ( count-- )
|
||||
*out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
|
||||
}
|
||||
|
||||
* Enable debugging support in your environment. This enables assertions
|
||||
and other run-time checks.
|
||||
|
||||
* Turn the compiler's optimizer is off. Sometimes an optimizer generates
|
||||
bad code.
|
||||
|
||||
* If multiple threads are being used, ensure that only one at a time is
|
||||
accessing a given set of objects from the library. This library is not
|
||||
in general thread-safe, though independent objects can be used in
|
||||
separate threads.
|
||||
|
||||
* If all else fails, see if the demos work.
|
||||
|
||||
|
||||
Thanks
|
||||
------
|
||||
Big thanks to Chris Moeller (kode54) for help with library testing and
|
||||
feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
|
||||
for original work on openspc++ that was used when developing Spc_Emu.
|
||||
Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
|
||||
start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz,
|
||||
nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using,
|
||||
and giving feedback for the library in their respective game music
|
||||
players. More recently, Lucas Paul and Michael Pyne have helped nudge the
|
||||
library into a public repository and get its interface more stable for use
|
||||
in shared libraries.
|
||||
Game_Music_Emu 0.6.2
|
||||
--------------------
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Maintainer : Michael Pyne <mpyne@purinchu.net>
|
||||
Website : https://bitbucket.org/mpyne/game-music-emu/
|
||||
Source : https://bitbucket.org/mpyne/game-music-emu/
|
||||
License : GNU Lesser General Public License (LGPL), see LICENSE.txt
|
||||
|
||||
Contents
|
||||
--------
|
||||
* Overview
|
||||
* Error handling
|
||||
* Emulator types
|
||||
* M3U playlist support
|
||||
* Information fields
|
||||
* Track length
|
||||
* Loading file data
|
||||
* Sound parameters
|
||||
* VGM/GYM YM2413 & YM2612 FM sound
|
||||
* Modular construction
|
||||
* Obscure features
|
||||
* Solving problems
|
||||
* Thanks
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
This library can open game music files, play tracks, and read game and
|
||||
track information tags. To play a game music file, do the following:
|
||||
|
||||
* Open the file with gme_open_file()
|
||||
* Start a track with gme_start_track();
|
||||
* Generate samples as needed with gme_play()
|
||||
* Play samples through speaker using your operating system
|
||||
* Delete emulator when done with gme_delete()
|
||||
|
||||
Your code must arrange for the generated samples to be played through
|
||||
the computer's speaker using whatever method your operating system
|
||||
requires.
|
||||
|
||||
There are many additional features available; you can:
|
||||
|
||||
* Determine of the type of a music file without opening it with
|
||||
gme_identify_*()
|
||||
* Load just the file's information tags with gme_info_only
|
||||
* Load from a block of memory rather than a file with gme_load_data()
|
||||
* Arrange for a fade-out at a particular time with gme_set_fade
|
||||
* Find when a track has ended with gme_track_ended()
|
||||
* Seek to a new time in the track with gme_seek()
|
||||
* Load an extended m3u playlist with gme_load_m3u()
|
||||
* Get a list of the voices (channels) and mute them individually with
|
||||
gme_voice_names() and gme_mute_voice()
|
||||
* Change the playback tempo without affecting pitch with gme_set_tempo()
|
||||
* Adjust treble/bass equalization with gme_set_equalizer()
|
||||
* Associate your own data with an emulator and later get it back with
|
||||
gme_set_user_data()
|
||||
* Register a function of yours to be called back when the emulator is
|
||||
deleted with gme_set_user_cleanup()
|
||||
|
||||
Refer to gme.h for a comprehensive summary of features.
|
||||
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
Functions which can fail have a return type of gme_err_t, which is a
|
||||
pointer to an error string (const char*). If a function is successful it
|
||||
returns NULL. Errors that you can easily avoid are checked with debug
|
||||
assertions; gme_err_t return values are only used for genuine run-time
|
||||
errors that can't be easily predicted in advance (out of memory, I/O
|
||||
errors, incompatible file data). Your code should check all error
|
||||
values.
|
||||
|
||||
When loading a music file in the wrong emulator or trying to load a
|
||||
non-music file, gme_wrong_file_type is returned. You can check for this
|
||||
error in C++ like this:
|
||||
|
||||
gme_err_t err = gme_open_file( path, &emu );
|
||||
if ( err == gme_wrong_file_type )
|
||||
...
|
||||
|
||||
To check for minor problems, call gme_warning() to get a string
|
||||
describing the last warning. Your player should allow the user some way
|
||||
of knowing when this is the case, since these minor errors could affect
|
||||
playback. Without this information the user can't solve problems as
|
||||
well. When playing a track, gme_warning() returns minor playback-related
|
||||
problems (major playback problems end the track immediately and set the
|
||||
warning string).
|
||||
|
||||
|
||||
Emulator types
|
||||
--------------
|
||||
The library includes several game music emulators that each support a
|
||||
different file type. Each is identified by a gme_type_t constant defined
|
||||
in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use
|
||||
gme_open_file() or gme_open_data(), the library does the work of
|
||||
determining the file type and creating an appropriate emulator. If you
|
||||
want more control over this process, read on.
|
||||
|
||||
There are two basic ways to identify a game music file's type: look at
|
||||
its file extension, or read the header data. The library includes
|
||||
functions to help with both methods. The first is preferable because it
|
||||
is fast and the most common way to identify files. Sometimes the
|
||||
extension is lost or wrong, so the header must be read.
|
||||
|
||||
Use gme_identify_extension() to find the correct game music type based
|
||||
on a filename. To identify a file based on its extension and header
|
||||
contents, use gme_identify_file(). If you read the header data yourself,
|
||||
use gme_identify_header().
|
||||
|
||||
If you want to remove support for some music types to reduce your
|
||||
executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to
|
||||
support just NSF and GBS, use this:
|
||||
|
||||
#define GME_TYPE_LIST \
|
||||
gme_nsf_type,\
|
||||
gme_gbs_type
|
||||
|
||||
|
||||
M3U playlist support
|
||||
--------------------
|
||||
The library supports playlists in an extended m3u format with
|
||||
gme_load_m3u() to give track names and times to multi-song formats: AY,
|
||||
GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format
|
||||
itself is not well-defined so some m3u files won't work properly
|
||||
(particularly those provided with KSS files). Only m3u files referencing
|
||||
a single file are supported; your code must handle m3u files covering
|
||||
more than one game music file, though it can use the built-in m3u
|
||||
parsing provided by the library.
|
||||
|
||||
|
||||
Information fields
|
||||
------------------
|
||||
Support is provided for the various text fields and length information
|
||||
in a file with gme_track_info(). If you just need track information for
|
||||
a file (for example, building a playlist), use gme_new_info() in place
|
||||
of gme_new_emu(), load the file normally, then you can access the track
|
||||
count and info, but nothing else.
|
||||
|
||||
M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS
|
||||
-------------------------------------------------------
|
||||
Track Count | * * * * * * * * *
|
||||
|
|
||||
System | * * * * * * * * * *
|
||||
|
|
||||
Game | * * * * * * *
|
||||
|
|
||||
Song | * * * * * * *
|
||||
|
|
||||
Author | * * * * * * * *
|
||||
|
|
||||
Copyright | * * * * * * * *
|
||||
|
|
||||
Comment | * * * *
|
||||
|
|
||||
Dumper | * * * *
|
||||
|
|
||||
Length | * * * * * *
|
||||
|
|
||||
Intro Length| * * *
|
||||
|
|
||||
Loop Length | * * *
|
||||
|
||||
As listed above, the HES and KSS file formats don't include a track
|
||||
count, and tracks are often scattered over the 0-255 range, so an m3u
|
||||
playlist for these is a must.
|
||||
|
||||
Unavailable text fields are set to an empty string and times to -1. Your
|
||||
code should be prepared for any combination of available and unavailable
|
||||
fields, as a particular music file might not use all of the supported
|
||||
fields listed above.
|
||||
|
||||
Currently text fields are truncated to 255 characters. Obscure fields of
|
||||
some formats are not currently decoded; contact me if you want one
|
||||
added.
|
||||
|
||||
|
||||
Track length
|
||||
------------
|
||||
The library leaves it up to you as to when to stop playing a track. You
|
||||
can ask for available length information and then tell the library what
|
||||
time it should start fading the track with gme_set_fade(). By default it
|
||||
also continually checks for 6 or more seconds of silence to mark the end
|
||||
of a track. Here is a reasonable algorithm you can use to decide how
|
||||
long to play a track:
|
||||
|
||||
* If the track length is > 0, use it
|
||||
* If the loop length > 0, play for intro + loop * 2
|
||||
* Otherwise, default to 2.5 minutes (150000 msec)
|
||||
|
||||
If you want to play a track longer than normal, be sure the loop length
|
||||
isn't zero. See Music_Player.cpp around line 145 for example code.
|
||||
|
||||
By default, the library skips silence at the beginning of a track. It
|
||||
also continually checks for the end of a non-looping track by watching
|
||||
for 6 seconds of unbroken silence. When doing this is scans *ahead* by
|
||||
several seconds so it can report the end of the track after only one
|
||||
second of silence has actually played. This feature can be disabled with
|
||||
gme_ignore_silence().
|
||||
|
||||
|
||||
Loading file data
|
||||
-----------------
|
||||
The library allows file data to be loaded in many different ways. All
|
||||
load functions return an error which you should check. The following
|
||||
examples assume these variables:
|
||||
|
||||
Music_Emu* emu;
|
||||
gme_err_t error;
|
||||
|
||||
If you're letting the library determine a file's type, you can use
|
||||
either gme_open_file() or gme_open_data():
|
||||
|
||||
error = gme_open_file( pathname, &emu );
|
||||
error = gme_open_data( pointer, size, &emu );
|
||||
|
||||
If you're manually determining file type and using used gme_new_emu() to
|
||||
create an emulator, you can use the following methods of loading:
|
||||
|
||||
* From a block of memory:
|
||||
|
||||
error = gme_load_data( emu, pointer, size );
|
||||
|
||||
* Have library call your function to read data:
|
||||
|
||||
gme_err_t my_read( void* my_data, void* out, long count )
|
||||
{
|
||||
// code that reads 'count' bytes into 'out' buffer
|
||||
// and return 0 if no error
|
||||
}
|
||||
|
||||
error = gme_load_custom( emu, my_read, file_size, my_data );
|
||||
|
||||
|
||||
Sound parameters
|
||||
----------------
|
||||
All emulators support an arbitrary output sampling rate. A rate of 44100
|
||||
Hz should work well on most systems. Since band-limited synthesis is
|
||||
used, a sampling rate above 48000 Hz is not necessary and will actually
|
||||
reduce sound quality and performance.
|
||||
|
||||
All emulators also support adjustable gain, mainly for the purpose of
|
||||
getting consistent volume between different music formats and avoiding
|
||||
excessive modulation. The gain can only be set *before* setting the
|
||||
emulator's sampling rate, so it's not useful as a general volume
|
||||
control. The default gains of emulators are set so that they give
|
||||
generally similar volumes, though some soundtracks are significantly
|
||||
louder or quieter than normal.
|
||||
|
||||
Some emulators support adjustable treble and bass frequency equalization
|
||||
(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer().
|
||||
Parameters are specified using gme_equalizer_t eq = { treble_dB,
|
||||
bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives
|
||||
normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble
|
||||
for an extra crisp sound. Bass_freq sets the frequency where bass
|
||||
response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass,
|
||||
and 15000 Hz removes all bass. For example, the following makes the
|
||||
sound extra-crisp but lacking bass:
|
||||
|
||||
gme_equalizer_t eq = { 5.0, 1000 };
|
||||
gme_set_equalizer( music_emu, &eq );
|
||||
|
||||
Each emulator's equalization defaults to approximate the particular
|
||||
console's sound quality; this default can be determined by calling
|
||||
equalizer() just after creating the emulator. The Music_Emu::tv_eq
|
||||
profile gives sound as if coming from a TV speaker, and some emulators
|
||||
include other profiles for different versions of the system. For
|
||||
example, to use Famicom sound equalization with the NSF emulator, do the
|
||||
following:
|
||||
|
||||
music_emu->set_equalizer( Nsf_Emu::famicom_eq );
|
||||
|
||||
|
||||
VGM/GYM YM2413 & YM2612 FM sound
|
||||
--------------------------------
|
||||
The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
|
||||
chip emulator based on the Gens project. Because this has some
|
||||
inaccuracies, other YM2612 emulators can be used in its place by
|
||||
re-implementing the interface in YM2612_Emu.h. Available on my website
|
||||
is a modified version of MAME's YM2612 emulator, which sounds better in
|
||||
some ways and whose author is still making improvements.
|
||||
|
||||
VGM music files using the YM2413 FM sound chip are also supported, but a
|
||||
YM2413 emulator isn't included with the library due to technical
|
||||
reasons. I have put one of the available YM2413 emulators on my website
|
||||
that can be used directly.
|
||||
|
||||
|
||||
Modular construction
|
||||
--------------------
|
||||
The library is made of many fairly independent modules. If you're using
|
||||
only one music file emulator, you can eliminate many of the library
|
||||
sources from your program. Refer to the files list in readme.txt to get
|
||||
a general idea of what can be removed, and be sure to edit GME_TYPE_LIST
|
||||
(see "Emulator types" above). Post to the forum if you'd like me to put
|
||||
together a smaller version for a particular use, as this only takes me a
|
||||
few minutes to do.
|
||||
|
||||
If you want to use one of the individual sound chip emulators (or CPU
|
||||
cores) in your own console emulator, first check the libraries page on
|
||||
my website since I have released several of them as stand alone
|
||||
libraries with included documentation and examples on their use. If you
|
||||
don't find it as a standalone library, contact me and I'll consider
|
||||
separating it.
|
||||
|
||||
The "classic" sound chips use my Blip_Buffer library, which greatly
|
||||
simplifies their implementation and efficiently handles band-limited
|
||||
synthesis. It is also available as a stand alone library with
|
||||
documentation and many examples.
|
||||
|
||||
|
||||
Obscure features
|
||||
----------------
|
||||
The library's flexibility allows many possibilities. Contact me if you
|
||||
want help implementing ideas or removing limitations.
|
||||
|
||||
* Uses no global/static variables, allowing multiple instances of any
|
||||
emulator. This is useful in a music player if you want to allow
|
||||
simultaneous recording or scanning of other tracks while one is already
|
||||
playing. This will also be useful if your platform disallows global
|
||||
data.
|
||||
|
||||
* Emulators that support a custom sound buffer can have *every* voice
|
||||
routed to a different Blip_Buffer, allowing custom processing on each
|
||||
voice. For example you could record a Game Boy track as a 4-channel
|
||||
sound file.
|
||||
|
||||
* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive
|
||||
synthesis on "classic" emulators, which might help on some really old
|
||||
processors. This significantly lowers sound quality and prevents treble
|
||||
equalization. Try this if your platform's processor isn't fast enough
|
||||
for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces
|
||||
processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth
|
||||
the quality loss.
|
||||
|
||||
|
||||
Solving problems
|
||||
----------------
|
||||
If you're having problems, try the following:
|
||||
|
||||
* If you're getting garbled sound, try this simple siren generator in
|
||||
place of your call to play(). This will quickly tell whether the problem
|
||||
is in the library or in your code.
|
||||
|
||||
static void play_siren( long count, short* out )
|
||||
{
|
||||
static double a, a2;
|
||||
while ( count-- )
|
||||
*out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
|
||||
}
|
||||
|
||||
* Enable debugging support in your environment. This enables assertions
|
||||
and other run-time checks.
|
||||
|
||||
* Turn the compiler's optimizer is off. Sometimes an optimizer generates
|
||||
bad code.
|
||||
|
||||
* If multiple threads are being used, ensure that only one at a time is
|
||||
accessing a given set of objects from the library. This library is not
|
||||
in general thread-safe, though independent objects can be used in
|
||||
separate threads.
|
||||
|
||||
* If all else fails, see if the demos work.
|
||||
|
||||
|
||||
Thanks
|
||||
------
|
||||
Big thanks to Chris Moeller (kode54) for help with library testing and
|
||||
feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
|
||||
for original work on openspc++ that was used when developing Spc_Emu.
|
||||
Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
|
||||
start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz,
|
||||
nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using,
|
||||
and giving feedback for the library in their respective game music
|
||||
players. More recently, Lucas Paul and Michael Pyne have helped nudge the
|
||||
library into a public repository and get its interface more stable for use
|
||||
in shared libraries.
|
||||
@@ -1,89 +0,0 @@
|
||||
/* Included at the beginning of library source files, after all other #include lines.
|
||||
* Sets up helpful macros and services used in my source code. They don't need
|
||||
* module an annoying module prefix on their names since they are defined after
|
||||
* all other #include lines. */
|
||||
|
||||
#ifndef BLARGG_SOURCE_H
|
||||
#define BLARGG_SOURCE_H
|
||||
|
||||
/* If debugging is enabled, abort program if expr is false. Meant for checking
|
||||
* internal state and consistency. A failed assertion indicates a bug in the module.
|
||||
* void assert( bool expr ); */
|
||||
#include <assert.h>
|
||||
|
||||
/* If debugging is enabled and expr is false, abort program. Meant for checking
|
||||
* caller-supplied parameters and operations that are outside the control of the
|
||||
* module. A failed requirement indicates a bug outside the module.
|
||||
* void require( bool expr ); */
|
||||
#undef require
|
||||
#define require( expr ) assert( expr )
|
||||
|
||||
/* Use to provide hints to compiler for optimized code layout in situations where we
|
||||
* can almost always expect a conditional to go one way or the other. Should only be
|
||||
* used in situations where an unexpected branch is truly exceptional though! */
|
||||
#undef likely
|
||||
#undef unlikely
|
||||
#ifdef __GNUC__
|
||||
#define likely( x ) __builtin_expect(x, 1)
|
||||
#define unlikely( x ) __builtin_expect(x, 0)
|
||||
#else
|
||||
#define likely( x ) (x)
|
||||
#define unlikely( x ) (x)
|
||||
#endif
|
||||
|
||||
/* Like printf() except output goes to debug log file. Might be defined to do
|
||||
* nothing (not even evaluate its arguments).
|
||||
* void debug_printf( const char* format, ... ); */
|
||||
#if defined(__cplusplus)
|
||||
static inline void blargg_dprintf_( const char* fmt_str, ... ) { (void) fmt_str; }
|
||||
#undef debug_printf
|
||||
#define debug_printf (1) ? (void) 0 : blargg_dprintf_
|
||||
#endif
|
||||
|
||||
/* If enabled, evaluate expr and if false, make debug log entry with source file
|
||||
* and line. Meant for finding situations that should be examined further, but that
|
||||
* don't indicate a problem. In all cases, execution continues normally. */
|
||||
#undef check
|
||||
#define check( expr ) ((void) 0)
|
||||
|
||||
/* If expr yields error string, return it from current function, otherwise continue. */
|
||||
#undef RETURN_ERR
|
||||
#define RETURN_ERR( expr ) do { \
|
||||
blargg_err_t blargg_return_err_ = (expr); \
|
||||
if ( blargg_return_err_ ) return blargg_return_err_; \
|
||||
} while ( 0 )
|
||||
|
||||
/* If ptr is 0, return out of memory error string. */
|
||||
#undef CHECK_ALLOC
|
||||
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
|
||||
|
||||
/* TODO: good idea? bad idea? */
|
||||
#undef byte
|
||||
#define byte byte_
|
||||
typedef unsigned char byte;
|
||||
|
||||
/* Setup compiler defines useful for exporting required public API symbols in gme.cpp */
|
||||
#ifndef BLARGG_EXPORT
|
||||
#if defined (_WIN32)
|
||||
#if defined(BLARGG_BUILD_DLL)
|
||||
#define BLARGG_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define BLARGG_EXPORT /* Leave blank: friendly with both static and shared linking */
|
||||
#endif
|
||||
#elif defined (LIBGME_VISIBILITY) && defined(__cplusplus)
|
||||
#define BLARGG_EXPORT __attribute__((visibility ("default")))
|
||||
#else
|
||||
#define BLARGG_EXPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* deprecated */
|
||||
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
|
||||
#define BLARGG_RETURN_ERR RETURN_ERR
|
||||
|
||||
/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check */
|
||||
#ifdef BLARGG_SOURCE_BEGIN
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
+504
-504
File diff suppressed because it is too large.
Load diff
+231
-231
@@ -1,231 +1,231 @@
|
||||
Game_Music_Emu 0.6.2: Game Music Emulators
|
||||
------------------------------------------
|
||||
Game_Music_Emu is a collection of video game music file emulators that
|
||||
support the following formats and systems:
|
||||
|
||||
AY ZX Spectrum/Amstrad CPC
|
||||
GBS Nintendo Game Boy
|
||||
GYM Sega Genesis/Mega Drive
|
||||
HES NEC TurboGrafx-16/PC Engine
|
||||
KSS MSX Home Computer/other Z80 systems (doesn't support FM sound)
|
||||
NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound)
|
||||
SAP Atari systems using POKEY sound chip
|
||||
SPC Super Nintendo/Super Famicom
|
||||
VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro
|
||||
|
||||
Features:
|
||||
* C interface for use in C, C++, and other compatible languages
|
||||
* High emphasis has been placed on making the library very easy to use
|
||||
* One set of common functions work with all emulators the same way
|
||||
* Several code examples, including music player using SDL
|
||||
* Portable code for use on any system with modern or older C++ compilers
|
||||
* Adjustable output sample rate using quality band-limited resampling
|
||||
* Uniform access to text information fields and track timing information
|
||||
* End-of-track fading and automatic look ahead silence detection
|
||||
* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM
|
||||
* Tempo can be adjusted and individual voices can be muted while playing
|
||||
* Can read music data from file, memory, or custom reader function/class
|
||||
* Can access track information without having to load into full emulator
|
||||
* M3U track listing support for multi-track formats
|
||||
* Modular design allows elimination of unneeded emulators/features
|
||||
|
||||
This library has been used in game music players for Windows, Linux on
|
||||
several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
|
||||
GP2X, and Nintendo DS.
|
||||
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
|
||||
License: GNU Lesser General Public License (LGPL)
|
||||
|
||||
Current Maintainer: Michael Pyne <mpyne@purinchu.net>
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
|
||||
all source files in gme/.
|
||||
|
||||
Or, if you have CMake 2.6 or later, execute at a command prompt (from the
|
||||
extracted source directory):
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ # <-- Pass any needed CMake flags here
|
||||
make # To build the library
|
||||
cd demo
|
||||
make # To build the demo itself
|
||||
|
||||
Be sure "test.nsf" is in the same directory as the demo program. Running it
|
||||
should generate the recording "out.wav".
|
||||
|
||||
You can use "make install" to install the library. To choose where to install
|
||||
the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
|
||||
(and replace /usr/local with the base path you wish to use). Alternately, you
|
||||
can specify the base path to install to when you run "make install" by passing
|
||||
'DESTDIR=/usr/local' on the make install command line (again, replace
|
||||
/usr/local as appropriate).
|
||||
|
||||
To build a static library instead of shared (the default), pass
|
||||
-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
|
||||
|
||||
A slightly more extensive demo application is available in the player/
|
||||
directory. It requires SDL to build.
|
||||
|
||||
Read gme.txt for more information. Post to the discussion forum for
|
||||
assistance.
|
||||
|
||||
Files
|
||||
-----
|
||||
gme.txt General notes about the library
|
||||
changes.txt Changes made since previous releases
|
||||
design.txt Library design notes
|
||||
license.txt GNU Lesser General Public License
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
test.nsf Test file for NSF emulator
|
||||
test.m3u Test m3u playlist for features.c demo
|
||||
|
||||
demo/
|
||||
basics.c Records NSF file to wave sound file
|
||||
features.c Demonstrates many additional features
|
||||
Wave_Writer.h WAVE sound file writer used for demo output
|
||||
Wave_Writer.cpp
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
player/ Player using the SDL multimedia library
|
||||
player.cpp Simple music player with waveform display
|
||||
Music_Player.cpp Stand alone player for background music
|
||||
Music_Player.h
|
||||
Audio_Scope.cpp Audio waveform scope
|
||||
Audio_Scope.h
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
gme/
|
||||
blargg_config.h Library configuration (modify this file as needed)
|
||||
|
||||
gme.h Library interface header file
|
||||
gme.cpp
|
||||
|
||||
Ay_Emu.h ZX Spectrum AY emulator
|
||||
Ay_Emu.cpp
|
||||
Ay_Apu.cpp
|
||||
Ay_Apu.h
|
||||
Ay_Cpu.cpp
|
||||
Ay_Cpu.h
|
||||
|
||||
Gbs_Emu.h Nintendo Game Boy GBS emulator
|
||||
Gbs_Emu.cpp
|
||||
Gb_Apu.cpp
|
||||
Gb_Apu.h
|
||||
Gb_Cpu.cpp
|
||||
Gb_Cpu.h
|
||||
gb_cpu_io.h
|
||||
Gb_Oscs.cpp
|
||||
Gb_Oscs.h
|
||||
|
||||
Hes_Emu.h TurboGrafx-16/PC Engine HES emulator
|
||||
Hes_Apu.cpp
|
||||
Hes_Apu.h
|
||||
Hes_Cpu.cpp
|
||||
Hes_Cpu.h
|
||||
hes_cpu_io.h
|
||||
Hes_Emu.cpp
|
||||
|
||||
Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator
|
||||
Kss_Emu.cpp
|
||||
Kss_Cpu.cpp
|
||||
Kss_Cpu.h
|
||||
Kss_Scc_Apu.cpp
|
||||
Kss_Scc_Apu.h
|
||||
Ay_Apu.h
|
||||
Ay_Apu.cpp
|
||||
Sms_Apu.h
|
||||
Sms_Apu.cpp
|
||||
Sms_Oscs.h
|
||||
|
||||
Nsf_Emu.h Nintendo NES NSF/NSFE emulator
|
||||
Nsf_Emu.cpp
|
||||
Nes_Apu.cpp
|
||||
Nes_Apu.h
|
||||
Nes_Cpu.cpp
|
||||
Nes_Cpu.h
|
||||
nes_cpu_io.h
|
||||
Nes_Oscs.cpp
|
||||
Nes_Oscs.h
|
||||
Nes_Fme7_Apu.cpp
|
||||
Nes_Fme7_Apu.h
|
||||
Nes_Namco_Apu.cpp
|
||||
Nes_Namco_Apu.h
|
||||
Nes_Vrc6_Apu.cpp
|
||||
Nes_Vrc6_Apu.h
|
||||
Nsfe_Emu.h NSFE support
|
||||
Nsfe_Emu.cpp
|
||||
|
||||
Spc_Emu.h Super Nintendo SPC emulator
|
||||
Spc_Emu.cpp
|
||||
Snes_Spc.cpp
|
||||
Snes_Spc.h
|
||||
Spc_Cpu.cpp
|
||||
Spc_Cpu.h
|
||||
Spc_Dsp.cpp
|
||||
Spc_Dsp.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
Sap_Emu.h Atari SAP emulator
|
||||
Sap_Emu.cpp
|
||||
Sap_Apu.cpp
|
||||
Sap_Apu.h
|
||||
Sap_Cpu.cpp
|
||||
Sap_Cpu.h
|
||||
sap_cpu_io.h
|
||||
|
||||
Vgm_Emu.h Sega VGM emulator
|
||||
Vgm_Emu_Impl.cpp
|
||||
Vgm_Emu_Impl.h
|
||||
Vgm_Emu.cpp
|
||||
Ym2413_Emu.cpp
|
||||
Ym2413_Emu.h
|
||||
Gym_Emu.h Sega Genesis GYM emulator
|
||||
Gym_Emu.cpp
|
||||
Sms_Apu.cpp Common Sega emulator files
|
||||
Sms_Apu.h
|
||||
Sms_Oscs.h
|
||||
Ym2612_Emu.cpp
|
||||
Ym2612_Emu.h
|
||||
Dual_Resampler.cpp
|
||||
Dual_Resampler.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
M3u_Playlist.h M3U playlist support
|
||||
M3u_Playlist.cpp
|
||||
|
||||
Effects_Buffer.h Sound buffer with stereo echo and panning
|
||||
Effects_Buffer.cpp
|
||||
|
||||
blargg_common.h Common files needed by all emulators
|
||||
blargg_endian.h
|
||||
blargg_source.h
|
||||
Blip_Buffer.cpp
|
||||
Blip_Buffer.h
|
||||
Gme_File.h
|
||||
Gme_File.cpp
|
||||
Music_Emu.h
|
||||
Music_Emu.cpp
|
||||
Classic_Emu.h
|
||||
Classic_Emu.cpp
|
||||
Multi_Buffer.h
|
||||
Multi_Buffer.cpp
|
||||
Data_Reader.h
|
||||
Data_Reader.cpp
|
||||
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
|
||||
Legal
|
||||
-----
|
||||
Game_Music_Emu library copyright (C) 2003-2009 Shay Green.
|
||||
Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
|
||||
|
||||
--
|
||||
Shay Green <gblargg@gmail.com>
|
||||
Game_Music_Emu 0.6.2: Game Music Emulators
|
||||
------------------------------------------
|
||||
Game_Music_Emu is a collection of video game music file emulators that
|
||||
support the following formats and systems:
|
||||
|
||||
AY ZX Spectrum/Amstrad CPC
|
||||
GBS Nintendo Game Boy
|
||||
GYM Sega Genesis/Mega Drive
|
||||
HES NEC TurboGrafx-16/PC Engine
|
||||
KSS MSX Home Computer/other Z80 systems (doesn't support FM sound)
|
||||
NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound)
|
||||
SAP Atari systems using POKEY sound chip
|
||||
SPC Super Nintendo/Super Famicom
|
||||
VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro
|
||||
|
||||
Features:
|
||||
* C interface for use in C, C++, and other compatible languages
|
||||
* High emphasis has been placed on making the library very easy to use
|
||||
* One set of common functions work with all emulators the same way
|
||||
* Several code examples, including music player using SDL
|
||||
* Portable code for use on any system with modern or older C++ compilers
|
||||
* Adjustable output sample rate using quality band-limited resampling
|
||||
* Uniform access to text information fields and track timing information
|
||||
* End-of-track fading and automatic look ahead silence detection
|
||||
* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM
|
||||
* Tempo can be adjusted and individual voices can be muted while playing
|
||||
* Can read music data from file, memory, or custom reader function/class
|
||||
* Can access track information without having to load into full emulator
|
||||
* M3U track listing support for multi-track formats
|
||||
* Modular design allows elimination of unneeded emulators/features
|
||||
|
||||
This library has been used in game music players for Windows, Linux on
|
||||
several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
|
||||
GP2X, and Nintendo DS.
|
||||
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
|
||||
License: GNU Lesser General Public License (LGPL)
|
||||
|
||||
Current Maintainer: Michael Pyne <mpyne@purinchu.net>
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
|
||||
all source files in gme/.
|
||||
|
||||
Or, if you have CMake 2.6 or later, execute at a command prompt (from the
|
||||
extracted source directory):
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ # <-- Pass any needed CMake flags here
|
||||
make # To build the library
|
||||
cd demo
|
||||
make # To build the demo itself
|
||||
|
||||
Be sure "test.nsf" is in the same directory as the demo program. Running it
|
||||
should generate the recording "out.wav".
|
||||
|
||||
You can use "make install" to install the library. To choose where to install
|
||||
the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
|
||||
(and replace /usr/local with the base path you wish to use). Alternately, you
|
||||
can specify the base path to install to when you run "make install" by passing
|
||||
'DESTDIR=/usr/local' on the make install command line (again, replace
|
||||
/usr/local as appropriate).
|
||||
|
||||
To build a static library instead of shared (the default), pass
|
||||
-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
|
||||
|
||||
A slightly more extensive demo application is available in the player/
|
||||
directory. It requires SDL to build.
|
||||
|
||||
Read gme.txt for more information. Post to the discussion forum for
|
||||
assistance.
|
||||
|
||||
Files
|
||||
-----
|
||||
gme.txt General notes about the library
|
||||
changes.txt Changes made since previous releases
|
||||
design.txt Library design notes
|
||||
license.txt GNU Lesser General Public License
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
test.nsf Test file for NSF emulator
|
||||
test.m3u Test m3u playlist for features.c demo
|
||||
|
||||
demo/
|
||||
basics.c Records NSF file to wave sound file
|
||||
features.c Demonstrates many additional features
|
||||
Wave_Writer.h WAVE sound file writer used for demo output
|
||||
Wave_Writer.cpp
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
player/ Player using the SDL multimedia library
|
||||
player.cpp Simple music player with waveform display
|
||||
Music_Player.cpp Stand alone player for background music
|
||||
Music_Player.h
|
||||
Audio_Scope.cpp Audio waveform scope
|
||||
Audio_Scope.h
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
gme/
|
||||
blargg_config.h Library configuration (modify this file as needed)
|
||||
|
||||
gme.h Library interface header file
|
||||
gme.cpp
|
||||
|
||||
Ay_Emu.h ZX Spectrum AY emulator
|
||||
Ay_Emu.cpp
|
||||
Ay_Apu.cpp
|
||||
Ay_Apu.h
|
||||
Ay_Cpu.cpp
|
||||
Ay_Cpu.h
|
||||
|
||||
Gbs_Emu.h Nintendo Game Boy GBS emulator
|
||||
Gbs_Emu.cpp
|
||||
Gb_Apu.cpp
|
||||
Gb_Apu.h
|
||||
Gb_Cpu.cpp
|
||||
Gb_Cpu.h
|
||||
gb_cpu_io.h
|
||||
Gb_Oscs.cpp
|
||||
Gb_Oscs.h
|
||||
|
||||
Hes_Emu.h TurboGrafx-16/PC Engine HES emulator
|
||||
Hes_Apu.cpp
|
||||
Hes_Apu.h
|
||||
Hes_Cpu.cpp
|
||||
Hes_Cpu.h
|
||||
hes_cpu_io.h
|
||||
Hes_Emu.cpp
|
||||
|
||||
Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator
|
||||
Kss_Emu.cpp
|
||||
Kss_Cpu.cpp
|
||||
Kss_Cpu.h
|
||||
Kss_Scc_Apu.cpp
|
||||
Kss_Scc_Apu.h
|
||||
Ay_Apu.h
|
||||
Ay_Apu.cpp
|
||||
Sms_Apu.h
|
||||
Sms_Apu.cpp
|
||||
Sms_Oscs.h
|
||||
|
||||
Nsf_Emu.h Nintendo NES NSF/NSFE emulator
|
||||
Nsf_Emu.cpp
|
||||
Nes_Apu.cpp
|
||||
Nes_Apu.h
|
||||
Nes_Cpu.cpp
|
||||
Nes_Cpu.h
|
||||
nes_cpu_io.h
|
||||
Nes_Oscs.cpp
|
||||
Nes_Oscs.h
|
||||
Nes_Fme7_Apu.cpp
|
||||
Nes_Fme7_Apu.h
|
||||
Nes_Namco_Apu.cpp
|
||||
Nes_Namco_Apu.h
|
||||
Nes_Vrc6_Apu.cpp
|
||||
Nes_Vrc6_Apu.h
|
||||
Nsfe_Emu.h NSFE support
|
||||
Nsfe_Emu.cpp
|
||||
|
||||
Spc_Emu.h Super Nintendo SPC emulator
|
||||
Spc_Emu.cpp
|
||||
Snes_Spc.cpp
|
||||
Snes_Spc.h
|
||||
Spc_Cpu.cpp
|
||||
Spc_Cpu.h
|
||||
Spc_Dsp.cpp
|
||||
Spc_Dsp.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
Sap_Emu.h Atari SAP emulator
|
||||
Sap_Emu.cpp
|
||||
Sap_Apu.cpp
|
||||
Sap_Apu.h
|
||||
Sap_Cpu.cpp
|
||||
Sap_Cpu.h
|
||||
sap_cpu_io.h
|
||||
|
||||
Vgm_Emu.h Sega VGM emulator
|
||||
Vgm_Emu_Impl.cpp
|
||||
Vgm_Emu_Impl.h
|
||||
Vgm_Emu.cpp
|
||||
Ym2413_Emu.cpp
|
||||
Ym2413_Emu.h
|
||||
Gym_Emu.h Sega Genesis GYM emulator
|
||||
Gym_Emu.cpp
|
||||
Sms_Apu.cpp Common Sega emulator files
|
||||
Sms_Apu.h
|
||||
Sms_Oscs.h
|
||||
Ym2612_Emu.cpp
|
||||
Ym2612_Emu.h
|
||||
Dual_Resampler.cpp
|
||||
Dual_Resampler.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
M3u_Playlist.h M3U playlist support
|
||||
M3u_Playlist.cpp
|
||||
|
||||
Effects_Buffer.h Sound buffer with stereo echo and panning
|
||||
Effects_Buffer.cpp
|
||||
|
||||
blargg_common.h Common files needed by all emulators
|
||||
blargg_endian.h
|
||||
blargg_source.h
|
||||
Blip_Buffer.cpp
|
||||
Blip_Buffer.h
|
||||
Gme_File.h
|
||||
Gme_File.cpp
|
||||
Music_Emu.h
|
||||
Music_Emu.cpp
|
||||
Classic_Emu.h
|
||||
Classic_Emu.cpp
|
||||
Multi_Buffer.h
|
||||
Multi_Buffer.cpp
|
||||
Data_Reader.h
|
||||
Data_Reader.cpp
|
||||
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
|
||||
Legal
|
||||
-----
|
||||
Game_Music_Emu library copyright (C) 2003-2009 Shay Green.
|
||||
Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
|
||||
|
||||
--
|
||||
Shay Green <gblargg@gmail.com>
|
||||
@@ -1,5 +1,5 @@
|
||||
/* backtrace.h -- Public header file for stack backtrace library.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Copyright (C) 2012-2020 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -54,14 +54,13 @@ struct backtrace_state;
|
||||
invalid after this function returns.
|
||||
|
||||
As a special case, the ERRNUM argument will be passed as -1 if no
|
||||
debug info can be found for the executable, or if the debug info
|
||||
exists but has an unsupported version, but the function requires
|
||||
debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
|
||||
this case will be something along the lines of "no debug info".
|
||||
Similarly, ERRNUM will be passed as -1 if there is no symbol table,
|
||||
but the function requires a symbol table (e.g., backtrace_syminfo).
|
||||
This may be used as a signal that some other approach should be
|
||||
tried. */
|
||||
debug info can be found for the executable, but the function
|
||||
requires debug info (e.g., backtrace_full, backtrace_pcinfo). The
|
||||
MSG in this case will be something along the lines of "no debug
|
||||
info". Similarly, ERRNUM will be passed as -1 if there is no
|
||||
symbol table, but the function requires a symbol table (e.g.,
|
||||
backtrace_syminfo). This may be used as a signal that some other
|
||||
approach should be tried. */
|
||||
|
||||
typedef void (*backtrace_error_callback) (void *data, const char *msg,
|
||||
int errnum);
|
||||
|
||||
Binary file not shown.
@@ -3,9 +3,6 @@ SRB2.res
|
||||
depend.dep
|
||||
depend.ped
|
||||
*.o
|
||||
buildvm*
|
||||
minilua*
|
||||
lj_vm.S
|
||||
#VC9 folder only
|
||||
/VC9/Win32
|
||||
/VC9/x64
|
||||
+1
-2
@@ -1,3 +1,2 @@
|
||||
/cscope.out
|
||||
*.json
|
||||
*.idx
|
||||
/srb2wii
|
||||
@@ -28,7 +28,6 @@ LOCAL_SRC_FILES := am_map.c \
|
||||
m_cheat.c \
|
||||
m_fixed.c \
|
||||
m_menu.c \
|
||||
m_textinput.c \
|
||||
m_misc.c \
|
||||
m_queue.c \
|
||||
m_random.c \
|
||||
|
||||
+551
-561
File diff suppressed because it is too large.
Load diff
+124
-107
@@ -31,6 +31,18 @@
|
||||
# compile all HW render and 3D sound DLLs for the set
|
||||
# opengl_dll
|
||||
# Pure Mingw only, compile OpenGL HW render DLL
|
||||
# minigl_dll
|
||||
# Pure Mingw only, compile MiniGL HW render DLL
|
||||
# ds3d_dll
|
||||
# Pure Mingw only, compile DirectX DirectSound HW sound DLL
|
||||
# fmod_dll
|
||||
# Pure Mingw only, compile FMOD HW sound DLL
|
||||
# openal_dll
|
||||
# Pure Mingw only, compile OpenAL HW sound DLL
|
||||
# fmod_so
|
||||
# Non-Mingw, compile FMOD HW sound SO
|
||||
# openal_so
|
||||
# Non-Mingw, compile OpenAL HW sound SO
|
||||
#
|
||||
#
|
||||
# Addon:
|
||||
@@ -57,7 +69,10 @@
|
||||
# Compile without SDL_Mixer, add 'NOMIXER=1'
|
||||
# Compile without BSD API, add 'NONET=1'
|
||||
# Compile without IPX/SPX, add 'NOIPX=1'
|
||||
# Compile Mingw/SDL with S_DS3S, add 'DS3D=1'
|
||||
# Compile without libopenmpt, add 'NOOPENMPT=1'
|
||||
# Compile with S_FMOD3D, add 'FMOD=1' (WIP)
|
||||
# Compile with S_OPENAL, add 'OPENAL=1' (WIP)
|
||||
# To link with the whole SDL_Image lib to load Icons, add 'SDL_IMAGE=1' but it isn't not realy needed
|
||||
# To link with SDLMain to hide console or make on a console-less binary, add 'SDLMAIN=1'
|
||||
#
|
||||
@@ -68,6 +83,7 @@ ALL_SYSTEMS=\
|
||||
LINUX64\
|
||||
MINGW64\
|
||||
HAIKU\
|
||||
DUMMY\
|
||||
MINGW\
|
||||
UNIX\
|
||||
LINUX\
|
||||
@@ -80,19 +96,16 @@ ALL_SYSTEMS=\
|
||||
ifeq (,$(filter $(ALL_SYSTEMS),$(.VARIABLES)))
|
||||
ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
|
||||
|
||||
ifeq ($(shell uname -s | grep -o MINGW64),MINGW64) # 64-bit MinGW
|
||||
$(info Detected a Windows system, compiling for 64-bit MinGW SDL2...)
|
||||
MINGW64=1
|
||||
else # 32-bit MinGW
|
||||
$(info Detected a Windows system, compiling for 32-bit MinGW SDL2...)
|
||||
MINGW=1
|
||||
endif
|
||||
$(info Detected a Windows system, compiling for 32-bit MinGW SDL2...)
|
||||
|
||||
# go for a 32-bit sdl mingw exe by default
|
||||
MINGW=1
|
||||
SDL=1
|
||||
WINDOWSHELL=1
|
||||
NOOBJDUMP=1
|
||||
NOUPX=1
|
||||
|
||||
else # if you on the *nix
|
||||
|
||||
system:=$(shell uname -s)
|
||||
|
||||
ifeq ($(system),Linux)
|
||||
@@ -116,7 +129,6 @@ else # if you on the *nix
|
||||
endif
|
||||
endif
|
||||
|
||||
NOUPX=1
|
||||
|
||||
# SRB2 data files
|
||||
D_DIR?=../bin/Resources
|
||||
@@ -157,6 +169,17 @@ endif
|
||||
|
||||
include Makefile.cfg
|
||||
|
||||
ifdef DUMMY
|
||||
NOPNG=1
|
||||
NOZLIB=1
|
||||
NONET=1
|
||||
NOHW=1
|
||||
NOIPX=1
|
||||
EXENAME?=srb2dummy
|
||||
OBJS=$(OBJDIR)/i_video.o
|
||||
LIBS=-lm
|
||||
endif
|
||||
|
||||
ifdef HAIKU
|
||||
NOIPX=1
|
||||
ifndef NONET
|
||||
@@ -182,15 +205,6 @@ USE_FBO_OGL=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NONET
|
||||
ifndef NOSATURNJOIN
|
||||
DOSATURNJOIN=1
|
||||
endif
|
||||
ifndef NOSATURNPAK
|
||||
DOSATURNPAK=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef MINGW
|
||||
include win32/Makefile.cfg
|
||||
endif #ifdef MINGW
|
||||
@@ -201,7 +215,6 @@ endif
|
||||
|
||||
ifdef LINUX
|
||||
UNIXCOMMON=1
|
||||
NOOBJDUMP=1
|
||||
ifndef NOGME
|
||||
HAVE_LIBGME=1
|
||||
endif
|
||||
@@ -233,10 +246,6 @@ endif
|
||||
|
||||
MSGFMT?=msgfmt
|
||||
|
||||
ifndef NOBLUAJIT
|
||||
include bluajit/Makefile.cfg
|
||||
endif
|
||||
|
||||
ifndef ECHO
|
||||
REMOVE:=@$(REMOVE)
|
||||
CC:=@$(CC)
|
||||
@@ -269,8 +278,8 @@ ifdef NOHW
|
||||
OPTS+=-DNOHW
|
||||
else
|
||||
OPTS+=-DHWRENDER
|
||||
OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o \
|
||||
$(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_portal.o\
|
||||
OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \
|
||||
$(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o \
|
||||
$(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o $(OBJDIR)/hw_shaders.o
|
||||
endif
|
||||
|
||||
@@ -284,10 +293,11 @@ else
|
||||
endif
|
||||
else
|
||||
ifdef X86_64
|
||||
ARCHOPTS?=-march=native
|
||||
ARCHOPTS?=-march=nocona
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifndef NOPNG
|
||||
OPTS+=-DHAVE_PNG
|
||||
|
||||
@@ -374,15 +384,6 @@ OPTS+=-DUSE_FBO_OGL
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NONET
|
||||
ifndef NOSATURNJOIN
|
||||
OPTS+=-DDOSATURNJOIN
|
||||
endif
|
||||
ifndef NOSATURNPAK
|
||||
OPTS+=-DDOSATURNPAK
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef HAVE_MINIUPNPC
|
||||
ifdef NONET
|
||||
HAVE_MINIUPNPC=''
|
||||
@@ -401,10 +402,7 @@ CFLAGS+=-DHAVE_DISCORDRPC -DUSE_STUN
|
||||
OBJS+=$(OBJDIR)/discord.o $(OBJDIR)/stun.o
|
||||
endif
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
OPTS+=-DNOBLUAJIT
|
||||
include blua/Makefile.cfg
|
||||
endif
|
||||
|
||||
ifdef NOMD5
|
||||
OPTS+=-DNOMD5
|
||||
@@ -441,24 +439,7 @@ else
|
||||
# build a normal optimised version
|
||||
WINDRESFLAGS = -DNDEBUG
|
||||
CFLAGS+=-O3
|
||||
CPPFLAGS+=-O3
|
||||
endif
|
||||
|
||||
ifndef DEBUGMODE
|
||||
ifdef USELTO
|
||||
# compile with LTO
|
||||
CFLAGS+=-flto=auto
|
||||
CPPFLAGS+=-flto=auto
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef USEASAN
|
||||
# compile with asan
|
||||
CFLAGS+=-fsanitize=address
|
||||
CPPFLAGS+=-fsanitize=address
|
||||
LDFLAGS+=-fsanitize=address
|
||||
endif
|
||||
|
||||
CFLAGS+=-g $(OPTS) $(ARCHOPTS) $(WINDRESFLAGS)
|
||||
|
||||
ifdef PROFILEMODE
|
||||
@@ -511,9 +492,7 @@ OBJS:=$(i_main_o) \
|
||||
$(OBJDIR)/hu_stuff.o \
|
||||
$(OBJDIR)/y_inter.o \
|
||||
$(OBJDIR)/st_stuff.o \
|
||||
$(OBJDIR)/k_director.o \
|
||||
$(OBJDIR)/k_kart.o \
|
||||
$(OBJDIR)/k_stats.o \
|
||||
$(OBJDIR)/m_aatree.o \
|
||||
$(OBJDIR)/m_anigif.o \
|
||||
$(OBJDIR)/m_argv.o \
|
||||
@@ -523,7 +502,6 @@ OBJS:=$(i_main_o) \
|
||||
$(OBJDIR)/m_fixed.o \
|
||||
$(OBJDIR)/m_menu.o \
|
||||
$(OBJDIR)/m_misc.o \
|
||||
$(OBJDIR)/m_textinput.o \
|
||||
$(OBJDIR)/m_perfstats.o \
|
||||
$(OBJDIR)/m_random.o \
|
||||
$(OBJDIR)/m_queue.o \
|
||||
@@ -556,9 +534,7 @@ OBJS:=$(i_main_o) \
|
||||
$(OBJDIR)/r_sky.o \
|
||||
$(OBJDIR)/r_splats.o \
|
||||
$(OBJDIR)/r_patch.o \
|
||||
$(OBJDIR)/r_patchrotation.o \
|
||||
$(OBJDIR)/r_things.o \
|
||||
$(OBJDIR)/r_portal.o \
|
||||
$(OBJDIR)/screen.o \
|
||||
$(OBJDIR)/v_video.o \
|
||||
$(OBJDIR)/s_sound.o \
|
||||
@@ -600,6 +576,10 @@ ifdef SDL
|
||||
all: pre-build $(BIN)/$(EXENAME)
|
||||
endif
|
||||
|
||||
ifdef DUMMY
|
||||
all: $(BIN)/$(EXENAME)
|
||||
endif
|
||||
|
||||
cleandep:
|
||||
$(REMOVE) $(OBJDIR)/depend.dep
|
||||
$(REMOVE) comptime.h
|
||||
@@ -614,13 +594,6 @@ endif
|
||||
clean:
|
||||
$(REMOVE) *~ *.flc
|
||||
$(REMOVE) $(OBJDIR)/*.o
|
||||
ifndef NOBLUAJIT
|
||||
$(REMOVE) $(OBJDIR)/buildvm* $(OBJDIR)/minilua* $(OBJDIR)/lj_vm* $(ALL_HDRGEN)
|
||||
|
||||
cleanjit:
|
||||
$(REMOVE) $(LJVMCORE_O) $(ALL_GEN)
|
||||
$(REMOVE) $(OBJDIR)/buildvm* $(OBJDIR)/minilua* $(OBJDIR)/lj_vm* $(ALL_HDRGEN)
|
||||
endif
|
||||
|
||||
ifdef MINGW
|
||||
$(REMOVE) $(OBJDIR)/*.res
|
||||
@@ -636,7 +609,7 @@ endif
|
||||
$(BIN)/$(EXENAME): $(POS) $(OBJS)
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking $(EXENAME)...
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $(BIN)/$(EXENAME) $(LIBS) -lstdc++
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $(BIN)/$(EXENAME) $(LIBS)
|
||||
ifndef VALGRIND
|
||||
ifndef NOOBJDUMP
|
||||
@echo Dumping debugging info
|
||||
@@ -657,17 +630,6 @@ ifndef NOUPX
|
||||
-$(UPX) $(UPX_OPTS) $(BIN)/$(EXENAME)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef APPIMAGE
|
||||
@echo Building AppImage...
|
||||
ifdef APPIMAGE_NOASSETS
|
||||
$(.)../AppImage_build.sh --small --quiet $(BIN)/
|
||||
else
|
||||
$(.)../AppImage_build.sh -q $(BIN)/
|
||||
endif
|
||||
@echo AppImage build complete, please look for $(EXENAME).AppImage in $(BIN)
|
||||
endif
|
||||
|
||||
@echo Build is done, please look for $(EXENAME) in $(BIN), \(checking for post steps\)
|
||||
|
||||
reobjdump:
|
||||
@@ -688,7 +650,7 @@ else
|
||||
dll : opengl_dll
|
||||
endif
|
||||
ifdef MINGW
|
||||
all_dll: opengl_dll
|
||||
all_dll: opengl_dll ds3d_dll fmod_dll openal_dll
|
||||
|
||||
opengl_dll: $(BIN)/r_opengl.dll
|
||||
$(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o
|
||||
@@ -698,24 +660,55 @@ $(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o
|
||||
ifndef NOUPX
|
||||
-$(UPX) $(UPX_OPTS) $@
|
||||
endif
|
||||
|
||||
ds3d_dll: $(BIN)/s_ds3d.dll
|
||||
$(BIN)/s_ds3d.dll: $(OBJDIR)/s_ds3d.o
|
||||
@echo Linking S_DS3d.dll...
|
||||
$(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -ldsound -luuid
|
||||
|
||||
fmod_dll: $(BIN)/s_fmod.dll
|
||||
$(BIN)/s_fmod.dll: $(OBJDIR)/s_fmod.o
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking S_FMOD.dll...
|
||||
$(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lfmod
|
||||
|
||||
openal_dll: $(BIN)/s_openal.dll
|
||||
$(BIN)/s_openal.dll: $(OBJDIR)/s_openal.o
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking S_OpenAL.dll...
|
||||
$(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lopenal32
|
||||
else
|
||||
all_dll: fmod_so openal_so
|
||||
|
||||
fmod_so: $(BIN)/s_fmod.so
|
||||
$(BIN)/s_fmod.so: $(OBJDIR)/s_fmod.o
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking S_FMOD.so...
|
||||
$(CC) --shared $^ -o $@ -g --nostartfiles -lm -lfmod
|
||||
|
||||
openal_so: $(BIN)/s_openal.so
|
||||
$(BIN)/s_openal.so: $(OBJDIR)/s_openal.o
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking S_OpenAL.so...
|
||||
$(CC) --shared $^ -o $@ -g --nostartfiles -lm -lopenal
|
||||
endif
|
||||
|
||||
else
|
||||
ifdef SDL
|
||||
ifdef MINGW
|
||||
$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
|
||||
doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gl.h screen.h \
|
||||
doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
|
||||
command.h hardware/hw_data.h hardware/hw_defs.h hardware/hw_md2.h \
|
||||
hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h hardware/hw_portal.h\
|
||||
hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
|
||||
hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
|
||||
am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
|
||||
p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
else
|
||||
$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
|
||||
doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gl.h screen.h \
|
||||
doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
|
||||
command.h hardware/hw_data.h hardware/hw_defs.h hardware/hw_md2.h \
|
||||
hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h hardware/hw_portal.h \
|
||||
hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
|
||||
hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
|
||||
am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
|
||||
p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
|
||||
@@ -727,34 +720,21 @@ endif
|
||||
|
||||
#dependecy made by gcc itself !
|
||||
$(OBJS):
|
||||
ifndef DUMMY
|
||||
-include $(OBJDIR)/depend.dep
|
||||
endif
|
||||
|
||||
$(OBJDIR)/depend.dep:
|
||||
@echo "Creating dependency file, depend.dep"
|
||||
@echo > comptime.h
|
||||
-$(MKDIR) $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -MM *.c > $(OBJDIR)/depend.ped
|
||||
$(CXX) $(CXXFLAGS) -MM *.cpp >> $(OBJDIR)/depend.ped
|
||||
$(CC) $(CFLAGS) -MM $(INTERFACE)/*.c >> $(OBJDIR)/depend.ped
|
||||
ifndef NOHW
|
||||
$(CC) $(CFLAGS) -MM hardware/*.c >> $(OBJDIR)/depend.ped
|
||||
endif
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
$(CC) $(CFLAGS) -MM blua/*.c >> $(OBJDIR)/depend.ped
|
||||
else
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -f $$file || touch $$file; \
|
||||
done
|
||||
@$(HOST_CC) $(HOST_ACFLAGS) -MM bluajit/*.c bluajit/host/*.c | \
|
||||
sed -e "s| [^ ]*/dasm_\S*\.h||g" \
|
||||
-e "s| lj_target_\S*\.h| lj_target_*.h|g" \
|
||||
-e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \
|
||||
-e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >>$(OBJDIR)/depend.ped
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -s $$file || $(HOST_RM) $$file; \
|
||||
done
|
||||
endif
|
||||
|
||||
@sed -e 's,\(.*\)\.o: ,$(subst /,\/,$(OBJDIR))\/&,g' < $(OBJDIR)/depend.ped > $(OBJDIR)/depend.dep
|
||||
$(REMOVE) $(OBJDIR)/depend.ped
|
||||
@@ -786,16 +766,8 @@ endif
|
||||
$(OBJDIR)/%.o: hardware/%.c
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
$(OBJDIR)/%.o: blua/%.c
|
||||
$(CC) $(CFLAGS) $(LUA_CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
else
|
||||
include bluajit/Makefile.cfg2
|
||||
endif
|
||||
|
||||
# C++
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.s
|
||||
$(CC) $(OPTS) -x assembler-with-cpp -c $< -o $@
|
||||
@@ -804,10 +776,55 @@ $(OBJDIR)/SRB2.res: win32/Srb2win.rc win32/afxres.h win32/resource.h
|
||||
$(WINDRES) -i $< -O rc $(WINDRESFLAGS) --include-dir=win32 -o $@ -O coff
|
||||
|
||||
ifdef SDL
|
||||
|
||||
ifdef MINGW
|
||||
$(OBJDIR)/win_dbg.o: win32/win_dbg.c
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
endif
|
||||
|
||||
ifdef STATICHS
|
||||
$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
|
||||
hardware/hw_dll.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/s_fmod.o: hardware/s_fmod/s_fmod.c hardware/hw3dsdrv.h \
|
||||
hardware/hw_dll.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
ifdef MINGW
|
||||
$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \
|
||||
hardware/hw_dll.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
endif
|
||||
else
|
||||
|
||||
$(OBJDIR)/s_fmod.o: hardware/s_fmod/s_fmod.c hardware/hw3dsdrv.h \
|
||||
hardware/hw_dll.h
|
||||
$(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_fmod/s_fmod.c
|
||||
|
||||
$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
|
||||
hardware/hw_dll.h
|
||||
$(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_openal/s_openal.c
|
||||
endif
|
||||
|
||||
ifdef FILTERS
|
||||
$(OBJDIR)/%.o: $(INTERFACE)/filter/%.c
|
||||
@echo $< needs deps
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/filters.o: $(INTERFACE)/filter/filters.c $(INTERFACE)/filter/filters.c \
|
||||
$(INTERFACE)/filter/filters.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/hq2x.o: $(INTERFACE)/filter/hq2x.c $(INTERFACE)/filter/hq2x.c \
|
||||
$(INTERFACE)/filter/filters.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/lq2x.o: $(INTERFACE)/filter/lq2x.c $(INTERFACE)/filter/lq2x.c \
|
||||
$(INTERFACE)/filter/filters.h $(INTERFACE)/filter/interp.h \
|
||||
$(INTERFACE)/filter/hq2x.h $(INTERFACE)/filter/lq2x.h
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
endif
|
||||
endif
|
||||
|
||||
#############################################################
|
||||
|
||||
+18
-25
@@ -12,8 +12,7 @@
|
||||
# to avoid a false positive with the version detection...
|
||||
|
||||
SUPPORTED_GCC_VERSIONS:=\
|
||||
151 150 15\
|
||||
142 141 140 14\
|
||||
141 14\
|
||||
132 131 130 13\
|
||||
123 122 121 120 12\
|
||||
114 113 112 111 110 11\
|
||||
@@ -25,7 +24,7 @@ SUPPORTED_GCC_VERSIONS:=\
|
||||
55 54 53 52 51 50\
|
||||
49 48 47 46 45 44 43 42 41 40
|
||||
|
||||
LATEST_GCC_VERSION=14.2.1
|
||||
LATEST_GCC_VERSION=14.1
|
||||
|
||||
# gcc or g++
|
||||
ifdef PREFIX
|
||||
@@ -79,32 +78,12 @@ ifeq (,$(filter GCC%,$(.VARIABLES)))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef GCC151
|
||||
GCC150=1
|
||||
endif
|
||||
|
||||
ifdef GCC15
|
||||
GCC150=1
|
||||
endif
|
||||
|
||||
ifdef GCC150
|
||||
GCC142=1
|
||||
endif
|
||||
|
||||
ifdef GCC142
|
||||
ifdef GCC141
|
||||
GCC141=1
|
||||
endif
|
||||
|
||||
ifdef GCC141
|
||||
GCC140=1
|
||||
endif
|
||||
|
||||
ifdef GCC14
|
||||
GCC140=1
|
||||
endif
|
||||
|
||||
ifdef GCC140
|
||||
GCC132=1
|
||||
GCC14=1
|
||||
endif
|
||||
|
||||
ifdef GCC132
|
||||
@@ -458,6 +437,9 @@ endif
|
||||
ifdef GCC44
|
||||
WFLAGS+=-Wno-error=array-bounds
|
||||
endif
|
||||
ifdef GCC45
|
||||
opts+=-fwrapv
|
||||
endif
|
||||
ifdef GCC46
|
||||
WFLAGS+=-Wno-error=suggest-attribute=noreturn
|
||||
endif
|
||||
@@ -482,6 +464,7 @@ ifdef GCC81
|
||||
WFLAGS+=-Wno-error=multistatement-macros
|
||||
endif
|
||||
|
||||
|
||||
#indicate platform and what interface use with
|
||||
ifndef LINUX
|
||||
ifndef FREEBSD
|
||||
@@ -489,6 +472,7 @@ ifndef CYGWIN32
|
||||
ifndef MINGW
|
||||
ifndef MINGW64
|
||||
ifndef SDL
|
||||
ifndef DUMMY
|
||||
$(error No interface or platform flag defined)
|
||||
endif
|
||||
endif
|
||||
@@ -496,6 +480,7 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#determine the interface directory (where you put all i_*.c)
|
||||
i_net_o=$(OBJDIR)/i_net.o
|
||||
@@ -520,6 +505,11 @@ UPX_OPTS+=-q
|
||||
endif
|
||||
|
||||
#Interface Setup
|
||||
ifdef DUMMY
|
||||
INTERFACE=dummy
|
||||
OBJDIR:=$(OBJDIR)/dummy
|
||||
BIN:=$(BIN)/dummy
|
||||
else
|
||||
ifdef LINUX
|
||||
SDL=1
|
||||
ifdef LINUX64
|
||||
@@ -566,6 +556,7 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef ARCHNAME
|
||||
OBJDIR:=$(OBJDIR)/$(ARCHNAME)
|
||||
@@ -580,6 +571,7 @@ ifdef SDL
|
||||
OBJDIR:=$(OBJDIR)/SDL
|
||||
endif
|
||||
|
||||
ifndef DUMMY
|
||||
ifdef DEBUGMODE
|
||||
OBJDIR:=$(OBJDIR)/Debug
|
||||
BIN:=$(BIN)/Debug
|
||||
@@ -587,3 +579,4 @@ else
|
||||
OBJDIR:=$(OBJDIR)/Release
|
||||
BIN:=$(BIN)/Release
|
||||
endif
|
||||
endif
|
||||
+137
-471
@@ -1,10 +1,8 @@
|
||||
// SONIC ROBO BLAST 2 KART
|
||||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
// Copyright (C) 2024 by Vivian "toastergrl" Grannell.
|
||||
// Copyright (C) 2024 by Kart Krew.
|
||||
// Copyright (C) 2020 by Sonic Team Junior.
|
||||
// Copyright (C) 2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2018 by Sonic Team Junior.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
@@ -35,31 +33,36 @@ static const UINT8 GRAYSRANGE = 16;
|
||||
static const UINT8 BROWNS = (3*16);
|
||||
static const UINT8 YELLOWS = (7*16);
|
||||
static const UINT8 GREENS = (10*16);
|
||||
//static const UINT8 CYANS = (13*16);
|
||||
static const UINT8 BLUES = (14*16);
|
||||
static const UINT8 DBLACK = 31;
|
||||
static const UINT8 DWHITE = 0;
|
||||
|
||||
static const UINT8 NOCLIMBREDS = 248;
|
||||
static const UINT8 NOCLIMBREDRANGE = 8;
|
||||
static const UINT8 NOCLIMBGRAYS = 204;
|
||||
static const UINT8 NOCLIMBBROWNS = (2*16);
|
||||
static const UINT8 NOCLIMBYELLOWS = (11*16);
|
||||
|
||||
// Automap colors
|
||||
#define BACKGROUND DBLACK
|
||||
#define WALLCOLORS (REDS + REDRANGE/2)
|
||||
#define WALLRANGE (REDRANGE/2)
|
||||
#define NOCLIMBWALLCOLORS (NOCLIMBREDS + NOCLIMBREDRANGE/2)
|
||||
#define NOCLIMBWALLRANGE (NOCLIMBREDRANGE/2)
|
||||
#define THOKWALLCOLORS REDS
|
||||
#define THOKWALLRANGE REDRANGE
|
||||
#define TSWALLCOLORS DWHITE
|
||||
#define TSFINISHLINE (GREENS + 2)
|
||||
#define TSFOFINFO (BLUES + 4)
|
||||
#define NOCLIMBTHOKWALLCOLORS NOCLIMBREDS
|
||||
#define NOCLIMBTHOKWALLRANGE NOCLIMBREDRANGE
|
||||
#define TSWALLCOLORS GRAYS
|
||||
#define TSWALLRANGE GRAYSRANGE
|
||||
#define NOCLIMBTSWALLCOLORS NOCLIMBGRAYS
|
||||
#define FDWALLCOLORS BROWNS
|
||||
#define NOCLIMBFDWALLCOLORS NOCLIMBBROWNS
|
||||
#define CDWALLCOLORS YELLOWS
|
||||
#define NOCLIMBCDWALLCOLORS NOCLIMBYELLOWS
|
||||
#define THINGCOLORS GREENS
|
||||
#define GRIDCOLORS (GRAYS + GRAYSRANGE/2)
|
||||
#define XHAIRCOLORS GRAYS
|
||||
|
||||
// Automap passes
|
||||
#define PASS_SOLID 1
|
||||
#define PASS_INTANGIBLE 2
|
||||
#define PASS_FOF 4
|
||||
|
||||
// controls
|
||||
#define AM_PANUPKEY KEY_UPARROW
|
||||
#define AM_PANDOWNKEY KEY_DOWNARROW
|
||||
@@ -107,6 +110,11 @@ typedef struct
|
||||
mpoint_t a, b;
|
||||
} mline_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fixed_t slp, islp;
|
||||
} islope_t;
|
||||
|
||||
//
|
||||
// The vector graphics for the automap.
|
||||
// A line drawing of the player pointing right,
|
||||
@@ -128,14 +136,16 @@ static const mline_t player_arrow[] = {
|
||||
#undef R
|
||||
#define NUMPLYRLINES (sizeof (player_arrow)/sizeof (mline_t))
|
||||
|
||||
#if 0
|
||||
#define R (FRACUNIT)
|
||||
static const mline_t cross_mark[] =
|
||||
{
|
||||
{ { -R, 0 }, { R, 0} },
|
||||
{ { 0, -R }, { 0, R } },
|
||||
static mline_t triangle_guy[] = {
|
||||
{ { (fixed_t)-.867f*R, (fixed_t)-.5f*R }, { (fixed_t) .867f*R, (fixed_t)-.5f*R } },
|
||||
{ { (fixed_t) .867f*R, (fixed_t)-.5f*R }, { (fixed_t) 0, (fixed_t) R } },
|
||||
{ { (fixed_t) 0, (fixed_t) R }, { (fixed_t)-.867f*R, (fixed_t)-.5f*R } }
|
||||
};
|
||||
#undef R
|
||||
#define NUMCROSSMARKLINES (sizeof(cross_mark)/sizeof(mline_t))
|
||||
#define NUMTRIANGLEGUYLINES (sizeof (triangle_guy)/sizeof (mline_t))
|
||||
#endif
|
||||
|
||||
#define R (FRACUNIT)
|
||||
static const mline_t thintriangle_guy[] = {
|
||||
@@ -152,14 +162,10 @@ static boolean draw_grid = false;
|
||||
boolean automapactive = false;
|
||||
boolean am_recalc = false; //added : 05-02-98 : true when screen size changes
|
||||
static boolean am_stopped = true;
|
||||
static boolean am_minigen = false;
|
||||
|
||||
static UINT8 *am_buf = NULL;
|
||||
|
||||
static INT32 f_x, f_y; // location of window on screen (always zero for both)
|
||||
static INT32 f_w, f_h; // size of window on screen (always the screen width and height respectively)
|
||||
|
||||
static boolean m_keydown[4]; // which window panning keys are being pressed down?
|
||||
static mpoint_t m_paninc; // how far the window pans each tic (map coords)
|
||||
static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
|
||||
static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords)
|
||||
@@ -199,13 +205,12 @@ static fixed_t scale_ftom;
|
||||
|
||||
static player_t *plr; // the player represented by an arrow
|
||||
|
||||
static boolean followplayer = true; // specifies whether to follow the player around
|
||||
static INT32 followplayer = true; // specifies whether to follow the player around
|
||||
|
||||
// function for drawing lines, depends on rendermode
|
||||
typedef void (*AMDRAWFLINEFUNC) (const fline_t *fl, INT32 color);
|
||||
static AMDRAWFLINEFUNC AM_drawFline;
|
||||
|
||||
static void AM_drawPixel(INT32 xx, INT32 yy, INT32 cc);
|
||||
static void AM_drawFline_soft(const fline_t *fl, INT32 color);
|
||||
|
||||
static void AM_activateNewScale(void)
|
||||
@@ -255,15 +260,28 @@ static inline void AM_restoreScaleAndLoc(void)
|
||||
*/
|
||||
static void AM_findMinMaxBoundaries(void)
|
||||
{
|
||||
fixed_t a, b;
|
||||
size_t i;
|
||||
fixed_t a;
|
||||
fixed_t b;
|
||||
|
||||
min_x = minimapinfo.min_x << MAPBITS;
|
||||
max_x = minimapinfo.max_x << MAPBITS;
|
||||
min_y = minimapinfo.min_y << MAPBITS;
|
||||
max_y = minimapinfo.max_y << MAPBITS;
|
||||
min_x = min_y = +INT32_MAX;
|
||||
max_x = max_y = -INT32_MAX;
|
||||
|
||||
max_w = minimapinfo.map_w << MAPBITS;
|
||||
max_h = minimapinfo.map_h << MAPBITS;
|
||||
for (i = 0; i < numvertexes; i++)
|
||||
{
|
||||
if (vertexes[i].x < min_x)
|
||||
min_x = vertexes[i].x;
|
||||
else if (vertexes[i].x > max_x)
|
||||
max_x = vertexes[i].x;
|
||||
|
||||
if (vertexes[i].y < min_y)
|
||||
min_y = vertexes[i].y;
|
||||
else if (vertexes[i].y > max_y)
|
||||
max_y = vertexes[i].y;
|
||||
}
|
||||
|
||||
max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);
|
||||
max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);
|
||||
|
||||
a = FixedDiv(f_w<<FRACBITS, max_w);
|
||||
b = FixedDiv(f_h<<FRACBITS, max_h);
|
||||
@@ -312,13 +330,12 @@ static void AM_initVariables(void)
|
||||
m_h = FTOM(f_h);
|
||||
|
||||
// find player to center on initially
|
||||
if (!players[pnum = consoleplayer].ingame)
|
||||
if (!playeringame[pnum = consoleplayer])
|
||||
for (pnum = 0; pnum < MAXPLAYERS; pnum++)
|
||||
if (players[pnum].ingame)
|
||||
if (playeringame[pnum])
|
||||
break;
|
||||
|
||||
plr = &players[pnum];
|
||||
|
||||
if (plr != NULL && plr->mo != NULL)
|
||||
{
|
||||
m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;
|
||||
@@ -333,23 +350,22 @@ static void AM_initVariables(void)
|
||||
old_m_h = m_h;
|
||||
}
|
||||
|
||||
//
|
||||
// Called when the screen size changes.
|
||||
//
|
||||
static void AM_FrameBufferInit(void)
|
||||
{
|
||||
f_x = f_y = 0;
|
||||
f_w = vid.width;
|
||||
f_h = vid.height;
|
||||
am_buf = screens[0];
|
||||
}
|
||||
|
||||
//
|
||||
// should be called at the start of every level
|
||||
// right now, i figure it out myself
|
||||
//
|
||||
static void AM_LevelInit(void)
|
||||
{
|
||||
f_x = f_y = 0;
|
||||
f_w = vid.width;
|
||||
f_h = vid.height;
|
||||
|
||||
AM_drawFline = AM_drawFline_soft;
|
||||
#ifdef HWRENDER
|
||||
if (rendermode == render_opengl)
|
||||
AM_drawFline = HWR_drawAMline;
|
||||
#endif
|
||||
|
||||
AM_findMinMaxBoundaries();
|
||||
scale_mtof = FixedDiv(min_scale_mtof*10, 7*FRACUNIT);
|
||||
if (scale_mtof > max_scale_mtof)
|
||||
@@ -371,7 +387,7 @@ void AM_Stop(void)
|
||||
*
|
||||
* \sa AM_Stop
|
||||
*/
|
||||
void AM_Start(void)
|
||||
static inline void AM_Start(void)
|
||||
{
|
||||
static INT32 lastlevel = -1;
|
||||
|
||||
@@ -380,12 +396,8 @@ void AM_Start(void)
|
||||
am_stopped = false;
|
||||
if (lastlevel != gamemap || am_recalc) // screen size changed
|
||||
{
|
||||
AM_FrameBufferInit();
|
||||
if (lastlevel != gamemap)
|
||||
{
|
||||
AM_LevelInit();
|
||||
lastlevel = gamemap;
|
||||
}
|
||||
AM_LevelInit();
|
||||
lastlevel = gamemap;
|
||||
am_recalc = false;
|
||||
}
|
||||
AM_initVariables();
|
||||
@@ -411,28 +423,6 @@ static void AM_maxOutWindowScale(void)
|
||||
AM_activateNewScale();
|
||||
}
|
||||
|
||||
//
|
||||
// set window panning
|
||||
//
|
||||
static void AM_setWindowPanning(void)
|
||||
{
|
||||
// up and down
|
||||
if (m_keydown[2]) // pan up
|
||||
m_paninc.y = FTOM(F_PANINC);
|
||||
else if (m_keydown[3]) // pan down
|
||||
m_paninc.y = -FTOM(F_PANINC);
|
||||
else
|
||||
m_paninc.y = 0;
|
||||
|
||||
// left and right
|
||||
if (m_keydown[0]) // pan right
|
||||
m_paninc.x = FTOM(F_PANINC);
|
||||
else if (m_keydown[1]) // pan left
|
||||
m_paninc.x = -FTOM(F_PANINC);
|
||||
else
|
||||
m_paninc.x = 0;
|
||||
}
|
||||
|
||||
/** Responds to user inputs in automap mode.
|
||||
*
|
||||
* \param ev Event to possibly respond to.
|
||||
@@ -465,49 +455,35 @@ boolean AM_Responder(event_t *ev)
|
||||
{
|
||||
case AM_PANRIGHTKEY: // pan right
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[0] = true;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.x = FTOM(F_PANINC);
|
||||
else
|
||||
rc = false;
|
||||
break;
|
||||
case AM_PANLEFTKEY: // pan left
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[1] = true;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.x = -FTOM(F_PANINC);
|
||||
else
|
||||
rc = false;
|
||||
break;
|
||||
case AM_PANUPKEY: // pan up
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[2] = true;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.y = FTOM(F_PANINC);
|
||||
else
|
||||
rc = false;
|
||||
break;
|
||||
case AM_PANDOWNKEY: // pan down
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[3] = true;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.y = -FTOM(F_PANINC);
|
||||
else
|
||||
rc = false;
|
||||
break;
|
||||
case AM_ZOOMOUTKEY: // zoom out
|
||||
mtof_zoommul = M_ZOOMOUT;
|
||||
ftom_zoommul = M_ZOOMIN;
|
||||
AM_setWindowPanning();
|
||||
break;
|
||||
case AM_ZOOMINKEY: // zoom in
|
||||
mtof_zoommul = M_ZOOMIN;
|
||||
ftom_zoommul = M_ZOOMOUT;
|
||||
AM_setWindowPanning();
|
||||
break;
|
||||
case AM_TOGGLEKEY:
|
||||
AM_Stop();
|
||||
@@ -521,7 +497,6 @@ boolean AM_Responder(event_t *ev)
|
||||
}
|
||||
else
|
||||
AM_restoreScaleAndLoc();
|
||||
AM_setWindowPanning();
|
||||
break;
|
||||
case AM_FOLLOWKEY:
|
||||
followplayer = !followplayer;
|
||||
@@ -541,32 +516,14 @@ boolean AM_Responder(event_t *ev)
|
||||
switch (ev->data1)
|
||||
{
|
||||
case AM_PANRIGHTKEY:
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[0] = false;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
break;
|
||||
case AM_PANLEFTKEY:
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[1] = false;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.x = 0;
|
||||
break;
|
||||
case AM_PANUPKEY:
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[2] = false;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
break;
|
||||
case AM_PANDOWNKEY:
|
||||
if (!followplayer)
|
||||
{
|
||||
m_keydown[3] = false;
|
||||
AM_setWindowPanning();
|
||||
}
|
||||
m_paninc.y = 0;
|
||||
break;
|
||||
case AM_ZOOMOUTKEY:
|
||||
case AM_ZOOMINKEY:
|
||||
@@ -767,16 +724,6 @@ static boolean AM_clipMline(const mline_t *ml, fline_t *fl)
|
||||
}
|
||||
#undef DOOUTCODE
|
||||
|
||||
//
|
||||
// Draws a pixel.
|
||||
//
|
||||
static void AM_drawPixel(INT32 xx, INT32 yy, INT32 cc)
|
||||
{
|
||||
if (xx < 0 || yy < 0 || xx >= f_w || yy >= f_h)
|
||||
return; // off the screen
|
||||
am_buf[(yy*f_w) + xx] = cc;
|
||||
}
|
||||
|
||||
//
|
||||
// Classic Bresenham w/ whatever optimizations needed for speed
|
||||
//
|
||||
@@ -798,6 +745,8 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PUTDOT(xx,yy,cc) V_DrawFill(xx,yy,1,1,cc|V_NOSCALESTART);
|
||||
|
||||
dx = fl->b.x - fl->a.x;
|
||||
ax = 2 * (dx < 0 ? -dx : dx);
|
||||
sx = dx < 0 ? -1 : 1;
|
||||
@@ -814,7 +763,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
|
||||
d = ay - ax/2;
|
||||
for (;;)
|
||||
{
|
||||
AM_drawPixel(x, y, color);
|
||||
PUTDOT(x, y, color)
|
||||
if (x == fl->b.x)
|
||||
return;
|
||||
if (d >= 0)
|
||||
@@ -831,7 +780,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
|
||||
d = ax - ay/2;
|
||||
for (;;)
|
||||
{
|
||||
AM_drawPixel(x, y, color);
|
||||
PUTDOT(x, y, color)
|
||||
if (y == fl->b.y)
|
||||
return;
|
||||
if (d >= 0)
|
||||
@@ -843,6 +792,8 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
|
||||
d += ax;
|
||||
}
|
||||
}
|
||||
|
||||
#undef PUTDOT
|
||||
}
|
||||
|
||||
//
|
||||
@@ -864,18 +815,17 @@ static void AM_drawGrid(INT32 color)
|
||||
fixed_t x, y;
|
||||
fixed_t start, end;
|
||||
mline_t ml;
|
||||
fixed_t gridsize = (MAPBLOCKUNITS<<MAPBITS);
|
||||
|
||||
// Figure out start of vertical gridlines
|
||||
start = m_x;
|
||||
if ((start - (bmaporgx>>FRACTOMAPBITS)) % gridsize)
|
||||
start += gridsize - ((start - (bmaporgx>>FRACTOMAPBITS)) % gridsize);
|
||||
if ((start - bmaporgx) % (MAPBLOCKUNITS<<FRACBITS))
|
||||
start += (MAPBLOCKUNITS<<FRACBITS) - ((start - bmaporgx) % (MAPBLOCKUNITS<<FRACBITS));
|
||||
end = m_x + m_w;
|
||||
|
||||
// draw vertical gridlines
|
||||
ml.a.y = m_y;
|
||||
ml.b.y = m_y + m_h;
|
||||
for (x = start; x < end; x += gridsize)
|
||||
for (x = start; x < end; x += (MAPBLOCKUNITS<<FRACBITS))
|
||||
{
|
||||
ml.a.x = x;
|
||||
ml.b.x = x;
|
||||
@@ -884,14 +834,14 @@ static void AM_drawGrid(INT32 color)
|
||||
|
||||
// Figure out start of horizontal gridlines
|
||||
start = m_y;
|
||||
if ((start - (bmaporgy>>FRACTOMAPBITS)) % gridsize)
|
||||
start += gridsize - ((start - (bmaporgy>>FRACTOMAPBITS)) % gridsize);
|
||||
if ((start - bmaporgy) % (MAPBLOCKUNITS<<FRACBITS))
|
||||
start += (MAPBLOCKUNITS<<FRACBITS) - ((start - bmaporgy) % (MAPBLOCKUNITS<<FRACBITS));
|
||||
end = m_y + m_h;
|
||||
|
||||
// draw horizontal gridlines
|
||||
ml.a.x = m_x;
|
||||
ml.b.x = m_x + m_w;
|
||||
for (y = start; y < end; y += gridsize)
|
||||
for (y = start; y < end; y += (MAPBLOCKUNITS<<FRACBITS))
|
||||
{
|
||||
ml.a.y = y;
|
||||
ml.b.y = y;
|
||||
@@ -899,118 +849,14 @@ static void AM_drawGrid(INT32 color)
|
||||
}
|
||||
}
|
||||
|
||||
static INT32 GetDamage(INT32 special)
|
||||
{
|
||||
switch (special)
|
||||
{
|
||||
case 1: // Damage (Generic)
|
||||
return 1;
|
||||
case 6: // Death Pit (Camera Tilt)
|
||||
return 6;
|
||||
case 7: // Death Pit (No Camera Tilt)
|
||||
return 7;
|
||||
case 8: // Instant Kill
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static INT32 GetOffroad(INT32 special)
|
||||
{
|
||||
switch (special)
|
||||
{
|
||||
case 2: // Offroad (Weak)
|
||||
return 2;
|
||||
case 3: // Offroad
|
||||
return 3;
|
||||
case 4: // Offroad (Strong)
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
|
||||
end1 = (P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y, ((normalheight + FRACUNIT/2) >> FRACBITS)) + FRACUNIT/2) >> FRACBITS; \
|
||||
end2 = (P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y, ((normalheight + FRACUNIT/2) >> FRACBITS)) + FRACUNIT/2) >> FRACBITS;
|
||||
|
||||
static ffloor_t *AM_CompareFOFs(size_t i, ffloor_t *rover, ffloor_t *secondarystore)
|
||||
{
|
||||
ffloor_t *secondaryrover = NULL;
|
||||
|
||||
for (; rover; rover = rover->next)
|
||||
{
|
||||
fixed_t rovt1, rovt2;
|
||||
fixed_t rovb1, rovb2;
|
||||
|
||||
if (!(rover->flags & FF_EXISTS))
|
||||
continue;
|
||||
if (!(rover->flags & FF_BLOCKPLAYER))
|
||||
continue;
|
||||
|
||||
SLOPEPARAMS(*rover->t_slope, rovt1, rovt2, *rover->topheight)
|
||||
SLOPEPARAMS(*rover->b_slope, rovb1, rovb2, *rover->bottomheight)
|
||||
|
||||
for (secondaryrover = secondarystore; secondaryrover; secondaryrover = secondaryrover->next)
|
||||
{
|
||||
fixed_t sect1, sect2;
|
||||
fixed_t secb1, secb2;
|
||||
|
||||
if (!(secondaryrover->flags & FF_EXISTS))
|
||||
continue;
|
||||
if (!(secondaryrover->flags & FF_BLOCKPLAYER))
|
||||
continue;
|
||||
if (secondaryrover->secnum == rover->secnum)
|
||||
break;
|
||||
|
||||
SLOPEPARAMS(*secondaryrover->t_slope, sect1, sect2, *secondaryrover->topheight)
|
||||
SLOPEPARAMS(*secondaryrover->b_slope, secb1, secb2, *secondaryrover->bottomheight)
|
||||
|
||||
if (rovt1 != sect1)
|
||||
continue;
|
||||
if (rovt2 != sect2)
|
||||
continue;
|
||||
if (rovb1 != secb1)
|
||||
continue;
|
||||
if (rovb2 != secb2)
|
||||
continue;
|
||||
|
||||
// this is fucking horrible
|
||||
INT32 damage1, damage2;
|
||||
INT32 offroad1, offroad2;
|
||||
INT32 special1, special2;
|
||||
special1 = GETSECSPECIAL(sectors[rover->secnum].special, 1);
|
||||
special2 = GETSECSPECIAL(sectors[secondaryrover->secnum].special, 1);
|
||||
|
||||
damage1 = GetDamage(special1);
|
||||
damage2 = GetDamage(special2);
|
||||
offroad1 = GetOffroad(special1);
|
||||
offroad2 = GetOffroad(special2);
|
||||
|
||||
if (damage1 != damage2
|
||||
//|| sectors[rover->secnum].friction != sectors[secondaryrover->secnum].friction
|
||||
|| offroad1 != offroad2)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (secondaryrover == NULL)
|
||||
break;
|
||||
}
|
||||
return rover;
|
||||
}
|
||||
|
||||
//
|
||||
// Determines visible lines, draws them.
|
||||
// This is LineDef based, not LineSeg based.
|
||||
//
|
||||
static void AM_drawWalls(UINT8 pass)
|
||||
static inline void AM_drawWalls(void)
|
||||
{
|
||||
size_t i;
|
||||
static mline_t l;
|
||||
const fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale)>>FRACBITS;
|
||||
fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
|
||||
fixed_t backf1 = 0, backf2 = 0, backc1 = 0, backc2 = 0; // back floor ceiling ends
|
||||
|
||||
@@ -1020,142 +866,74 @@ static void AM_drawWalls(UINT8 pass)
|
||||
l.a.y = lines[i].v1->y >> FRACTOMAPBITS;
|
||||
l.b.x = lines[i].v2->x >> FRACTOMAPBITS;
|
||||
l.b.y = lines[i].v2->y >> FRACTOMAPBITS;
|
||||
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
|
||||
if (slope) { \
|
||||
end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y); \
|
||||
end2 = P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y); \
|
||||
} else \
|
||||
end1 = end2 = normalheight;
|
||||
|
||||
SLOPEPARAMS(lines[i].frontsector->f_slope, frontf1, frontf2, lines[i].frontsector->floorheight)
|
||||
SLOPEPARAMS(lines[i].frontsector->c_slope, frontc1, frontc2, lines[i].frontsector->ceilingheight)
|
||||
if (lines[i].backsector) {
|
||||
SLOPEPARAMS(lines[i].backsector->f_slope, backf1, backf2, lines[i].backsector->floorheight)
|
||||
SLOPEPARAMS(lines[i].backsector->c_slope, backc1, backc2, lines[i].backsector->ceilingheight)
|
||||
}
|
||||
#undef SLOPEPARAMS
|
||||
|
||||
if (!lines[i].backsector) // 1-sided
|
||||
{
|
||||
if (!(pass & PASS_SOLID))
|
||||
;
|
||||
else if (frontf1 != frontc1 || frontf2 != frontc2 || lines[i].frontsector->f_slope)
|
||||
{
|
||||
AM_drawMline(&l, TSWALLCOLORS);
|
||||
}
|
||||
else if (!am_minigen)
|
||||
{
|
||||
if (lines[i].flags & ML_NOCLIMB)
|
||||
AM_drawMline(&l, NOCLIMBWALLCOLORS);
|
||||
else
|
||||
AM_drawMline(&l, WALLCOLORS);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SLOPEPARAMS(lines[i].backsector->f_slope, backf1, backf2, lines[i].backsector->floorheight)
|
||||
SLOPEPARAMS(lines[i].backsector->c_slope, backc1, backc2, lines[i].backsector->ceilingheight)
|
||||
|
||||
if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier
|
||||
else if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier
|
||||
|| (frontf1 == frontc1 && frontf2 == frontc2)) // Front is thok barrier
|
||||
{
|
||||
if (backf1 == backc1 && backf2 == backc2
|
||||
&& frontf1 == frontc1 && frontf2 == frontc2) // BOTH are thok barriers
|
||||
{
|
||||
if (!am_minigen && (pass & PASS_INTANGIBLE))
|
||||
{
|
||||
AM_drawMline(&l, GRIDCOLORS);
|
||||
}
|
||||
if (lines[i].flags & ML_NOCLIMB)
|
||||
AM_drawMline(&l, NOCLIMBTSWALLCOLORS);
|
||||
else
|
||||
AM_drawMline(&l, TSWALLCOLORS);
|
||||
}
|
||||
else if (pass & PASS_SOLID)
|
||||
else
|
||||
{
|
||||
AM_drawMline(&l, TSWALLCOLORS);
|
||||
if (lines[i].flags & ML_NOCLIMB)
|
||||
AM_drawMline(&l, NOCLIMBTHOKWALLCOLORS);
|
||||
else
|
||||
AM_drawMline(&l, THOKWALLCOLORS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lines[i].flags & ML_IMPASSIBLE) // (ML_IMPASSABLE|ML_BLOCKPLAYERS)
|
||||
{
|
||||
if (pass & PASS_SOLID)
|
||||
AM_drawMline(&l, TSWALLCOLORS); // Completely solid course boundary
|
||||
}
|
||||
else if ((lines[i].flags & ML_EFFECT4) // ML_MIDSOLID
|
||||
&& sides[lines->sidenum[0]].midtexture)
|
||||
{
|
||||
if (pass & PASS_SOLID)
|
||||
AM_drawMline(&l, TSWALLCOLORS); // solid midtexture, likely a course boundary
|
||||
}
|
||||
if ((backf1 != frontf1 && abs(backf1 - frontf1) > maxstep)
|
||||
|| (backf2 != frontf2 && abs(backf2 - frontf2) > maxstep))
|
||||
{
|
||||
if (pass & PASS_SOLID)
|
||||
AM_drawMline(&l, TSWALLCOLORS); // floor-wall, likely a course boundary
|
||||
}
|
||||
else if (GETSECSPECIAL(lines[i].frontsector->special, 4) == 10 || GETSECSPECIAL(lines[i].backsector->special, 4) == 10)
|
||||
{
|
||||
if (pass & PASS_SOLID)
|
||||
AM_drawMline(&l, TSFINISHLINE); // finish line
|
||||
}
|
||||
else if (backf1 != frontf1 || backf2 != frontf2)
|
||||
{
|
||||
if (pass & PASS_INTANGIBLE)
|
||||
AM_drawMline(&l, FDWALLCOLORS); // floorlevel change
|
||||
}
|
||||
else if (backc1 != frontc1 || backc2 != frontc2)
|
||||
{
|
||||
if (!(pass & PASS_INTANGIBLE))
|
||||
;
|
||||
else if (abs(backc1 - frontc1) < maxstep
|
||||
|| abs(backc2 - frontc2) < maxstep)
|
||||
{
|
||||
AM_drawMline(&l, CDWALLCOLORS); // ceilinglevel change
|
||||
if (lines[i].flags & ML_NOCLIMB) {
|
||||
if (backf1 != frontf1 || backf2 != frontf2) {
|
||||
AM_drawMline(&l, NOCLIMBFDWALLCOLORS); // floor level change
|
||||
}
|
||||
else if (backc1 != frontc1 || backc2 != frontc2) {
|
||||
AM_drawMline(&l, NOCLIMBCDWALLCOLORS); // ceiling level change
|
||||
}
|
||||
else
|
||||
{
|
||||
AM_drawMline(&l, GRIDCOLORS); // ceiling-wall, not likely to be a course boundary but flagged up just in case
|
||||
}
|
||||
AM_drawMline(&l, NOCLIMBTSWALLCOLORS);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffloor_t *rover = NULL;
|
||||
|
||||
if (lines[i].frontsector->ffloors || lines[i].backsector->ffloors)
|
||||
{
|
||||
if (lines[i].backsector->ffloors == NULL)
|
||||
{
|
||||
// Check frontside for one solid
|
||||
for (rover = lines[i].frontsector->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS))
|
||||
continue;
|
||||
if (!(rover->flags & FF_BLOCKPLAYER))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (lines[i].frontsector->ffloors == NULL)
|
||||
{
|
||||
// Check backside for one solid
|
||||
for (rover = lines[i].backsector->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS))
|
||||
continue;
|
||||
if (!(rover->flags & FF_BLOCKPLAYER))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check to see if any secnums exist in one but not the other.
|
||||
rover = AM_CompareFOFs(i, lines[i].frontsector->ffloors, lines[i].backsector->ffloors);
|
||||
if (rover == NULL)
|
||||
rover = AM_CompareFOFs(i, lines[i].backsector->ffloors, lines[i].frontsector->ffloors);
|
||||
}
|
||||
if (backf1 != frontf1 || backf2 != frontf2) {
|
||||
AM_drawMline(&l, FDWALLCOLORS); // floor level change
|
||||
}
|
||||
if (rover != NULL)
|
||||
{
|
||||
if (pass & PASS_FOF)
|
||||
AM_drawMline(&l, TSFOFINFO); // a FOF is here but we don't know how to distinguish them yet
|
||||
}
|
||||
else if (!am_minigen)
|
||||
{
|
||||
if (pass & PASS_INTANGIBLE)
|
||||
AM_drawMline(&l, GRIDCOLORS); // likely low-relevance line
|
||||
else if (backc1 != frontc1 || backc2 != frontc2) {
|
||||
AM_drawMline(&l, CDWALLCOLORS); // ceiling level change
|
||||
}
|
||||
else
|
||||
AM_drawMline(&l, TSWALLCOLORS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef SLOPEPARAMS
|
||||
|
||||
//
|
||||
// Rotation in 2D.
|
||||
// Used to rotate player arrow line character.
|
||||
@@ -1226,19 +1004,23 @@ static inline void AM_drawPlayers(void)
|
||||
player_t *p;
|
||||
INT32 color = GREENS;
|
||||
|
||||
if (!multiplayer)
|
||||
{
|
||||
AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle, DWHITE, plr->mo->x, plr->mo->y);
|
||||
return;
|
||||
}
|
||||
|
||||
// multiplayer (how??)
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!players[i].ingame || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (!players[i].mo)
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
|
||||
p = &players[i];
|
||||
if (p->skincolor > 0)
|
||||
color = R_GetTranslationColormap(TC_DEFAULT, p->skincolor, GTC_CACHE)[GREENS + 8];
|
||||
|
||||
AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 16<<FRACBITS, p->mo->angle, color, p->mo->x, p->mo->y);
|
||||
AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, color, p->mo->x, p->mo->y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1252,65 +1034,19 @@ static inline void AM_drawThings(UINT8 colors)
|
||||
t = sectors[i].thinglist;
|
||||
while (t)
|
||||
{
|
||||
if (!t->player)
|
||||
AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<<FRACBITS, t->angle, colors, t->x, t->y);
|
||||
AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<<FRACBITS, t->angle, colors, t->x, t->y);
|
||||
t = t->snext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void AM_drawSpecialThingsOnly(UINT8 colors)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < numsectors; i++)
|
||||
{
|
||||
mobj_t *t = NULL;
|
||||
|
||||
// this is fucking horrible
|
||||
const INT32 special = GETSECSPECIAL(sectors[i].special, 1);
|
||||
|
||||
if (GetDamage(special) != 0
|
||||
//|| sectors[i].friction < ORIG_FRICTION
|
||||
|| GetOffroad(special) != 0)
|
||||
continue;
|
||||
|
||||
t = sectors[i].thinglist;
|
||||
while (t)
|
||||
{
|
||||
if (t->type == MT_RANDOMITEM
|
||||
|| t->type == MT_STARPOST
|
||||
|| (t->flags & MF_SPRING))
|
||||
AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<<FRACBITS, t->angle, colors, t->x, t->y);
|
||||
t = t->snext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws the crosshair.
|
||||
/** Draws the crosshair, actually just a dot in software mode.
|
||||
*
|
||||
* \param color Color for the crosshair.
|
||||
*/
|
||||
static inline void AM_drawCrosshair(UINT8 color)
|
||||
{
|
||||
const fixed_t scale = 4<<FRACBITS;
|
||||
size_t i;
|
||||
fline_t fl;
|
||||
|
||||
for (i = 0; i < NUMCROSSMARKLINES; i++)
|
||||
{
|
||||
fl.a.x = FixedMul(cross_mark[i].a.x, scale) >> FRACBITS;
|
||||
fl.a.y = FixedMul(cross_mark[i].a.y, scale) >> FRACBITS;
|
||||
fl.b.x = FixedMul(cross_mark[i].b.x, scale) >> FRACBITS;
|
||||
fl.b.y = FixedMul(cross_mark[i].b.y, scale) >> FRACBITS;
|
||||
|
||||
fl.a.x += f_x + (f_w / 2);
|
||||
fl.a.y += f_y + (f_h / 2);
|
||||
fl.b.x += f_x + (f_w / 2);
|
||||
fl.b.y += f_y + (f_h / 2);
|
||||
|
||||
AM_drawFline(&fl, color);
|
||||
}
|
||||
V_DrawFill(f_w/2 + f_x, f_h/2 + f_y, 1, 1, color|V_NOSCALESTART);
|
||||
}
|
||||
|
||||
/** Draws the automap.
|
||||
@@ -1320,81 +1056,11 @@ void AM_Drawer(void)
|
||||
if (!automapactive)
|
||||
return;
|
||||
|
||||
AM_drawFline = AM_drawFline_soft;
|
||||
#ifdef HWRENDER
|
||||
if (rendermode == render_opengl)
|
||||
AM_drawFline = HWR_drawAMline;
|
||||
#endif
|
||||
|
||||
AM_clearFB(BACKGROUND);
|
||||
if (draw_grid) AM_drawGrid(GRIDCOLORS);
|
||||
AM_drawWalls(PASS_FOF|PASS_INTANGIBLE|PASS_SOLID);
|
||||
AM_drawThings(THINGCOLORS);
|
||||
AM_drawWalls();
|
||||
AM_drawPlayers();
|
||||
AM_drawThings(THINGCOLORS);
|
||||
|
||||
if (!followplayer) AM_drawCrosshair(XHAIRCOLORS);
|
||||
}
|
||||
|
||||
minigen_t *AM_MinimapGenerate(INT32 mul)
|
||||
{
|
||||
static minigen_t ret = {0};
|
||||
|
||||
if (automapactive)
|
||||
return NULL;
|
||||
|
||||
am_minigen = true;
|
||||
|
||||
AM_drawFline = AM_drawFline_soft; // yes, even in GL
|
||||
|
||||
//AM_FrameBufferInit();
|
||||
f_x = f_y = 0;
|
||||
ret.w = f_w = minimapinfo.minimap_w * mul;
|
||||
ret.h = f_h = minimapinfo.minimap_h * mul;
|
||||
am_buf = ret.buf = NULL;
|
||||
|
||||
//AM_LevelInit();
|
||||
AM_findMinMaxBoundaries();
|
||||
scale_mtof = min_scale_mtof - (min_scale_mtof/20);
|
||||
scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
|
||||
|
||||
//AM_initVariables();
|
||||
f_oldloc.x = INT32_MAX;
|
||||
|
||||
m_paninc.x = m_paninc.y = 0;
|
||||
ftom_zoommul = FRACUNIT;
|
||||
mtof_zoommul = FRACUNIT;
|
||||
|
||||
m_w = FTOM(f_w);
|
||||
m_h = FTOM(f_h);
|
||||
|
||||
//AM_changeWindowLoc();
|
||||
m_x = min_x - FixedMul(m_w, FRACUNIT/40);
|
||||
m_y = min_y - FixedMul(m_h, FRACUNIT/40);
|
||||
m_x2 = m_x + m_w;
|
||||
m_y2 = m_y + m_h;
|
||||
|
||||
// for saving & restoring
|
||||
old_m_x = m_x;
|
||||
old_m_y = m_y;
|
||||
old_m_w = m_w;
|
||||
old_m_h = m_h;
|
||||
|
||||
am_buf = ret.buf = malloc((f_w*f_h));
|
||||
|
||||
if (ret.buf == NULL)
|
||||
return NULL;
|
||||
|
||||
//AM_clearFB(BACKGROUND);
|
||||
memset(am_buf, 0xff, (f_w*f_h));
|
||||
AM_drawSpecialThingsOnly(BACKGROUND);
|
||||
AM_drawWalls(PASS_FOF);
|
||||
AM_drawWalls(PASS_INTANGIBLE);
|
||||
AM_drawWalls(PASS_SOLID);
|
||||
|
||||
am_buf = NULL;
|
||||
am_recalc = true;
|
||||
|
||||
am_minigen = false;
|
||||
|
||||
return &ret;
|
||||
AM_drawCrosshair(XHAIRCOLORS);
|
||||
}
|
||||
+8
-17
@@ -1,10 +1,8 @@
|
||||
// SONIC ROBO BLAST 2 KART
|
||||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
// Copyright (C) 2024 by Vivian "toastergrl" Grannell.
|
||||
// Copyright (C) 2024 by Kart Krew.
|
||||
// Copyright (C) 2020 by Sonic Team Junior.
|
||||
// Copyright (C) 2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2018 by Sonic Team Junior.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
@@ -28,6 +26,11 @@ typedef struct
|
||||
fpoint_t a, b;
|
||||
} fline_t;
|
||||
|
||||
// Used by ST StatusBar stuff.
|
||||
#define AM_MSGHEADER (('a'<<24)+('m'<<16))
|
||||
#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8))
|
||||
#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8))
|
||||
|
||||
extern boolean am_recalc; // true if screen size changes
|
||||
extern boolean automapactive; // In AutoMap mode?
|
||||
|
||||
@@ -40,19 +43,7 @@ void AM_Ticker(void);
|
||||
// Called by main loop, instead of view drawer if automap is active.
|
||||
void AM_Drawer(void);
|
||||
|
||||
// Enables the automap.
|
||||
void AM_Start(void);
|
||||
|
||||
// Called to force the automap to quit if the level is completed while it is up.
|
||||
void AM_Stop(void);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
INT32 w, h;
|
||||
UINT8 *buf;
|
||||
} minigen_t;
|
||||
|
||||
// Minimap generation
|
||||
minigen_t *AM_MinimapGenerate(INT32 mul);
|
||||
|
||||
#endif
|
||||
@@ -184,6 +184,8 @@ void I_OutputMsg(const char *fmt, ...)
|
||||
|
||||
void I_StartupMouse(void){}
|
||||
|
||||
void I_StartupMouse2(void){}
|
||||
|
||||
void I_StartupKeyboard(void){}
|
||||
|
||||
INT32 I_GetKey(void)
|
||||
@@ -240,6 +242,8 @@ void I_GetJoystickEvents(void){}
|
||||
|
||||
void I_GetJoystick2Events(void){}
|
||||
|
||||
void I_GetMouseEvents(void){}
|
||||
|
||||
char *I_GetEnv(const char *name)
|
||||
{
|
||||
LOGW("I_GetEnv() called?!");
|
||||
|
||||
+5
-2
@@ -36,7 +36,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
|
||||
return;
|
||||
|
||||
// Lua can handle it!
|
||||
if (LUA_HookBotAI(sonic, tails, cmd))
|
||||
if (LUAh_BotAI(sonic, tails, cmd))
|
||||
return;
|
||||
|
||||
if (tails->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
|
||||
@@ -118,8 +118,11 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
return;
|
||||
}
|
||||
|
||||
// Bot AI isn't programmed in analog.
|
||||
//CV_SetValue(&cv_analog2, false);
|
||||
|
||||
// Let Lua scripts build ticcmds
|
||||
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)))
|
||||
if (LUAh_BotTiccmd(player, cmd))
|
||||
return;
|
||||
|
||||
// We don't have any main character AI, sorry. D:
|
||||
|
||||
@@ -49,9 +49,7 @@ OBJS:=$(OBJS) \
|
||||
$(OBJDIR)/lua_skinlib.o \
|
||||
$(OBJDIR)/lua_thinkerlib.o \
|
||||
$(OBJDIR)/lua_maplib.o \
|
||||
$(OBJDIR)/hashtable.o \
|
||||
$(OBJDIR)/lua_hudlib.o \
|
||||
$(OBJDIR)/lua_hudlib_drawlist.o \
|
||||
$(OBJDIR)/lua_udatalib.o \
|
||||
$(OBJDIR)/lua_glib.o
|
||||
|
||||
+70
-5
@@ -17,14 +17,27 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "../i_system.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../d_main.h"
|
||||
#include "../m_misc.h"
|
||||
|
||||
|
||||
|
||||
#define IO_INPUT 1
|
||||
#define IO_OUTPUT 2
|
||||
|
||||
#define FILELIMIT 1024*1024 // Size limit for reading/writing files
|
||||
|
||||
|
||||
static const char *const fnames[] = {"input", "output"};
|
||||
static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder
|
||||
".txt",
|
||||
".sav2",
|
||||
".cfg",
|
||||
".png",
|
||||
".bmp"
|
||||
};
|
||||
|
||||
|
||||
static int pushresult (lua_State *L, int i, const char *filename) {
|
||||
@@ -146,12 +159,59 @@ static int io_tostring (lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int StartsWith(const char *a, const char *b) // this is wolfs being lazy yet again
|
||||
{
|
||||
if(strncmp(a, b, strlen(b)) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int io_open (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
FILE **pf = newfile(L);
|
||||
*pf = fopen(filename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
FILE **pf;
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
int pass = 0;
|
||||
size_t i;
|
||||
int length = strlen(filename);
|
||||
char *splitter, *forward, *backward;
|
||||
char *destFilename;
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
|
||||
for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++)
|
||||
{
|
||||
if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i]))
|
||||
{
|
||||
pass = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strstr(filename, "..") || strchr(filename, ':') || StartsWith(filename, "\\")
|
||||
|| StartsWith(filename, "/") || !pass)
|
||||
{
|
||||
luaL_error(L,"access denied to %s", filename);
|
||||
return pushresult(L,0,filename);
|
||||
}
|
||||
|
||||
destFilename = va("%s"PATHSEP"luafiles"PATHSEP"%s", srb2home, filename);
|
||||
|
||||
// Make directories as needed
|
||||
splitter = destFilename;
|
||||
|
||||
forward = strchr(splitter, '/');
|
||||
backward = strchr(splitter, '\\');
|
||||
while ((splitter = (forward && backward) ? min(forward, backward) : (forward ?: backward)))
|
||||
{
|
||||
*splitter = 0;
|
||||
I_mkdir(destFilename, 0755);
|
||||
*splitter = '/';
|
||||
splitter++;
|
||||
|
||||
forward = strchr(splitter, '/');
|
||||
backward = strchr(splitter, '\\');
|
||||
}
|
||||
|
||||
pf = newfile(L);
|
||||
*pf = fopen(destFilename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +454,11 @@ static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, arg, &l);
|
||||
if (ftell(f) + l > FILELIMIT)
|
||||
{
|
||||
luaL_error(L,"write limit bypassed in file. Changes have been discarded.");
|
||||
break;
|
||||
}
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
luajit
|
||||
luajit.h
|
||||
luajit_relver.txt
|
||||
lj_bcdef.h
|
||||
lj_ffdef.h
|
||||
lj_libdef.h
|
||||
lj_recdef.h
|
||||
lj_folddef.h
|
||||
lj_vm.[sS]
|
||||
@@ -1,408 +0,0 @@
|
||||
##############################################################################
|
||||
# LuaJIT Makefile. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Also works with MinGW and Cygwin on Windows.
|
||||
# Please check msvcbuild.bat for building with MSVC on Windows.
|
||||
#
|
||||
# Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
# this is a very dumbed down version of the LuaJIT makefile.
|
||||
# to get the original makefile, visit
|
||||
# https://github.com/GenericHeroGuy/bluajit
|
||||
|
||||
XCFLAGS=
|
||||
XCFLAGS+= -DLUAJIT_DISABLE_FFI
|
||||
#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
|
||||
#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
#XCFLAGS+= -DLUAJIT_USE_VALGRIND
|
||||
#XCFLAGS+= -DLUAJIT_USE_GDBJIT
|
||||
#XCFLAGS+= -DLUA_USE_APICHECK
|
||||
#XCFLAGS+= -DLUA_USE_ASSERT
|
||||
XCFLAGS+= -DLUAJIT_DISABLE_BYTECODE
|
||||
|
||||
# load standard libraries in debug mode, so jit.dump etc works
|
||||
ifndef DEBUGMODE
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB -DLUAJIT_DISABLE_BUFFER
|
||||
endif
|
||||
# additional functions
|
||||
#XCFLAGS+= -DLUAJIT_SRB2LIB_OS
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB_DEBUG
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB_IO
|
||||
|
||||
# don't touch these unless you wanna have a bad time
|
||||
XCFLAGS+= -DLUAJIT_NUMMODE=2
|
||||
XCFLAGS+= -DLUAJIT_INTONLY
|
||||
# needed for the headers
|
||||
OPTS+=-DLUAJIT_INTONLY
|
||||
|
||||
ifdef MINGW
|
||||
TARGET_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM))
|
||||
HOST_SYS= Windows
|
||||
else
|
||||
HOST_SYS:= $(shell uname -s)
|
||||
ifneq (,$(findstring MINGW,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring MSYS,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring CYGWIN,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= cygwin
|
||||
endif
|
||||
endif
|
||||
|
||||
ASOPTIONS= $(CCOPT) $(XCFLAGS) $(CFLAGS)
|
||||
CCOPTIONS= $(ASOPTIONS)
|
||||
|
||||
HOST_CC= $(CC)
|
||||
HOST_RM?= rm -f
|
||||
|
||||
HOST_XCFLAGS= -Ibluajit
|
||||
HOST_XLIBS=
|
||||
HOST_ACFLAGS= $(XCFLAGS) $(HOST_XCFLAGS) $(TARGET_ARCH)
|
||||
|
||||
TARGET_CC= $(CROSS)$(CC)
|
||||
|
||||
TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE
|
||||
TARGET_XLIBS= -lm
|
||||
TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS)
|
||||
TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS)
|
||||
TARGET_ACFLAGS+= $(LUA_CFLAGS)
|
||||
|
||||
TARGET_TESTARCH:=$(shell $(TARGET_CC) $(TARGET_ACFLAGS) -E bluajit/lj_arch.h -dM)
|
||||
ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x86
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= arm
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__AARCH64EB__=1
|
||||
endif
|
||||
TARGET_LJARCH= arm64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE
|
||||
else
|
||||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE
|
||||
endif
|
||||
TARGET_LJARCH= ppc
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__MIPSEL__=1
|
||||
endif
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= mips64
|
||||
else
|
||||
TARGET_LJARCH= mips
|
||||
endif
|
||||
else
|
||||
$(error Unsupported target architecture)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH)))
|
||||
TARGET_SYS= PS3
|
||||
TARGET_ARCH+= -D__CELLOS_LV2__
|
||||
TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
TARGET_XLIBS+= -lpthread
|
||||
endif
|
||||
|
||||
TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH))
|
||||
|
||||
ifneq (,$(PREFIX))
|
||||
ifneq (/usr/local,$(PREFIX))
|
||||
TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\"
|
||||
endif
|
||||
endif
|
||||
ifneq (,$(MULTILIB))
|
||||
TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\"
|
||||
endif
|
||||
ifneq (,$(LMULTILIB))
|
||||
TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\"
|
||||
endif
|
||||
ifneq (,$(INSTALL_LJLIBD))
|
||||
TARGET_XCFLAGS+= -DLUA_LJDIR=\"$(INSTALL_LJLIBD)\"
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Target system detection.
|
||||
##############################################################################
|
||||
|
||||
TARGET_SYS?= $(HOST_SYS)
|
||||
ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1))
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
ifeq (,$(MACOSX_DEPLOYMENT_TARGET))
|
||||
$(error missing: export MACOSX_DEPLOYMENT_TARGET=XX.YY)
|
||||
endif
|
||||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
ifeq (arm64,$(TARGET_LJARCH))
|
||||
TARGET_XCFLAGS+= -fno-omit-frame-pointer
|
||||
endif
|
||||
else
|
||||
ifeq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
# Find out whether the target toolchain always generates unwind tables.
|
||||
TARGET_TESTUNWIND=$(shell exec 2>/dev/null; echo 'extern void b(void);int a(void){b();return 0;}' | $(TARGET_CC) -c -x c - -o tmpunwind.o && { grep -qa -e eh_frame -e __unwind_info tmpunwind.o || grep -qU -e eh_frame -e __unwind_info tmpunwind.o; } && echo E; rm -f tmpunwind.o)
|
||||
ifneq (,$(findstring E,$(TARGET_TESTUNWIND)))
|
||||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL
|
||||
endif
|
||||
endif
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
ifeq (GNU/kFreeBSD,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(HOST_SYS),$(TARGET_SYS))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS
|
||||
else
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DTARGET_OS_IPHONE=1
|
||||
else
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Files and pathnames.
|
||||
##############################################################################
|
||||
|
||||
MINILUA_O= $(OBJDIR)/minilua.o
|
||||
MINILUA_LIBS= -lm
|
||||
MINILUA_T= $(OBJDIR)/minilua
|
||||
MINILUA_X= $(MINILUA_T)
|
||||
MINILUA_DEP=
|
||||
|
||||
HOST_LUA= $(MINILUA_X)
|
||||
MINILUA_DEP= $(MINILUA_T)
|
||||
|
||||
DASM_DIR= bluajit/dynasm
|
||||
DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua
|
||||
DASM_XFLAGS=
|
||||
DASM_AFLAGS=
|
||||
DASM_ARCH= $(TARGET_LJARCH)
|
||||
|
||||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ENDIAN_LE
|
||||
else
|
||||
DASM_AFLAGS+= -D ENDIAN_BE
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D P64
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D JIT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FFI
|
||||
endif
|
||||
ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D DUALNUM
|
||||
endif
|
||||
ifneq (,$(findstring LJ_INTONLY 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D INTONLY
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FPU
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=1
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=0
|
||||
endif
|
||||
ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D HFABI
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=0
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=1
|
||||
endif
|
||||
ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D NO_UNWIND
|
||||
TARGET_ARCH+= -DLUAJIT_NO_UNWIND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ABI_PAUTH 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D PAUTH
|
||||
TARGET_ARCH+= -DLJ_ABI_PAUTH=1
|
||||
endif
|
||||
DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH))))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D WIN
|
||||
endif
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH)))
|
||||
DASM_ARCH= x86
|
||||
endif
|
||||
else
|
||||
ifeq (arm,$(TARGET_LJARCH))
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D IOS
|
||||
endif
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPSR6 ,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D MIPSR6
|
||||
endif
|
||||
ifeq (ppc,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SQRT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ROUND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D GPR64
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D PPE -D TOC
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS)
|
||||
DASM_DASC= bluajit/vm_$(DASM_ARCH).dasc
|
||||
|
||||
GIT= git
|
||||
ifeq (Windows,$(HOST_SYS)$(HOST_MSYS))
|
||||
GIT_RELVER= if exist ..\.git ( $(GIT) show -s --format=%%ct >bluajit/luajit_relver.txt ) else ( type ..\.relver >bluajit/luajit_relver.txt )
|
||||
else
|
||||
GIT_RELVER= [ -e ../.git ] && $(GIT) show -s --format=%ct >bluajit/luajit_relver.txt 2>/dev/null || cat ../.relver >bluajit/luajit_relver.txt 2>/dev/null || :
|
||||
endif
|
||||
GIT_DEP= $(wildcard ../.git/HEAD ../.git/refs/heads/*)
|
||||
|
||||
BUILDVM_O= $(OBJDIR)/buildvm.o $(OBJDIR)/buildvm_asm.o $(OBJDIR)/buildvm_peobj.o \
|
||||
$(OBJDIR)/buildvm_lib.o $(OBJDIR)/buildvm_fold.o
|
||||
BUILDVM_T= $(OBJDIR)/buildvm
|
||||
BUILDVM_X= $(BUILDVM_T)
|
||||
|
||||
HOST_O= $(MINILUA_O) $(BUILDVM_O)
|
||||
HOST_T= $(MINILUA_T) $(BUILDVM_T)
|
||||
|
||||
LJVM_S= $(OBJDIR)/lj_vm.S
|
||||
LJVM_O= $(OBJDIR)/lj_vm.o
|
||||
LJVM_BOUT= $(LJVM_S)
|
||||
LJVM_MODE= elfasm
|
||||
|
||||
LJLIB_O= lib_base.o lib_string.o lib_table.o lib_io.o lib_jit.o
|
||||
ifeq (,$(filter -DLUAJIT_SRB2LIB,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_math.o lib_bit.o lib_os.o lib_package.o lib_debug.o lib_ffi.o lib_buffer.o
|
||||
else
|
||||
ifneq (,$(filter -DLUAJIT_SRB2LIB_OS,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_os.o
|
||||
endif
|
||||
ifneq (,$(filter -DLUAJIT_SRB2LIB_DEBUG,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_debug.o
|
||||
endif
|
||||
endif
|
||||
LJLIB_C= $(addprefix bluajit/,$(LJLIB_O:.o=.c))
|
||||
|
||||
LJCORE_O= lj_assert.o lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
|
||||
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
|
||||
lj_prng.o lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o \
|
||||
lj_strscan.o lj_strfmt.o lj_strfmt_num.o lj_serialize.o \
|
||||
lj_api.o lj_profile.o \
|
||||
lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
|
||||
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
|
||||
lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
|
||||
lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
|
||||
lj_asm.o lj_trace.o lj_gdbjit.o \
|
||||
lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \
|
||||
lj_carith.o lj_clib.o lj_cparse.o \
|
||||
lj_lib.o lj_alloc.o lib_aux.o \
|
||||
$(LJLIB_O) lib_init.o
|
||||
|
||||
LJCORE_O:= $(addprefix $(OBJDIR)/,$(LJCORE_O))
|
||||
LJVMCORE_O= $(LJVM_O) $(LJCORE_O)
|
||||
|
||||
LIB_VMDEF= bluajit/jit/vmdef.lua
|
||||
LIB_VMDEFP= $(LIB_VMDEF)
|
||||
|
||||
ALL_T= $(HOST_T)
|
||||
ALL_HDRGEN:= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \
|
||||
host/buildvm_arch.h luajit.h
|
||||
ALL_HDRGEN:= $(addprefix bluajit/,$(ALL_HDRGEN))
|
||||
ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) bluajit/luajit_relver.txt $(LIB_VMDEFP)
|
||||
|
||||
##############################################################################
|
||||
# Build mode handling.
|
||||
##############################################################################
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
LJVM_MODE= peobj
|
||||
LJVM_BOUT= $(LJVM_O)
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
MINILUA_T= $(OBJDIR)/minilua.exe
|
||||
BUILDVM_T= $(OBJDIR)/buildvm.exe
|
||||
ifeq (,$(HOST_MSYS))
|
||||
MINILUA_X= $(OBJDIR)\minilua
|
||||
BUILDVM_X= $(OBJDIR)\buildvm
|
||||
HOST_RM= del
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
OBJS:=$(OBJS) \
|
||||
$(LJVMCORE_O) \
|
||||
$(OBJDIR)/lua_script.o \
|
||||
$(OBJDIR)/lua_baselib.o \
|
||||
$(OBJDIR)/lua_blockmaplib.o \
|
||||
$(OBJDIR)/lua_mathlib.o \
|
||||
$(OBJDIR)/lua_hooklib.o \
|
||||
$(OBJDIR)/lua_consolelib.o \
|
||||
$(OBJDIR)/lua_infolib.o \
|
||||
$(OBJDIR)/lua_mobjlib.o \
|
||||
$(OBJDIR)/lua_playerlib.o \
|
||||
$(OBJDIR)/lua_skinlib.o \
|
||||
$(OBJDIR)/lua_thinkerlib.o \
|
||||
$(OBJDIR)/lua_maplib.o \
|
||||
$(OBJDIR)/hashtable.o \
|
||||
$(OBJDIR)/lua_hudlib.o \
|
||||
$(OBJDIR)/lua_hudlib_drawlist.o \
|
||||
$(OBJDIR)/lua_udatalib.o \
|
||||
$(OBJDIR)/lua_glib.o
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
Q= @
|
||||
E= @true
|
||||
#Q=
|
||||
#E= @:
|
||||
|
||||
$(MINILUA_T): $(MINILUA_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(HOST_CC) -o $@ $(MINILUA_O) $(MINILUA_LIBS)
|
||||
|
||||
bluajit/luajit.h: $(MINILUA_DEP) $(GIT_DEP) bluajit/luajit_rolling.h
|
||||
$(E) "VERSION $@"
|
||||
$(Q)$(GIT_RELVER)
|
||||
$(Q)cd bluajit && ../$(HOST_LUA) host/genversion.lua
|
||||
|
||||
bluajit/host/buildvm_arch.h: $(DASM_DASC) $(MINILUA_DEP) bluajit/lj_arch.h bluajit/lua.h bluajit/luaconf.h
|
||||
$(E) "DYNASM $@"
|
||||
$(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC)
|
||||
|
||||
$(OBJDIR)/buildvm.o: $(DASM_DIR)/dasm_*.h
|
||||
|
||||
$(BUILDVM_T): $(BUILDVM_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(HOST_CC) -o $@ $(BUILDVM_O)
|
||||
|
||||
$(LJVM_BOUT): $(BUILDVM_T)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@
|
||||
|
||||
bluajit/lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_libdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_recdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C)
|
||||
|
||||
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C)
|
||||
|
||||
bluajit/lj_folddef.h: $(BUILDVM_T) bluajit/lj_opt_fold.c
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m folddef -o $@ bluajit/lj_opt_fold.c
|
||||
|
||||
$(OBJDIR)/%.o: bluajit/%.c
|
||||
$(E) "CC $@"
|
||||
$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: $(OBJDIR)/lj_vm.S
|
||||
$(E) "ASM $@"
|
||||
$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $<
|
||||
|
||||
$(HOST_O): $(OBJDIR)/%.o: bluajit/host/%.c
|
||||
$(E) "HOSTCC $@"
|
||||
$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $<
|
||||
@@ -1,456 +0,0 @@
|
||||
/*
|
||||
** DynASM ARM encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC,
|
||||
DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12, DASM_IMMV8,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
|
||||
if (n <= 255) return (int)(n + (i << 8));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
case DASM_IMM16:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMV8:
|
||||
CK((n & 3) == 0, RANGE_I);
|
||||
n >>= 2;
|
||||
/* fallthrough */
|
||||
case DASM_IMML8:
|
||||
case DASM_IMML12:
|
||||
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
|
||||
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
|
||||
case DASM_IMML8: case DASM_IMML12: case DASM_IMMV8: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp - 4);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4;
|
||||
patchrel:
|
||||
if ((ins & 0x800) == 0) {
|
||||
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x00ffffff);
|
||||
} else if ((ins & 0x1000)) {
|
||||
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
|
||||
goto patchimml8;
|
||||
} else if ((ins & 0x2000) == 0) {
|
||||
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
|
||||
goto patchimml;
|
||||
} else {
|
||||
CK((n & 3) == 0 && -1020 <= n && n <= 1020, RANGE_REL);
|
||||
n >>= 2;
|
||||
goto patchimml;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= dasm_imm12((unsigned int)n);
|
||||
break;
|
||||
case DASM_IMM16:
|
||||
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
|
||||
break;
|
||||
case DASM_IMML8: patchimml8:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
|
||||
((-n & 0x0f) | ((-n & 0xf0) << 4));
|
||||
break;
|
||||
case DASM_IMML12: case DASM_IMMV8: patchimml:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
** DynASM ARM64 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm64"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_REL_A,
|
||||
DASM_IMM, DASM_IMM6, DASM_IMM12, DASM_IMM13W, DASM_IMM13X, DASM_IMML,
|
||||
DASM_IMMV, DASM_VREG,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_RANGE_VREG 0x16000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
if ((n >> 12) == 0)
|
||||
return n;
|
||||
else if ((n & 0xff000fff) == 0)
|
||||
return (n >> 12) | 0x1000;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dasm_ffs(unsigned long long x)
|
||||
{
|
||||
int n = -1;
|
||||
while (x) { x >>= 1; n++; }
|
||||
return n;
|
||||
}
|
||||
|
||||
static int dasm_imm13(int lo, int hi)
|
||||
{
|
||||
int inv = 0, w = 64, s = 0xfff, xa, xb;
|
||||
unsigned long long n = (((unsigned long long)hi) << 32) | (unsigned int)lo;
|
||||
unsigned long long m = 1ULL, a, b, c;
|
||||
if (n & 1) { n = ~n; inv = 1; }
|
||||
a = n & (unsigned long long)-(long long)n;
|
||||
b = (n+a)&(unsigned long long)-(long long)(n+a);
|
||||
c = (n+a-b)&(unsigned long long)-(long long)(n+a-b);
|
||||
xa = dasm_ffs(a); xb = dasm_ffs(b);
|
||||
if (c) {
|
||||
w = dasm_ffs(c) - xa;
|
||||
if (w == 32) m = 0x0000000100000001UL;
|
||||
else if (w == 16) m = 0x0001000100010001UL;
|
||||
else if (w == 8) m = 0x0101010101010101UL;
|
||||
else if (w == 4) m = 0x1111111111111111UL;
|
||||
else if (w == 2) m = 0x5555555555555555UL;
|
||||
else return -1;
|
||||
s = (-2*w & 0x3f) - 1;
|
||||
} else if (!a) {
|
||||
return -1;
|
||||
} else if (xb == -1) {
|
||||
xb = 64;
|
||||
}
|
||||
if ((b-a) * m != n) return -1;
|
||||
if (inv) {
|
||||
return ((w - xb) << 6) | (s+w+xa-xb);
|
||||
} else {
|
||||
return ((w - xa) << 6) | (s+xb-xa);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: if ((ins & 0x8000)) ofs += 8; break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
if ((ins & 0x8000)) ofs += 8;
|
||||
break;
|
||||
case DASM_REL_A:
|
||||
b[pos++] = n;
|
||||
b[pos++] = va_arg(ap, int);
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM6:
|
||||
CK((n >> 6) == 0, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM13W:
|
||||
CK(dasm_imm13(n, n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM13X: {
|
||||
int m = va_arg(ap, int);
|
||||
CK(dasm_imm13(n, m) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
b[pos++] = m;
|
||||
break;
|
||||
}
|
||||
case DASM_IMML: {
|
||||
#ifdef DASM_CHECKS
|
||||
int scale = (ins & 3);
|
||||
CK((!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ||
|
||||
(unsigned int)(n+256) < 512, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
case DASM_IMMV:
|
||||
ofs += 4;
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_VREG:
|
||||
CK(n < 32, RANGE_VREG);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM6: case DASM_IMM12: case DASM_IMM13W:
|
||||
case DASM_IMML: case DASM_IMMV: case DASM_VREG: pos++; break;
|
||||
case DASM_IMM13X: case DASM_REL_A: pos += 2; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(int)(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xd503201f;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
ptrdiff_t na = (ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp + 4;
|
||||
n = (int)na;
|
||||
CK((ptrdiff_t)n == na, RANGE_REL);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) + 4;
|
||||
patchrel:
|
||||
if (!(ins & 0xf800)) { /* B, BL */
|
||||
CK((n & 3) == 0 && ((n+0x08000000) >> 28) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x03ffffff);
|
||||
} else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */
|
||||
CK((n & 3) == 0 && ((n+0x00100000) >> 21) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x00ffffe0);
|
||||
} else if ((ins & 0x3000) == 0x2000) { /* ADR */
|
||||
CK(((n+0x00100000) >> 21) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x00ffffe0) | ((n & 3) << 29);
|
||||
} else if ((ins & 0x3000) == 0x3000) { /* ADRP */
|
||||
cp[-1] |= ((n >> 9) & 0x00ffffe0) | (((n >> 12) & 3) << 29);
|
||||
} else if ((ins & 0x1000)) { /* TBZ, TBNZ */
|
||||
CK((n & 3) == 0 && ((n+0x00008000) >> 16) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x0007ffe0);
|
||||
} else if ((ins & 0x8000)) { /* absolute */
|
||||
cp[0] = (unsigned int)((ptrdiff_t)cp - 4 + n);
|
||||
cp[1] = (unsigned int)(((ptrdiff_t)cp - 4 + n) >> 32);
|
||||
cp += 2;
|
||||
}
|
||||
break;
|
||||
case DASM_REL_A: {
|
||||
ptrdiff_t na = (((ptrdiff_t)(*b++) << 32) | (unsigned int)n);
|
||||
if ((ins & 0x3000) == 0x3000) { /* ADRP */
|
||||
ins &= ~0x1000;
|
||||
na = (na >> 12) - (((ptrdiff_t)cp - 4) >> 12);
|
||||
} else {
|
||||
na = na - (ptrdiff_t)cp + 4;
|
||||
}
|
||||
n = (int)na;
|
||||
CK((ptrdiff_t)n == na, RANGE_REL);
|
||||
goto patchrel;
|
||||
}
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM6:
|
||||
cp[-1] |= ((n&31) << 19) | ((n&32) << 26);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= (dasm_imm12((unsigned int)n) << 10);
|
||||
break;
|
||||
case DASM_IMM13W:
|
||||
cp[-1] |= (dasm_imm13(n, n) << 10);
|
||||
break;
|
||||
case DASM_IMM13X:
|
||||
cp[-1] |= (dasm_imm13(n, *b++) << 10);
|
||||
break;
|
||||
case DASM_IMML: {
|
||||
int scale = (ins & 3);
|
||||
cp[-1] |= (!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ?
|
||||
((n << (10-scale)) | 0x01000000) : ((n & 511) << 12);
|
||||
break;
|
||||
}
|
||||
case DASM_IMMV:
|
||||
*cp++ = n;
|
||||
break;
|
||||
case DASM_VREG:
|
||||
cp[-1] |= (n & 0x1f) << (ins & 0x1f);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
** DynASM MIPS encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "mips"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMS,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM: case DASM_IMMS:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMMS: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(int)(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1);
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n);
|
||||
if (ins & 2048)
|
||||
n = (n + (int)(size_t)base) & 0x0fffffff;
|
||||
else
|
||||
n = n - (int)((char *)cp - base);
|
||||
patchrel: {
|
||||
unsigned int e = 16 + ((ins >> 12) & 15);
|
||||
CK((n & 3) == 0 &&
|
||||
((n + ((ins & 2048) ? 0 : (1<<(e+1)))) >> (e+2)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n>>2) & ((1<<e)-1));
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMMS:
|
||||
cp[-1] |= ((n>>3) & 4); n &= 0x1f;
|
||||
/* fallthrough */
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,12 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM MIPS64 module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
-- This module just sets 64 bit mode for the combined MIPS/MIPS64 module.
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
mips64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_mips")
|
||||
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
** DynASM PPC/PPC64 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "ppc"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMSH,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
CK((n >> 6) == 0, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMMSH: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1) - 4;
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
|
||||
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
cp[-1] |= (ins & 1) ? ((n&31)<<11)|((n&32)>>4) : ((n&31)<<6)|(n&32);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
** DynASM encoding engine prototypes.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#ifndef _DASM_PROTO_H
|
||||
#define _DASM_PROTO_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define DASM_IDENT "DynASM 1.5.0"
|
||||
#define DASM_VERSION 10500 /* 1.5.0 */
|
||||
|
||||
#ifndef Dst_DECL
|
||||
#define Dst_DECL dasm_State **Dst
|
||||
#endif
|
||||
|
||||
#ifndef Dst_REF
|
||||
#define Dst_REF (*Dst)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_FDEF
|
||||
#define DASM_FDEF extern
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_GROW
|
||||
#define DASM_M_GROW(ctx, t, p, sz, need) \
|
||||
do { \
|
||||
size_t _sz = (sz), _need = (need); \
|
||||
if (_sz < _need) { \
|
||||
if (_sz < 16) _sz = 16; \
|
||||
while (_sz < _need) _sz += _sz; \
|
||||
(p) = (t *)realloc((p), _sz); \
|
||||
if ((p) == NULL) exit(1); \
|
||||
(sz) = _sz; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_FREE
|
||||
#define DASM_M_FREE(ctx, p, sz) free(p)
|
||||
#endif
|
||||
|
||||
/* Internal DynASM encoder state. */
|
||||
typedef struct dasm_State dasm_State;
|
||||
|
||||
|
||||
/* Initialize and free DynASM state. */
|
||||
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
|
||||
DASM_FDEF void dasm_free(Dst_DECL);
|
||||
|
||||
/* Setup global array. Must be called before dasm_setup(). */
|
||||
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
||||
|
||||
/* Setup encoder. */
|
||||
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
|
||||
|
||||
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
||||
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
|
||||
|
||||
/* Link sections and return the resulting size. */
|
||||
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
|
||||
|
||||
/* Encode sections into buffer. */
|
||||
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
|
||||
|
||||
/* Get PC label offset. */
|
||||
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
|
||||
#else
|
||||
#define dasm_checkstep(a, b) 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _DASM_PROTO_H */
|
||||
@@ -1,12 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM x64 module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
-- This module just sets 64 bit mode for the combined x86/x64 module.
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
x64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_x86")
|
||||
@@ -1,523 +0,0 @@
|
||||
/*
|
||||
** DynASM x86 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "x86"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. DASM_STOP must be 255. */
|
||||
enum {
|
||||
DASM_DISP = 233,
|
||||
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
|
||||
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
|
||||
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
|
||||
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_VREG 0x15000000
|
||||
#define DASM_S_UNDEF_L 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned char *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs, mrm = -1;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
if (action < DASM_DISP) {
|
||||
ofs++;
|
||||
} else if (action <= DASM_REL_A) {
|
||||
int n = va_arg(ap, int);
|
||||
b[pos++] = n;
|
||||
switch (action) {
|
||||
case DASM_DISP:
|
||||
if (n == 0) { if (mrm < 0) mrm = p[-2]; if ((mrm&7) != 5) break; }
|
||||
/* fallthrough */
|
||||
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
|
||||
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
|
||||
case DASM_IMM_D: ofs += 4; break;
|
||||
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
|
||||
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
|
||||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
|
||||
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
|
||||
case DASM_SPACE: p++; ofs += n; break;
|
||||
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
|
||||
case DASM_VREG: CK((n&-16) == 0 && (n != 4 || (*p>>5) != 2), RANGE_VREG);
|
||||
if (*p < 0x40 && p[1] == DASM_DISP) mrm = n;
|
||||
if (*p < 0x20 && (n&7) == 4) ofs++;
|
||||
switch ((*p++ >> 3) & 3) {
|
||||
case 3: n |= b[pos-3]; /* fallthrough */
|
||||
case 2: n |= b[pos-2]; /* fallthrough */
|
||||
case 1: if (n <= 7) { b[pos-1] |= 0x10; ofs--; }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
mrm = -1;
|
||||
} else {
|
||||
int *pl, n;
|
||||
switch (action) {
|
||||
case DASM_REL_LG:
|
||||
case DASM_IMM_LG:
|
||||
n = *p++; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n <= 246) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl -= 246; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
ofs += 4; /* Maximum offset needed. */
|
||||
if (action == DASM_REL_LG || action == DASM_REL_PC) {
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
} else if (sizeof(ptrdiff_t) == 8) {
|
||||
ofs += 4;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; }
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_ALIGN:
|
||||
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_EXTERN: p += 2; ofs += 4; break;
|
||||
case DASM_ESC: p++; ofs++; break;
|
||||
case DASM_MARK: mrm = p[-2]; break;
|
||||
case DASM_SECTION:
|
||||
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
|
||||
case DASM_STOP: goto stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
int op = 0;
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
switch (action) {
|
||||
case DASM_REL_LG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC: {
|
||||
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
|
||||
if (shrink) { /* Shrinkable branch opcode? */
|
||||
int lofs, lpos = b[pos];
|
||||
if (lpos < 0) goto noshrink; /* Ext global? */
|
||||
lofs = *DASM_POS2PTR(D, lpos);
|
||||
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
|
||||
int i;
|
||||
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
|
||||
lofs += D->sections[i].ofs;
|
||||
} else {
|
||||
lofs -= ofs; /* Bkwd label: unfix offset. */
|
||||
}
|
||||
lofs -= b[pos+1]; /* Short branch ok? */
|
||||
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
|
||||
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
|
||||
}
|
||||
b[pos+1] = shrink;
|
||||
pos += 2;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
|
||||
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
|
||||
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
|
||||
case DASM_LABEL_LG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
|
||||
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
|
||||
case DASM_EXTERN: p += 2; break;
|
||||
case DASM_ESC: op = *p++; break;
|
||||
case DASM_MARK: break;
|
||||
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||
default: op = action; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#define dasmb(x) *cp++ = (unsigned char)(x)
|
||||
#ifndef DASM_ALIGNED_WRITES
|
||||
#define dasmw(x) \
|
||||
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
|
||||
#define dasmd(x) \
|
||||
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
|
||||
#define dasmq(x) \
|
||||
do { *((unsigned long long *)cp) = (unsigned long long)(x); cp+=8; } while (0)
|
||||
#else
|
||||
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
|
||||
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
|
||||
#define dasmq(x) do { dasmd(x); dasmd((x)>>32); } while (0)
|
||||
#endif
|
||||
static unsigned char *dasma_(unsigned char *cp, ptrdiff_t x)
|
||||
{
|
||||
if (sizeof(ptrdiff_t) == 8)
|
||||
dasmq((unsigned long long)x);
|
||||
else
|
||||
dasmd((unsigned int)x);
|
||||
return cp;
|
||||
}
|
||||
#define dasma(x) (cp = dasma_(cp, (x)))
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
unsigned char *base = (unsigned char *)buffer;
|
||||
unsigned char *cp = base;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
unsigned char *mark = NULL;
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_DISP: if (!mark) mark = cp; {
|
||||
unsigned char *mm = mark;
|
||||
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
|
||||
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
|
||||
if (mrm != 5) { mm[-1] -= 0x80; break; } }
|
||||
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
|
||||
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
|
||||
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
|
||||
} else mark = NULL;
|
||||
/* fallthrough */
|
||||
case DASM_IMM_D: wd: dasmd(n); break;
|
||||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
|
||||
/* fallthrough */
|
||||
case DASM_IMM_W: dasmw(n); break;
|
||||
case DASM_VREG: {
|
||||
int t = *p++;
|
||||
unsigned char *ex = cp - (t&7);
|
||||
if ((n & 8) && t < 0xa0) {
|
||||
if (*ex & 0x80) ex[1] ^= 0x20 << (t>>6); else *ex ^= 1 << (t>>6);
|
||||
n &= 7;
|
||||
} else if (n & 0x10) {
|
||||
if (*ex & 0x80) {
|
||||
*ex = 0xc5; ex[1] = (ex[1] & 0x80) | ex[2]; ex += 2;
|
||||
}
|
||||
while (++ex < cp) ex[-1] = *ex;
|
||||
if (mark) mark--;
|
||||
cp--;
|
||||
n &= 7;
|
||||
}
|
||||
if (t >= 0xc0) n <<= 4;
|
||||
else if (t >= 0x40) n <<= 3;
|
||||
else if (n == 4 && t < 0x20) { cp[-1] ^= n; *cp++ = 0x20; }
|
||||
cp[-1] ^= n;
|
||||
break;
|
||||
}
|
||||
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
|
||||
b++; n = (int)(ptrdiff_t)D->globals[-n-10];
|
||||
/* fallthrough */
|
||||
case DASM_REL_A: rel_a:
|
||||
n -= (unsigned int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
|
||||
case DASM_REL_PC: rel_pc: {
|
||||
int shrink = *b++;
|
||||
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
|
||||
n = *pb - ((int)(cp-base) + 4-shrink);
|
||||
if (shrink == 0) goto wd;
|
||||
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
|
||||
goto wb;
|
||||
}
|
||||
case DASM_IMM_LG:
|
||||
p++;
|
||||
if (n < 0) { dasma((ptrdiff_t)D->globals[-n-10]); break; }
|
||||
/* fallthrough */
|
||||
case DASM_IMM_PC: {
|
||||
int *pb = DASM_POS2PTR(D, n);
|
||||
dasma(*pb < 0 ? (ptrdiff_t)pb[1] : (*pb + (ptrdiff_t)base));
|
||||
break;
|
||||
}
|
||||
case DASM_LABEL_LG: {
|
||||
int idx = *p++;
|
||||
if (idx >= 10)
|
||||
D->globals[idx-10] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
|
||||
break;
|
||||
}
|
||||
case DASM_LABEL_PC: case DASM_SETLABEL: break;
|
||||
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
|
||||
case DASM_ALIGN:
|
||||
n = *p++;
|
||||
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
|
||||
break;
|
||||
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
|
||||
case DASM_MARK: mark = cp; break;
|
||||
case DASM_ESC: action = *p++;
|
||||
/* fallthrough */
|
||||
default: *cp++ = action; break;
|
||||
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,3 +0,0 @@
|
||||
minilua
|
||||
buildvm
|
||||
buildvm_arch.h
|
||||
@@ -1,4 +0,0 @@
|
||||
The files in this directory are only used during the build process of LuaJIT.
|
||||
For cross-compilation, they must be executed on the host, not on the target.
|
||||
|
||||
These files should NOT be installed!
|
||||
@@ -1,529 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** This is a tool to build the hand-tuned assembler code required for
|
||||
** LuaJIT's bytecode interpreter. It supports a variety of output formats
|
||||
** to feed different toolchains (see usage() below).
|
||||
**
|
||||
** This tool is not particularly optimized because it's only used while
|
||||
** _building_ LuaJIT. There's no point in distributing or installing it.
|
||||
** Only the object code generated by this tool is linked into LuaJIT.
|
||||
**
|
||||
** Caveat: some memory is not free'd, error handling is lazy.
|
||||
** It's a one-shot tool -- any effort fixing this would be wasted.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
#include "lj_ircall.h"
|
||||
#endif
|
||||
#include "lj_frame.h"
|
||||
#include "lj_dispatch.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_ccall.h"
|
||||
#endif
|
||||
#include "luajit.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* DynASM glue definitions. */
|
||||
#define Dst ctx
|
||||
#define Dst_DECL BuildCtx *ctx
|
||||
#define Dst_REF (ctx->D)
|
||||
#define DASM_CHECKS 1
|
||||
|
||||
#include "../dynasm/dasm_proto.h"
|
||||
|
||||
/* Glue macros for DynASM. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type);
|
||||
|
||||
#define DASM_EXTERN(ctx, addr, idx, type) \
|
||||
collect_reloc(ctx, addr, idx, type)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */
|
||||
#define DASM_ALIGNED_WRITES 1
|
||||
|
||||
/* Embed architecture-specific DynASM encoder. */
|
||||
#if LJ_TARGET_X86ORX64
|
||||
#include "../dynasm/dasm_x86.h"
|
||||
#elif LJ_TARGET_ARM
|
||||
#include "../dynasm/dasm_arm.h"
|
||||
#elif LJ_TARGET_ARM64
|
||||
#include "../dynasm/dasm_arm64.h"
|
||||
#elif LJ_TARGET_PPC
|
||||
#include "../dynasm/dasm_ppc.h"
|
||||
#elif LJ_TARGET_MIPS
|
||||
#include "../dynasm/dasm_mips.h"
|
||||
#else
|
||||
#error "No support for this architecture (yet)"
|
||||
#endif
|
||||
|
||||
/* Embed generated architecture-specific backend. */
|
||||
#include "buildvm_arch.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
void owrite(BuildCtx *ctx, const void *ptr, size_t sz)
|
||||
{
|
||||
if (fwrite(ptr, 1, sz, ctx->fp) != sz) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Emit code as raw bytes. Only used for DynASM debugging. */
|
||||
static void emit_raw(BuildCtx *ctx)
|
||||
{
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
}
|
||||
|
||||
/* -- Build machine code -------------------------------------------------- */
|
||||
|
||||
static const char *sym_decorate(BuildCtx *ctx,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
char name[256];
|
||||
char *p;
|
||||
#if LJ_64
|
||||
const char *symprefix = ctx->mode == BUILD_machasm ? "_" : "";
|
||||
#elif LJ_TARGET_XBOX360
|
||||
const char *symprefix = "";
|
||||
#else
|
||||
const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : "";
|
||||
#endif
|
||||
sprintf(name, "%s%s%s", symprefix, prefix, suffix);
|
||||
p = strchr(name, '@');
|
||||
if (p) {
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj))
|
||||
name[0] = name[1] == 'R' ? '_' : '@'; /* Just for _RtlUnwind@16. */
|
||||
else
|
||||
*p = '\0';
|
||||
#elif LJ_TARGET_PPC && !LJ_TARGET_CONSOLE
|
||||
/* Keep @plt etc. */
|
||||
#else
|
||||
*p = '\0';
|
||||
#endif
|
||||
}
|
||||
p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */
|
||||
strcpy(p, name);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1)
|
||||
|
||||
static int relocmap[NRELOCSYM];
|
||||
|
||||
/* Collect external relocations. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type)
|
||||
{
|
||||
if (ctx->nreloc >= BUILD_MAX_RELOC) {
|
||||
fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (relocmap[idx] < 0) {
|
||||
relocmap[idx] = ctx->nrelocsym;
|
||||
ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]);
|
||||
ctx->nrelocsym++;
|
||||
}
|
||||
ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code);
|
||||
ctx->reloc[ctx->nreloc].sym = relocmap[idx];
|
||||
ctx->reloc[ctx->nreloc].type = type;
|
||||
ctx->nreloc++;
|
||||
#if LJ_TARGET_XBOX360
|
||||
return (int)(ctx->code - addr) + 4; /* Encode symbol offset of .text. */
|
||||
#else
|
||||
return 0; /* Encode symbol offset of 0. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Naive insertion sort. Performance doesn't matter here. */
|
||||
static void sym_insert(BuildCtx *ctx, int32_t ofs,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
ptrdiff_t i = ctx->nsym++;
|
||||
while (i > 0) {
|
||||
if (ctx->sym[i-1].ofs <= ofs)
|
||||
break;
|
||||
ctx->sym[i] = ctx->sym[i-1];
|
||||
i--;
|
||||
}
|
||||
ctx->sym[i].ofs = ofs;
|
||||
ctx->sym[i].name = sym_decorate(ctx, prefix, suffix);
|
||||
}
|
||||
|
||||
/* Build the machine code. */
|
||||
static int build_code(BuildCtx *ctx)
|
||||
{
|
||||
int status;
|
||||
int i;
|
||||
|
||||
/* Initialize DynASM structures. */
|
||||
ctx->nglob = GLOB__MAX;
|
||||
ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *));
|
||||
memset(ctx->glob, 0, ctx->nglob*sizeof(void *));
|
||||
ctx->nreloc = 0;
|
||||
|
||||
ctx->globnames = globnames;
|
||||
ctx->extnames = extnames;
|
||||
ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *));
|
||||
ctx->nrelocsym = 0;
|
||||
for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1;
|
||||
|
||||
ctx->dasm_ident = DASM_IDENT;
|
||||
ctx->dasm_arch = DASM_ARCH;
|
||||
|
||||
dasm_init(Dst, DASM_MAXSECTION);
|
||||
dasm_setupglobal(Dst, ctx->glob, ctx->nglob);
|
||||
dasm_setup(Dst, build_actionlist);
|
||||
|
||||
/* Call arch-specific backend to emit the code. */
|
||||
ctx->npc = build_backend(ctx);
|
||||
|
||||
/* Finalize the code. */
|
||||
(void)dasm_checkstep(Dst, -1);
|
||||
if ((status = dasm_link(Dst, &ctx->codesz))) return status;
|
||||
ctx->code = (uint8_t *)malloc(ctx->codesz);
|
||||
if ((status = dasm_encode(Dst, (void *)ctx->code))) return status;
|
||||
|
||||
/* Allocate symbol table and bytecode offsets. */
|
||||
ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin");
|
||||
ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym));
|
||||
ctx->nsym = 0;
|
||||
ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t));
|
||||
|
||||
/* Collect the opcodes (PC labels). */
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
int32_t ofs = dasm_getpclabel(Dst, i);
|
||||
if (ofs < 0) return 0x22000000|i;
|
||||
ctx->bc_ofs[i] = ofs;
|
||||
if ((LJ_HASJIT ||
|
||||
!(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP ||
|
||||
i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) &&
|
||||
(LJ_HASFFI || i != BC_KCDATA))
|
||||
sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]);
|
||||
}
|
||||
|
||||
/* Collect the globals (named labels). */
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = globnames[i];
|
||||
int len = (int)strlen(gl);
|
||||
if (!ctx->glob[i]) {
|
||||
fprintf(stderr, "Error: undefined global %s\n", gl);
|
||||
exit(2);
|
||||
}
|
||||
/* Skip the _Z symbols. */
|
||||
if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z'))
|
||||
sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code),
|
||||
LABEL_PREFIX, globnames[i]);
|
||||
}
|
||||
|
||||
/* Close the address range. */
|
||||
sym_insert(ctx, (int32_t)ctx->codesz, "", "");
|
||||
ctx->nsym--;
|
||||
|
||||
dasm_free(Dst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Generate VM enums --------------------------------------------------- */
|
||||
|
||||
const char *const bc_names[] = {
|
||||
#define BCNAME(name, ma, mb, mc, mt) #name,
|
||||
BCDEF(BCNAME)
|
||||
#undef BCNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
#if LJ_HASJIT
|
||||
const char *const ir_names[] = {
|
||||
#define IRNAME(name, m, m1, m2) #name,
|
||||
IRDEF(IRNAME)
|
||||
#undef IRNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irt_names[] = {
|
||||
#define IRTNAME(name, size) #name,
|
||||
IRTDEF(IRTNAME)
|
||||
#undef IRTNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfpm_names[] = {
|
||||
#define FPMNAME(name) #name,
|
||||
IRFPMDEF(FPMNAME)
|
||||
#undef FPMNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfield_names[] = {
|
||||
#define FLNAME(name, ofs) #name,
|
||||
IRFLDEF(FLNAME)
|
||||
#undef FLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const ircall_names[] = {
|
||||
#define IRCALLNAME(cond, name, nargs, kind, type, flags) #name,
|
||||
IRCALLDEF(IRCALLNAME)
|
||||
#undef IRCALLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *const trace_errors[] = {
|
||||
#define TREDEF(name, msg) msg,
|
||||
#include "lj_traceerr.h"
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#if LJ_HASJIT
|
||||
static const char *lower(char *buf, const char *s)
|
||||
{
|
||||
char *p = buf;
|
||||
while (*s) {
|
||||
*p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s;
|
||||
s++;
|
||||
}
|
||||
*p = '\0';
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Emit C source code for bytecode-related definitions. */
|
||||
static void emit_bcdef(BuildCtx *ctx)
|
||||
{
|
||||
int i;
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n");
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
if (i != 0)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
fprintf(ctx->fp, "%d", ctx->bc_ofs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit VM definitions as Lua code for debug modules. */
|
||||
static void emit_vmdef(BuildCtx *ctx)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
char buf[80];
|
||||
#endif
|
||||
int i;
|
||||
fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n");
|
||||
fprintf(ctx->fp, "assert(require(\"jit\").version == \"%s\", \"LuaJIT core/library version mismatch\")\n\n", LUAJIT_VERSION);
|
||||
fprintf(ctx->fp, "return {\n\n");
|
||||
|
||||
fprintf(ctx->fp, "bcnames = \"");
|
||||
for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]);
|
||||
fprintf(ctx->fp, "\",\n\n");
|
||||
|
||||
#if LJ_HASJIT
|
||||
fprintf(ctx->fp, "irnames = \"");
|
||||
for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]);
|
||||
fprintf(ctx->fp, "\",\n\n");
|
||||
|
||||
fprintf(ctx->fp, "irfpm = { [0]=");
|
||||
for (i = 0; irfpm_names[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i]));
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "irfield = { [0]=");
|
||||
for (i = 0; irfield_names[i]; i++) {
|
||||
char *p;
|
||||
lower(buf, irfield_names[i]);
|
||||
p = strchr(buf, '_');
|
||||
if (p) *p = '.';
|
||||
fprintf(ctx->fp, "\"%s\", ", buf);
|
||||
}
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "ircall = {\n[0]=");
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]);
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "traceerr = {\n[0]=");
|
||||
for (i = 0; trace_errors[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]);
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -- Argument parsing ---------------------------------------------------- */
|
||||
|
||||
/* Build mode names. */
|
||||
static const char *const modenames[] = {
|
||||
#define BUILDNAME(name) #name,
|
||||
BUILDDEF(BUILDNAME)
|
||||
#undef BUILDNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Print usage information and exit. */
|
||||
static void usage(void)
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, LUAJIT_VERSION " VM builder.\n");
|
||||
fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n");
|
||||
fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n");
|
||||
fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n");
|
||||
fprintf(stderr, "Available modes:\n");
|
||||
for (i = 0; i < BUILD__MAX; i++)
|
||||
fprintf(stderr, " %s\n", modenames[i]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Parse the output mode name. */
|
||||
static BuildMode parsemode(const char *mode)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; modenames[i]; i++)
|
||||
if (!strcmp(mode, modenames[i]))
|
||||
return (BuildMode)i;
|
||||
usage();
|
||||
return (BuildMode)-1;
|
||||
}
|
||||
|
||||
/* Parse arguments. */
|
||||
static void parseargs(BuildCtx *ctx, char **argv)
|
||||
{
|
||||
const char *a;
|
||||
int i;
|
||||
ctx->mode = (BuildMode)-1;
|
||||
ctx->outname = "-";
|
||||
for (i = 1; (a = argv[i]) != NULL; i++) {
|
||||
if (a[0] != '-')
|
||||
break;
|
||||
switch (a[1]) {
|
||||
case '-':
|
||||
if (a[2]) goto err;
|
||||
i++;
|
||||
goto ok;
|
||||
case '\0':
|
||||
goto ok;
|
||||
case 'm':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->mode = parsemode(argv[i]);
|
||||
break;
|
||||
case 'o':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->outname = argv[i];
|
||||
break;
|
||||
default: err:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok:
|
||||
ctx->args = argv+i;
|
||||
if (ctx->mode == (BuildMode)-1) goto err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
BuildCtx ctx_;
|
||||
BuildCtx *ctx = &ctx_;
|
||||
int status, binmode;
|
||||
|
||||
if (sizeof(void *) != 4*LJ_32+8*LJ_64) {
|
||||
fprintf(stderr,"Error: pointer size mismatch in cross-build.\n");
|
||||
fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
UNUSED(argc);
|
||||
parseargs(ctx, argv);
|
||||
|
||||
if ((status = build_code(ctx))) {
|
||||
fprintf(stderr,"Error: DASM error %08x\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_peobj:
|
||||
case BUILD_raw:
|
||||
binmode = 1;
|
||||
break;
|
||||
default:
|
||||
binmode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') {
|
||||
ctx->fp = stdout;
|
||||
#if defined(_WIN32)
|
||||
if (binmode)
|
||||
_setmode(_fileno(stdout), _O_BINARY); /* Yuck. */
|
||||
#endif
|
||||
} else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) {
|
||||
fprintf(stderr, "Error: cannot open output file '%s': %s\n",
|
||||
ctx->outname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
case BUILD_coffasm:
|
||||
case BUILD_machasm:
|
||||
emit_asm(ctx);
|
||||
emit_asm_debug(ctx);
|
||||
break;
|
||||
case BUILD_peobj:
|
||||
emit_peobj(ctx);
|
||||
break;
|
||||
case BUILD_raw:
|
||||
emit_raw(ctx);
|
||||
break;
|
||||
case BUILD_bcdef:
|
||||
emit_bcdef(ctx);
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_vmdef:
|
||||
emit_vmdef(ctx);
|
||||
emit_lib(ctx);
|
||||
fprintf(ctx->fp, "}\n\n");
|
||||
break;
|
||||
case BUILD_ffdef:
|
||||
case BUILD_libdef:
|
||||
case BUILD_recdef:
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_folddef:
|
||||
emit_fold(ctx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(ctx->fp);
|
||||
if (ferror(ctx->fp)) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
fclose(ctx->fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _BUILDVM_H
|
||||
#define _BUILDVM_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lj_def.h"
|
||||
#include "lj_arch.h"
|
||||
|
||||
/* Hardcoded limits. Increase as needed. */
|
||||
#define BUILD_MAX_RELOC 200 /* Max. number of relocations. */
|
||||
#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */
|
||||
|
||||
/* Prefix for scanned library definitions. */
|
||||
#define LIBDEF_PREFIX "LJLIB_"
|
||||
|
||||
/* Prefix for scanned fold definitions. */
|
||||
#define FOLDDEF_PREFIX "LJFOLD"
|
||||
|
||||
/* Prefixes for generated labels. */
|
||||
#define LABEL_PREFIX "lj_"
|
||||
#define LABEL_PREFIX_BC LABEL_PREFIX "BC_"
|
||||
#define LABEL_PREFIX_FF LABEL_PREFIX "ff_"
|
||||
#define LABEL_PREFIX_CF LABEL_PREFIX "cf_"
|
||||
#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_"
|
||||
#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_"
|
||||
#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_"
|
||||
|
||||
/* Forward declaration. */
|
||||
struct dasm_State;
|
||||
|
||||
/* Build modes. */
|
||||
#define BUILDDEF(_) \
|
||||
_(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \
|
||||
_(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \
|
||||
_(folddef)
|
||||
|
||||
typedef enum {
|
||||
#define BUILDENUM(name) BUILD_##name,
|
||||
BUILDDEF(BUILDENUM)
|
||||
#undef BUILDENUM
|
||||
BUILD__MAX
|
||||
} BuildMode;
|
||||
|
||||
/* Code relocation. */
|
||||
typedef struct BuildReloc {
|
||||
int32_t ofs;
|
||||
int sym;
|
||||
int type;
|
||||
} BuildReloc;
|
||||
|
||||
typedef struct BuildSym {
|
||||
const char *name;
|
||||
int32_t ofs;
|
||||
} BuildSym;
|
||||
|
||||
/* Build context structure. */
|
||||
typedef struct BuildCtx {
|
||||
/* DynASM state pointer. Should be first member. */
|
||||
struct dasm_State *D;
|
||||
/* Parsed command line. */
|
||||
BuildMode mode;
|
||||
FILE *fp;
|
||||
const char *outname;
|
||||
char **args;
|
||||
/* Code and symbols generated by DynASM. */
|
||||
uint8_t *code;
|
||||
size_t codesz;
|
||||
int npc, nglob, nsym, nreloc, nrelocsym;
|
||||
void **glob;
|
||||
BuildSym *sym;
|
||||
const char **relocsym;
|
||||
int32_t *bc_ofs;
|
||||
const char *beginsym;
|
||||
/* Strings generated by DynASM. */
|
||||
const char *const *globnames;
|
||||
const char *const *extnames;
|
||||
const char *dasm_ident;
|
||||
const char *dasm_arch;
|
||||
/* Relocations. */
|
||||
BuildReloc reloc[BUILD_MAX_RELOC];
|
||||
} BuildCtx;
|
||||
|
||||
extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz);
|
||||
extern void emit_asm(BuildCtx *ctx);
|
||||
extern void emit_peobj(BuildCtx *ctx);
|
||||
extern void emit_lib(BuildCtx *ctx);
|
||||
extern void emit_fold(BuildCtx *ctx);
|
||||
|
||||
extern const char *const bc_names[];
|
||||
extern const char *const ir_names[];
|
||||
extern const char *const irt_names[];
|
||||
extern const char *const irfpm_names[];
|
||||
extern const char *const irfield_names[];
|
||||
extern const char *const ircall_names[];
|
||||
|
||||
#endif
|
||||
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder: Assembler source code emitter.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
/* Emit bytes piecewise as assembler text. */
|
||||
static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.byte %d", p[i]);
|
||||
else
|
||||
fprintf(ctx->fp, ",%d", p[i]);
|
||||
if ((i & 15) == 15) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation */
|
||||
static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym);
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
default: /* BUILD_machasm for relative relocations handled below. */
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const jccnames[] = {
|
||||
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja",
|
||||
"js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg"
|
||||
};
|
||||
|
||||
/* Emit x86/x64 text relocations. */
|
||||
static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n,
|
||||
const char *sym)
|
||||
{
|
||||
const char *opname = NULL;
|
||||
if (--n < 0) goto err;
|
||||
if (cp[n] == 0xe8) {
|
||||
opname = "call";
|
||||
} else if (cp[n] == 0xe9) {
|
||||
opname = "jmp";
|
||||
} else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) {
|
||||
opname = jccnames[cp[n]-0x80];
|
||||
n--;
|
||||
} else {
|
||||
err:
|
||||
fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n",
|
||||
sym);
|
||||
exit(1);
|
||||
}
|
||||
emit_asm_bytes(ctx, cp, n);
|
||||
if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) {
|
||||
/* Various fixups for external symbols outside of our binary. */
|
||||
if (ctx->mode == BUILD_elfasm) {
|
||||
if (LJ_32)
|
||||
fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n", opname, sym);
|
||||
fprintf(ctx->fp, "\t%s %s@PLT\n", opname, sym);
|
||||
if (LJ_32)
|
||||
fprintf(ctx->fp, "#endif\n");
|
||||
return;
|
||||
} else if (LJ_32 && ctx->mode == BUILD_machasm) {
|
||||
fprintf(ctx->fp, "\t%s L%s$stub\n", opname, sym);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(ctx->fp, "\t%s %s\n", opname, sym);
|
||||
}
|
||||
#else
|
||||
/* Emit words piecewise as assembler text. */
|
||||
static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i += 4) {
|
||||
uint32_t ins = *(uint32_t *)(p+i);
|
||||
#if LJ_TARGET_ARM64 && LJ_BE
|
||||
ins = lj_bswap(ins); /* ARM64 instructions are always little-endian. */
|
||||
#endif
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.long 0x%08x", ins);
|
||||
else
|
||||
fprintf(ctx->fp, ",0x%08x", ins);
|
||||
if ((i & 15) == 12) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation as part of an instruction. */
|
||||
static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n,
|
||||
const char *sym)
|
||||
{
|
||||
uint32_t ins;
|
||||
emit_asm_words(ctx, p, n-4);
|
||||
ins = *(uint32_t *)(p+n-4);
|
||||
#if LJ_TARGET_ARM
|
||||
if ((ins & 0xff000000u) == 0xfa000000u) {
|
||||
fprintf(ctx->fp, "\tblx %s\n", sym);
|
||||
} else if ((ins & 0x0e000000u) == 0x0a000000u) {
|
||||
fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b",
|
||||
&"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_ARM64
|
||||
if ((ins >> 26) == 0x25u) {
|
||||
fprintf(ctx->fp, "\tbl %s\n", sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_PPC
|
||||
#if LJ_TARGET_PS3
|
||||
#define TOCPREFIX "."
|
||||
#else
|
||||
#define TOCPREFIX ""
|
||||
#endif
|
||||
if ((ins >> 26) == 16) {
|
||||
fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n",
|
||||
(ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym);
|
||||
} else if ((ins >> 26) == 18) {
|
||||
fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_MIPS
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
#else
|
||||
#error "missing relocation support for this architecture"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_ARM
|
||||
#define ELFASM_PX "%%"
|
||||
#else
|
||||
#define ELFASM_PX "@"
|
||||
#endif
|
||||
|
||||
/* Emit an assembler label. */
|
||||
static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if LJ_TARGET_PS3
|
||||
if (!strncmp(name, "lj_vm_", 6) &&
|
||||
strcmp(name, ctx->beginsym) &&
|
||||
!strstr(name, "hook")) {
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.section \".opd\",\"aw\"\n"
|
||||
"%s:\n"
|
||||
"\t.long .%s,.TOC.@tocbase32\n"
|
||||
"\t.size %s,8\n"
|
||||
"\t.previous\n"
|
||||
"\t.globl .%s\n"
|
||||
"\t.hidden .%s\n"
|
||||
"\t.type .%s, " ELFASM_PX "function\n"
|
||||
"\t.size .%s, %d\n"
|
||||
".%s:\n",
|
||||
name, name, name, name, name, name, name, name, size, name);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.hidden %s\n"
|
||||
"\t.type %s, " ELFASM_PX "%s\n"
|
||||
"\t.size %s, %d\n"
|
||||
"%s:\n",
|
||||
name, name, name, isfunc ? "function" : "object", name, size, name);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\n\t.globl %s\n", name);
|
||||
if (isfunc)
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name);
|
||||
fprintf(ctx->fp, "%s:\n", name);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.private_extern %s\n"
|
||||
"\t.no_dead_strip %s\n"
|
||||
"%s:\n", name, name, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit alignment. */
|
||||
static void emit_asm_align(BuildCtx *ctx, int bits)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.p2align %d\n", bits);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp, "\t.align %d\n", bits);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Emit assembler source code. */
|
||||
void emit_asm(BuildCtx *ctx)
|
||||
{
|
||||
int i, rel;
|
||||
|
||||
fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
|
||||
fprintf(ctx->fp, "\t.text\n");
|
||||
#if LJ_TARGET_MIPS32 && !LJ_ABI_SOFTFP
|
||||
fprintf(ctx->fp, "\t.module fp=32\n");
|
||||
#endif
|
||||
#if LJ_TARGET_MIPS
|
||||
fprintf(ctx->fp, "\t.set nomips16\n\t.abicalls\n\t.set noreorder\n\t.set nomacro\n");
|
||||
#endif
|
||||
emit_asm_align(ctx, 4);
|
||||
|
||||
#if LJ_TARGET_PS3
|
||||
emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0);
|
||||
#else
|
||||
emit_asm_label(ctx, ctx->beginsym, 0, 0);
|
||||
#endif
|
||||
if (ctx->mode != BUILD_machasm)
|
||||
fprintf(ctx->fp, ".Lbegin:\n");
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
/* This should really be moved into buildvm_arm.dasc. */
|
||||
#if LJ_ARCH_HASFPU
|
||||
fprintf(ctx->fp,
|
||||
".fnstart\n"
|
||||
".save {r5, r6, r7, r8, r9, r10, r11, lr}\n"
|
||||
".vsave {d8-d15}\n"
|
||||
".save {r4}\n"
|
||||
".pad #28\n");
|
||||
#else
|
||||
fprintf(ctx->fp,
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
|
||||
".pad #28\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (i = rel = 0; i < ctx->nsym; i++) {
|
||||
int32_t ofs = ctx->sym[i].ofs;
|
||||
int32_t next = ctx->sym[i+1].ofs;
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI
|
||||
if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call"))
|
||||
fprintf(ctx->fp,
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
".fnend\n"
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r11, lr}\n"
|
||||
".setfp r11, sp\n");
|
||||
#endif
|
||||
emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1);
|
||||
while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) {
|
||||
BuildReloc *r = &ctx->reloc[rel];
|
||||
int n = r->ofs - ofs;
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (r->type != 0 &&
|
||||
(ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) {
|
||||
emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
} else {
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, n);
|
||||
emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]);
|
||||
}
|
||||
ofs += n+4;
|
||||
#else
|
||||
emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
ofs += n;
|
||||
#endif
|
||||
rel++;
|
||||
}
|
||||
#if LJ_TARGET_X86ORX64
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, next-ofs);
|
||||
#else
|
||||
emit_asm_words(ctx, ctx->code+ofs, next-ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
fprintf(ctx->fp,
|
||||
#if !LJ_HASFFI
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
#endif
|
||||
".fnend\n");
|
||||
#endif
|
||||
|
||||
fprintf(ctx->fp, "\n");
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA)
|
||||
fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n");
|
||||
#endif
|
||||
#if LJ_TARGET_PPC && !LJ_TARGET_PS3 && !LJ_ABI_SOFTFP
|
||||
/* Hard-float ABI. */
|
||||
fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n");
|
||||
#endif
|
||||
/* fallthrough */
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
#if defined(__apple_build_version__) && __apple_build_version__ >= 15000000 && __apple_build_version__ < 15000300
|
||||
/* Workaround for XCode 15.0 - 15.2. */
|
||||
fprintf(ctx->fp, "\t.subsections_via_symbols\n");
|
||||
#endif
|
||||
fprintf(ctx->fp,
|
||||
"\t.cstring\n"
|
||||
"\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(ctx->fp, "\n");
|
||||
}
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder: IR folding hash table generator.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
|
||||
/* Context for the folding hash table generator. */
|
||||
static int lineno;
|
||||
static uint32_t funcidx;
|
||||
static uint32_t foldkeys[BUILD_MAX_FOLD];
|
||||
static uint32_t nkeys;
|
||||
|
||||
/* Try to fill the hash table with keys using the hash parameters. */
|
||||
static int tryhash(uint32_t *htab, uint32_t sz, uint32_t r, int dorol)
|
||||
{
|
||||
uint32_t i;
|
||||
if (dorol && ((r & 31) == 0 || (r>>5) == 0))
|
||||
return 0; /* Avoid zero rotates. */
|
||||
memset(htab, 0xff, (sz+1)*sizeof(uint32_t));
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
uint32_t key = foldkeys[i];
|
||||
uint32_t k = key & 0xffffff;
|
||||
uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) :
|
||||
(((k << (r>>5)) - k) << (r&31))) % sz;
|
||||
if (htab[h] != 0xffffffff) { /* Collision on primary slot. */
|
||||
if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */
|
||||
/* Try to move the colliding key, if possible. */
|
||||
if (h < sz-1 && htab[h+2] == 0xffffffff) {
|
||||
uint32_t k2 = htab[h+1] & 0xffffff;
|
||||
uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) :
|
||||
(((k2 << (r>>5)) - k2) << (r&31))) % sz;
|
||||
if (h2 != h+1) return 0; /* Cannot resolve collision. */
|
||||
htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */
|
||||
} else {
|
||||
return 0; /* Collision. */
|
||||
}
|
||||
}
|
||||
htab[h+1] = key;
|
||||
} else {
|
||||
htab[h] = key;
|
||||
}
|
||||
}
|
||||
return 1; /* Success, all keys could be stored. */
|
||||
}
|
||||
|
||||
/* Print the generated hash table. */
|
||||
static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz)
|
||||
{
|
||||
uint32_t i;
|
||||
fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x",
|
||||
sz+1, htab[0]);
|
||||
for (i = 1; i < sz+1; i++)
|
||||
fprintf(ctx->fp, ",\n0x%08x", htab[i]);
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
}
|
||||
|
||||
/* Exhaustive search for the shortest semi-perfect hash table. */
|
||||
static void makehash(BuildCtx *ctx)
|
||||
{
|
||||
uint32_t htab[BUILD_MAX_FOLD*2+1];
|
||||
uint32_t sz, r;
|
||||
/* Search for the smallest hash table with an odd size. */
|
||||
for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) {
|
||||
/* First try all shift hash combinations. */
|
||||
for (r = 0; r < 32*32; r++) {
|
||||
if (tryhash(htab, sz, r, 0)) {
|
||||
printhash(ctx, htab, sz);
|
||||
fprintf(ctx->fp,
|
||||
"#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n",
|
||||
r>>5, r&31, sz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Then try all rotate hash combinations. */
|
||||
for (r = 0; r < 32*32; r++) {
|
||||
if (tryhash(htab, sz, r, 1)) {
|
||||
printhash(ctx, htab, sz);
|
||||
fprintf(ctx->fp,
|
||||
"#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n",
|
||||
r>>5, r&31, sz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: search for perfect hash failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Parse one token of a fold rule. */
|
||||
static uint32_t nexttoken(char **pp, int allowlit, int allowany)
|
||||
{
|
||||
char *p = *pp;
|
||||
if (p) {
|
||||
uint32_t i;
|
||||
char *q = strchr(p, ' ');
|
||||
if (q) *q++ = '\0';
|
||||
*pp = q;
|
||||
if (allowlit && !strncmp(p, "IRFPM_", 6)) {
|
||||
for (i = 0; irfpm_names[i]; i++)
|
||||
if (!strcmp(irfpm_names[i], p+6))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRFL_", 5)) {
|
||||
for (i = 0; irfield_names[i]; i++)
|
||||
if (!strcmp(irfield_names[i], p+5))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRCALL_", 7)) {
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
if (!strcmp(ircall_names[i], p+7))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRCONV_", 7)) {
|
||||
for (i = 0; irt_names[i]; i++) {
|
||||
const char *r = strchr(p+7, '_');
|
||||
if (r && !strncmp(irt_names[i], p+7, r-(p+7))) {
|
||||
uint32_t j;
|
||||
for (j = 0; irt_names[j]; j++)
|
||||
if (!strcmp(irt_names[j], r+1))
|
||||
return (i << 5) + j;
|
||||
}
|
||||
}
|
||||
} else if (allowlit && *p >= '0' && *p <= '9') {
|
||||
for (i = 0; *p >= '0' && *p <= '9'; p++)
|
||||
i = i*10 + (*p - '0');
|
||||
if (*p == '\0')
|
||||
return i;
|
||||
} else if (allowany && !strcmp("any", p)) {
|
||||
return allowany;
|
||||
} else {
|
||||
for (i = 0; ir_names[i]; i++)
|
||||
if (!strcmp(ir_names[i], p))
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a fold rule. */
|
||||
static void foldrule(char *p)
|
||||
{
|
||||
uint32_t op = nexttoken(&p, 0, 0);
|
||||
uint32_t left = nexttoken(&p, 0, 0x7f);
|
||||
uint32_t right = nexttoken(&p, 1, 0x3ff);
|
||||
uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right;
|
||||
uint32_t i;
|
||||
if (nkeys >= BUILD_MAX_FOLD) {
|
||||
fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n");
|
||||
exit(1);
|
||||
}
|
||||
/* Simple insertion sort to detect duplicates. */
|
||||
for (i = nkeys; i > 0; i--) {
|
||||
if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff))
|
||||
break;
|
||||
if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) {
|
||||
fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
foldkeys[i] = foldkeys[i-1];
|
||||
}
|
||||
foldkeys[i] = key;
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
/* Emit C source code for IR folding hash table. */
|
||||
void emit_fold(BuildCtx *ctx)
|
||||
{
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
const char *fname = ctx->args[0];
|
||||
FILE *fp;
|
||||
|
||||
if (fname == NULL) {
|
||||
fprintf(stderr, "Error: missing input filename\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n");
|
||||
|
||||
lineno = 0;
|
||||
funcidx = 0;
|
||||
nkeys = 0;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
lineno++;
|
||||
/* The prefix must be at the start of a line, otherwise it's ignored. */
|
||||
if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) {
|
||||
char *p = buf+sizeof(FOLDDEF_PREFIX)-1;
|
||||
char *q = strchr(p, ')');
|
||||
if (p[0] == '(' && q) {
|
||||
p++;
|
||||
*q = '\0';
|
||||
foldrule(p);
|
||||
} else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) {
|
||||
p += 2;
|
||||
*q = '\0';
|
||||
if (funcidx)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
if (p[-2] == 'X')
|
||||
fprintf(ctx->fp, " %s", p);
|
||||
else
|
||||
fprintf(ctx->fp, " fold_%s", p);
|
||||
funcidx++;
|
||||
} else {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n",
|
||||
FOLDDEF_PREFIX, p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
|
||||
makehash(ctx);
|
||||
}
|
||||
#else
|
||||
void emit_fold(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,470 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder: library definition compiler.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_lib.h"
|
||||
#include "buildvm_libbc.h"
|
||||
|
||||
/* Context for library definitions. */
|
||||
static uint8_t obuf[8192];
|
||||
static uint8_t *optr;
|
||||
static char modname[80];
|
||||
static size_t modnamelen;
|
||||
static char funcname[80];
|
||||
static int modstate, regfunc;
|
||||
static int ffid, recffid, ffasmfunc;
|
||||
|
||||
enum {
|
||||
REGFUNC_OK,
|
||||
REGFUNC_NOREG,
|
||||
REGFUNC_NOREGUV
|
||||
};
|
||||
|
||||
static void libdef_name(const char *p, int kind)
|
||||
{
|
||||
size_t n = strlen(p);
|
||||
if (kind != LIBINIT_STRING) {
|
||||
if (n > modnamelen && p[modnamelen] == '_' &&
|
||||
!strncmp(p, modname, modnamelen)) {
|
||||
p += modnamelen+1;
|
||||
n -= modnamelen+1;
|
||||
}
|
||||
}
|
||||
if (n > LIBINIT_MAXSTR) {
|
||||
fprintf(stderr, "Error: string too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = (uint8_t)(n | kind);
|
||||
memcpy(optr, p, n);
|
||||
optr += n;
|
||||
}
|
||||
|
||||
static void libdef_endmodule(BuildCtx *ctx)
|
||||
{
|
||||
if (modstate != 0) {
|
||||
char line[80];
|
||||
const uint8_t *p;
|
||||
int n;
|
||||
if (modstate == 1)
|
||||
fprintf(ctx->fp, " (lua_CFunction)0");
|
||||
fprintf(ctx->fp, "\n};\n");
|
||||
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBINIT, modname);
|
||||
line[0] = '\0';
|
||||
for (n = 0, p = obuf; p < optr; p++) {
|
||||
n += sprintf(line+n, "%d,", *p);
|
||||
if (n >= 75) {
|
||||
fprintf(ctx->fp, "%s\n", line);
|
||||
n = 0;
|
||||
line[0] = '\0';
|
||||
}
|
||||
}
|
||||
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_module(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
optr = obuf;
|
||||
*optr++ = (uint8_t)ffid;
|
||||
*optr++ = (uint8_t)ffasmfunc;
|
||||
*optr++ = 0; /* Hash table size. */
|
||||
modstate = 1;
|
||||
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBCF, p);
|
||||
}
|
||||
modnamelen = strlen(p);
|
||||
if (modnamelen > sizeof(modname)-1) {
|
||||
fprintf(stderr, "Error: module name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(modname, p);
|
||||
}
|
||||
|
||||
static int find_ffofs(BuildCtx *ctx, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = ctx->globnames[i];
|
||||
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) {
|
||||
return (int)((uint8_t *)ctx->glob[i] - ctx->code);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: undefined fast function %s%s\n",
|
||||
LABEL_PREFIX_FF, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void libdef_func(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
if (arg != LIBINIT_CF)
|
||||
ffasmfunc++;
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (modstate == 0) {
|
||||
fprintf(stderr, "Error: no module for function definition %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (regfunc == REGFUNC_NOREG) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_FFID;
|
||||
} else {
|
||||
if (arg != LIBINIT_ASM_) {
|
||||
if (modstate != 1) fprintf(ctx->fp, ",\n");
|
||||
modstate = 2;
|
||||
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
|
||||
}
|
||||
if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */
|
||||
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
|
||||
}
|
||||
} else if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "FFDEF(%s)\n", p);
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
if (strlen(p) > sizeof(funcname)-1) {
|
||||
fprintf(stderr, "Error: function name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(funcname, p);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
int i;
|
||||
for (i = 1; p[i] && modname[i-1]; i++)
|
||||
if (p[i] == '_') p[i] = '.';
|
||||
fprintf(ctx->fp, "\"%s\",\n", p);
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
if (arg != LIBINIT_CF)
|
||||
fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p));
|
||||
}
|
||||
ffid++;
|
||||
regfunc = REGFUNC_OK;
|
||||
}
|
||||
|
||||
static uint8_t *libdef_uleb128(uint8_t *p, uint32_t *vv)
|
||||
{
|
||||
uint32_t v = *p++;
|
||||
if (v >= 0x80) {
|
||||
int sh = 0; v &= 0x7f;
|
||||
do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80);
|
||||
}
|
||||
*vv = v;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void libdef_fixupbc(uint8_t *p)
|
||||
{
|
||||
uint32_t i, sizebc;
|
||||
p += 4;
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
for (i = 0; i < sizebc; i++, p += 4) {
|
||||
uint8_t op = p[libbc_endian ? 3 : 0];
|
||||
uint8_t ra = p[libbc_endian ? 2 : 1];
|
||||
uint8_t rc = p[libbc_endian ? 1 : 2];
|
||||
uint8_t rb = p[libbc_endian ? 0 : 3];
|
||||
if (!LJ_DUALNUM && op == BC_ISTYPE && rc == ~LJ_TNUMX+1) {
|
||||
op = BC_ISNUM; rc++;
|
||||
}
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = op;
|
||||
p[LJ_ENDIAN_SELECT(1, 2)] = ra;
|
||||
p[LJ_ENDIAN_SELECT(2, 1)] = rc;
|
||||
p[LJ_ENDIAN_SELECT(3, 0)] = rb;
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_lua(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
int i;
|
||||
for (i = 0; libbc_map[i].name != NULL; i++) {
|
||||
if (!strcmp(libbc_map[i].name, p)) {
|
||||
int ofs = libbc_map[i].ofs;
|
||||
int len = libbc_map[i+1].ofs - ofs;
|
||||
obuf[2]++; /* Bump hash table size. */
|
||||
*optr++ = LIBINIT_LUA;
|
||||
libdef_name(p, 0);
|
||||
memcpy(optr, libbc_code + ofs, len);
|
||||
libdef_fixupbc(optr);
|
||||
optr += len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: missing libbc definition for %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t find_rec(char *name)
|
||||
{
|
||||
char *p = (char *)obuf;
|
||||
uint32_t n;
|
||||
for (n = 2; *p; n++) {
|
||||
if (strcmp(p, name) == 0)
|
||||
return n;
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
strcpy(p, name);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void libdef_rec(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_recdef) {
|
||||
char *q;
|
||||
uint32_t n;
|
||||
for (; recffid+1 < ffid; recffid++)
|
||||
fprintf(ctx->fp, ",\n0");
|
||||
recffid = ffid;
|
||||
if (*p == '.') p = funcname;
|
||||
q = strchr(p, ' ');
|
||||
if (q) *q++ = '\0';
|
||||
n = find_rec(p);
|
||||
if (q)
|
||||
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q);
|
||||
else
|
||||
fprintf(ctx->fp, ",\n0x%02x00", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void memcpy_endian(void *dst, void *src, size_t n)
|
||||
{
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) {
|
||||
memcpy(dst, src, n);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_push(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
int len = (int)strlen(p);
|
||||
if (*p == '"') {
|
||||
if (len > 1 && p[len-1] == '"') {
|
||||
p[len-1] = '\0';
|
||||
libdef_name(p+1, LIBINIT_STRING);
|
||||
return;
|
||||
}
|
||||
} else if (*p >= '0' && *p <= '9') {
|
||||
char *ep;
|
||||
double d = strtod(p, &ep);
|
||||
if (*ep == '\0') {
|
||||
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_NUMBER;
|
||||
memcpy_endian(optr, &d, sizeof(double));
|
||||
optr += sizeof(double);
|
||||
return;
|
||||
}
|
||||
} else if (!strcmp(p, "lastcl")) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_LASTCL;
|
||||
return;
|
||||
} else if (len > 4 && !strncmp(p, "top-", 4)) {
|
||||
if (optr+2 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_COPY;
|
||||
*optr++ = (uint8_t)atoi(p+4);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_set(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */
|
||||
libdef_name(p, LIBINIT_STRING);
|
||||
*optr++ = LIBINIT_SET;
|
||||
obuf[2]++; /* Bump hash table size. */
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(ctx); UNUSED(p);
|
||||
regfunc = arg;
|
||||
}
|
||||
|
||||
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg);
|
||||
|
||||
typedef struct LibDefHandler {
|
||||
const char *suffix;
|
||||
const char *stop;
|
||||
const LibDefFunc func;
|
||||
const int arg;
|
||||
} LibDefHandler;
|
||||
|
||||
static const LibDefHandler libdef_handlers[] = {
|
||||
{ "MODULE_", " \t\r\n", libdef_module, 0 },
|
||||
{ "CF(", ")", libdef_func, LIBINIT_CF },
|
||||
{ "ASM(", ")", libdef_func, LIBINIT_ASM },
|
||||
{ "ASM_(", ")", libdef_func, LIBINIT_ASM_ },
|
||||
{ "LUA(", ")", libdef_lua, 0 },
|
||||
{ "REC(", ")", libdef_rec, 0 },
|
||||
{ "PUSH(", ")", libdef_push, 0 },
|
||||
{ "SET(", ")", libdef_set, 0 },
|
||||
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV },
|
||||
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG },
|
||||
{ NULL, NULL, (LibDefFunc)0, 0 }
|
||||
};
|
||||
|
||||
/* Emit C source code for library function definitions. */
|
||||
void emit_lib(BuildCtx *ctx)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
|
||||
ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
else if (ctx->mode == BUILD_vmdef)
|
||||
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
|
||||
if (ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
|
||||
recffid = ffid = FF_C+1;
|
||||
ffasmfunc = 0;
|
||||
|
||||
while ((fname = *ctx->args++)) {
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
FILE *fp;
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
modstate = 0;
|
||||
regfunc = REGFUNC_OK;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
char *p;
|
||||
/* Simplistic pre-processor. Only handles top-level #if/#endif. */
|
||||
if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
int ok = 1;
|
||||
size_t len = strlen(buf);
|
||||
if (buf[len-1] == '\n') {
|
||||
buf[len-1] = 0;
|
||||
if (buf[len-2] == '\r') {
|
||||
buf[len-2] = 0;
|
||||
}
|
||||
}
|
||||
if (!strcmp(buf, "#if LJ_52"))
|
||||
ok = LJ_52;
|
||||
else if (!strcmp(buf, "#if LJ_HASJIT"))
|
||||
ok = LJ_HASJIT;
|
||||
else if (!strcmp(buf, "#if LJ_HASFFI"))
|
||||
ok = LJ_HASFFI;
|
||||
else if (!strcmp(buf, "#if LJ_HASBUFFER"))
|
||||
ok = LJ_HASBUFFER;
|
||||
else if (!strcmp(buf, "#if !LJ_SRB2LIB"))
|
||||
ok = !LJ_SRB2LIB;
|
||||
else if (!strcmp(buf, "#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO)"))
|
||||
ok = !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO); /* whacky! */
|
||||
if (!ok) {
|
||||
int lvl = 1;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') {
|
||||
if (--lvl == 0) break;
|
||||
} else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
lvl++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
|
||||
const LibDefHandler *ldh;
|
||||
p += sizeof(LIBDEF_PREFIX)-1;
|
||||
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
|
||||
size_t n, len = strlen(ldh->suffix);
|
||||
if (!strncmp(p, ldh->suffix, len)) {
|
||||
p += len;
|
||||
n = ldh->stop ? strcspn(p, ldh->stop) : 0;
|
||||
if (!p[n]) break;
|
||||
p[n] = '\0';
|
||||
ldh->func(ctx, p, ldh->arg);
|
||||
p += n+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ldh->suffix == NULL) {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown library definition tag %s%s\n",
|
||||
LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "\n#undef FFDEF\n\n");
|
||||
fprintf(ctx->fp,
|
||||
"#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n",
|
||||
ffasmfunc);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
int i;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n");
|
||||
fprintf(ctx->fp, "BCDEF(BCMODE)\n");
|
||||
for (i = ffasmfunc-1; i > 0; i--)
|
||||
fprintf(ctx->fp, "BCMODE_FF,\n");
|
||||
fprintf(ctx->fp, "BCMODE_FF\n};\n\n");
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
char *p = (char *)obuf;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
|
||||
"recff_nyi,\n"
|
||||
"recff_c");
|
||||
while (*p) {
|
||||
fprintf(ctx->fp, ",\nrecff_%s", p);
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/* This is a generated file. DO NOT EDIT! */
|
||||
|
||||
static const int libbc_endian = 0;
|
||||
|
||||
static const uint8_t libbc_code[] = {
|
||||
#if LJ_FR2
|
||||
/* math.deg */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,241,135,158,166,3,
|
||||
220,203,178,130,4,
|
||||
/* math.rad */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,243,244,148,165,20,
|
||||
198,190,199,252,3,
|
||||
/* string.len */ 0,1,2,0,0,0,3,BC_ISTYPE,0,5,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.foreachi */ 0,2,10,0,0,0,15,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,
|
||||
BC_KSHORT,2,1,0,BC_LEN,3,0,0,BC_KSHORT,4,1,0,BC_FORI,2,8,128,BC_MOV,6,1,0,
|
||||
BC_MOV,8,5,0,BC_TGETR,9,5,0,BC_CALL,6,3,2,BC_ISEQP,6,0,0,BC_JMP,7,1,128,
|
||||
BC_RET1,6,2,0,BC_FORL,2,248,127,BC_RET0,0,1,0,
|
||||
/* table.foreach */ 0,2,11,0,0,1,16,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,BC_KPRI,
|
||||
2,0,0,BC_MOV,3,0,0,BC_KNUM,4,0,0,BC_JMP,5,7,128,BC_MOV,7,1,0,BC_MOV,9,5,0,
|
||||
BC_MOV,10,6,0,BC_CALL,7,3,2,BC_ISEQP,7,0,0,BC_JMP,8,1,128,BC_RET1,7,2,0,
|
||||
BC_ITERN,5,3,3,BC_ITERL,5,247,127,BC_RET0,0,1,0,1,255,255,249,255,15,
|
||||
/* table.getn */ 0,1,2,0,0,0,3,BC_ISTYPE,0,12,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.remove */ 0,2,10,0,0,2,30,BC_ISTYPE,0,12,0,BC_LEN,2,0,0,BC_ISNEP,1,0,
|
||||
0,BC_JMP,3,7,128,BC_ISEQN,2,0,0,BC_JMP,3,23,128,BC_TGETR,3,2,0,BC_KPRI,4,0,0,
|
||||
BC_TSETR,4,2,0,BC_RET1,3,2,0,BC_JMP,3,18,128,BC_ISTYPE,1,14,0,BC_KSHORT,3,1,0,
|
||||
BC_ISGT,3,1,0,BC_JMP,3,14,128,BC_ISGT,1,2,0,BC_JMP,3,12,128,BC_TGETR,3,1,0,
|
||||
BC_ADDVN,4,1,1,BC_MOV,5,2,0,BC_KSHORT,6,1,0,BC_FORI,4,4,128,BC_SUBVN,8,1,7,
|
||||
BC_TGETR,9,7,0,BC_TSETR,9,8,0,BC_FORL,4,252,127,BC_KPRI,4,0,0,BC_TSETR,4,2,0,
|
||||
BC_RET1,3,2,0,BC_RET0,0,1,0,0,2,
|
||||
/* table.move */ 0,5,12,0,0,0,35,BC_ISTYPE,0,12,0,BC_ISTYPE,1,14,0,BC_ISTYPE,
|
||||
2,14,0,BC_ISTYPE,3,14,0,BC_ISNEP,4,0,0,BC_JMP,5,1,128,BC_MOV,4,0,0,BC_ISTYPE,
|
||||
4,12,0,BC_ISGT,1,2,0,BC_JMP,5,24,128,BC_SUBVV,5,1,3,BC_ISLT,2,3,0,BC_JMP,6,4,
|
||||
128,BC_ISLE,3,1,0,BC_JMP,6,2,128,BC_ISEQV,4,0,0,BC_JMP,6,9,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,2,0,BC_KSHORT,8,1,0,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,
|
||||
BC_TSETR,11,10,4,BC_FORL,6,252,127,BC_JMP,6,8,128,BC_MOV,6,2,0,BC_MOV,7,1,0,
|
||||
BC_KSHORT,8,255,255,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,BC_TSETR,
|
||||
11,10,4,BC_FORL,6,252,127,BC_RET1,4,2,0,
|
||||
#else
|
||||
/* math.deg */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,241,135,158,166,3,
|
||||
220,203,178,130,4,
|
||||
/* math.rad */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,243,244,148,165,20,
|
||||
198,190,199,252,3,
|
||||
/* string.len */ 0,1,2,0,0,0,3,BC_ISTYPE,0,5,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.foreachi */ 0,2,9,0,0,0,15,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,
|
||||
BC_KSHORT,2,1,0,BC_LEN,3,0,0,BC_KSHORT,4,1,0,BC_FORI,2,8,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,5,0,BC_TGETR,8,5,0,BC_CALL,6,3,2,BC_ISEQP,6,0,0,BC_JMP,7,1,128,
|
||||
BC_RET1,6,2,0,BC_FORL,2,248,127,BC_RET0,0,1,0,
|
||||
/* table.foreach */ 0,2,10,0,0,1,16,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,BC_KPRI,
|
||||
2,0,0,BC_MOV,3,0,0,BC_KNUM,4,0,0,BC_JMP,5,7,128,BC_MOV,7,1,0,BC_MOV,8,5,0,
|
||||
BC_MOV,9,6,0,BC_CALL,7,3,2,BC_ISEQP,7,0,0,BC_JMP,8,1,128,BC_RET1,7,2,0,
|
||||
BC_ITERN,5,3,3,BC_ITERL,5,247,127,BC_RET0,0,1,0,1,255,255,249,255,15,
|
||||
/* table.getn */ 0,1,2,0,0,0,3,BC_ISTYPE,0,12,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.remove */ 0,2,10,0,0,2,30,BC_ISTYPE,0,12,0,BC_LEN,2,0,0,BC_ISNEP,1,0,
|
||||
0,BC_JMP,3,7,128,BC_ISEQN,2,0,0,BC_JMP,3,23,128,BC_TGETR,3,2,0,BC_KPRI,4,0,0,
|
||||
BC_TSETR,4,2,0,BC_RET1,3,2,0,BC_JMP,3,18,128,BC_ISTYPE,1,14,0,BC_KSHORT,3,1,0,
|
||||
BC_ISGT,3,1,0,BC_JMP,3,14,128,BC_ISGT,1,2,0,BC_JMP,3,12,128,BC_TGETR,3,1,0,
|
||||
BC_ADDVN,4,1,1,BC_MOV,5,2,0,BC_KSHORT,6,1,0,BC_FORI,4,4,128,BC_SUBVN,8,1,7,
|
||||
BC_TGETR,9,7,0,BC_TSETR,9,8,0,BC_FORL,4,252,127,BC_KPRI,4,0,0,BC_TSETR,4,2,0,
|
||||
BC_RET1,3,2,0,BC_RET0,0,1,0,0,2,
|
||||
/* table.move */ 0,5,12,0,0,0,35,BC_ISTYPE,0,12,0,BC_ISTYPE,1,14,0,BC_ISTYPE,
|
||||
2,14,0,BC_ISTYPE,3,14,0,BC_ISNEP,4,0,0,BC_JMP,5,1,128,BC_MOV,4,0,0,BC_ISTYPE,
|
||||
4,12,0,BC_ISGT,1,2,0,BC_JMP,5,24,128,BC_SUBVV,5,1,3,BC_ISLT,2,3,0,BC_JMP,6,4,
|
||||
128,BC_ISLE,3,1,0,BC_JMP,6,2,128,BC_ISEQV,4,0,0,BC_JMP,6,9,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,2,0,BC_KSHORT,8,1,0,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,
|
||||
BC_TSETR,11,10,4,BC_FORL,6,252,127,BC_JMP,6,8,128,BC_MOV,6,2,0,BC_MOV,7,1,0,
|
||||
BC_KSHORT,8,255,255,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,BC_TSETR,
|
||||
11,10,4,BC_FORL,6,252,127,BC_RET1,4,2,0,
|
||||
#endif
|
||||
0
|
||||
};
|
||||
|
||||
static const struct { const char *name; int ofs; } libbc_map[] = {
|
||||
{"math_deg",0},
|
||||
{"math_rad",25},
|
||||
{"string_len",50},
|
||||
{"table_foreachi",69},
|
||||
{"table_foreach",136},
|
||||
{"table_getn",213},
|
||||
{"table_remove",232},
|
||||
{"table_move",361},
|
||||
{NULL,508}
|
||||
};
|
||||
|
||||
@@ -1,473 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder: PE object emitter.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Only used for building on Windows, since we cannot assume the presence
|
||||
** of a suitable assembler. The host and target byte order must match.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
#if LJ_TARGET_WINDOWS || LJ_TARGET_CYGWIN
|
||||
|
||||
/* Context for PE object emitter. */
|
||||
static char *strtab;
|
||||
static size_t strtabofs;
|
||||
|
||||
/* -- PE object definitions ----------------------------------------------- */
|
||||
|
||||
/* PE header. */
|
||||
typedef struct PEheader {
|
||||
uint16_t arch;
|
||||
uint16_t nsects;
|
||||
uint32_t time;
|
||||
uint32_t symtabofs;
|
||||
uint32_t nsyms;
|
||||
uint16_t opthdrsz;
|
||||
uint16_t flags;
|
||||
} PEheader;
|
||||
|
||||
/* PE section. */
|
||||
typedef struct PEsection {
|
||||
char name[8];
|
||||
uint32_t vsize;
|
||||
uint32_t vaddr;
|
||||
uint32_t size;
|
||||
uint32_t ofs;
|
||||
uint32_t relocofs;
|
||||
uint32_t lineofs;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
|
||||
/* PE relocation. */
|
||||
typedef struct PEreloc {
|
||||
uint32_t vaddr;
|
||||
uint32_t symidx;
|
||||
uint16_t type;
|
||||
} PEreloc;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_RELOC_SIZE (4+4+2)
|
||||
|
||||
/* PE symbol table entry. */
|
||||
typedef struct PEsym {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
} n;
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl;
|
||||
uint8_t naux;
|
||||
} PEsym;
|
||||
|
||||
/* PE symbol table auxiliary entry for a section. */
|
||||
typedef struct PEsymaux {
|
||||
uint32_t size;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel;
|
||||
uint8_t unused[3];
|
||||
} PEsymaux;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
|
||||
|
||||
/* PE object CPU specific defines. */
|
||||
#if LJ_TARGET_X86
|
||||
#define PEOBJ_ARCH_TARGET 0x014c
|
||||
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x06
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_X64
|
||||
#define PEOBJ_ARCH_TARGET 0x8664
|
||||
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x03
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#define PEOBJ_PDATA_NRELOC 6
|
||||
#define PEOBJ_XDATA_SIZE (8*2+4+6*2)
|
||||
#elif LJ_TARGET_ARM64
|
||||
#define PEOBJ_ARCH_TARGET 0xaa64
|
||||
#define PEOBJ_RELOC_REL32 0x03 /* MS: BRANCH26. */
|
||||
#define PEOBJ_RELOC_DIR32 0x01
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x02
|
||||
#define PEOBJ_RELOC_OFS (-4)
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#define PEOBJ_PDATA_NRELOC 4
|
||||
#define PEOBJ_XDATA_SIZE (4+24+4 +4+8)
|
||||
#endif
|
||||
|
||||
/* Section numbers (0-based). */
|
||||
enum {
|
||||
PEOBJ_SECT_ABS = -2,
|
||||
PEOBJ_SECT_UNDEF = -1,
|
||||
PEOBJ_SECT_TEXT,
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
PEOBJ_SECT_PDATA,
|
||||
PEOBJ_SECT_XDATA,
|
||||
#elif LJ_TARGET_X86
|
||||
PEOBJ_SECT_SXDATA,
|
||||
#endif
|
||||
PEOBJ_SECT_RDATA_Z,
|
||||
PEOBJ_NSECTIONS
|
||||
};
|
||||
|
||||
/* Symbol types. */
|
||||
#define PEOBJ_TYPE_NULL 0
|
||||
#define PEOBJ_TYPE_FUNC 0x20
|
||||
|
||||
/* Symbol storage class. */
|
||||
#define PEOBJ_SCL_EXTERN 2
|
||||
#define PEOBJ_SCL_STATIC 3
|
||||
|
||||
/* -- PE object emitter --------------------------------------------------- */
|
||||
|
||||
/* Emit PE object symbol. */
|
||||
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
|
||||
int sect, int type, int scl)
|
||||
{
|
||||
PEsym sym;
|
||||
size_t len = strlen(name);
|
||||
if (!strtab) { /* Pass 1: only calculate string table length. */
|
||||
if (len > 8) strtabofs += len+1;
|
||||
return;
|
||||
}
|
||||
if (len <= 8) {
|
||||
memcpy(sym.n.name, name, len);
|
||||
memset(sym.n.name+len, 0, 8-len);
|
||||
} else {
|
||||
sym.n.nameref[0] = 0;
|
||||
sym.n.nameref[1] = (uint32_t)strtabofs;
|
||||
memcpy(strtab + strtabofs, name, len);
|
||||
strtab[strtabofs+len] = 0;
|
||||
strtabofs += len+1;
|
||||
}
|
||||
sym.value = value;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = (uint16_t)type;
|
||||
sym.scl = (uint8_t)scl;
|
||||
sym.naux = 0;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit PE object section symbol. */
|
||||
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
|
||||
{
|
||||
PEsym sym;
|
||||
PEsymaux aux;
|
||||
if (!strtab) return; /* Pass 1: no output. */
|
||||
memcpy(sym.n.name, pesect[sect].name, 8);
|
||||
sym.value = 0;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = PEOBJ_TYPE_NULL;
|
||||
sym.scl = PEOBJ_SCL_STATIC;
|
||||
sym.naux = 1;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
memset(&aux, 0, sizeof(PEsymaux));
|
||||
aux.size = pesect[sect].size;
|
||||
aux.nreloc = pesect[sect].nreloc;
|
||||
owrite(ctx, &aux, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit Windows PE object file. */
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
PEheader pehdr;
|
||||
PEsection pesect[PEOBJ_NSECTIONS];
|
||||
uint32_t sofs;
|
||||
int i, nrsym;
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
|
||||
#endif
|
||||
|
||||
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
|
||||
|
||||
/* Fill in PE sections. */
|
||||
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
|
||||
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
|
||||
pesect[PEOBJ_SECT_TEXT].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
|
||||
pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */
|
||||
pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS;
|
||||
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1);
|
||||
pesect[PEOBJ_SECT_PDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].size = PEOBJ_PDATA_NRELOC*4);
|
||||
pesect[PEOBJ_SECT_PDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = PEOBJ_PDATA_NRELOC) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_PDATA].flags = 0x40300040;
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1);
|
||||
pesect[PEOBJ_SECT_XDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].size = PEOBJ_XDATA_SIZE); /* See below. */
|
||||
pesect[PEOBJ_SECT_XDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
|
||||
#elif LJ_TARGET_X86
|
||||
memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1);
|
||||
pesect[PEOBJ_SECT_SXDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4);
|
||||
pesect[PEOBJ_SECT_SXDATA].relocofs = sofs;
|
||||
/* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240;
|
||||
#endif
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
|
||||
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
|
||||
|
||||
/* Fill in PE header. */
|
||||
pehdr.arch = PEOBJ_ARCH_TARGET;
|
||||
pehdr.nsects = PEOBJ_NSECTIONS;
|
||||
pehdr.time = 0; /* Timestamp is optional. */
|
||||
pehdr.symtabofs = sofs;
|
||||
pehdr.opthdrsz = 0;
|
||||
pehdr.flags = 0;
|
||||
|
||||
/* Compute the size of the symbol table:
|
||||
** @feat.00 + nsections*2
|
||||
** + asm_start + nsym
|
||||
** + nrsym
|
||||
*/
|
||||
nrsym = ctx->nrelocsym;
|
||||
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win. */
|
||||
#endif
|
||||
|
||||
/* Write PE object header and all sections. */
|
||||
owrite(ctx, &pehdr, sizeof(PEheader));
|
||||
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
|
||||
|
||||
/* Write .text section. */
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
|
||||
fprintf(stderr, "Error: different byte order for host and target\n");
|
||||
exit(1);
|
||||
}
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
for (i = 0; i < ctx->nreloc; i++) {
|
||||
PEreloc reloc;
|
||||
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS;
|
||||
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
|
||||
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint16_t xdata[8+2+6];
|
||||
PEreloc reloc;
|
||||
xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */
|
||||
xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */
|
||||
xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */
|
||||
xdata[3] = 0x3000; /* Push rbx. */
|
||||
xdata[4] = 0x6000; /* Push rsi. */
|
||||
xdata[5] = 0x7000; /* Push rdi. */
|
||||
xdata[6] = 0x5000; /* Push rbp. */
|
||||
xdata[7] = 0; /* Alignment. */
|
||||
xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */
|
||||
xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */
|
||||
xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */
|
||||
xdata[12] = 0x0300; /* set_fpreg. */
|
||||
xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */
|
||||
xdata[14] = 0x3000; /* Push rbx. */
|
||||
xdata[15] = 0x5000; /* Push rbp. */
|
||||
owrite(ctx, &xdata, sizeof(xdata));
|
||||
reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_ARM64
|
||||
/* https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling */
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t pdata[4];
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0;
|
||||
pdata[1] = 0;
|
||||
pdata[2] = fcofs;
|
||||
pdata[3] = 4+24+4;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
/* Start of .text and start of .xdata. */
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
/* Start of vm_ffi_call and start of second part of .xdata. */
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint32_t u32;
|
||||
uint8_t *p, uwc[24];
|
||||
PEreloc reloc;
|
||||
|
||||
#define CBE16(x) (*p = ((x) >> 8) & 0xff, p[1] = (x) & 0xff, p += 2)
|
||||
#define CALLOC_S(s) (*p++ = ((s) >> 4)) /* s < 512 */
|
||||
#define CSAVE_FPLR(o) (*p++ = 0x40 | ((o) >> 3)) /* o <= 504 */
|
||||
#define CSAVE_REGP(r,o) CBE16(0xc800 | (((r) - 19) << 6) | ((o) >> 3))
|
||||
#define CSAVE_REGS(r1,r2,o1) do { \
|
||||
int r, o; for (r = r1, o = o1; r <= r2; r += 2, o -= 16) CSAVE_REGP(r, o); \
|
||||
} while (0)
|
||||
#define CSAVE_REGPX(r,o) CBE16(0xcc00 | (((r) - 19) << 6) | (~(o) >> 3))
|
||||
#define CSAVE_FREGP(r,o) CBE16(0xd800 | (((r) - 8) << 6) | ((o) >> 3))
|
||||
#define CSAVE_FREGS(r1,r2,o1) do { \
|
||||
int r, o; for (r = r1, o = o1; r <= r2; r += 2, o -= 16) CSAVE_FREGP(r, o); \
|
||||
} while (0)
|
||||
#define CADD_FP(s) CBE16(0xe200 | ((s) >> 3)) /* s < 8*256 */
|
||||
#define CODE_NOP 0xe3
|
||||
#define CODE_END 0xe4
|
||||
#define CEND_ALIGN do { \
|
||||
*p++ = CODE_END; \
|
||||
while ((p - uwc) & 3) *p++ = CODE_NOP; \
|
||||
} while (0)
|
||||
|
||||
/* Unwind codes for .text section with handler. */
|
||||
p = uwc;
|
||||
CADD_FP(192); /* +2 */
|
||||
CSAVE_REGS(19, 28, 176); /* +5*2 */
|
||||
CSAVE_FREGS(8, 15, 96); /* +4*2 */
|
||||
CSAVE_FPLR(192); /* +1 */
|
||||
CALLOC_S(208); /* +1 */
|
||||
CEND_ALIGN; /* +1 +1 -> 24 */
|
||||
|
||||
u32 = ((24u >> 2) << 27) | (1u << 20) | (fcofs >> 2);
|
||||
owrite(ctx, &u32, 4);
|
||||
owrite(ctx, &uwc, 24);
|
||||
|
||||
u32 = 0; /* Handler RVA to be relocated at 4 + 24. */
|
||||
owrite(ctx, &u32, 4);
|
||||
|
||||
/* Unwind codes for vm_ffi_call without handler. */
|
||||
p = uwc;
|
||||
CADD_FP(16); /* +2 */
|
||||
CSAVE_FPLR(16); /* +1 */
|
||||
CSAVE_REGPX(19, -32); /* +2 */
|
||||
CEND_ALIGN; /* +1 +2 -> 8 */
|
||||
|
||||
u32 = ((8u >> 2) << 27) | (((uint32_t)ctx->codesz - fcofs) >> 2);
|
||||
owrite(ctx, &u32, 4);
|
||||
owrite(ctx, &uwc, 8);
|
||||
|
||||
reloc.vaddr = 4 + 24; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_X86
|
||||
/* Write .sxdata section. */
|
||||
for (i = 0; i < nrsym; i++) {
|
||||
if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) {
|
||||
uint32_t symidx = 1+2+i;
|
||||
owrite(ctx, &symidx, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == nrsym) {
|
||||
fprintf(stderr, "Error: extern lj_err_unwind_win not used\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write .rdata$Z section. */
|
||||
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
|
||||
|
||||
/* Write symbol table. */
|
||||
strtab = NULL; /* 1st pass: collect string sizes. */
|
||||
for (;;) {
|
||||
strtabofs = 4;
|
||||
/* Mark as SafeSEH compliant. */
|
||||
emit_peobj_sym(ctx, "@feat.00", 1,
|
||||
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
|
||||
for (i = 0; i < nrsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->relocsym[i], 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
|
||||
emit_peobj_sym(ctx, "lj_err_unwind_win", 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
#elif LJ_TARGET_X86
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA);
|
||||
#endif
|
||||
|
||||
emit_peobj_sym(ctx, ctx->beginsym, 0,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
|
||||
for (i = 0; i < ctx->nsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
|
||||
|
||||
if (strtab)
|
||||
break;
|
||||
/* 2nd pass: alloc strtab, write syms and copy strings. */
|
||||
strtab = (char *)malloc(strtabofs);
|
||||
*(uint32_t *)strtab = (uint32_t)strtabofs;
|
||||
}
|
||||
|
||||
/* Write string table. */
|
||||
owrite(ctx, strtab, strtabofs);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
fprintf(stderr, "Error: no PE object support for this target\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,234 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to dump the bytecode of the library functions written in Lua.
|
||||
-- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local ffi = require("ffi")
|
||||
local bit = require("bit")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bcnames = vmdef.bcnames
|
||||
|
||||
local format = string.format
|
||||
|
||||
local isbe = (string.byte(string.dump(function() end), 5) % 2 == 1)
|
||||
|
||||
local function usage(arg)
|
||||
io.stderr:write("Usage: ", arg and arg[0] or "genlibbc",
|
||||
" [-o buildvm_libbc.h] lib_*.c\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function parse_arg(arg)
|
||||
local outfile = "-"
|
||||
if not (arg and arg[1]) then
|
||||
usage(arg)
|
||||
end
|
||||
if arg[1] == "-o" then
|
||||
outfile = arg[2]
|
||||
if not outfile then usage(arg) end
|
||||
table.remove(arg, 1)
|
||||
table.remove(arg, 1)
|
||||
end
|
||||
return outfile
|
||||
end
|
||||
|
||||
local function read_files(names)
|
||||
local src = ""
|
||||
for _,name in ipairs(names) do
|
||||
local fp = assert(io.open(name))
|
||||
src = src .. fp:read("*a")
|
||||
fp:close()
|
||||
end
|
||||
return src
|
||||
end
|
||||
|
||||
local function transform_lua(code)
|
||||
local fixup = {}
|
||||
local n = -30000
|
||||
code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var)
|
||||
n = n + 1
|
||||
fixup[n] = { "CHECK", tp }
|
||||
return format("%s=%d", var, n)
|
||||
end)
|
||||
code = string.gsub(code, "PAIRS%((.-)%)", function(var)
|
||||
fixup.PAIRS = true
|
||||
return format("nil, %s, 0x4dp80", var)
|
||||
end)
|
||||
return "return "..code, fixup
|
||||
end
|
||||
|
||||
local function read_uleb128(p)
|
||||
local v = p[0]; p = p + 1
|
||||
if v >= 128 then
|
||||
local sh = 7; v = v - 128
|
||||
repeat
|
||||
local r = p[0]
|
||||
v = v + bit.lshift(bit.band(r, 127), sh)
|
||||
sh = sh + 7
|
||||
p = p + 1
|
||||
until r < 128
|
||||
end
|
||||
return p, v
|
||||
end
|
||||
|
||||
-- ORDER LJ_T
|
||||
local name2itype = {
|
||||
str = 5, func = 9, tab = 12, int = 14, num = 15
|
||||
}
|
||||
|
||||
local BC, BCN = {}, {}
|
||||
for i=0,#bcnames/6-1 do
|
||||
local name = bcnames:sub(i*6+1, i*6+6):gsub(" ", "")
|
||||
BC[name] = i
|
||||
BCN[i] = name
|
||||
end
|
||||
local xop, xra = isbe and 3 or 0, isbe and 2 or 1
|
||||
local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3
|
||||
|
||||
local function fixup_dump(dump, fixup)
|
||||
local buf = ffi.new("uint8_t[?]", #dump+1, dump)
|
||||
local p = buf+5
|
||||
local n, sizebc
|
||||
p, n = read_uleb128(p)
|
||||
local start = p
|
||||
p = p + 4
|
||||
p = read_uleb128(p)
|
||||
p = read_uleb128(p)
|
||||
p, sizebc = read_uleb128(p)
|
||||
local startbc = tonumber(p - start)
|
||||
local rawtab = {}
|
||||
for i=0,sizebc-1 do
|
||||
local op = p[xop]
|
||||
if op == BC.KSHORT then
|
||||
local rd = p[xrc] + 256*p[xrb]
|
||||
rd = bit.arshift(bit.lshift(rd, 16), 16)
|
||||
local f = fixup[rd]
|
||||
if f then
|
||||
if f[1] == "CHECK" then
|
||||
local tp = f[2]
|
||||
if tp == "tab" then rawtab[p[xra]] = true end
|
||||
p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE
|
||||
p[xrb] = 0
|
||||
p[xrc] = name2itype[tp]
|
||||
else
|
||||
error("unhandled fixup type: "..f[1])
|
||||
end
|
||||
end
|
||||
elseif op == BC.TGETV then
|
||||
if rawtab[p[xrb]] then
|
||||
p[xop] = BC.TGETR
|
||||
end
|
||||
elseif op == BC.TSETV then
|
||||
if rawtab[p[xrb]] then
|
||||
p[xop] = BC.TSETR
|
||||
end
|
||||
elseif op == BC.ITERC then
|
||||
if fixup.PAIRS then
|
||||
p[xop] = BC.ITERN
|
||||
end
|
||||
end
|
||||
p = p + 4
|
||||
end
|
||||
local ndump = ffi.string(start, n)
|
||||
-- Fixup hi-part of 0x4dp80 to LJ_KEYINDEX.
|
||||
ndump = ndump:gsub("\x80\x80\xcd\xaa\x04", "\xff\xff\xf9\xff\x0f")
|
||||
return { dump = ndump, startbc = startbc, sizebc = sizebc }
|
||||
end
|
||||
|
||||
local function find_defs(src, mode)
|
||||
local defs = {}
|
||||
for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do
|
||||
local tcode, fixup = transform_lua(code)
|
||||
local func = assert(load(tcode, "", mode))
|
||||
defs[name] = fixup_dump(string.dump(func, mode), fixup)
|
||||
defs[#defs+1] = name
|
||||
end
|
||||
return defs
|
||||
end
|
||||
|
||||
local function gen_header(defs32, defs64)
|
||||
local t = {}
|
||||
local function w(x) t[#t+1] = x end
|
||||
w("/* This is a generated file. DO NOT EDIT! */\n\n")
|
||||
w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n")
|
||||
for j,defs in ipairs{defs64, defs32} do
|
||||
local s, sb = "", ""
|
||||
for i,name in ipairs(defs) do
|
||||
local d = defs[name]
|
||||
s = s .. d.dump
|
||||
sb = sb .. string.char(i) .. ("\0"):rep(d.startbc - 1)
|
||||
.. (isbe and "\0\0\0\255" or "\255\0\0\0"):rep(d.sizebc)
|
||||
.. ("\0"):rep(#d.dump - d.startbc - d.sizebc*4)
|
||||
end
|
||||
if j == 1 then
|
||||
w("static const uint8_t libbc_code[] = {\n#if LJ_FR2\n")
|
||||
else
|
||||
w("\n#else\n")
|
||||
end
|
||||
local n = 0
|
||||
for i=1,#s do
|
||||
local x = string.byte(s, i)
|
||||
local xb = string.byte(sb, i)
|
||||
if xb == 255 then
|
||||
local name = BCN[x]
|
||||
local m = #name + 4
|
||||
if n + m > 78 then n = 0; w("\n") end
|
||||
n = n + m
|
||||
w("BC_"); w(name)
|
||||
else
|
||||
local m = x < 10 and 2 or (x < 100 and 3 or 4)
|
||||
if xb == 0 then
|
||||
if n + m > 78 then n = 0; w("\n") end
|
||||
else
|
||||
local name = defs[xb]:gsub("_", ".")
|
||||
if n ~= 0 then w("\n") end
|
||||
w("/* "); w(name); w(" */ ")
|
||||
n = #name + 7
|
||||
end
|
||||
n = n + m
|
||||
w(x)
|
||||
end
|
||||
w(",")
|
||||
end
|
||||
end
|
||||
w("\n#endif\n0\n};\n\n")
|
||||
w("static const struct { const char *name; int ofs; } libbc_map[] = {\n")
|
||||
local m32, m64 = 0, 0
|
||||
for i,name in ipairs(defs32) do
|
||||
assert(name == defs64[i])
|
||||
w('{"'); w(name); w('",'); w(m32) w('},\n')
|
||||
m32 = m32 + #defs32[name].dump
|
||||
m64 = m64 + #defs64[name].dump
|
||||
assert(m32 == m64)
|
||||
end
|
||||
w("{NULL,"); w(m32); w("}\n};\n\n")
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function write_file(name, data)
|
||||
if name == "-" then
|
||||
assert(io.write(data))
|
||||
assert(io.flush())
|
||||
else
|
||||
local fp = io.open(name)
|
||||
if fp then
|
||||
local old = fp:read("*a")
|
||||
fp:close()
|
||||
if data == old then return end
|
||||
end
|
||||
fp = assert(io.open(name, "w"))
|
||||
assert(fp:write(data))
|
||||
assert(fp:close())
|
||||
end
|
||||
end
|
||||
|
||||
local outfile = parse_arg(arg)
|
||||
local src = read_files(arg)
|
||||
local defs32 = find_defs(src, "Wdts")
|
||||
local defs64 = find_defs(src, "Xdts")
|
||||
local hdr = gen_header(defs32, defs64)
|
||||
write_file(outfile, hdr)
|
||||
|
||||
@@ -1,436 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to generate a customized, minified version of Lua.
|
||||
-- The resulting 'minilua' is used for the build process of LuaJIT.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local sub, match, gsub = string.sub, string.match, string.gsub
|
||||
|
||||
local LUA_VERSION = "5.1.5"
|
||||
local LUA_SOURCE
|
||||
|
||||
local function usage()
|
||||
io.stderr:write("Usage: ", arg and arg[0] or "genminilua",
|
||||
" lua-", LUA_VERSION, "-source-dir\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function find_sources()
|
||||
LUA_SOURCE = arg and arg[1]
|
||||
if not LUA_SOURCE then usage() end
|
||||
if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end
|
||||
local fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then
|
||||
LUA_SOURCE = LUA_SOURCE.."src/"
|
||||
fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then usage() end
|
||||
end
|
||||
local all = fp:read("*a")
|
||||
fp:close()
|
||||
if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then
|
||||
io.stderr:write("Error: version mismatch\n")
|
||||
usage()
|
||||
end
|
||||
end
|
||||
|
||||
local LUA_FILES = {
|
||||
"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c",
|
||||
"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c",
|
||||
"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c",
|
||||
"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c",
|
||||
}
|
||||
|
||||
local REMOVE_LIB = {}
|
||||
gsub([[
|
||||
collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset
|
||||
select tostring xpcall
|
||||
foreach foreachi getn maxn setn
|
||||
popen tmpfile seek setvbuf __tostring
|
||||
clock date difftime execute getenv rename setlocale time tmpname
|
||||
dump gfind len reverse
|
||||
LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME
|
||||
]], "%S+", function(name)
|
||||
REMOVE_LIB[name] = true
|
||||
end)
|
||||
|
||||
local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, }
|
||||
|
||||
local CUSTOM_MAIN = [[
|
||||
typedef unsigned int UB;
|
||||
static UB barg(lua_State *L,int idx){
|
||||
union{lua_Number n;U64 b;}bn;
|
||||
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
|
||||
if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
|
||||
return(UB)bn.b;
|
||||
}
|
||||
#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1;
|
||||
static int tobit(lua_State *L){
|
||||
BRET(barg(L,1))}
|
||||
static int bnot(lua_State *L){
|
||||
BRET(~barg(L,1))}
|
||||
static int band(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
|
||||
static int bor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
|
||||
static int bxor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
|
||||
static int lshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
|
||||
static int rshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
|
||||
static int arshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
|
||||
static int rol(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
|
||||
static int ror(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
|
||||
static int bswap(lua_State *L){
|
||||
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
|
||||
static int tohex(lua_State *L){
|
||||
UB b=barg(L,1);
|
||||
int n=lua_isnone(L,2)?8:(int)barg(L,2);
|
||||
const char *hexdigits="0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
|
||||
if(n>8)n=8;
|
||||
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
|
||||
lua_pushlstring(L,buf,(size_t)n);
|
||||
return 1;
|
||||
}
|
||||
static const struct luaL_Reg bitlib[] = {
|
||||
{"tobit",tobit},
|
||||
{"bnot",bnot},
|
||||
{"band",band},
|
||||
{"bor",bor},
|
||||
{"bxor",bxor},
|
||||
{"lshift",lshift},
|
||||
{"rshift",rshift},
|
||||
{"arshift",arshift},
|
||||
{"rol",rol},
|
||||
{"ror",ror},
|
||||
{"bswap",bswap},
|
||||
{"tohex",tohex},
|
||||
{NULL,NULL}
|
||||
};
|
||||
int main(int argc, char **argv){
|
||||
lua_State *L = luaL_newstate();
|
||||
int i;
|
||||
luaL_openlibs(L);
|
||||
luaL_register(L, "bit", bitlib);
|
||||
if (argc < 2) return sizeof(void *);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushstring(L, argv[1]);
|
||||
lua_rawseti(L, -2, 0);
|
||||
lua_setglobal(L, "arg");
|
||||
if (luaL_loadfile(L, argv[1]))
|
||||
goto err;
|
||||
for (i = 2; i < argc; i++)
|
||||
lua_pushstring(L, argv[i]);
|
||||
if (lua_pcall(L, argc - 2, 0, 0)) {
|
||||
err:
|
||||
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
|
||||
return 1;
|
||||
}
|
||||
lua_close(L);
|
||||
return 0;
|
||||
}
|
||||
]]
|
||||
|
||||
local function read_sources()
|
||||
local t = {}
|
||||
for i, name in ipairs(LUA_FILES) do
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
t[i] = fp:read("*a")
|
||||
assert(fp:close())
|
||||
end
|
||||
t[#t+1] = CUSTOM_MAIN
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local includes = {}
|
||||
|
||||
local function merge_includes(src)
|
||||
return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name)
|
||||
if includes[name] then return "" end
|
||||
includes[name] = true
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
local inc = fp:read("*a")
|
||||
assert(fp:close())
|
||||
inc = gsub(inc, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "")
|
||||
inc = gsub(inc, "#endif%s*$", "")
|
||||
return merge_includes(inc)
|
||||
end)
|
||||
end
|
||||
|
||||
local function get_license(src)
|
||||
return match(src, "/%*+\n%* Copyright %(.-%*/\n")
|
||||
end
|
||||
|
||||
local function fold_lines(src)
|
||||
return gsub(src, "\\\n", " ")
|
||||
end
|
||||
|
||||
local strings = {}
|
||||
|
||||
local function save_str(str)
|
||||
local n = #strings+1
|
||||
strings[n] = str
|
||||
return "\1"..n.."\2"
|
||||
end
|
||||
|
||||
local function save_strings(src)
|
||||
src = gsub(src, '"[^"\n]*"', save_str)
|
||||
return gsub(src, "'[^'\n]*'", save_str)
|
||||
end
|
||||
|
||||
local function restore_strings(src)
|
||||
return gsub(src, "\1(%d+)\2", function(numstr)
|
||||
return strings[tonumber(numstr)]
|
||||
end)
|
||||
end
|
||||
|
||||
local function def_istrue(def)
|
||||
return def == "INT_MAX > 2147483640L" or
|
||||
def == "LUAI_BITSINT >= 32" or
|
||||
def == "SIZE_Bx < LUAI_BITSINT-1" or
|
||||
def == "cast" or
|
||||
def == "defined(LUA_CORE)" or
|
||||
def == "MINSTRTABSIZE" or
|
||||
def == "LUA_MINBUFFER" or
|
||||
def == "HARDSTACKTESTS" or
|
||||
def == "UNUSED"
|
||||
end
|
||||
|
||||
local head, defs = {[[
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int64 U64;
|
||||
#else
|
||||
typedef unsigned long long U64;
|
||||
#endif
|
||||
int _CRT_glob = 0;
|
||||
]]}, {}
|
||||
|
||||
local function preprocess(src)
|
||||
local t = { match(src, "^(.-)#") }
|
||||
local lvl, on, oldon = 0, true, {}
|
||||
for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do
|
||||
if pp == "if" or pp == "ifdef" or pp == "ifndef" then
|
||||
lvl = lvl + 1
|
||||
oldon[lvl] = on
|
||||
on = def_istrue(def)
|
||||
elseif pp == "else" then
|
||||
if oldon[lvl] then
|
||||
if on == false then on = true else on = false end
|
||||
end
|
||||
elseif pp == "elif" then
|
||||
if oldon[lvl] then
|
||||
on = def_istrue(def)
|
||||
end
|
||||
elseif pp == "endif" then
|
||||
on = oldon[lvl]
|
||||
lvl = lvl - 1
|
||||
elseif on then
|
||||
if pp == "include" then
|
||||
if not head[def] and not REMOVE_EXTINC[def] then
|
||||
head[def] = true
|
||||
head[#head+1] = "#include "..def.."\n"
|
||||
end
|
||||
elseif pp == "define" then
|
||||
local k, sp, v = match(def, "([%w_]+)(%s*)(.*)")
|
||||
if k and not (sp == "" and sub(v, 1, 1) == "(") then
|
||||
defs[k] = gsub(v, "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
else
|
||||
t[#t+1] = "#define "..def.."\n"
|
||||
end
|
||||
elseif pp ~= "undef" then
|
||||
error("unexpected directive: "..pp.." "..def)
|
||||
end
|
||||
end
|
||||
if on then t[#t+1] = txt end
|
||||
end
|
||||
return gsub(table.concat(t), "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
end
|
||||
|
||||
local function merge_header(src, license)
|
||||
local hdr = string.format([[
|
||||
/* This is a heavily customized and minimized copy of Lua %s. */
|
||||
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
|
||||
]], LUA_VERSION)
|
||||
return hdr..license..table.concat(head)..src
|
||||
end
|
||||
|
||||
local function strip_unused1(src)
|
||||
return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func)
|
||||
return REMOVE_LIB[func] and "" or line
|
||||
end)
|
||||
end
|
||||
|
||||
local function strip_unused2(src)
|
||||
return gsub(src, "Symbolic Execution.-}=", "")
|
||||
end
|
||||
|
||||
local function strip_unused3(src)
|
||||
src = gsub(src, "extern", "static")
|
||||
src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(")
|
||||
src = gsub(src, "#define lua_assert[^\n]*\n", "")
|
||||
src = gsub(src, "lua_assert%b();?", "")
|
||||
src = gsub(src, "default:\n}", "default:;\n}")
|
||||
src = gsub(src, "lua_lock%b();", "")
|
||||
src = gsub(src, "lua_unlock%b();", "")
|
||||
src = gsub(src, "luai_threadyield%b();", "")
|
||||
src = gsub(src, "luai_userstateopen%b();", "{}")
|
||||
src = gsub(src, "luai_userstate%w+%b();", "")
|
||||
src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser")
|
||||
src = gsub(src, "trydecpoint%(ls,seminfo%)",
|
||||
"luaX_lexerror(ls,\"malformed number\",TK_NUMBER)")
|
||||
src = gsub(src, "int c=luaZ_lookahead%b();", "")
|
||||
src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;",
|
||||
"return 1;")
|
||||
src = gsub(src, "getfuncname%b():", "NULL:")
|
||||
src = gsub(src, "getobjname%b():", "NULL:")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "")
|
||||
src = gsub(src, "(twoto%b()%()", "%1(size_t)")
|
||||
src = gsub(src, "i<sizenode", "i<(int)sizenode")
|
||||
src = gsub(src, "cast%(unsigned int,key%-1%)", "cast(unsigned int,key)-1")
|
||||
return gsub(src, "\n\n+", "\n")
|
||||
end
|
||||
|
||||
local function strip_comments(src)
|
||||
return gsub(src, "/%*.-%*/", " ")
|
||||
end
|
||||
|
||||
local function strip_whitespace(src)
|
||||
src = gsub(src, "^%s+", "")
|
||||
src = gsub(src, "%s*\n%s*", "\n")
|
||||
src = gsub(src, "[ \t]+", " ")
|
||||
src = gsub(src, "(%W) ", "%1")
|
||||
return gsub(src, " (%W)", "%1")
|
||||
end
|
||||
|
||||
local function rename_tokens1(src)
|
||||
src = gsub(src, "getline", "getline_")
|
||||
src = gsub(src, "struct ([%w_]+)", "ZX%1")
|
||||
return gsub(src, "union ([%w_]+)", "ZY%1")
|
||||
end
|
||||
|
||||
local function rename_tokens2(src)
|
||||
src = gsub(src, "ZX([%w_]+)", "struct %1")
|
||||
return gsub(src, "ZY([%w_]+)", "union %1")
|
||||
end
|
||||
|
||||
local function fix_bugs_and_warnings(src)
|
||||
src = gsub(src, "(luaD_checkstack%(L,p%->maxstacksize)%)", "%1+p->numparams)")
|
||||
src = gsub(src, "if%(sep==%-1%)(return'%[';)\nelse (luaX_lexerror%b();)", "if (sep!=-1)%2\n%1")
|
||||
return gsub(src, "(default:{\nNode%*n=mainposition)", "/*fallthrough*/\n%1")
|
||||
end
|
||||
|
||||
local function func_gather(src)
|
||||
local nodes, list = {}, {}
|
||||
local pos, len = 1, #src
|
||||
while pos < len do
|
||||
local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos)
|
||||
if d then
|
||||
local n = #list+1
|
||||
list[n] = d
|
||||
nodes[w] = n
|
||||
else
|
||||
local s
|
||||
d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos)
|
||||
if not d then
|
||||
d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos)
|
||||
if not d then d = match(src, "^[^\n]*\n", pos) end
|
||||
end
|
||||
if s == "{" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
if sub(d, -2) == "{\n" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
end
|
||||
end
|
||||
local k, v = nil, d
|
||||
if w == "typedef" then
|
||||
if match(d, "^typedef enum") then
|
||||
head[#head+1] = d
|
||||
else
|
||||
k = match(d, "([%w_]+);\n$")
|
||||
if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end
|
||||
end
|
||||
elseif w == "enum" then
|
||||
head[#head+1] = v
|
||||
elseif w ~= nil then
|
||||
k = match(d, "^[^\n]-([%w_]+)[(%[=]")
|
||||
if k then
|
||||
if w ~= "static" and k ~= "main" then v = "static "..d end
|
||||
else
|
||||
k = w
|
||||
end
|
||||
end
|
||||
if w and k then
|
||||
local o = nodes[k]
|
||||
if o then nodes["*"..k] = o end
|
||||
local n = #list+1
|
||||
list[n] = v
|
||||
nodes[k] = n
|
||||
end
|
||||
end
|
||||
pos = pos + #d
|
||||
end
|
||||
return nodes, list
|
||||
end
|
||||
|
||||
local function func_visit(nodes, list, used, n)
|
||||
local i = nodes[n]
|
||||
for m in string.gmatch(list[i], "[%w_]+") do
|
||||
if nodes[m] then
|
||||
local j = used[m]
|
||||
if not j then
|
||||
used[m] = i
|
||||
func_visit(nodes, list, used, m)
|
||||
elseif i < j then
|
||||
used[m] = i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function func_collect(src)
|
||||
local nodes, list = func_gather(src)
|
||||
local used = {}
|
||||
func_visit(nodes, list, used, "main")
|
||||
for n,i in pairs(nodes) do
|
||||
local j = used[n]
|
||||
if j and j < i then used["*"..n] = j end
|
||||
end
|
||||
for n,i in pairs(nodes) do
|
||||
if not used[n] then list[i] = "" end
|
||||
end
|
||||
return table.concat(list)
|
||||
end
|
||||
|
||||
find_sources()
|
||||
local src = read_sources()
|
||||
src = merge_includes(src)
|
||||
local license = get_license(src)
|
||||
src = fold_lines(src)
|
||||
src = strip_unused1(src)
|
||||
src = save_strings(src)
|
||||
src = strip_unused2(src)
|
||||
src = strip_comments(src)
|
||||
src = preprocess(src)
|
||||
src = strip_whitespace(src)
|
||||
src = strip_unused3(src)
|
||||
src = rename_tokens1(src)
|
||||
src = func_collect(src)
|
||||
src = rename_tokens2(src)
|
||||
src = restore_strings(src)
|
||||
src = fix_bugs_and_warnings(src)
|
||||
src = merge_header(src, license)
|
||||
io.write(src)
|
||||
@@ -1,45 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to embed the rolling release version in luajit.h.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local arg = {...}
|
||||
local FILE_ROLLING_H = arg[1] or "luajit_rolling.h"
|
||||
local FILE_RELVER_TXT = arg[2] or "luajit_relver.txt"
|
||||
local FILE_LUAJIT_H = arg[3] or "luajit.h"
|
||||
|
||||
local function file_read(file)
|
||||
local fp = assert(io.open(file, "rb"), "run from the wrong directory")
|
||||
local data = assert(fp:read("*a"))
|
||||
fp:close()
|
||||
return data
|
||||
end
|
||||
|
||||
local function file_write_mod(file, data)
|
||||
local fp = io.open(file, "rb")
|
||||
if fp then
|
||||
local odata = assert(fp:read("*a"))
|
||||
fp:close()
|
||||
if odata == data then return end
|
||||
end
|
||||
fp = assert(io.open(file, "wb"))
|
||||
assert(fp:write(data))
|
||||
assert(fp:close())
|
||||
end
|
||||
|
||||
local text = file_read(FILE_ROLLING_H):gsub("#error.-\n", "")
|
||||
local relver = file_read(FILE_RELVER_TXT):match("(%d+)")
|
||||
|
||||
if relver then
|
||||
text = text:gsub("ROLLING", relver)
|
||||
else
|
||||
io.stderr:write([[
|
||||
**** WARNING Cannot determine rolling release version from git log.
|
||||
**** WARNING The 'git' command must be available during the build.
|
||||
]])
|
||||
file_write_mod(FILE_RELVER_TXT, "ROLLING\n") -- Fallback for install target.
|
||||
end
|
||||
|
||||
file_write_mod(FILE_LUAJIT_H, text)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1 +0,0 @@
|
||||
vmdef.lua
|
||||
@@ -1,189 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT bytecode listing module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
|
||||
-- it hooks into the parser and lists all functions of a chunk as they
|
||||
-- are parsed.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
|
||||
-- luajit -jbc=- foo.lua
|
||||
-- luajit -jbc=foo.list foo.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
|
||||
-- is started.
|
||||
--
|
||||
-- This module can also be used programmatically:
|
||||
--
|
||||
-- local bc = require("jit.bc")
|
||||
--
|
||||
-- local function foo() print("hello") end
|
||||
--
|
||||
-- bc.dump(foo) --> -- BYTECODE -- [...]
|
||||
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
|
||||
--
|
||||
-- local out = {
|
||||
-- -- Do something with each line:
|
||||
-- write = function(t, ...) io.write(...) end,
|
||||
-- close = function(t) end,
|
||||
-- flush = function(t) end,
|
||||
-- }
|
||||
-- bc.dump(foo, out)
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bit = require("bit")
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, band, shr = string.byte, bit.band, bit.rshift
|
||||
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
|
||||
local funcuvname = jutil.funcuvname
|
||||
local bcnames = vmdef.bcnames
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
-- Return one bytecode line.
|
||||
local function bcline(func, pc, prefix)
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then return end
|
||||
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
|
||||
local a = band(shr(ins, 8), 0xff)
|
||||
local oidx = 6*band(ins, 0xff)
|
||||
local op = sub(bcnames, oidx+1, oidx+6)
|
||||
local s = format("%04d %s %-6s %3s ",
|
||||
pc, prefix or " ", op, ma == 0 and "" or a)
|
||||
local d = shr(ins, 16)
|
||||
if mc == 13*128 then -- BCMjump
|
||||
return format("%s=> %04d\n", s, pc+d-0x7fff)
|
||||
end
|
||||
if mb ~= 0 then
|
||||
d = band(d, 0xff)
|
||||
elseif mc == 0 then
|
||||
return s.."\n"
|
||||
end
|
||||
local kc
|
||||
if mc == 10*128 then -- BCMstr
|
||||
kc = funck(func, -d-1)
|
||||
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
|
||||
elseif mc == 9*128 then -- BCMnum
|
||||
kc = funck(func, d)
|
||||
if op == "TSETM " then kc = kc - 2^52 end
|
||||
elseif mc == 12*128 then -- BCMfunc
|
||||
local fi = funcinfo(funck(func, -d-1))
|
||||
if fi.ffid then
|
||||
kc = vmdef.ffnames[fi.ffid]
|
||||
else
|
||||
kc = fi.loc
|
||||
end
|
||||
elseif mc == 5*128 then -- BCMuv
|
||||
kc = funcuvname(func, d)
|
||||
end
|
||||
if ma == 5 then -- BCMuv
|
||||
local ka = funcuvname(func, a)
|
||||
if kc then kc = ka.." ; "..kc else kc = ka end
|
||||
end
|
||||
if mb ~= 0 then
|
||||
local b = shr(ins, 24)
|
||||
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
|
||||
return format("%s%3d %3d\n", s, b, d)
|
||||
end
|
||||
if kc then return format("%s%3d ; %s\n", s, d, kc) end
|
||||
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
|
||||
return format("%s%3d\n", s, d)
|
||||
end
|
||||
|
||||
-- Collect branch targets of a function.
|
||||
local function bctargets(func)
|
||||
local target = {}
|
||||
for pc=1,1000000000 do
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then break end
|
||||
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
-- Dump bytecode instructions of a function.
|
||||
local function bcdump(func, out, all)
|
||||
if not out then out = stdout end
|
||||
local fi = funcinfo(func)
|
||||
if all and fi.children then
|
||||
for n=-1,-1000000000,-1 do
|
||||
local k = funck(func, n)
|
||||
if not k then break end
|
||||
if type(k) == "proto" then bcdump(k, out, true) end
|
||||
end
|
||||
end
|
||||
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
|
||||
local target = bctargets(func)
|
||||
for pc=1,1000000000 do
|
||||
local s = bcline(func, pc, target[pc] and "=>")
|
||||
if not s then break end
|
||||
out:write(s)
|
||||
end
|
||||
out:write("\n")
|
||||
out:flush()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
-- List handler.
|
||||
local function h_list(func)
|
||||
return bcdump(func, out)
|
||||
end
|
||||
|
||||
-- Detach list handler.
|
||||
local function bclistoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(h_list)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach list handler.
|
||||
local function bcliston(outfile)
|
||||
if active then bclistoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(h_list, "bc")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
line = bcline,
|
||||
dump = bcdump,
|
||||
targets = bctargets,
|
||||
on = bcliston,
|
||||
off = bclistoff,
|
||||
start = bcliston -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -1,629 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT module to save/list bytecode.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module saves or lists the bytecode for an input file.
|
||||
-- It's run by the -b command line option.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
|
||||
local bit = require("bit")
|
||||
|
||||
-- Symbol name prefix for LuaJIT bytecode.
|
||||
local LJBC_PREFIX = "luaJIT_BC_"
|
||||
|
||||
local type, assert = type, assert
|
||||
local format = string.format
|
||||
local tremove, tconcat = table.remove, table.concat
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function usage()
|
||||
io.stderr:write[[
|
||||
Save LuaJIT bytecode: luajit -b[options] input output
|
||||
-l Only list bytecode.
|
||||
-s Strip debug info (default).
|
||||
-g Keep debug info.
|
||||
-W Generate 32 bit (non-GC64) bytecode.
|
||||
-X Generate 64 bit (GC64) bytecode.
|
||||
-d Generate bytecode in deterministic manner.
|
||||
-n name Set module name (default: auto-detect from input name).
|
||||
-t type Set output file type (default: auto-detect from output name).
|
||||
-a arch Override architecture for object files (default: native).
|
||||
-o os Override OS for object files (default: native).
|
||||
-F name Override filename (default: input filename).
|
||||
-e chunk Use chunk string as input.
|
||||
-- Stop handling options.
|
||||
- Use stdin as input and/or stdout as output.
|
||||
|
||||
File types: c cc h obj o raw (default)
|
||||
]]
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function check(ok, ...)
|
||||
if ok then return ok, ... end
|
||||
io.stderr:write("luajit: ", ...)
|
||||
io.stderr:write("\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function readfile(ctx, input)
|
||||
if ctx.string then
|
||||
return check(loadstring(input, nil, ctx.mode))
|
||||
elseif ctx.filename then
|
||||
local data
|
||||
if input == "-" then
|
||||
data = io.stdin:read("*a")
|
||||
else
|
||||
local fp = assert(io.open(input, "rb"))
|
||||
data = assert(fp:read("*a"))
|
||||
assert(fp:close())
|
||||
end
|
||||
return check(load(data, ctx.filename, ctx.mode))
|
||||
else
|
||||
if input == "-" then input = nil end
|
||||
return check(loadfile(input, ctx.mode))
|
||||
end
|
||||
end
|
||||
|
||||
local function savefile(name, mode)
|
||||
if name == "-" then return io.stdout end
|
||||
return check(io.open(name, mode))
|
||||
end
|
||||
|
||||
local function set_stdout_binary(ffi)
|
||||
ffi.cdef[[int _setmode(int fd, int mode);]]
|
||||
ffi.C._setmode(1, 0x8000)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_type = {
|
||||
raw = "raw", c = "c", cc = "c", h = "h", o = "obj", obj = "obj",
|
||||
}
|
||||
|
||||
local map_arch = {
|
||||
x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
|
||||
x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
|
||||
arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
|
||||
arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
|
||||
arm64be = { e = "be", b = 64, m = 183, },
|
||||
ppc = { e = "be", b = 32, m = 20, },
|
||||
mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
|
||||
mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
|
||||
mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
|
||||
mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
|
||||
mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
|
||||
mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
|
||||
}
|
||||
|
||||
local map_os = {
|
||||
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
|
||||
openbsd = true, dragonfly = true, solaris = true,
|
||||
}
|
||||
|
||||
local function checkarg(str, map, err)
|
||||
str = str:lower()
|
||||
local s = check(map[str], "unknown ", err)
|
||||
return type(s) == "string" and s or str
|
||||
end
|
||||
|
||||
local function detecttype(str)
|
||||
local ext = str:lower():match("%.(%a+)$")
|
||||
return map_type[ext] or "raw"
|
||||
end
|
||||
|
||||
local function checkmodname(str)
|
||||
check(str:match("^[%w_.%-]+$"), "bad module name")
|
||||
return str:gsub("[%.%-]", "_")
|
||||
end
|
||||
|
||||
local function detectmodname(str)
|
||||
if type(str) == "string" then
|
||||
local tail = str:match("[^/\\]+$")
|
||||
if tail then str = tail end
|
||||
local head = str:match("^(.*)%.[^.]*$")
|
||||
if head then str = head end
|
||||
str = str:match("^[%w_.%-]+")
|
||||
else
|
||||
str = nil
|
||||
end
|
||||
check(str, "cannot derive module name, use -n name")
|
||||
return str:gsub("[%.%-]", "_")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bcsave_tail(fp, output, s)
|
||||
local ok, err = fp:write(s)
|
||||
if ok and output ~= "-" then ok, err = fp:close() end
|
||||
check(ok, "cannot write ", output, ": ", err)
|
||||
end
|
||||
|
||||
local function bcsave_raw(output, s)
|
||||
if output == "-" and jit.os == "Windows" then
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write binary file to stdout")
|
||||
set_stdout_binary(ffi)
|
||||
end
|
||||
local fp = savefile(output, "wb")
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_c(ctx, output, s)
|
||||
local fp = savefile(output, "w")
|
||||
if ctx.type == "c" then
|
||||
fp:write(format([[
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
const unsigned char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname))
|
||||
else
|
||||
fp:write(format([[
|
||||
#define %s%s_SIZE %d
|
||||
static const unsigned char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
|
||||
end
|
||||
local t, n, m = {}, 0, 0
|
||||
for i=1,#s do
|
||||
local b = tostring(string.byte(s, i))
|
||||
m = m + #b + 1
|
||||
if m > 78 then
|
||||
fp:write(tconcat(t, ",", 1, n), ",\n")
|
||||
n, m = 0, #b + 1
|
||||
end
|
||||
n = n + 1
|
||||
t[n] = b
|
||||
end
|
||||
bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
|
||||
end
|
||||
|
||||
local function bcsave_elfobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint32_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF32header;
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint64_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF64header;
|
||||
typedef struct {
|
||||
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
|
||||
} ELF32sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, type;
|
||||
uint64_t flags, addr, ofs, size;
|
||||
uint32_t link, info;
|
||||
uint64_t align, entsize;
|
||||
} ELF64sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, value, size;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
} ELF32symbol;
|
||||
typedef struct {
|
||||
uint32_t name;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
uint64_t value, size;
|
||||
} ELF64symbol;
|
||||
typedef struct {
|
||||
ELF32header hdr;
|
||||
ELF32sectheader sect[6];
|
||||
ELF32symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF32obj;
|
||||
typedef struct {
|
||||
ELF64header hdr;
|
||||
ELF64sectheader sect[6];
|
||||
ELF64symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF64obj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local ai = assert(map_arch[ctx.arch])
|
||||
local is64, isbe = ai.b == 64, ai.e == "be"
|
||||
|
||||
-- Handle different host/target endianess.
|
||||
local function f32(x) return x end
|
||||
local f16, fofs = f32, f32
|
||||
if ffi.abi("be") ~= isbe then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
if is64 then
|
||||
local two32 = ffi.cast("int64_t", 2^32)
|
||||
function fofs(x) return bit.bswap(x)*two32 end
|
||||
else
|
||||
fofs = f32
|
||||
end
|
||||
end
|
||||
|
||||
-- Create ELF object and fill in header.
|
||||
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
|
||||
local hdr = o.hdr
|
||||
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
|
||||
local bf = assert(io.open("/bin/ls", "rb"))
|
||||
local bs = bf:read(9)
|
||||
bf:close()
|
||||
ffi.copy(o, bs, 9)
|
||||
check(hdr.emagic[0] == 127, "no support for writing native object files")
|
||||
else
|
||||
hdr.emagic = "\127ELF"
|
||||
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
|
||||
end
|
||||
hdr.eclass = is64 and 2 or 1
|
||||
hdr.eendian = isbe and 2 or 1
|
||||
hdr.eversion = 1
|
||||
hdr.type = f16(1)
|
||||
hdr.machine = f16(ai.m)
|
||||
hdr.flags = f32(ai.f or 0)
|
||||
hdr.version = f32(1)
|
||||
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
|
||||
hdr.ehsize = f16(ffi.sizeof(hdr))
|
||||
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
|
||||
hdr.shnum = f16(6)
|
||||
hdr.shstridx = f16(2)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
local sofs, ofs = ffi.offsetof(o, "space"), 1
|
||||
for i,name in ipairs{
|
||||
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
|
||||
} do
|
||||
local sect = o.sect[i]
|
||||
sect.align = fofs(1)
|
||||
sect.name = f32(ofs)
|
||||
ffi.copy(o.space+ofs, name)
|
||||
ofs = ofs + #name+1
|
||||
end
|
||||
o.sect[1].type = f32(2) -- .symtab
|
||||
o.sect[1].link = f32(3)
|
||||
o.sect[1].info = f32(1)
|
||||
o.sect[1].align = fofs(8)
|
||||
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
|
||||
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
|
||||
o.sect[1].size = fofs(ffi.sizeof(o.sym))
|
||||
o.sym[1].name = f32(1)
|
||||
o.sym[1].sectidx = f16(4)
|
||||
o.sym[1].size = fofs(#s)
|
||||
o.sym[1].info = 17
|
||||
o.sect[2].type = f32(3) -- .shstrtab
|
||||
o.sect[2].ofs = fofs(sofs)
|
||||
o.sect[2].size = fofs(ofs)
|
||||
o.sect[3].type = f32(3) -- .strtab
|
||||
o.sect[3].ofs = fofs(sofs + ofs)
|
||||
o.sect[3].size = fofs(#symname+2)
|
||||
ffi.copy(o.space+ofs+1, symname)
|
||||
ofs = ofs + #symname + 2
|
||||
o.sect[4].type = f32(1) -- .rodata
|
||||
o.sect[4].flags = fofs(2)
|
||||
o.sect[4].ofs = fofs(sofs + ofs)
|
||||
o.sect[4].size = fofs(#s)
|
||||
o.sect[5].type = f32(1) -- .note.GNU-stack
|
||||
o.sect[5].ofs = fofs(sofs + ofs + #s)
|
||||
|
||||
-- Write ELF object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_peobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint16_t arch, nsects;
|
||||
uint32_t time, symtabofs, nsyms;
|
||||
uint16_t opthdrsz, flags;
|
||||
} PEheader;
|
||||
typedef struct {
|
||||
char name[8];
|
||||
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
typedef struct __attribute((packed)) {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
};
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl, naux;
|
||||
} PEsym;
|
||||
typedef struct __attribute((packed)) {
|
||||
uint32_t size;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel, unused[3];
|
||||
} PEsymaux;
|
||||
typedef struct {
|
||||
PEheader hdr;
|
||||
PEsection sect[2];
|
||||
// Must be an even number of symbol structs.
|
||||
PEsym sym0;
|
||||
PEsymaux sym0aux;
|
||||
PEsym sym1;
|
||||
PEsymaux sym1aux;
|
||||
PEsym sym2;
|
||||
PEsym sym3;
|
||||
uint32_t strtabsize;
|
||||
uint8_t space[4096];
|
||||
} PEobj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local ai = assert(map_arch[ctx.arch])
|
||||
local is64 = ai.b == 64
|
||||
local symexport = " /EXPORT:"..symname..",DATA "
|
||||
|
||||
-- The file format is always little-endian. Swap if the host is big-endian.
|
||||
local function f32(x) return x end
|
||||
local f16 = f32
|
||||
if ffi.abi("be") then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
end
|
||||
|
||||
-- Create PE object and fill in header.
|
||||
local o = ffi.new("PEobj")
|
||||
local hdr = o.hdr
|
||||
hdr.arch = f16(assert(ai.p))
|
||||
hdr.nsects = f16(2)
|
||||
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
|
||||
hdr.nsyms = f32(6)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.sect[0].name = ".drectve"
|
||||
o.sect[0].size = f32(#symexport)
|
||||
o.sect[0].flags = f32(0x00100a00)
|
||||
o.sym0.sect = f16(1)
|
||||
o.sym0.scl = 3
|
||||
o.sym0.name = ".drectve"
|
||||
o.sym0.naux = 1
|
||||
o.sym0aux.size = f32(#symexport)
|
||||
o.sect[1].name = ".rdata"
|
||||
o.sect[1].size = f32(#s)
|
||||
o.sect[1].flags = f32(0x40300040)
|
||||
o.sym1.sect = f16(2)
|
||||
o.sym1.scl = 3
|
||||
o.sym1.name = ".rdata"
|
||||
o.sym1.naux = 1
|
||||
o.sym1aux.size = f32(#s)
|
||||
o.sym2.sect = f16(2)
|
||||
o.sym2.scl = 2
|
||||
o.sym2.nameref[1] = f32(4)
|
||||
o.sym3.sect = f16(-1)
|
||||
o.sym3.scl = 2
|
||||
o.sym3.value = f32(1)
|
||||
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
|
||||
ffi.copy(o.space, symname)
|
||||
local ofs = #symname + 1
|
||||
o.strtabsize = f32(ofs + 4)
|
||||
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
ffi.copy(o.space + ofs, symexport)
|
||||
ofs = ofs + #symexport
|
||||
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
|
||||
-- Write PE object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_machobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
|
||||
} mach_header;
|
||||
typedef struct
|
||||
{
|
||||
mach_header; uint32_t reserved;
|
||||
} mach_header_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint64_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command_64;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint64_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2, reserved3;
|
||||
} mach_section_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
|
||||
} mach_symtab_command;
|
||||
typedef struct {
|
||||
int32_t strx;
|
||||
uint8_t type, sect;
|
||||
uint16_t desc;
|
||||
uint64_t value;
|
||||
} mach_nlist_64;
|
||||
typedef struct {
|
||||
mach_header_64 hdr;
|
||||
mach_segment_command_64 seg;
|
||||
mach_section_64 sec;
|
||||
mach_symtab_command sym;
|
||||
mach_nlist_64 sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj_64;
|
||||
]]
|
||||
local symname = '_'..LJBC_PREFIX..ctx.modname
|
||||
local cputype, cpusubtype = 0x01000007, 3
|
||||
if ctx.arch ~= "x64" then
|
||||
check(ctx.arch == "arm64", "unsupported architecture for OSX")
|
||||
cputype, cpusubtype = 0x0100000c, 0
|
||||
end
|
||||
local function aligned(v, a) return bit.band(v+a-1, -a) end
|
||||
|
||||
-- Create Mach-O object and fill in header.
|
||||
local o = ffi.new("mach_obj_64")
|
||||
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, 8)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.hdr.magic = 0xfeedfacf
|
||||
o.hdr.cputype = cputype
|
||||
o.hdr.cpusubtype = cpusubtype
|
||||
o.hdr.filetype = 1
|
||||
o.hdr.ncmds = 2
|
||||
o.hdr.sizeofcmds = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)+ffi.sizeof(o.sym)
|
||||
o.seg.cmd = 0x19
|
||||
o.seg.cmdsize = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)
|
||||
o.seg.vmsize = #s
|
||||
o.seg.fileoff = mach_size
|
||||
o.seg.filesize = #s
|
||||
o.seg.maxprot = 1
|
||||
o.seg.initprot = 1
|
||||
o.seg.nsects = 1
|
||||
ffi.copy(o.sec.sectname, "__data")
|
||||
ffi.copy(o.sec.segname, "__DATA")
|
||||
o.sec.size = #s
|
||||
o.sec.offset = mach_size
|
||||
o.sym.cmd = 2
|
||||
o.sym.cmdsize = ffi.sizeof(o.sym)
|
||||
o.sym.symoff = ffi.offsetof(o, "sym_entry")
|
||||
o.sym.nsyms = 1
|
||||
o.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)
|
||||
o.sym.strsize = aligned(#symname+2, 8)
|
||||
o.sym_entry.type = 0xf
|
||||
o.sym_entry.sect = 1
|
||||
o.sym_entry.strx = 1
|
||||
ffi.copy(o.space+1, symname)
|
||||
|
||||
-- Write Mach-O object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, mach_size))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_obj(ctx, output, s)
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write this file type")
|
||||
if output == "-" and jit.os == "Windows" then
|
||||
set_stdout_binary(ffi)
|
||||
end
|
||||
if ctx.os == "windows" then
|
||||
return bcsave_peobj(ctx, output, s, ffi)
|
||||
elseif ctx.os == "osx" then
|
||||
return bcsave_machobj(ctx, output, s, ffi)
|
||||
else
|
||||
return bcsave_elfobj(ctx, output, s, ffi)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bclist(ctx, input, output)
|
||||
local f = readfile(ctx, input)
|
||||
require("jit.bc").dump(f, savefile(output, "w"), true)
|
||||
end
|
||||
|
||||
local function bcsave(ctx, input, output)
|
||||
local f = readfile(ctx, input)
|
||||
local s = string.dump(f, ctx.mode)
|
||||
local t = ctx.type
|
||||
if not t then
|
||||
t = detecttype(output)
|
||||
ctx.type = t
|
||||
end
|
||||
if t == "raw" then
|
||||
bcsave_raw(output, s)
|
||||
else
|
||||
if not ctx.modname then ctx.modname = detectmodname(input) end
|
||||
if t == "obj" then
|
||||
bcsave_obj(ctx, output, s)
|
||||
else
|
||||
bcsave_c(ctx, output, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function docmd(...)
|
||||
local arg = {...}
|
||||
local n = 1
|
||||
local list = false
|
||||
local ctx = {
|
||||
mode = "bt", arch = jit.arch, os = jit.os:lower(),
|
||||
type = false, modname = false, string = false,
|
||||
}
|
||||
local strip = "s"
|
||||
local gc64 = ""
|
||||
while n <= #arg do
|
||||
local a = arg[n]
|
||||
if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
|
||||
tremove(arg, n)
|
||||
if a == "--" then break end
|
||||
for m=2,#a do
|
||||
local opt = a:sub(m, m)
|
||||
if opt == "l" then
|
||||
list = true
|
||||
elseif opt == "s" then
|
||||
strip = "s"
|
||||
elseif opt == "g" then
|
||||
strip = ""
|
||||
elseif opt == "W" or opt == "X" then
|
||||
gc64 = opt
|
||||
elseif opt == "d" then
|
||||
ctx.mode = ctx.mode .. opt
|
||||
else
|
||||
if arg[n] == nil or m ~= #a then usage() end
|
||||
if opt == "e" then
|
||||
if n ~= 1 then usage() end
|
||||
ctx.string = true
|
||||
elseif opt == "n" then
|
||||
ctx.modname = checkmodname(tremove(arg, n))
|
||||
elseif opt == "t" then
|
||||
ctx.type = checkarg(tremove(arg, n), map_type, "file type")
|
||||
elseif opt == "a" then
|
||||
ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
|
||||
elseif opt == "o" then
|
||||
ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
|
||||
elseif opt == "F" then
|
||||
ctx.filename = "@"..tremove(arg, n)
|
||||
else
|
||||
usage()
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
ctx.mode = ctx.mode .. strip .. gc64
|
||||
if list then
|
||||
if #arg == 0 or #arg > 2 then usage() end
|
||||
bclist(ctx, arg[1], arg[2] or "-")
|
||||
else
|
||||
if #arg ~= 2 then usage() end
|
||||
bcsave(ctx, arg[1], arg[2])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
start = docmd -- Process -b command line option.
|
||||
}
|
||||
|
||||
@@ -1,689 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles most user-mode ARMv7 instructions
|
||||
-- NYI: Advanced SIMD and VFP instructions.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch = string.match, string.gmatch
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_loadc = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFmDN", "vstmFNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrFdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrFdl", "vldmdbFNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
[11] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGmDN", "vstmGNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrGdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrGdl", "vldmdbGNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
_ = {
|
||||
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfps = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaF.dnm", "vmlsF.dnm",
|
||||
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
|
||||
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
|
||||
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
|
||||
[0x20000] = "vdivF.dnm",
|
||||
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
|
||||
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
|
||||
[0x2c000] = "vmovF.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovF.dm", "vabsF.dm",
|
||||
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
|
||||
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
|
||||
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
|
||||
[0x0e01] = "vcvtG.dF.m",
|
||||
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
|
||||
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
|
||||
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfpd = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaG.dnm", "vmlsG.dnm",
|
||||
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
|
||||
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
|
||||
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
|
||||
[0x20000] = "vdivG.dnm",
|
||||
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
|
||||
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
|
||||
[0x2c000] = "vmovG.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovG.dm", "vabsG.dm",
|
||||
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
|
||||
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
|
||||
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
|
||||
[0x0e01] = "vcvtF.dG.m",
|
||||
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
|
||||
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
|
||||
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
|
||||
},
|
||||
}
|
||||
|
||||
local map_datac = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = map_vfps,
|
||||
[11] = map_vfpd,
|
||||
-- NYI cdp, mcr, mrc.
|
||||
},
|
||||
{
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 15,
|
||||
[0] = "vmovFnD", "vmovFDn",
|
||||
[14] = "vmsrD",
|
||||
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
|
||||
},
|
||||
},
|
||||
},
|
||||
"svcT",
|
||||
}
|
||||
|
||||
local map_loadcu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP load/store.
|
||||
}
|
||||
|
||||
local map_datacu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP data.
|
||||
}
|
||||
|
||||
local map_simddata = {
|
||||
shift = 0, mask = 0, -- NYI SIMD data.
|
||||
}
|
||||
|
||||
local map_simdload = {
|
||||
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
|
||||
}
|
||||
|
||||
local map_preload = {
|
||||
shift = 0, mask = 0, -- NYI preload.
|
||||
}
|
||||
|
||||
local map_media = {
|
||||
shift = 20, mask = 31,
|
||||
[0] = false,
|
||||
{ --01
|
||||
shift = 5, mask = 7,
|
||||
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
|
||||
"sadd8DNM", false, false, "ssub8DNM",
|
||||
},
|
||||
{ --02
|
||||
shift = 5, mask = 7,
|
||||
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
|
||||
"qadd8DNM", false, false, "qsub8DNM",
|
||||
},
|
||||
{ --03
|
||||
shift = 5, mask = 7,
|
||||
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
|
||||
"shadd8DNM", false, false, "shsub8DNM",
|
||||
},
|
||||
false,
|
||||
{ --05
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
|
||||
"uadd8DNM", false, false, "usub8DNM",
|
||||
},
|
||||
{ --06
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
|
||||
"uqadd8DNM", false, false, "uqsub8DNM",
|
||||
},
|
||||
{ --07
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
|
||||
"uhadd8DNM", false, false, "uhsub8DNM",
|
||||
},
|
||||
{ --08
|
||||
shift = 5, mask = 7,
|
||||
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
|
||||
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
|
||||
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
|
||||
},
|
||||
false,
|
||||
{ --0a
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
|
||||
"ssatDxMu", false, "ssatDxMu",
|
||||
},
|
||||
{ --0b
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "revDM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
|
||||
"ssatDxMu", "rev16DM", "ssatDxMu",
|
||||
},
|
||||
{ --0c
|
||||
shift = 5, mask = 7,
|
||||
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
|
||||
},
|
||||
false,
|
||||
{ --0e
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
|
||||
"usatDwMu", false, "usatDwMu",
|
||||
},
|
||||
{ --0f
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "rbitDM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
|
||||
"usatDwMu", "revshDM", "usatDwMu",
|
||||
},
|
||||
{ --10
|
||||
shift = 12, mask = 15,
|
||||
[15] = {
|
||||
shift = 5, mask = 7,
|
||||
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
|
||||
},
|
||||
_ = {
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
|
||||
},
|
||||
},
|
||||
false, false, false,
|
||||
{ --14
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
|
||||
},
|
||||
{ --15
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
|
||||
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
|
||||
false, false, false, false,
|
||||
"smmlsNMSD", "smmlsrNMSD",
|
||||
},
|
||||
false, false,
|
||||
{ --18
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
|
||||
},
|
||||
false,
|
||||
{ --1a
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1b
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1c
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1d
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1e
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
{ --1f
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
}
|
||||
|
||||
local map_load = {
|
||||
shift = 21, mask = 9,
|
||||
{
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
|
||||
}
|
||||
}
|
||||
|
||||
local map_load1 = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = map_load, map_media,
|
||||
}
|
||||
|
||||
local map_loadm = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "stmdaNR", "stmNR",
|
||||
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
|
||||
"ldmdbNR", "ldmibNR",
|
||||
},
|
||||
}
|
||||
|
||||
local map_data = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
|
||||
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
|
||||
"tstNP", "teqNP", "cmpNP", "cmnNP",
|
||||
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
|
||||
}
|
||||
|
||||
local map_mul = {
|
||||
shift = 21, mask = 7,
|
||||
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
|
||||
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
|
||||
}
|
||||
|
||||
local map_sync = {
|
||||
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
|
||||
[0] = "swpDMN", false, false, false,
|
||||
"swpbDMN", false, false, false,
|
||||
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
|
||||
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
|
||||
}
|
||||
|
||||
local map_mulh = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = { shift = 5, mask = 3,
|
||||
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
|
||||
}
|
||||
|
||||
local map_misc = {
|
||||
shift = 4, mask = 7,
|
||||
-- NYI: decode PSR bits of msr.
|
||||
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
|
||||
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
|
||||
{ shift = 21, mask = 3, "bxjM", },
|
||||
{ shift = 21, mask = 3, "blxM", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, "bkptK", },
|
||||
}
|
||||
|
||||
local map_datar = {
|
||||
shift = 4, mask = 9,
|
||||
[9] = {
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
|
||||
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
|
||||
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
|
||||
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 25,
|
||||
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
|
||||
_ = {
|
||||
shift = 0, mask = 0xffffffff,
|
||||
[bor(0xe1a00000)] = "nop",
|
||||
_ = map_data,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
local map_datai = {
|
||||
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
|
||||
[16] = "movwDW", [20] = "movtDW",
|
||||
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
|
||||
[22] = "msrNW",
|
||||
_ = map_data,
|
||||
}
|
||||
|
||||
local map_branch = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = "bB", "blB"
|
||||
}
|
||||
|
||||
local map_condins = {
|
||||
[0] = map_datar, map_datai, map_load, map_load1,
|
||||
map_loadm, map_branch, map_loadc, map_datac
|
||||
}
|
||||
|
||||
-- NYI: setend.
|
||||
local map_uncondins = {
|
||||
[0] = false, map_simddata, map_simdload, map_preload,
|
||||
false, "blxB", map_loadcu, map_datacu,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
}
|
||||
|
||||
local map_cond = {
|
||||
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
|
||||
"hi", "ls", "ge", "lt", "gt", "le", "al",
|
||||
}
|
||||
|
||||
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then
|
||||
extra = "\t->"..sym
|
||||
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
|
||||
extra = "\t; 0x"..tohex(ctx.rel)
|
||||
end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-5s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-5s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Format operand 2 of load/store opcodes.
|
||||
local function fmtload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local x, ofs
|
||||
local ext = (band(op, 0x04000000) == 0)
|
||||
if not ext and band(op, 0x02000000) == 0 then
|
||||
ofs = band(op, 4095)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
elseif ext and band(op, 0x00400000) ~= 0 then
|
||||
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
else
|
||||
ofs = map_gpr[band(op, 15)]
|
||||
if ext or band(op, 0xfe0) == 0 then
|
||||
elseif band(op, 0xfe0) == 0x60 then
|
||||
ofs = format("%s, rrx", ofs)
|
||||
else
|
||||
local sh = band(rshift(op, 7), 31)
|
||||
if sh == 0 then sh = 32 end
|
||||
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
|
||||
end
|
||||
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
|
||||
end
|
||||
if ofs == "#0" then
|
||||
x = format("[%s]", base)
|
||||
elseif band(op, 0x01000000) == 0 then
|
||||
x = format("[%s], %s", base, ofs)
|
||||
else
|
||||
x = format("[%s, %s]", base, ofs)
|
||||
end
|
||||
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
|
||||
return x
|
||||
end
|
||||
|
||||
-- Format operand 2 of vector load/store opcodes.
|
||||
local function fmtvload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local ofs = band(op, 255)*4
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
if ofs == 0 then
|
||||
return format("[%s]", base)
|
||||
else
|
||||
return format("[%s, #%d]", base, ofs)
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtvr(op, vr, sh0, sh1)
|
||||
if vr == "s" then
|
||||
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
|
||||
else
|
||||
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
|
||||
end
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
local operands = {}
|
||||
local suffix = ""
|
||||
local last, name, pat
|
||||
local vr
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local cond = rshift(op, 28)
|
||||
local opat
|
||||
if cond == 15 then
|
||||
opat = map_uncondins[band(rshift(op, 25), 7)]
|
||||
else
|
||||
if cond ~= 14 then suffix = map_cond[cond] end
|
||||
opat = map_condins[band(rshift(op, 25), 7)]
|
||||
end
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
name, pat = match(opat, "^([a-z0-9]*)(.*)")
|
||||
if sub(pat, 1, 1) == "." then
|
||||
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
|
||||
suffix = suffix..s2
|
||||
pat = p2
|
||||
end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "D" then
|
||||
x = map_gpr[band(rshift(op, 12), 15)]
|
||||
elseif p == "N" then
|
||||
x = map_gpr[band(rshift(op, 16), 15)]
|
||||
elseif p == "S" then
|
||||
x = map_gpr[band(rshift(op, 8), 15)]
|
||||
elseif p == "M" then
|
||||
x = map_gpr[band(op, 15)]
|
||||
elseif p == "d" then
|
||||
x = fmtvr(op, vr, 12, 22)
|
||||
elseif p == "n" then
|
||||
x = fmtvr(op, vr, 16, 7)
|
||||
elseif p == "m" then
|
||||
x = fmtvr(op, vr, 0, 5)
|
||||
elseif p == "P" then
|
||||
if band(op, 0x02000000) ~= 0 then
|
||||
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
|
||||
else
|
||||
x = map_gpr[band(op, 15)]
|
||||
if band(op, 0xff0) ~= 0 then
|
||||
operands[#operands+1] = x
|
||||
local s = map_shift[band(rshift(op, 5), 3)]
|
||||
local r = nil
|
||||
if band(op, 0xf90) == 0 then
|
||||
if s == "ror" then s = "rrx" else r = "#32" end
|
||||
elseif band(op, 0x10) == 0 then
|
||||
r = "#"..band(rshift(op, 7), 31)
|
||||
else
|
||||
r = map_gpr[band(rshift(op, 8), 15)]
|
||||
end
|
||||
if name == "mov" then name = s; x = r
|
||||
elseif r then x = format("%s %s", s, r)
|
||||
else x = s end
|
||||
end
|
||||
end
|
||||
elseif p == "L" then
|
||||
x = fmtload(ctx, op, pos)
|
||||
elseif p == "l" then
|
||||
x = fmtvload(ctx, op, pos)
|
||||
elseif p == "B" then
|
||||
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
|
||||
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
|
||||
ctx.rel = addr
|
||||
x = "0x"..tohex(addr)
|
||||
elseif p == "F" then
|
||||
vr = "s"
|
||||
elseif p == "G" then
|
||||
vr = "d"
|
||||
elseif p == "." then
|
||||
suffix = suffix..(vr == "s" and ".f32" or ".f64")
|
||||
elseif p == "R" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 1 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local t = {}
|
||||
for i=0,15 do
|
||||
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
|
||||
end
|
||||
x = "{"..concat(t, ", ").."}"
|
||||
elseif p == "r" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 2 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local s = tonumber(sub(last, 2))
|
||||
local n = band(op, 255)
|
||||
if vr == "d" then n = rshift(n, 1) end
|
||||
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
|
||||
elseif p == "W" then
|
||||
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
|
||||
elseif p == "T" then
|
||||
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
|
||||
elseif p == "U" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "u" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if band(op, 0x40) == 0 then
|
||||
if x == 0 then x = nil else x = "lsl #"..x end
|
||||
else
|
||||
if x == 0 then x = "asr #32" else x = "asr #"..x end
|
||||
end
|
||||
elseif p == "v" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
elseif p == "w" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "x" then
|
||||
x = band(rshift(op, 16), 31) + 1
|
||||
elseif p == "X" then
|
||||
x = band(rshift(op, 16), 31) - last + 1
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
|
||||
elseif p == "K" then
|
||||
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
|
||||
elseif p == "s" then
|
||||
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then
|
||||
last = x
|
||||
if type(x) == "number" then x = "#"..x end
|
||||
operands[#operands+1] = x
|
||||
end
|
||||
end
|
||||
|
||||
return putop(ctx, name..suffix, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ctx.pos = ofs
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 16 then return map_gpr[r] end
|
||||
return "d"..(r-16)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
disass = disass,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,12 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM64BE disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- ARM64 instructions are always little-endian. So just forward to the
|
||||
-- common ARM64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
return require((string.match(..., ".*%.") or "").."dis_arm64")
|
||||
|
||||
@@ -1,694 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all standard MIPS32R1/R2 instructions.
|
||||
-- Default mode is big-endian, but see: dis_mipsel.lua
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local byte, format = string.byte, string.format
|
||||
local match, gmatch = string.match, string.gmatch
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Extended opcode maps common to all MIPS releases
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
|
||||
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
|
||||
|
||||
local map_cop0 = {
|
||||
shift = 25, mask = 1,
|
||||
[0] = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "mfc0TDW", [4] = "mtc0TDW",
|
||||
[10] = "rdpgprDT",
|
||||
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
|
||||
[14] = "wrpgprDT",
|
||||
}, {
|
||||
shift = 0, mask = 63,
|
||||
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
|
||||
[24] = "eret", [31] = "deret",
|
||||
[32] = "wait",
|
||||
},
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps for MIPS R1-R5
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
|
||||
|
||||
local map_special = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
map_movci, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", "movzDST", "movnDST",
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"mfhiD", "mthiS", "mfloD", "mtloS",
|
||||
"dsllvDST", false, "dsrlvDST", "dsravDST",
|
||||
"multST", "multuST", "divST", "divuST",
|
||||
"dmultST", "dmultuST", "ddivST", "ddivuST",
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", false, "tneSTZ", false,
|
||||
"dsllDTA", false, "dsrlDTA", "dsraDTA",
|
||||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
|
||||
}
|
||||
|
||||
local map_special2 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "maddST", "madduST", "mulDST", false,
|
||||
"msubST", "msubuST",
|
||||
[32] = "clzDS", [33] = "cloDS",
|
||||
[63] = "sdbbpY",
|
||||
}
|
||||
|
||||
local map_bshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
|
||||
local map_dbshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "dsbhDT",
|
||||
[5] = "dshdDT",
|
||||
}
|
||||
|
||||
local map_special3 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
|
||||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
|
||||
[32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
|
||||
false, false, false, false,
|
||||
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
|
||||
"teqiSI", false, "tneiSI", false,
|
||||
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, "synciSO",
|
||||
}
|
||||
|
||||
local map_cop1s = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
|
||||
"movz.sFGT", "movn.sFGT",
|
||||
false, "recip.sFG", "rsqrt.sFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
|
||||
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
|
||||
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
|
||||
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
|
||||
}
|
||||
|
||||
local map_cop1d = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
|
||||
"movz.dFGT", "movn.dFGT",
|
||||
false, "recip.dFG", "rsqrt.dFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG", false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
|
||||
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
|
||||
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
|
||||
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
|
||||
}
|
||||
|
||||
local map_cop1ps = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
|
||||
false, "abs.psFG", "mov.psFG", "neg.psFG",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
|
||||
"movz.psFGT", "movn.psFGT",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.puFG", false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.plFG", false, false, false,
|
||||
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
|
||||
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
|
||||
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
|
||||
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
|
||||
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
|
||||
}
|
||||
|
||||
local map_cop1w = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1bc = {
|
||||
shift = 16, mask = 3,
|
||||
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
|
||||
}
|
||||
|
||||
local map_cop1 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
|
||||
map_cop1bc, false, false, false,
|
||||
false, false, false, false,
|
||||
map_cop1s, map_cop1d, false, false,
|
||||
map_cop1w, map_cop1l, map_cop1ps,
|
||||
}
|
||||
|
||||
local map_cop1x = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
|
||||
false, "luxc1FSX", false, false,
|
||||
"swxc1FSX", "sdxc1FSX", false, false,
|
||||
false, "suxc1FSX", false, "prefxMSX",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, "alnv.psFGHS", false,
|
||||
"madd.sFRGH", "madd.dFRGH", false, false,
|
||||
false, false, "madd.psFRGH", false,
|
||||
"msub.sFRGH", "msub.dFRGH", false, false,
|
||||
false, false, "msub.psFRGH", false,
|
||||
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
|
||||
false, false, "nmadd.psFRGH", false,
|
||||
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
|
||||
false, false, "nmsub.psFRGH", false,
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = map_special, map_regimm, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
|
||||
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
|
||||
map_cop0, map_cop1, false, map_cop1x,
|
||||
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
|
||||
"daddiTSI", "daddiuTSI", false, false,
|
||||
map_special2, "jalxJ", false, map_special3,
|
||||
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
|
||||
"lbuTSO", "lhuTSO", "lwrTSO", false,
|
||||
"sbTSO", "shTSO", "swlTSO", "swTSO",
|
||||
false, false, "swrTSO", "cacheNSO",
|
||||
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
|
||||
false, "ldc1HSO", "ldc2TSO", "ldTSO",
|
||||
"scTSO", "swc1HSO", "swc2TSO", false,
|
||||
false, "sdc1HSO", "sdc2TSO", "sdTSO",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps for MIPS R6
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
|
||||
local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
|
||||
local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
|
||||
local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
|
||||
local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
|
||||
local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
|
||||
local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
|
||||
local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
|
||||
|
||||
local map_special_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
false, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", false, false,
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"clzDS", "cloDS", "dclzDS", "dcloDS",
|
||||
"dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
|
||||
map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
|
||||
map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
|
||||
"dsllDTA", false, "dsrlDTA", "dsraDTA",
|
||||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
|
||||
}
|
||||
|
||||
local map_bshfl_r6 = {
|
||||
shift = 9, mask = 3,
|
||||
[1] = "alignDSTa",
|
||||
_ = {
|
||||
shift = 6, mask = 31,
|
||||
[0] = "bitswapDT",
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
}
|
||||
|
||||
local map_dbshfl_r6 = {
|
||||
shift = 9, mask = 3,
|
||||
[1] = "dalignDSTa",
|
||||
_ = {
|
||||
shift = 6, mask = 31,
|
||||
[0] = "dbitswapDT",
|
||||
[2] = "dsbhDT",
|
||||
[5] = "dshdDT",
|
||||
}
|
||||
}
|
||||
|
||||
local map_special3_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
|
||||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
|
||||
[32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm_r6 = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", [1] = "bgezSB",
|
||||
[6] = "dahiSI", [30] = "datiSI",
|
||||
[23] = "sigrieI", [31] = "synciSO",
|
||||
}
|
||||
|
||||
local map_pcrel_r6 = {
|
||||
shift = 19, mask = 3,
|
||||
[0] = "addiupcS2", "lwpcS2", "lwupcS2", {
|
||||
shift = 18, mask = 1,
|
||||
[0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
|
||||
}
|
||||
}
|
||||
|
||||
local map_cop1s_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
"sel.sFGH", false, false, false,
|
||||
"seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
|
||||
"maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
|
||||
"min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG",
|
||||
}
|
||||
|
||||
local map_cop1d_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
"sel.dFGH", false, false, false,
|
||||
"seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
|
||||
"maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
|
||||
"min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG",
|
||||
}
|
||||
|
||||
local map_cop1w_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
|
||||
"cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
|
||||
"cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
|
||||
"cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
|
||||
false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
|
||||
false, false, false, false,
|
||||
false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
|
||||
false, false, false, false,
|
||||
"cvt.s.wFG", "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
|
||||
"cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
|
||||
"cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
|
||||
"cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
|
||||
false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
|
||||
false, false, false, false,
|
||||
false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
|
||||
false, false, false, false,
|
||||
"cvt.s.lFG", "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1_r6 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
|
||||
false, "bc1eqzHB", false, false,
|
||||
false, "bc1nezHB", false, false,
|
||||
map_cop1s_r6, map_cop1d_r6, false, false,
|
||||
map_cop1w_r6, map_cop1l_r6,
|
||||
}
|
||||
|
||||
local function maprs_popTS(rs, rt)
|
||||
if rt == 0 then return 0 elseif rs == 0 then return 1
|
||||
elseif rs == rt then return 2 else return 3 end
|
||||
end
|
||||
|
||||
local map_pop06_r6 = {
|
||||
maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
|
||||
}
|
||||
local map_pop07_r6 = {
|
||||
maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
|
||||
}
|
||||
local map_pop26_r6 = {
|
||||
maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
|
||||
}
|
||||
local map_pop27_r6 = {
|
||||
maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
|
||||
}
|
||||
|
||||
local function maprs_popS(rs, rt)
|
||||
if rs == 0 then return 0 else return 1 end
|
||||
end
|
||||
|
||||
local map_pop66_r6 = {
|
||||
maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
|
||||
}
|
||||
local map_pop76_r6 = {
|
||||
maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
|
||||
}
|
||||
|
||||
local function maprs_popST(rs, rt)
|
||||
if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
|
||||
end
|
||||
|
||||
local map_pop10_r6 = {
|
||||
maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
|
||||
}
|
||||
local map_pop30_r6 = {
|
||||
maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
|
||||
}
|
||||
|
||||
local map_pri_r6 = {
|
||||
[0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
|
||||
map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
|
||||
map_cop0, map_cop1_r6, false, false,
|
||||
false, false, map_pop26_r6, map_pop27_r6,
|
||||
map_pop30_r6, "daddiuTSI", false, false,
|
||||
false, "dauiTSI", false, map_special3_r6,
|
||||
"lbTSO", "lhTSO", false, "lwTSO",
|
||||
"lbuTSO", "lhuTSO", false, false,
|
||||
"sbTSO", "shTSO", false, "swTSO",
|
||||
false, false, false, false,
|
||||
false, "lwc1HSO", "bc#", false,
|
||||
false, "ldc1HSO", map_pop66_r6, "ldTSO",
|
||||
false, "swc1HSO", "balc#", map_pcrel_r6,
|
||||
false, "sdc1HSO", map_pop76_r6, "sdTSO",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
local function get_be(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
end
|
||||
|
||||
local function get_le(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local op = ctx:get()
|
||||
local operands = {}
|
||||
local last = nil
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = ctx.map_pri[rshift(op, 26)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
if opat.maprs then
|
||||
opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
|
||||
else
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "S" then
|
||||
x = map_gpr[band(rshift(op, 21), 31)]
|
||||
elseif p == "T" then
|
||||
x = map_gpr[band(rshift(op, 16), 31)]
|
||||
elseif p == "D" then
|
||||
x = map_gpr[band(rshift(op, 11), 31)]
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, 6), 31)
|
||||
elseif p == "G" then
|
||||
x = "f"..band(rshift(op, 11), 31)
|
||||
elseif p == "H" then
|
||||
x = "f"..band(rshift(op, 16), 31)
|
||||
elseif p == "R" then
|
||||
x = "f"..band(rshift(op, 21), 31)
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, 6), 31)
|
||||
elseif p == "a" then
|
||||
x = band(rshift(op, 6), 7)
|
||||
elseif p == "E" then
|
||||
x = band(rshift(op, 6), 31) + 32
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, 11), 31)
|
||||
elseif p == "N" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "C" then
|
||||
x = band(rshift(op, 18), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "K" then
|
||||
x = band(rshift(op, 11), 31) + 1
|
||||
elseif p == "P" then
|
||||
x = band(rshift(op, 11), 31) + 33
|
||||
elseif p == "L" then
|
||||
x = band(rshift(op, 11), 31) - last + 1
|
||||
elseif p == "Q" then
|
||||
x = band(rshift(op, 11), 31) - last + 33
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "2" then
|
||||
x = arshift(lshift(op, 13), 11)
|
||||
elseif p == "3" then
|
||||
x = arshift(lshift(op, 14), 11)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "O" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "X" then
|
||||
local index = map_gpr[band(rshift(op, 16), 31)]
|
||||
operands[#operands] = format("%s(%s)", index, last)
|
||||
elseif p == "B" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "b" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "#" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "J" then
|
||||
local a = ctx.addr + ctx.pos
|
||||
x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "V" then
|
||||
x = band(rshift(op, 8), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "W" then
|
||||
x = band(op, 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 6), 0x000fffff)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, 6), 1023)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "0" then
|
||||
if last == "r0" or last == 0 then
|
||||
local n = #operands
|
||||
operands[n] = nil
|
||||
last = operands[n-1]
|
||||
if altname then
|
||||
local a1, a2 = match(altname, "([^|]*)|(.*)")
|
||||
if a1 then name, altname = a1, a2
|
||||
else name = altname end
|
||||
end
|
||||
end
|
||||
elseif p == "1" then
|
||||
if last == "ra" then
|
||||
operands[#operands] = nil
|
||||
end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
ctx.get = get_be
|
||||
ctx.map_pri = map_pri
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_el(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.get = get_le
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_r6(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.map_pri = map_pri_r6
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_r6_el(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.get = get_le
|
||||
ctx.map_pri = map_pri_r6
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_el(code, addr, out)
|
||||
create_el(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_r6(code, addr, out)
|
||||
create_r6(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_r6_el(code, addr, out)
|
||||
create_r6_el(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
create_el = create_el,
|
||||
create_r6 = create_r6,
|
||||
create_r6_el = create_r6_el,
|
||||
disass = disass,
|
||||
disass_el = disass_el,
|
||||
disass_r6 = disass_r6,
|
||||
disass_r6_el = disass_r6_el,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the big-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create,
|
||||
disass = dis_mips.disass,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64EL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_el,
|
||||
disass = dis_mips.disass_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64R6 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the r6 big-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_r6,
|
||||
disass = dis_mips.disass_r6,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64R6EL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the r6 little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_r6_el,
|
||||
disass = dis_mips.disass_r6_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPSEL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_el,
|
||||
disass = dis_mips.disass_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -1,591 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT PPC disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
|
||||
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
|
||||
--
|
||||
-- NYI: VMX, VMX128
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local byte, format = string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_crops = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "mcrfXX",
|
||||
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
|
||||
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
|
||||
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
|
||||
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
|
||||
[16] = "b_lrKB", [528] = "b_ctrKB",
|
||||
[150] = "isync",
|
||||
}
|
||||
|
||||
local map_rlwinm = setmetatable({
|
||||
shift = 0, mask = -1,
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
local rot = band(rshift(x, 11), 31)
|
||||
local mb = band(rshift(x, 6), 31)
|
||||
local me = band(rshift(x, 1), 31)
|
||||
if mb == 0 and me == 31-rot then
|
||||
return "slwiRR~A."
|
||||
elseif me == 31 and mb == 32-rot then
|
||||
return "srwiRR~-A."
|
||||
else
|
||||
return "rlwinmRR~AAA."
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local map_rld = {
|
||||
shift = 2, mask = 7,
|
||||
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
|
||||
{
|
||||
shift = 1, mask = 1,
|
||||
[0] = "rldclRR~RM.", "rldcrRR~RM.",
|
||||
},
|
||||
}
|
||||
|
||||
local map_ext = setmetatable({
|
||||
shift = 1, mask = 1023,
|
||||
|
||||
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
|
||||
[4] = "twARR", [68] = "tdARR",
|
||||
|
||||
[8] = "subfcRRR.", [40] = "subfRRR.",
|
||||
[104] = "negRR.", [136] = "subfeRRR.",
|
||||
[200] = "subfzeRR.", [232] = "subfmeRR.",
|
||||
[520] = "subfcoRRR.", [552] = "subfoRRR.",
|
||||
[616] = "negoRR.", [648] = "subfeoRRR.",
|
||||
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
|
||||
|
||||
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
|
||||
[457] = "divduRRR.", [489] = "divdRRR.",
|
||||
[745] = "mulldoRRR.",
|
||||
[969] = "divduoRRR.", [1001] = "divdoRRR.",
|
||||
|
||||
[10] = "addcRRR.", [138] = "addeRRR.",
|
||||
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
|
||||
[522] = "addcoRRR.", [650] = "addeoRRR.",
|
||||
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
|
||||
|
||||
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
|
||||
[459] = "divwuRRR.", [491] = "divwRRR.",
|
||||
[747] = "mullwoRRR.",
|
||||
[971] = "divwouRRR.", [1003] = "divwoRRR.",
|
||||
|
||||
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
|
||||
|
||||
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
|
||||
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
|
||||
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
|
||||
[339] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
|
||||
},
|
||||
[467] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
|
||||
},
|
||||
|
||||
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
|
||||
|
||||
[21] = "ldxRR0R", [53] = "lduxRRR",
|
||||
[149] = "stdxRR0R", [181] = "stduxRRR",
|
||||
[341] = "lwaxRR0R", [373] = "lwauxRRR",
|
||||
|
||||
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
|
||||
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
|
||||
[151] = "stwxRR0R", [183] = "stwuxRRR",
|
||||
[215] = "stbxRR0R", [247] = "stbuxRRR",
|
||||
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
|
||||
[343] = "lhaxRR0R", [375] = "lhauxRRR",
|
||||
[407] = "sthxRR0R", [439] = "sthuxRRR",
|
||||
|
||||
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
|
||||
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
|
||||
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
|
||||
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
|
||||
[470] = "dcbi-RR",
|
||||
|
||||
[598] = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = "sync", "lwsync", "ptesync",
|
||||
},
|
||||
[758] = "dcba-RR",
|
||||
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
|
||||
|
||||
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
|
||||
[122] = "popcntbRR~",
|
||||
[154] = "prtywRR~", [186] = "prtydRR~",
|
||||
|
||||
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
|
||||
[284] = "eqvRR~R.", [316] = "xorRR~R.",
|
||||
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
|
||||
[508] = "cmpbRR~R",
|
||||
|
||||
[512] = "mcrxrX",
|
||||
|
||||
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
|
||||
|
||||
[533] = "lswxRR0R", [597] = "lswiRR0A",
|
||||
[661] = "stswxRR0R", [725] = "stswiRR0A",
|
||||
|
||||
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
|
||||
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
|
||||
|
||||
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
|
||||
[599] = "lfdxFR0R", [631] = "lfduxFRR",
|
||||
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
|
||||
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
|
||||
[855] = "lfiwaxFR0R",
|
||||
[983] = "stfiwxFR0R",
|
||||
|
||||
[24] = "slwRR~R.",
|
||||
|
||||
[27] = "sldRR~R.", [536] = "srwRR~R.",
|
||||
[792] = "srawRR~R.", [824] = "srawiRR~A.",
|
||||
|
||||
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
|
||||
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
|
||||
|
||||
[539] = "srdRR~R.",
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
if band(x, 31) == 15 then return "iselRRRC" end
|
||||
end
|
||||
})
|
||||
|
||||
local map_ld = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "ldRRE", "lduRRE", "lwaRRE",
|
||||
}
|
||||
|
||||
local map_std = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "stdRRE", "stduRRE",
|
||||
}
|
||||
|
||||
local map_fps = {
|
||||
shift = 5, mask = 1,
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivsFFF.", false,
|
||||
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
|
||||
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
|
||||
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_fpd = {
|
||||
shift = 5, mask = 1,
|
||||
[0] = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
|
||||
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
|
||||
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
|
||||
[136] = "fnabsF-F.", [264] = "fabsF-F.",
|
||||
[12] = "frspF-F.",
|
||||
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
|
||||
[583] = "mffsF.", [711] = "mtfsfZF.",
|
||||
[392] = "frinF-F.", [424] = "frizF-F.",
|
||||
[456] = "fripF-F.", [488] = "frimF-F.",
|
||||
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
|
||||
},
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivFFF.", false,
|
||||
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
|
||||
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
|
||||
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_spe = {
|
||||
shift = 0, mask = 2047,
|
||||
|
||||
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
|
||||
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
|
||||
[520] = "evabsRR", [521] = "evnegRR",
|
||||
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
|
||||
[525] = "evcntlzwRR", [526] = "evcntlswRR",
|
||||
|
||||
[527] = "brincRRR",
|
||||
|
||||
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
|
||||
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
|
||||
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
|
||||
|
||||
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
|
||||
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
|
||||
[548] = "evslwRRR", [550] = "evslwiRRA",
|
||||
[552] = "evrlwRRR", [553] = "evsplatiRS",
|
||||
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
|
||||
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
|
||||
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
|
||||
|
||||
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
|
||||
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
|
||||
[564] = "evcmpeqYRR",
|
||||
|
||||
[632] = "evselRRR", [633] = "evselRRRW",
|
||||
[634] = "evselRRRW", [635] = "evselRRRW",
|
||||
[636] = "evselRRRW", [637] = "evselRRRW",
|
||||
[638] = "evselRRRW", [639] = "evselRRRW",
|
||||
|
||||
[640] = "evfsaddRRR", [641] = "evfssubRRR",
|
||||
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
|
||||
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
|
||||
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
|
||||
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
|
||||
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
|
||||
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
|
||||
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
|
||||
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
|
||||
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
|
||||
|
||||
[704] = "efsaddRRR", [705] = "efssubRRR",
|
||||
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
|
||||
[712] = "efsmulRRR", [713] = "efsdivRRR",
|
||||
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
|
||||
[719] = "efscfdR-R",
|
||||
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
|
||||
[722] = "efscfufR-R", [723] = "efscfsfR-R",
|
||||
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
|
||||
[726] = "efsctufR-R", [727] = "efsctsfR-R",
|
||||
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
|
||||
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
|
||||
|
||||
[736] = "efdaddRRR", [737] = "efdsubRRR",
|
||||
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
|
||||
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
|
||||
[744] = "efdmulRRR", [745] = "efddivRRR",
|
||||
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
|
||||
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
|
||||
[751] = "efdcfsR-R",
|
||||
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
|
||||
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
|
||||
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
|
||||
[758] = "efdctufR-R", [759] = "efdctsfR-R",
|
||||
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
|
||||
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
|
||||
|
||||
[768] = "evlddxRR0R", [769] = "evlddRR8",
|
||||
[770] = "evldwxRR0R", [771] = "evldwRR8",
|
||||
[772] = "evldhxRR0R", [773] = "evldhRR8",
|
||||
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
|
||||
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
|
||||
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
|
||||
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
|
||||
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
|
||||
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
|
||||
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
|
||||
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
|
||||
|
||||
[800] = "evstddxRR0R", [801] = "evstddRR8",
|
||||
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
|
||||
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
|
||||
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
|
||||
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
|
||||
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
|
||||
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
|
||||
|
||||
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
|
||||
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
|
||||
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
|
||||
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
|
||||
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
|
||||
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
|
||||
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
|
||||
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
|
||||
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
|
||||
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
|
||||
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
|
||||
[1147] = "evmwsmfaRRR",
|
||||
|
||||
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
|
||||
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
|
||||
[1220] = "evmraRR",
|
||||
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
|
||||
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
|
||||
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
|
||||
|
||||
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
|
||||
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
|
||||
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
|
||||
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
|
||||
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
|
||||
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
|
||||
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
|
||||
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
|
||||
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
|
||||
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
|
||||
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
|
||||
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
|
||||
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
|
||||
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
|
||||
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
|
||||
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
|
||||
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
|
||||
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
|
||||
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
|
||||
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
|
||||
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
|
||||
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
|
||||
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
|
||||
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
|
||||
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
|
||||
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = false, false, "tdiARI", "twiARI",
|
||||
map_spe, false, false, "mulliRRI",
|
||||
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
|
||||
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
|
||||
"b_KBJ", "sc", "bKJ", map_crops,
|
||||
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
|
||||
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
|
||||
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
|
||||
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
|
||||
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
|
||||
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
|
||||
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
|
||||
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
|
||||
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
|
||||
false, false, map_ld, map_fps,
|
||||
false, false, map_std, map_fpd,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
||||
}
|
||||
|
||||
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
|
||||
|
||||
-- Format a condition bit.
|
||||
local function condfmt(cond)
|
||||
if cond <= 3 then
|
||||
return map_cond[band(cond, 3)]
|
||||
else
|
||||
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
local operands = {}
|
||||
local last = nil
|
||||
local rs = 21
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(b0, 2)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)]
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "R" then
|
||||
x = map_gpr[band(rshift(op, rs), 31)]
|
||||
rs = rs - 5
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "S" then
|
||||
x = arshift(lshift(op, 27-rs), 27)
|
||||
rs = rs - 5
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "D" or p == "E" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
if p == "E" then disp = band(disp, -4) end
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p >= "2" and p <= "8" then
|
||||
local disp = band(rshift(op, rs), 31) * p
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "H" then
|
||||
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
|
||||
rs = rs - 5
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, rs), 31) + band(op, 0x20)
|
||||
elseif p == "C" then
|
||||
x = condfmt(band(rshift(op, rs), 31))
|
||||
rs = rs - 5
|
||||
elseif p == "B" then
|
||||
local bo = rshift(op, 21)
|
||||
local cond = band(rshift(op, 16), 31)
|
||||
local cn = ""
|
||||
rs = rs - 10
|
||||
if band(bo, 4) == 0 then
|
||||
cn = band(bo, 2) == 0 and "dnz" or "dz"
|
||||
if band(bo, 0x10) == 0 then
|
||||
cn = cn..(band(bo, 8) == 0 and "f" or "t")
|
||||
end
|
||||
if band(bo, 0x10) == 0 then x = condfmt(cond) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
elseif band(bo, 0x10) == 0 then
|
||||
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
|
||||
if cond > 3 then x = "cr"..rshift(cond, 2) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
end
|
||||
name = gsub(name, "_", cn)
|
||||
elseif p == "J" then
|
||||
x = arshift(lshift(op, 27-rs), 29-rs)*4
|
||||
if band(op, 2) == 0 then x = ctx.addr + pos + x end
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "K" then
|
||||
if band(op, 1) ~= 0 then name = name.."l" end
|
||||
if band(op, 2) ~= 0 then name = name.."a" end
|
||||
elseif p == "X" or p == "Y" then
|
||||
x = band(rshift(op, rs+2), 7)
|
||||
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
|
||||
rs = rs - 5
|
||||
elseif p == "W" then
|
||||
x = "cr"..band(op, 7)
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, rs-4), 255)
|
||||
rs = rs - 10
|
||||
elseif p == ">" then
|
||||
operands[#operands] = rshift(operands[#operands], 1)
|
||||
elseif p == "0" then
|
||||
if last == "r0" then
|
||||
operands[#operands] = nil
|
||||
if altname then name = altname end
|
||||
end
|
||||
elseif p == "L" then
|
||||
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
|
||||
elseif p == "." then
|
||||
if band(op, 1) == 1 then name = name.."." end
|
||||
elseif p == "N" then
|
||||
if op == 0x60000000 then name = "nop"; break end
|
||||
elseif p == "~" then
|
||||
local n = #operands
|
||||
operands[n-1], operands[n] = operands[n], operands[n-1]
|
||||
elseif p == "=" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] then
|
||||
operands[n] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "%" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] and last == operands[n-2] then
|
||||
operands[n] = nil
|
||||
operands[n-1] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "-" then
|
||||
rs = rs - 5
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
disass = disass,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the 64 bit functions from the combined
|
||||
-- x86/x64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
|
||||
return {
|
||||
create = dis_x86.create64,
|
||||
disass = dis_x86.disass64,
|
||||
regname = dis_x86.regname64
|
||||
}
|
||||
|
||||
@@ -1,953 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x86/x64 disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- Sending small code snippets to an external disassembler and mixing the
|
||||
-- output with our own stuff was too fragile. So I had to bite the bullet
|
||||
-- and write yet another x86 disassembler. Oh well ...
|
||||
--
|
||||
-- The output format is very similar to what ndisasm generates. But it has
|
||||
-- been developed independently by looking at the opcode tables from the
|
||||
-- Intel and AMD manuals. The supported instruction set is quite extensive
|
||||
-- and reflects what a current generation Intel or AMD CPU implements in
|
||||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
|
||||
-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
|
||||
-- (VMX/SVM) instructions.
|
||||
--
|
||||
-- Notes:
|
||||
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
|
||||
-- * No attempt at optimization has been made -- it's fast enough for my needs.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local lower, rep = string.lower, string.rep
|
||||
local bit = require("bit")
|
||||
local tohex = bit.tohex
|
||||
|
||||
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
|
||||
local map_opc1_32 = {
|
||||
--0x
|
||||
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
|
||||
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
|
||||
--1x
|
||||
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
|
||||
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
|
||||
--2x
|
||||
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
|
||||
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
|
||||
--3x
|
||||
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
|
||||
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
|
||||
--4x
|
||||
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
|
||||
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
|
||||
--5x
|
||||
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
|
||||
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
|
||||
--6x
|
||||
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
|
||||
"fs:seg","gs:seg","o16:","a16",
|
||||
"pushUi","imulVrmi","pushBs","imulVrms",
|
||||
"insb","insVS","outsb","outsVS",
|
||||
--7x
|
||||
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
|
||||
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
|
||||
--8x
|
||||
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
|
||||
"testBmr","testVmr","xchgBrm","xchgVrm",
|
||||
"movBmr","movVmr","movBrm","movVrm",
|
||||
"movVmg","leaVrm","movWgm","popUm",
|
||||
--9x
|
||||
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
|
||||
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
|
||||
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
|
||||
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
|
||||
--Ax
|
||||
"movBao","movVao","movBoa","movVoa",
|
||||
"movsb","movsVS","cmpsb","cmpsVS",
|
||||
"testBai","testVai","stosb","stosVS",
|
||||
"lodsb","lodsVS","scasb","scasVS",
|
||||
--Bx
|
||||
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
|
||||
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
|
||||
--Cx
|
||||
"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
|
||||
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
|
||||
--Dx
|
||||
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
|
||||
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
|
||||
--Ex
|
||||
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
|
||||
"inBau","inVau","outBua","outVua",
|
||||
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
|
||||
--Fx
|
||||
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
|
||||
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
|
||||
}
|
||||
assert(#map_opc1_32 == 255)
|
||||
|
||||
-- Map for 1st opcode byte in 64 bit mode (overrides only).
|
||||
local map_opc1_64 = setmetatable({
|
||||
[0x06]=false, [0x07]=false, [0x0e]=false,
|
||||
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
|
||||
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
|
||||
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
|
||||
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
|
||||
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
|
||||
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
|
||||
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
|
||||
[0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
|
||||
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
|
||||
}, { __index = map_opc1_32 })
|
||||
|
||||
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
|
||||
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
|
||||
local map_opc2 = {
|
||||
--0x
|
||||
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
|
||||
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
|
||||
--1x
|
||||
"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
|
||||
"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
|
||||
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
|
||||
"movlpsXmr||movlpdXmr",
|
||||
"unpcklpsXrvm||unpcklpdXrvm",
|
||||
"unpckhpsXrvm||unpckhpdXrvm",
|
||||
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
|
||||
"movhpsXmr||movhpdXmr",
|
||||
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
|
||||
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
|
||||
--2x
|
||||
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
|
||||
"movapsXrm||movapdXrm",
|
||||
"movapsXmr||movapdXmr",
|
||||
"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
|
||||
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
|
||||
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
|
||||
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
|
||||
"ucomissXrm||ucomisdXrm",
|
||||
"comissXrm||comisdXrm",
|
||||
--3x
|
||||
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
|
||||
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
|
||||
--4x
|
||||
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
|
||||
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
|
||||
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
|
||||
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
|
||||
--5x
|
||||
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
|
||||
"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
|
||||
"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
|
||||
"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
|
||||
"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
|
||||
"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
|
||||
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
|
||||
"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
|
||||
"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
|
||||
--6x
|
||||
"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
|
||||
"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
|
||||
"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
|
||||
"||punpcklqdqXrvm","||punpckhqdqXrvm",
|
||||
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
|
||||
--7x
|
||||
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
|
||||
"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
|
||||
"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
|
||||
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
|
||||
nil,nil,
|
||||
"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
|
||||
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
|
||||
--8x
|
||||
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
|
||||
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
|
||||
--9x
|
||||
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
|
||||
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
|
||||
--Ax
|
||||
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
|
||||
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
|
||||
--Bx
|
||||
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
|
||||
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
|
||||
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
|
||||
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
|
||||
--Cx
|
||||
"xaddBmr","xaddVmr",
|
||||
"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
|
||||
"pinsrwPrvWmu","pextrwDrPmu",
|
||||
"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
|
||||
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
|
||||
--Dx
|
||||
"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
|
||||
"paddqPrvm","pmullwPrvm",
|
||||
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
|
||||
"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
|
||||
"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
|
||||
--Ex
|
||||
"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
|
||||
"pmulhuwPrvm","pmulhwPrvm",
|
||||
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
|
||||
"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
|
||||
"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
|
||||
--Fx
|
||||
"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
|
||||
"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
|
||||
"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
|
||||
"paddbPrvm","paddwPrvm","padddPrvm","ud",
|
||||
}
|
||||
assert(map_opc2[255] == "ud")
|
||||
|
||||
-- Map for three-byte opcodes. Can't wait for their next invention.
|
||||
local map_opc3 = {
|
||||
["38"] = { -- [66] 0f 38 xx
|
||||
--0x
|
||||
[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
|
||||
"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
|
||||
"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
|
||||
"||permilpsXrvm","||permilpdXrvm",nil,nil,
|
||||
--1x
|
||||
"||pblendvbXrma",nil,nil,nil,
|
||||
"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
|
||||
"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
|
||||
"pabsbPrm","pabswPrm","pabsdPrm",nil,
|
||||
--2x
|
||||
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
|
||||
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
|
||||
"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
|
||||
"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
|
||||
--3x
|
||||
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
|
||||
"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
|
||||
"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
|
||||
"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
|
||||
--4x
|
||||
"||pmulddXrvm","||phminposuwXrm",nil,nil,
|
||||
nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
|
||||
--5x
|
||||
[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
|
||||
[0x5a] = "||broadcasti128XrlXm",
|
||||
--7x
|
||||
[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
|
||||
--8x
|
||||
[0x8c] = "||pmaskmovXrvVSm",
|
||||
[0x8e] = "||pmaskmovVSmXvr",
|
||||
--9x
|
||||
[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
|
||||
[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
|
||||
[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
|
||||
[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
|
||||
[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
|
||||
--Ax
|
||||
[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
|
||||
[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
|
||||
[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
|
||||
[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
|
||||
[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
|
||||
--Bx
|
||||
[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
|
||||
[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
|
||||
[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
|
||||
[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
|
||||
[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
|
||||
--Dx
|
||||
[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
|
||||
[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
|
||||
--Fx
|
||||
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
|
||||
[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
|
||||
},
|
||||
|
||||
["3a"] = { -- [66] 0f 3a xx
|
||||
--0x
|
||||
[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
|
||||
"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
|
||||
"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
|
||||
"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
|
||||
--1x
|
||||
nil,nil,nil,nil,
|
||||
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
|
||||
"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
|
||||
nil,nil,nil,nil,
|
||||
--2x
|
||||
"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
|
||||
--3x
|
||||
[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
|
||||
--4x
|
||||
[0x40] = "||dppsXrvmu",
|
||||
[0x41] = "||dppdXrvmu",
|
||||
[0x42] = "||mpsadbwXrvmu",
|
||||
[0x44] = "||pclmulqdqXrvmu",
|
||||
[0x46] = "||perm2i128Xrvmu",
|
||||
[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
|
||||
[0x4c] = "||pblendvbXrvmb",
|
||||
--6x
|
||||
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
|
||||
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
|
||||
[0xdf] = "||aeskeygenassistXrmu",
|
||||
--Fx
|
||||
[0xf0] = "||| rorxVrmu",
|
||||
},
|
||||
}
|
||||
|
||||
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
|
||||
local map_opcvm = {
|
||||
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
|
||||
[0xc8]="monitor",[0xc9]="mwait",
|
||||
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
|
||||
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
|
||||
[0xf8]="swapgs",[0xf9]="rdtscp",
|
||||
}
|
||||
|
||||
-- Map for FP opcodes. And you thought stack machines are simple?
|
||||
local map_opcfp = {
|
||||
-- D8-DF 00-BF: opcodes with a memory operand.
|
||||
-- D8
|
||||
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
|
||||
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
|
||||
-- DA
|
||||
"fiaddDm","fimulDm","ficomDm","ficompDm",
|
||||
"fisubDm","fisubrDm","fidivDm","fidivrDm",
|
||||
-- DB
|
||||
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
|
||||
-- DC
|
||||
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
|
||||
-- DD
|
||||
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
|
||||
-- DE
|
||||
"fiaddWm","fimulWm","ficomWm","ficompWm",
|
||||
"fisubWm","fisubrWm","fidivWm","fidivrWm",
|
||||
-- DF
|
||||
"fildWm","fisttpWm","fistWm","fistpWm",
|
||||
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
|
||||
-- xx C0-FF: opcodes with a pseudo-register operand.
|
||||
-- D8
|
||||
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
|
||||
-- D9
|
||||
"fldFf","fxchFf",{"fnop"},nil,
|
||||
{"fchs","fabs",nil,nil,"ftst","fxam"},
|
||||
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
|
||||
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
|
||||
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
|
||||
-- DA
|
||||
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
|
||||
-- DB
|
||||
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
|
||||
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
|
||||
-- DC
|
||||
"fadd toFf","fmul toFf",nil,nil,
|
||||
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
|
||||
-- DD
|
||||
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
|
||||
-- DE
|
||||
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
|
||||
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
|
||||
-- DF
|
||||
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
|
||||
}
|
||||
assert(map_opcfp[126] == "fcomipFf")
|
||||
|
||||
-- Map for opcode groups. The subkey is sp from the ModRM byte.
|
||||
local map_opcgroup = {
|
||||
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
|
||||
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
|
||||
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
incb = { "inc", "dec" },
|
||||
incd = { "inc", "dec", "callUmp", "$call farDmp",
|
||||
"jmpUmp", "$jmp farDmp", "pushUm" },
|
||||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
|
||||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
|
||||
"smsw", nil, "lmsw", "vm*$invlpg" },
|
||||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
|
||||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
|
||||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
|
||||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
|
||||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
|
||||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
|
||||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
|
||||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
|
||||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
|
||||
prefetch = { "prefetch", "prefetchw" },
|
||||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Maps for register names.
|
||||
local map_regs = {
|
||||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
|
||||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
|
||||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
|
||||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
|
||||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
|
||||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
|
||||
Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
|
||||
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
|
||||
}
|
||||
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
|
||||
|
||||
-- Maps for size names.
|
||||
local map_sz2n = {
|
||||
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
|
||||
}
|
||||
local map_sz2prefix = {
|
||||
B = "byte", W = "word", D = "dword",
|
||||
Q = "qword",
|
||||
M = "qword", X = "xword", Y = "yword",
|
||||
F = "dword", G = "qword", -- No need for sizes/register names for these two.
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local code, pos, hex = ctx.code, ctx.pos, ""
|
||||
local hmax = ctx.hexdump
|
||||
if hmax > 0 then
|
||||
for i=ctx.start,pos-1 do
|
||||
hex = hex..format("%02X", byte(code, i, i))
|
||||
end
|
||||
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
|
||||
else hex = hex..rep(" ", hmax-#hex+2) end
|
||||
end
|
||||
if operands then text = text.." "..operands end
|
||||
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
|
||||
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
|
||||
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
|
||||
if ctx.rex then
|
||||
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
|
||||
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
|
||||
(ctx.vexl and "l" or "")
|
||||
if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
|
||||
if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
|
||||
elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false; ctx.vexl = false; ctx.vexv = false
|
||||
end
|
||||
if ctx.seg then
|
||||
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
|
||||
if n == 0 then text = ctx.seg.." "..text else text = text2 end
|
||||
ctx.seg = false
|
||||
end
|
||||
if ctx.lock then text = "lock "..text; ctx.lock = false end
|
||||
local imm = ctx.imm
|
||||
if imm then
|
||||
local sym = ctx.symtab[imm]
|
||||
if sym then text = text.."\t->"..sym end
|
||||
end
|
||||
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
|
||||
ctx.mrm = false
|
||||
ctx.vexv = false
|
||||
ctx.start = pos
|
||||
ctx.imm = nil
|
||||
end
|
||||
|
||||
-- Clear all prefix flags.
|
||||
local function clearprefixes(ctx)
|
||||
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false; ctx.a32 = false; ctx.vexl = false
|
||||
end
|
||||
|
||||
-- Fallback for incomplete opcodes at the end.
|
||||
local function incomplete(ctx)
|
||||
ctx.pos = ctx.stop+1
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(incomplete)")
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(unknown)")
|
||||
end
|
||||
|
||||
-- Return an immediate of the specified size.
|
||||
local function getimm(ctx, pos, n)
|
||||
if pos+n-1 > ctx.stop then return incomplete(ctx) end
|
||||
local code = ctx.code
|
||||
if n == 1 then
|
||||
local b1 = byte(code, pos, pos)
|
||||
return b1
|
||||
elseif n == 2 then
|
||||
local b1, b2 = byte(code, pos, pos+1)
|
||||
return b1+b2*256
|
||||
else
|
||||
local b1, b2, b3, b4 = byte(code, pos, pos+3)
|
||||
local imm = b1+b2*256+b3*65536+b4*16777216
|
||||
ctx.imm = imm
|
||||
return imm
|
||||
end
|
||||
end
|
||||
|
||||
-- Process pattern string and generate the operands.
|
||||
local function putpat(ctx, name, pat)
|
||||
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
|
||||
local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
|
||||
|
||||
-- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "V" or p == "U" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false
|
||||
elseif ctx.o16 then sz = "W"; ctx.o16 = false
|
||||
elseif p == "U" and ctx.x64 then sz = "Q"
|
||||
else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "T" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "B" then
|
||||
sz = "B"
|
||||
regs = ctx.rex and map_regs.B64 or map_regs.B
|
||||
elseif match(p, "[WDQMXYFG]") then
|
||||
sz = p
|
||||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "P" then
|
||||
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
|
||||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "H" then
|
||||
name = name..(ctx.rexw and "d" or "s")
|
||||
ctx.rexw = false
|
||||
elseif p == "S" then
|
||||
name = name..lower(sz)
|
||||
elseif p == "s" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = imm <= 127 and format("+0x%02x", imm)
|
||||
or format("-0x%02x", 256-imm)
|
||||
pos = pos+1
|
||||
elseif p == "u" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = format("0x%02x", imm)
|
||||
pos = pos+1
|
||||
elseif p == "b" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = regs[imm/16+1]
|
||||
pos = pos+1
|
||||
elseif p == "w" then
|
||||
local imm = getimm(ctx, pos, 2); if not imm then return end
|
||||
x = format("0x%x", imm)
|
||||
pos = pos+2
|
||||
elseif p == "o" then -- [offset]
|
||||
if ctx.x64 then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("[0x%08x%08x]", imm2, imm1)
|
||||
pos = pos+8
|
||||
else
|
||||
local imm = getimm(ctx, pos, 4); if not imm then return end
|
||||
x = format("[0x%08x]", imm)
|
||||
pos = pos+4
|
||||
end
|
||||
elseif p == "i" or p == "I" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 and ctx.x64 and p == "I" then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("0x%08x%08x", imm2, imm1)
|
||||
else
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
|
||||
imm = (0xffffffff+1)-imm
|
||||
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
|
||||
else
|
||||
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
|
||||
end
|
||||
end
|
||||
pos = pos+n
|
||||
elseif p == "j" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "B" and imm > 127 then imm = imm-256
|
||||
elseif imm > 2147483647 then imm = imm-4294967296 end
|
||||
pos = pos+n
|
||||
imm = imm + pos + ctx.addr
|
||||
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
|
||||
ctx.imm = imm
|
||||
if sz == "W" then
|
||||
x = format("word 0x%04x", imm%65536)
|
||||
elseif ctx.x64 then
|
||||
local lo = imm % 0x1000000
|
||||
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
|
||||
else
|
||||
x = "0x"..tohex(imm)
|
||||
end
|
||||
elseif p == "R" then
|
||||
local r = byte(code, pos-1, pos-1)%8
|
||||
if ctx.rexb then r = r + 8; ctx.rexb = false end
|
||||
x = regs[r+1]
|
||||
elseif p == "a" then x = regs[1]
|
||||
elseif p == "c" then x = "cl"
|
||||
elseif p == "d" then x = "dx"
|
||||
elseif p == "1" then x = "1"
|
||||
else
|
||||
if not mode then
|
||||
mode = ctx.mrm
|
||||
if not mode then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
mode = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
end
|
||||
rm = mode%8; mode = (mode-rm)/8
|
||||
sp = mode%8; mode = (mode-sp)/8
|
||||
sdisp = ""
|
||||
if mode < 3 then
|
||||
if rm == 4 then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
sc = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
rm = sc%8; sc = (sc-rm)/8
|
||||
rx = sc%8; sc = (sc-rx)/8
|
||||
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
|
||||
if rx == 4 then rx = nil end
|
||||
end
|
||||
if mode > 0 or rm == 5 then
|
||||
local dsz = mode
|
||||
if dsz ~= 1 then dsz = 4 end
|
||||
local disp = getimm(ctx, pos, dsz); if not disp then return end
|
||||
if mode == 0 then rm = nil end
|
||||
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
|
||||
if dsz == 1 and disp > 127 then
|
||||
sdisp = format("-0x%x", 256-disp)
|
||||
elseif disp >= 0 and disp <= 0x7fffffff then
|
||||
sdisp = format("+0x%x", disp)
|
||||
else
|
||||
sdisp = format("-0x%x", (0xffffffff+1)-disp)
|
||||
end
|
||||
else
|
||||
sdisp = format(ctx.x64 and not ctx.a32 and
|
||||
not (disp >= 0 and disp <= 0x7fffffff)
|
||||
and "0xffffffff%08x" or "0x%08x", disp)
|
||||
end
|
||||
pos = pos+dsz
|
||||
end
|
||||
end
|
||||
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
|
||||
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
|
||||
end
|
||||
if p == "m" then
|
||||
if mode == 3 then x = regs[rm+1]
|
||||
else
|
||||
local aregs = ctx.a32 and map_regs.D or ctx.aregs
|
||||
local srm, srx = "", ""
|
||||
if rm then srm = aregs[rm+1]
|
||||
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
|
||||
ctx.a32 = false
|
||||
if rx then
|
||||
if rm then srm = srm.."+" end
|
||||
srx = aregs[rx+1]
|
||||
if sc > 0 then srx = srx.."*"..(2^sc) end
|
||||
end
|
||||
x = format("[%s%s%s]", srm, srx, sdisp)
|
||||
end
|
||||
if mode < 3 and
|
||||
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
|
||||
x = map_sz2prefix[sz].." "..x
|
||||
end
|
||||
elseif p == "r" then x = regs[sp+1]
|
||||
elseif p == "g" then x = map_segregs[sp+1]
|
||||
elseif p == "p" then -- Suppress prefix.
|
||||
elseif p == "f" then x = "st"..rm
|
||||
elseif p == "x" then
|
||||
if sp == 0 and ctx.lock and not ctx.x64 then
|
||||
x = "CR8"; ctx.lock = false
|
||||
else
|
||||
x = "CR"..sp
|
||||
end
|
||||
elseif p == "v" then
|
||||
if ctx.vexv then
|
||||
x = regs[ctx.vexv+1]; ctx.vexv = false
|
||||
end
|
||||
elseif p == "y" then x = "DR"..sp
|
||||
elseif p == "z" then x = "TR"..sp
|
||||
elseif p == "l" then vexl = false
|
||||
elseif p == "t" then
|
||||
else
|
||||
error("bad pattern `"..pat.."'")
|
||||
end
|
||||
end
|
||||
if x then operands = operands and operands..", "..x or x end
|
||||
end
|
||||
ctx.pos = pos
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
-- Forward declaration.
|
||||
local map_act
|
||||
|
||||
-- Fetch and cache MRM byte.
|
||||
local function getmrm(ctx)
|
||||
local mrm = ctx.mrm
|
||||
if not mrm then
|
||||
local pos = ctx.pos
|
||||
if pos > ctx.stop then return nil end
|
||||
mrm = byte(ctx.code, pos, pos)
|
||||
ctx.pos = pos+1
|
||||
ctx.mrm = mrm
|
||||
end
|
||||
return mrm
|
||||
end
|
||||
|
||||
-- Dispatch to handler depending on pattern.
|
||||
local function dispatch(ctx, opat, patgrp)
|
||||
if not opat then return unknown(ctx) end
|
||||
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
|
||||
local p
|
||||
if ctx.rep then
|
||||
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
|
||||
ctx.rep = false
|
||||
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
|
||||
else p = "^[^%|]*" end
|
||||
opat = match(opat, p)
|
||||
if not opat then return unknown(ctx) end
|
||||
-- ctx.rep = false; ctx.o16 = false
|
||||
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
|
||||
--XXX remove in branches?
|
||||
end
|
||||
if match(opat, "%$") then -- reg$mem variants.
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
|
||||
if opat == "" then return unknown(ctx) end
|
||||
end
|
||||
if opat == "" then return unknown(ctx) end
|
||||
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
|
||||
if pat == "" and patgrp then pat = patgrp end
|
||||
return map_act[sub(pat, 1, 1)](ctx, name, pat)
|
||||
end
|
||||
|
||||
-- Get a pattern from an opcode map and dispatch to handler.
|
||||
local function dispatchmap(ctx, opcmap)
|
||||
local pos = ctx.pos
|
||||
local opat = opcmap[byte(ctx.code, pos, pos)]
|
||||
pos = pos + 1
|
||||
ctx.pos = pos
|
||||
return dispatch(ctx, opat)
|
||||
end
|
||||
|
||||
-- Map for action codes. The key is the first char after the name.
|
||||
map_act = {
|
||||
-- Simple opcodes without operands.
|
||||
[""] = function(ctx, name, pat)
|
||||
return putop(ctx, name)
|
||||
end,
|
||||
|
||||
-- Operand size chars fall right through.
|
||||
B = putpat, W = putpat, D = putpat, Q = putpat,
|
||||
V = putpat, U = putpat, T = putpat,
|
||||
M = putpat, X = putpat, P = putpat,
|
||||
F = putpat, G = putpat, Y = putpat,
|
||||
H = putpat,
|
||||
|
||||
-- Collect prefixes.
|
||||
[":"] = function(ctx, name, pat)
|
||||
ctx[pat == ":" and name or sub(pat, 2)] = name
|
||||
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
|
||||
end,
|
||||
|
||||
-- Chain to special handler specified by name.
|
||||
["*"] = function(ctx, name, pat)
|
||||
return map_act[name](ctx, name, sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- Use named subtable for opcode group.
|
||||
["!"] = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- o16,o32[,o64] variants.
|
||||
sz = function(ctx, name, pat)
|
||||
if ctx.o16 then ctx.o16 = false
|
||||
else
|
||||
pat = match(pat, ",(.*)")
|
||||
if ctx.rexw then
|
||||
local p = match(pat, ",(.*)")
|
||||
if p then pat = p; ctx.rexw = false end
|
||||
end
|
||||
end
|
||||
pat = match(pat, "^[^,]*")
|
||||
return dispatch(ctx, pat)
|
||||
end,
|
||||
|
||||
-- Two-byte opcode dispatch.
|
||||
opc2 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc2)
|
||||
end,
|
||||
|
||||
-- Three-byte opcode dispatch.
|
||||
opc3 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc3[pat])
|
||||
end,
|
||||
|
||||
-- VMX/SVM dispatch.
|
||||
vm = function(ctx, name, pat)
|
||||
return dispatch(ctx, map_opcvm[ctx.mrm])
|
||||
end,
|
||||
|
||||
-- Floating point opcode dispatch.
|
||||
fp = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
local rm = mrm%8
|
||||
local idx = pat*8 + ((mrm-rm)/8)%8
|
||||
if mrm >= 192 then idx = idx + 64 end
|
||||
local opat = map_opcfp[idx]
|
||||
if type(opat) == "table" then opat = opat[rm+1] end
|
||||
return dispatch(ctx, opat)
|
||||
end,
|
||||
|
||||
-- REX prefix.
|
||||
rex = function(ctx, name, pat)
|
||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
|
||||
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
|
||||
ctx.rex = "rex"
|
||||
end,
|
||||
|
||||
-- VEX prefix.
|
||||
vex = function(ctx, name, pat)
|
||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
|
||||
ctx.rex = "vex"
|
||||
local pos = ctx.pos
|
||||
if ctx.mrm then
|
||||
ctx.mrm = nil
|
||||
pos = pos-1
|
||||
end
|
||||
local b = byte(ctx.code, pos, pos)
|
||||
if not b then return incomplete(ctx) end
|
||||
pos = pos+1
|
||||
if b < 128 then ctx.rexr = true end
|
||||
local m = 1
|
||||
if pat == "3" then
|
||||
m = b%32; b = (b-m)/32
|
||||
local nb = b%2; b = (b-nb)/2
|
||||
if nb == 0 then ctx.rexb = true end
|
||||
local nx = b%2
|
||||
if nx == 0 then ctx.rexx = true end
|
||||
b = byte(ctx.code, pos, pos)
|
||||
if not b then return incomplete(ctx) end
|
||||
pos = pos+1
|
||||
if b >= 128 then ctx.rexw = true end
|
||||
end
|
||||
ctx.pos = pos
|
||||
local map
|
||||
if m == 1 then map = map_opc2
|
||||
elseif m == 2 then map = map_opc3["38"]
|
||||
elseif m == 3 then map = map_opc3["3a"]
|
||||
else return unknown(ctx) end
|
||||
local p = b%4; b = (b-p)/4
|
||||
if p == 1 then ctx.o16 = "o16"
|
||||
elseif p == 2 then ctx.rep = "rep"
|
||||
elseif p == 3 then ctx.rep = "repne" end
|
||||
local l = b%2; b = (b-l)/2
|
||||
if l ~= 0 then ctx.vexl = true end
|
||||
ctx.vexv = (-1-b)%16
|
||||
return dispatchmap(ctx, map)
|
||||
end,
|
||||
|
||||
-- Special case for nop with REX prefix.
|
||||
nop = function(ctx, name, pat)
|
||||
return dispatch(ctx, ctx.rex and pat or "nop")
|
||||
end,
|
||||
|
||||
-- Special case for 0F 77.
|
||||
emms = function(ctx, name, pat)
|
||||
if ctx.rex ~= "vex" then
|
||||
return putop(ctx, "emms")
|
||||
elseif ctx.vexl then
|
||||
ctx.vexl = false
|
||||
return putop(ctx, "zeroall")
|
||||
else
|
||||
return putop(ctx, "zeroupper")
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ofs = ofs + 1
|
||||
ctx.start = ofs
|
||||
ctx.pos = ofs
|
||||
ctx.stop = stop
|
||||
ctx.imm = nil
|
||||
ctx.mrm = false
|
||||
clearprefixes(ctx)
|
||||
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
|
||||
if ctx.pos ~= ctx.start then incomplete(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = (addr or 0) - 1
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 16
|
||||
ctx.x64 = false
|
||||
ctx.map1 = map_opc1_32
|
||||
ctx.aregs = map_regs.D
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create64(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.x64 = true
|
||||
ctx.map1 = map_opc1_64
|
||||
ctx.aregs = map_regs.Q
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass64(code, addr, out)
|
||||
create64(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 8 then return map_regs.D[r+1] end
|
||||
return map_regs.X[r-7]
|
||||
end
|
||||
|
||||
local function regname64(r)
|
||||
if r < 16 then return map_regs.Q[r+1] end
|
||||
return map_regs.X[r-15]
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
create64 = create64,
|
||||
disass = disass,
|
||||
disass64 = disass64,
|
||||
regname = regname,
|
||||
regname64 = regname64
|
||||
}
|
||||
|
||||
@@ -1,731 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT compiler dump module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module can be used to debug the JIT compiler itself. It dumps the
|
||||
-- code representations and structures used in various compiler stages.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
|
||||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
|
||||
-- luajit -jdump=is myapp.lua | less -R
|
||||
-- luajit -jdump=-b myapp.lua
|
||||
-- luajit -jdump=+aH,myapp.html myapp.lua
|
||||
-- luajit -jdump=ixT,myapp.dump myapp.lua
|
||||
--
|
||||
-- The first argument specifies the dump mode. The second argument gives
|
||||
-- the output file name. Default output is to stdout, unless the environment
|
||||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- Different features can be turned on or off with the dump mode. If the
|
||||
-- mode starts with a '+', the following features are added to the default
|
||||
-- set of features; a '-' removes them. Otherwise the features are replaced.
|
||||
--
|
||||
-- The following dump features are available (* marks the default):
|
||||
--
|
||||
-- * t Print a line for each started, ended or aborted trace (see also -jv).
|
||||
-- * b Dump the traced bytecode.
|
||||
-- * i Dump the IR (intermediate representation).
|
||||
-- r Augment the IR with register/stack slots.
|
||||
-- s Dump the snapshot map.
|
||||
-- * m Dump the generated machine code.
|
||||
-- x Print each taken trace exit.
|
||||
-- X Print each taken trace exit and the contents of all registers.
|
||||
-- a Print the IR of aborted traces, too.
|
||||
--
|
||||
-- The output format can be set with the following characters:
|
||||
--
|
||||
-- T Plain text output.
|
||||
-- A ANSI-colored text output
|
||||
-- H Colorized HTML + CSS output.
|
||||
--
|
||||
-- The default output format is plain text. It's set to ANSI-colored text
|
||||
-- if the COLORTERM variable is set. Note: this is independent of any output
|
||||
-- redirection, which is actually considered a feature.
|
||||
--
|
||||
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
|
||||
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
|
||||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
|
||||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
|
||||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
|
||||
local bit = require("bit")
|
||||
local band, shr, tohex = bit.band, bit.rshift, bit.tohex
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, rep = string.byte, string.rep
|
||||
local type, tostring = type, tostring
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Load other modules on-demand.
|
||||
local bcline, disass
|
||||
|
||||
-- Active flag, output file handle and dump mode.
|
||||
local active, out, dumpmode
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local symtabmt = { __index = false }
|
||||
local symtab = {}
|
||||
local nexitsym = 0
|
||||
|
||||
-- Fill nested symbol table with per-trace exit stub addresses.
|
||||
local function fillsymtab_tr(tr, nexit)
|
||||
local t = {}
|
||||
symtabmt.__index = t
|
||||
if jit.arch:sub(1, 4) == "mips" then
|
||||
t[traceexitstub(tr, 0)] = "exit"
|
||||
return
|
||||
end
|
||||
for i=0,nexit-1 do
|
||||
local addr = traceexitstub(tr, i)
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
local addr = traceexitstub(tr, nexit)
|
||||
if addr then t[addr] = "stack_check" end
|
||||
end
|
||||
|
||||
-- Fill symbol table with trace exit stub addresses.
|
||||
local function fillsymtab(tr, nexit)
|
||||
local t = symtab
|
||||
if nexitsym == 0 then
|
||||
local maskaddr = jit.arch == "arm" and -2
|
||||
local ircall = vmdef.ircall
|
||||
for i=0,#ircall do
|
||||
local addr = ircalladdr(i)
|
||||
if addr ~= 0 then
|
||||
if maskaddr then addr = band(addr, maskaddr) end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = ircall[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
if nexitsym == 1000000 then -- Per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
elseif nexit > nexitsym then -- Shared exit stubs.
|
||||
for i=nexitsym,nexit-1 do
|
||||
local addr = traceexitstub(i)
|
||||
if addr == nil then -- Fall back to per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
setmetatable(symtab, symtabmt)
|
||||
nexit = 1000000
|
||||
break
|
||||
end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
nexitsym = nexit
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function dumpwrite(s)
|
||||
out:write(s)
|
||||
end
|
||||
|
||||
-- Disassemble machine code.
|
||||
local function dump_mcode(tr)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local mcode, addr, loop = tracemc(tr)
|
||||
if not mcode then return end
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
|
||||
local ctx = disass.create(mcode, addr, dumpwrite)
|
||||
ctx.hexdump = 0
|
||||
ctx.symtab = fillsymtab(tr, info.nexit)
|
||||
if loop ~= 0 then
|
||||
symtab[addr+loop] = "LOOP"
|
||||
ctx:disass(0, loop)
|
||||
out:write("->LOOP:\n")
|
||||
ctx:disass(loop, #mcode-loop)
|
||||
symtab[addr+loop] = nil
|
||||
else
|
||||
ctx:disass(0, #mcode)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local irtype_text = {
|
||||
[0] = "nil",
|
||||
"fal",
|
||||
"tru",
|
||||
"lud",
|
||||
"str",
|
||||
"p32",
|
||||
"thr",
|
||||
"pro",
|
||||
"fun",
|
||||
"p64",
|
||||
"cdt",
|
||||
"tab",
|
||||
"udt",
|
||||
"flt",
|
||||
"num",
|
||||
"i8 ",
|
||||
"u8 ",
|
||||
"i16",
|
||||
"u16",
|
||||
"int",
|
||||
"u32",
|
||||
"i64",
|
||||
"u64",
|
||||
"sfp",
|
||||
}
|
||||
|
||||
local colortype_ansi = {
|
||||
[0] = "%s",
|
||||
"%s",
|
||||
"%s",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[32m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[33m%s\027[m",
|
||||
"\027[31m%s\027[m",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
}
|
||||
|
||||
local function colorize_text(s)
|
||||
return s
|
||||
end
|
||||
|
||||
local function colorize_ansi(s, t, extra)
|
||||
local out = format(colortype_ansi[t], s)
|
||||
if extra then out = "\027[3m"..out end
|
||||
return out
|
||||
end
|
||||
|
||||
local irtype_ansi = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
|
||||
|
||||
local function colorize_html(s, t, extra)
|
||||
s = gsub(s, "[<>&]", html_escape)
|
||||
return format('<span class="irt_%s%s">%s</span>',
|
||||
irtype_text[t], extra and " irt_extra" or "", s)
|
||||
end
|
||||
|
||||
local irtype_html = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local header_html = [[
|
||||
<style type="text/css">
|
||||
background { background: #ffffff; color: #000000; }
|
||||
pre.ljdump {
|
||||
font-size: 10pt;
|
||||
background: #f0f4ff;
|
||||
color: #000000;
|
||||
border: 1px solid #bfcfff;
|
||||
padding: 0.5em;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
span.irt_str { color: #00a000; }
|
||||
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
|
||||
span.irt_tab { color: #c00000; }
|
||||
span.irt_udt, span.irt_lud { color: #00c0c0; }
|
||||
span.irt_num { color: #4040c0; }
|
||||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
|
||||
span.irt_extra { font-style: italic; }
|
||||
</style>
|
||||
]]
|
||||
|
||||
local colorize, irtype
|
||||
|
||||
-- Lookup tables to convert some literals into names.
|
||||
local litname = {
|
||||
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = ""
|
||||
if band(mode, 1) ~= 0 then s = s.."P" end
|
||||
if band(mode, 2) ~= 0 then s = s.."F" end
|
||||
if band(mode, 4) ~= 0 then s = s.."T" end
|
||||
if band(mode, 8) ~= 0 then s = s.."C" end
|
||||
if band(mode, 16) ~= 0 then s = s.."R" end
|
||||
if band(mode, 32) ~= 0 then s = s.."I" end
|
||||
if band(mode, 64) ~= 0 then s = s.."K" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
|
||||
["CONV "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = irtype[band(mode, 31)]
|
||||
s = irtype[band(shr(mode, 5), 31)].."."..s
|
||||
if band(mode, 0x800) ~= 0 then s = s.." sext" end
|
||||
local c = shr(mode, 12)
|
||||
if c == 1 then s = s.." none"
|
||||
elseif c == 2 then s = s.." index"
|
||||
elseif c == 3 then s = s.." check" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["FLOAD "] = vmdef.irfield,
|
||||
["FREF "] = vmdef.irfield,
|
||||
["FPMATH"] = vmdef.irfpm,
|
||||
["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
|
||||
["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
|
||||
["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
|
||||
}
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
local function formatk(tr, idx, sn)
|
||||
local k, t, slot = tracek(tr, idx)
|
||||
local tn = type(k)
|
||||
local s
|
||||
if tn == "number" then
|
||||
if t < 12 then
|
||||
s = k == 0 and "NULL" or format("[0x%08x]", k)
|
||||
elseif band(sn or 0, 0x30000) ~= 0 then
|
||||
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
|
||||
elseif k == 2^52+2^51 then
|
||||
s = "bias"
|
||||
else
|
||||
local m = tonumber("0x1p-1026")
|
||||
s = format(m ~= nil and 0 < k and k < m and "%+a" or "%+.14g", k)
|
||||
end
|
||||
elseif tn == "string" then
|
||||
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
|
||||
elseif tn == "function" then
|
||||
s = fmtfunc(k)
|
||||
elseif tn == "table" then
|
||||
s = format("{%p}", k)
|
||||
elseif tn == "userdata" then
|
||||
if t == 12 then
|
||||
s = format("userdata:%p", k)
|
||||
else
|
||||
s = format("[%p]", k)
|
||||
if s == "[NULL]" then s = "NULL" end
|
||||
end
|
||||
elseif t == 21 then -- int64_t
|
||||
s = sub(tostring(k), 1, -3)
|
||||
if sub(s, 1, 1) ~= "-" then s = "+"..s end
|
||||
elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
|
||||
return "----" -- Special case for LJ_FR2 slot 1.
|
||||
else
|
||||
s = tostring(k) -- For primitives.
|
||||
end
|
||||
s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
|
||||
if slot then
|
||||
s = format("%s @%d", s, slot)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function printsnap(tr, snap)
|
||||
local n = 2
|
||||
for s=0,snap[1]-1 do
|
||||
local sn = snap[n]
|
||||
if shr(sn, 24) == s then
|
||||
n = n + 1
|
||||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
|
||||
if ref < 0 then
|
||||
out:write(formatk(tr, ref, sn))
|
||||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
|
||||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ref)
|
||||
out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
|
||||
end
|
||||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
|
||||
else
|
||||
out:write("---- ")
|
||||
end
|
||||
end
|
||||
out:write("]\n")
|
||||
end
|
||||
|
||||
-- Dump snapshots (not interleaved with IR).
|
||||
local function dump_snap(tr)
|
||||
out:write("---- TRACE ", tr, " snapshots\n")
|
||||
for i=0,1000000000 do
|
||||
local snap = tracesnap(tr, i)
|
||||
if not snap then break end
|
||||
out:write(format("#%-3d %04d [ ", i, snap[0]))
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a register name or stack slot for a rid/sp location.
|
||||
local function ridsp_name(ridsp, ins)
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
|
||||
if rid == 253 or rid == 254 then
|
||||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
|
||||
end
|
||||
if ridsp > 255 then return format("[%x]", slot*4) end
|
||||
if rid < 128 then return disass.regname(rid) end
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Dump CALL* function ref and return optional ctype.
|
||||
local function dumpcallfunc(tr, ins)
|
||||
local ctype
|
||||
if ins > 0 then
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
|
||||
ins = op1
|
||||
ctype = formatk(tr, op2)
|
||||
end
|
||||
end
|
||||
if ins < 0 then
|
||||
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
|
||||
else
|
||||
out:write(format("%04d (", ins))
|
||||
end
|
||||
return ctype
|
||||
end
|
||||
|
||||
-- Recursively gather CALL* args and dump them.
|
||||
local function dumpcallargs(tr, ins)
|
||||
if ins < 0 then
|
||||
out:write(formatk(tr, ins))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
local oidx = 6*shr(ot, 8)
|
||||
local op = sub(vmdef.irnames, oidx+1, oidx+6)
|
||||
if op == "CARG " then
|
||||
dumpcallargs(tr, op1)
|
||||
if op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(" ", format("%04d", op2))
|
||||
end
|
||||
else
|
||||
out:write(format("%04d", ins))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Dump IR and interleaved snapshots.
|
||||
local function dump_ir(tr, dumpsnap, dumpreg)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local nins = info.nins
|
||||
out:write("---- TRACE ", tr, " IR\n")
|
||||
local irnames = vmdef.irnames
|
||||
local snapref = 65536
|
||||
local snap, snapno
|
||||
if dumpsnap then
|
||||
snap = tracesnap(tr, 0)
|
||||
snapref = snap[0]
|
||||
snapno = 0
|
||||
end
|
||||
for ins=1,nins do
|
||||
if ins >= snapref then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
snapno = snapno + 1
|
||||
snap = tracesnap(tr, snapno)
|
||||
snapref = snap and snap[0] or 65536
|
||||
end
|
||||
local m, ot, op1, op2, ridsp = traceir(tr, ins)
|
||||
local oidx, t = 6*shr(ot, 8), band(ot, 31)
|
||||
local op = sub(irnames, oidx+1, oidx+6)
|
||||
if op == "LOOP " then
|
||||
if dumpreg then
|
||||
out:write(format("%04d ------------ LOOP ------------\n", ins))
|
||||
else
|
||||
out:write(format("%04d ------ LOOP ------------\n", ins))
|
||||
end
|
||||
elseif op ~= "NOP " and op ~= "CARG " and
|
||||
(dumpreg or op ~= "RENAME") then
|
||||
local rid = band(ridsp, 255)
|
||||
if dumpreg then
|
||||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
|
||||
else
|
||||
out:write(format("%04d ", ins))
|
||||
end
|
||||
out:write(format("%s%s %s %s ",
|
||||
(rid == 254 or rid == 253) and "}" or
|
||||
(band(ot, 128) == 0 and " " or ">"),
|
||||
band(ot, 64) == 0 and " " or "+",
|
||||
irtype[t], op))
|
||||
local m1, m2 = band(m, 3), band(m, 3*4)
|
||||
if sub(op, 1, 4) == "CALL" then
|
||||
local ctype
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
out:write(format("%-10s (", vmdef.ircall[op2]))
|
||||
else
|
||||
ctype = dumpcallfunc(tr, op2)
|
||||
end
|
||||
if op1 ~= -1 then dumpcallargs(tr, op1) end
|
||||
out:write(")")
|
||||
if ctype then out:write(" ctype ", ctype) end
|
||||
elseif op == "CNEW " and op2 == -1 then
|
||||
out:write(formatk(tr, op1))
|
||||
elseif m1 ~= 3 then -- op1 != IRMnone
|
||||
if op1 < 0 then
|
||||
out:write(formatk(tr, op1))
|
||||
else
|
||||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
|
||||
end
|
||||
if m2 ~= 3*4 then -- op2 != IRMnone
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
local litn = litname[op]
|
||||
if litn and litn[op2] then
|
||||
out:write(" ", litn[op2])
|
||||
elseif op == "UREFO " or op == "UREFC " then
|
||||
out:write(format(" #%-3d", shr(op2, 8)))
|
||||
else
|
||||
out:write(format(" #%-3d", op2))
|
||||
end
|
||||
elseif op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(format(" %04d", op2))
|
||||
end
|
||||
end
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
end
|
||||
if snap then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local recprefix = ""
|
||||
local recdepth = 0
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
local fmt = vmdef.traceerr[err]
|
||||
if fmt == "NYI: bytecode %s" then
|
||||
local oidx = 6 * info
|
||||
info = sub(vmdef.bcnames, oidx+1, oidx+6)
|
||||
end
|
||||
err = format(fmt, info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "stop" or (what == "abort" and dumpmode.a) then
|
||||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
|
||||
elseif dumpmode.s then dump_snap(tr) end
|
||||
if dumpmode.m then dump_mcode(tr) end
|
||||
end
|
||||
if what == "start" then
|
||||
if dumpmode.H then out:write('<pre class="ljdump">\n') end
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
|
||||
out:write(" ", fmtfunc(func, pc), "\n")
|
||||
elseif what == "stop" or what == "abort" then
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if what == "abort" then
|
||||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
|
||||
else
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if link == tr or link == 0 then
|
||||
out:write(" -> ", ltype, "\n")
|
||||
elseif ltype == "root" then
|
||||
out:write(" -> ", link, "\n")
|
||||
else
|
||||
out:write(" -> ", link, " ", ltype, "\n")
|
||||
end
|
||||
end
|
||||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
|
||||
else
|
||||
if what == "flush" then symtab, nexitsym = {}, 0 end
|
||||
out:write("---- TRACE ", what, "\n\n")
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
|
||||
-- Dump recorded bytecode.
|
||||
local function dump_record(tr, func, pc, depth)
|
||||
if depth ~= recdepth then
|
||||
recdepth = depth
|
||||
recprefix = rep(" .", depth)
|
||||
end
|
||||
local line
|
||||
if pc >= 0 then
|
||||
line = bcline(func, pc, recprefix)
|
||||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
|
||||
else
|
||||
line = "0000 "..recprefix.." FUNCC \n"
|
||||
end
|
||||
if pc <= 0 then
|
||||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
|
||||
else
|
||||
out:write(line)
|
||||
end
|
||||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
|
||||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local gpr64 = jit.arch:match("64")
|
||||
local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
|
||||
|
||||
-- Dump taken trace exits.
|
||||
local function dump_texit(tr, ex, ngpr, nfpr, ...)
|
||||
out:write("---- TRACE ", tr, " exit ", ex, "\n")
|
||||
if dumpmode.X then
|
||||
local regs = {...}
|
||||
if gpr64 then
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %016x", regs[i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,ngpr do
|
||||
out:write(" ", tohex(regs[i]))
|
||||
if i % 8 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
if fprmips32 then
|
||||
for i=1,nfpr,2 do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 8 == 7 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,nfpr do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_texit)
|
||||
jit.attach(dump_record)
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(opt, outfile)
|
||||
if active then dumpoff() end
|
||||
|
||||
local term = os.getenv("TERM")
|
||||
local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
|
||||
if opt then
|
||||
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
|
||||
end
|
||||
|
||||
local m = { t=true, b=true, i=true, m=true, }
|
||||
if opt and opt ~= "" then
|
||||
local o = sub(opt, 1, 1)
|
||||
if o ~= "+" and o ~= "-" then m = {} end
|
||||
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
|
||||
end
|
||||
dumpmode = m
|
||||
|
||||
if m.t or m.b or m.i or m.s or m.m then
|
||||
jit.attach(dump_trace, "trace")
|
||||
end
|
||||
if m.b then
|
||||
jit.attach(dump_record, "record")
|
||||
if not bcline then bcline = require("jit.bc").line end
|
||||
end
|
||||
if m.x or m.X then
|
||||
jit.attach(dump_texit, "texit")
|
||||
end
|
||||
|
||||
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stdout
|
||||
end
|
||||
|
||||
m[colormode] = true
|
||||
if colormode == "A" then
|
||||
colorize = colorize_ansi
|
||||
irtype = irtype_ansi
|
||||
elseif colormode == "H" then
|
||||
colorize = colorize_html
|
||||
irtype = irtype_html
|
||||
out:write(header_html)
|
||||
else
|
||||
colorize = colorize_text
|
||||
irtype = irtype_text
|
||||
end
|
||||
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
on = dumpon,
|
||||
off = dumpoff,
|
||||
start = dumpon -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT profiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module is a simple command line interface to the built-in
|
||||
-- low-overhead profiler of LuaJIT.
|
||||
--
|
||||
-- The lower-level API of the profiler is accessible via the "jit.profile"
|
||||
-- module or the luaJIT_profile_* C API.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jp myapp.lua
|
||||
-- luajit -jp=s myapp.lua
|
||||
-- luajit -jp=-s myapp.lua
|
||||
-- luajit -jp=vl myapp.lua
|
||||
-- luajit -jp=G,profile.txt myapp.lua
|
||||
--
|
||||
-- The following dump features are available:
|
||||
--
|
||||
-- f Stack dump: function name, otherwise module:line. Default mode.
|
||||
-- F Stack dump: ditto, but always prepend module.
|
||||
-- l Stack dump: module:line.
|
||||
-- <number> stack dump depth (callee < caller). Default: 1.
|
||||
-- -<number> Inverse stack dump depth (caller > callee).
|
||||
-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
|
||||
-- p Show full path for module names.
|
||||
-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
|
||||
-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
|
||||
-- r Show raw sample counts. Default: show percentages.
|
||||
-- a Annotate excerpts from source code files.
|
||||
-- A Annotate complete source code files.
|
||||
-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
|
||||
-- m<number> Minimum sample percentage to be shown. Default: 3.
|
||||
-- i<number> Sampling interval in milliseconds. Default: 10.
|
||||
--
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local profile = require("jit.profile")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local math = math
|
||||
local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
|
||||
local sort, format = table.sort, string.format
|
||||
local stdout = io.stdout
|
||||
local zone -- Load jit.zone module on demand.
|
||||
|
||||
-- Output file handle.
|
||||
local out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local prof_ud
|
||||
local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
|
||||
local prof_ann, prof_count1, prof_count2, prof_samples
|
||||
|
||||
local map_vmmode = {
|
||||
N = "Compiled",
|
||||
I = "Interpreted",
|
||||
C = "C code",
|
||||
G = "Garbage Collector",
|
||||
J = "JIT Compiler",
|
||||
}
|
||||
|
||||
-- Profiler callback.
|
||||
local function prof_cb(th, samples, vmmode)
|
||||
prof_samples = prof_samples + samples
|
||||
local key_stack, key_stack2, key_state
|
||||
-- Collect keys for sample.
|
||||
if prof_states then
|
||||
if prof_states == "v" then
|
||||
key_state = map_vmmode[vmmode] or vmmode
|
||||
else
|
||||
key_state = zone:get() or "(none)"
|
||||
end
|
||||
end
|
||||
if prof_fmt then
|
||||
key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
|
||||
key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
|
||||
return vmdef.ffnames[tonumber(x)]
|
||||
end)
|
||||
if prof_split == 2 then
|
||||
local k1, k2 = key_stack:match("(.-) [<>] (.*)")
|
||||
if k2 then key_stack, key_stack2 = k1, k2 end
|
||||
elseif prof_split == 3 then
|
||||
key_stack2 = profile.dumpstack(th, "l", 1)
|
||||
end
|
||||
end
|
||||
-- Order keys.
|
||||
local k1, k2
|
||||
if prof_split == 1 then
|
||||
if key_state then
|
||||
k1 = key_state
|
||||
if key_stack then k2 = key_stack end
|
||||
end
|
||||
elseif key_stack then
|
||||
k1 = key_stack
|
||||
if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
|
||||
end
|
||||
-- Coalesce samples in one or two levels.
|
||||
if k1 then
|
||||
local t1 = prof_count1
|
||||
t1[k1] = (t1[k1] or 0) + samples
|
||||
if k2 then
|
||||
local t2 = prof_count2
|
||||
local t3 = t2[k1]
|
||||
if not t3 then t3 = {}; t2[k1] = t3 end
|
||||
t3[k2] = (t3[k2] or 0) + samples
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Show top N list.
|
||||
local function prof_top(count1, count2, samples, indent)
|
||||
local t, n = {}, 0
|
||||
for k in pairs(count1) do
|
||||
n = n + 1
|
||||
t[n] = k
|
||||
end
|
||||
sort(t, function(a, b) return count1[a] > count1[b] end)
|
||||
for i=1,n do
|
||||
local k = t[i]
|
||||
local v = count1[k]
|
||||
local pct = floor(v*100/samples + 0.5)
|
||||
if pct < prof_min then break end
|
||||
if not prof_raw then
|
||||
out:write(format("%s%2d%% %s\n", indent, pct, k))
|
||||
elseif prof_raw == "r" then
|
||||
out:write(format("%s%5d %s\n", indent, v, k))
|
||||
else
|
||||
out:write(format("%s %d\n", k, v))
|
||||
end
|
||||
if count2 then
|
||||
local r = count2[k]
|
||||
if r then
|
||||
prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
|
||||
(prof_depth < 0 and " -> " or " <- "))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Annotate source code
|
||||
local function prof_annotate(count1, samples)
|
||||
local files = {}
|
||||
local ms = 0
|
||||
for k, v in pairs(count1) do
|
||||
local pct = floor(v*100/samples + 0.5)
|
||||
ms = math.max(ms, v)
|
||||
if pct >= prof_min then
|
||||
local file, line = k:match("^(.*):(%d+)$")
|
||||
if not file then file = k; line = 0 end
|
||||
local fl = files[file]
|
||||
if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
|
||||
line = tonumber(line)
|
||||
fl[line] = prof_raw and v or pct
|
||||
end
|
||||
end
|
||||
sort(files)
|
||||
local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
|
||||
if prof_raw then
|
||||
local n = math.max(5, math.ceil(math.log10(ms)))
|
||||
fmtv = "%"..n.."d | %s\n"
|
||||
fmtn = (" "):rep(n).." | %s\n"
|
||||
end
|
||||
local ann = prof_ann
|
||||
for _, file in ipairs(files) do
|
||||
local f0 = file:byte()
|
||||
if f0 == 40 or f0 == 91 then
|
||||
out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
|
||||
break
|
||||
end
|
||||
local fp, err = io.open(file)
|
||||
if not fp then
|
||||
out:write(format("====== ERROR: %s: %s\n", file, err))
|
||||
break
|
||||
end
|
||||
out:write(format("\n====== %s ======\n", file))
|
||||
local fl = files[file]
|
||||
local n, show = 1, false
|
||||
if ann ~= 0 then
|
||||
for i=1,ann do
|
||||
if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
|
||||
end
|
||||
end
|
||||
for line in fp:lines() do
|
||||
if line:byte() == 27 then
|
||||
out:write("[Cannot annotate bytecode file]\n")
|
||||
break
|
||||
end
|
||||
local v = fl[n]
|
||||
if ann ~= 0 then
|
||||
local v2 = fl[n+ann]
|
||||
if show then
|
||||
if v2 then show = n+ann elseif v then show = n
|
||||
elseif show+ann < n then show = false end
|
||||
elseif v2 then
|
||||
show = n+ann
|
||||
out:write(format("@@ %d @@\n", n))
|
||||
end
|
||||
if not show then goto next end
|
||||
end
|
||||
if v then
|
||||
out:write(format(fmtv, v, line))
|
||||
else
|
||||
out:write(format(fmtn, line))
|
||||
end
|
||||
::next::
|
||||
n = n + 1
|
||||
end
|
||||
fp:close()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Finish profiling and dump result.
|
||||
local function prof_finish()
|
||||
if prof_ud then
|
||||
profile.stop()
|
||||
local samples = prof_samples
|
||||
if samples == 0 then
|
||||
if prof_raw ~= true then out:write("[No samples collected]\n") end
|
||||
elseif prof_ann then
|
||||
prof_annotate(prof_count1, samples)
|
||||
else
|
||||
prof_top(prof_count1, prof_count2, samples, "")
|
||||
end
|
||||
prof_count1 = nil
|
||||
prof_count2 = nil
|
||||
prof_ud = nil
|
||||
if out ~= stdout then out:close() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Start profiling.
|
||||
local function prof_start(mode)
|
||||
local interval = ""
|
||||
mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
|
||||
prof_min = 3
|
||||
mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
|
||||
prof_depth = 1
|
||||
mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
|
||||
local m = {}
|
||||
for c in mode:gmatch(".") do m[c] = c end
|
||||
prof_states = m.z or m.v
|
||||
if prof_states == "z" then zone = require("jit.zone") end
|
||||
local scope = m.l or m.f or m.F or (prof_states and "" or "f")
|
||||
local flags = (m.p or "")
|
||||
prof_raw = m.r
|
||||
if m.s then
|
||||
prof_split = 2
|
||||
if prof_depth == -1 or m["-"] then prof_depth = -2
|
||||
elseif prof_depth == 1 then prof_depth = 2 end
|
||||
elseif mode:find("[fF].*l") then
|
||||
scope = "l"
|
||||
prof_split = 3
|
||||
else
|
||||
prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
|
||||
end
|
||||
prof_ann = m.A and 0 or (m.a and 3)
|
||||
if prof_ann then
|
||||
scope = "l"
|
||||
prof_fmt = "pl"
|
||||
prof_split = 0
|
||||
prof_depth = 1
|
||||
elseif m.G and scope ~= "" then
|
||||
prof_fmt = flags..scope.."Z;"
|
||||
prof_depth = -100
|
||||
prof_raw = true
|
||||
prof_min = 0
|
||||
elseif scope == "" then
|
||||
prof_fmt = false
|
||||
else
|
||||
local sc = prof_split == 3 and m.f or m.F or scope
|
||||
prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
|
||||
end
|
||||
prof_count1 = {}
|
||||
prof_count2 = {}
|
||||
prof_samples = 0
|
||||
profile.start(scope:lower()..interval, prof_cb)
|
||||
prof_ud = newproxy(true)
|
||||
getmetatable(prof_ud).__gc = prof_finish
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function start(mode, outfile)
|
||||
if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stdout
|
||||
end
|
||||
prof_start(mode or "f")
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
start = start, -- For -j command line option.
|
||||
stop = prof_finish
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Verbose mode of the LuaJIT compiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module shows verbose information about the progress of the
|
||||
-- JIT compiler. It prints one line for each generated trace. This module
|
||||
-- is useful to see which code has been compiled or where the compiler
|
||||
-- punts and falls back to the interpreter.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
|
||||
-- luajit -jv=myapp.out myapp.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- The output from the first example should look like this:
|
||||
--
|
||||
-- [TRACE 1 (command line):1 loop]
|
||||
-- [TRACE 2 (1/3) (command line):1 -> 1]
|
||||
--
|
||||
-- The first number in each line is the internal trace number. Next are
|
||||
-- the file name ('(command line)') and the line number (':1') where the
|
||||
-- trace has started. Side traces also show the parent trace number and
|
||||
-- the exit number where they are attached to in parentheses ('(1/3)').
|
||||
-- An arrow at the end shows where the trace links to ('-> 1'), unless
|
||||
-- it loops to itself.
|
||||
--
|
||||
-- In this case the inner loop gets hot and is traced first, generating
|
||||
-- a root trace. Then the last exit from the 1st trace gets hot, too,
|
||||
-- and triggers generation of the 2nd trace. The side trace follows the
|
||||
-- path along the outer loop and *around* the inner loop, back to its
|
||||
-- start, and then links to the 1st trace. Yes, this may seem unusual,
|
||||
-- if you know how traditional compilers work. Trace compilers are full
|
||||
-- of surprises like this -- have fun! :-)
|
||||
--
|
||||
-- Aborted traces are shown like this:
|
||||
--
|
||||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
|
||||
--
|
||||
-- Don't worry -- trace aborts are quite common, even in programs which
|
||||
-- can be fully compiled. The compiler may retry several times until it
|
||||
-- finds a suitable trace.
|
||||
--
|
||||
-- Of course this doesn't work with features that are not-yet-implemented
|
||||
-- (NYI error messages). The VM simply falls back to the interpreter. This
|
||||
-- may not matter at all if the particular trace is not very high up in
|
||||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
|
||||
--
|
||||
-- Also check out the -jdump module, which prints all the gory details.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
|
||||
local type, sub, format = type, string.sub, string.format
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local startloc, startex
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
local fmt = vmdef.traceerr[err]
|
||||
if fmt == "NYI: bytecode %s" then
|
||||
local oidx = 6 * info
|
||||
info = sub(vmdef.bcnames, oidx+1, oidx+6)
|
||||
end
|
||||
err = format(fmt, info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "start" then
|
||||
startloc = fmtfunc(func, pc)
|
||||
startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
|
||||
else
|
||||
if what == "abort" then
|
||||
local loc = fmtfunc(func, pc)
|
||||
if loc ~= startloc then
|
||||
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
|
||||
startex, startloc, fmterr(otr, oex), loc))
|
||||
else
|
||||
out:write(format("[TRACE --- %s%s -- %s]\n",
|
||||
startex, startloc, fmterr(otr, oex)))
|
||||
end
|
||||
elseif what == "stop" then
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if ltype == "interpreter" then
|
||||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
|
||||
tr, startex, startloc))
|
||||
elseif ltype == "stitch" then
|
||||
out:write(format("[TRACE %3s %s%s %s %s]\n",
|
||||
tr, startex, startloc, ltype, fmtfunc(func, pc)))
|
||||
elseif link == tr or link == 0 then
|
||||
out:write(format("[TRACE %3s %s%s %s]\n",
|
||||
tr, startex, startloc, ltype))
|
||||
elseif ltype == "root" then
|
||||
out:write(format("[TRACE %3s %s%s -> %d]\n",
|
||||
tr, startex, startloc, link))
|
||||
else
|
||||
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
|
||||
tr, startex, startloc, link, ltype))
|
||||
end
|
||||
else
|
||||
out:write(format("[TRACE %s]\n", what))
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(outfile)
|
||||
if active then dumpoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(dump_trace, "trace")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
on = dumpon,
|
||||
off = dumpoff,
|
||||
start = dumpon -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT profiler zones.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module implements a simple hierarchical zone model.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- local zone = require("jit.zone")
|
||||
-- zone("AI")
|
||||
-- ...
|
||||
-- zone("A*")
|
||||
-- ...
|
||||
-- print(zone:get()) --> "A*"
|
||||
-- ...
|
||||
-- zone()
|
||||
-- ...
|
||||
-- print(zone:get()) --> "AI"
|
||||
-- ...
|
||||
-- zone()
|
||||
--
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local remove = table.remove
|
||||
|
||||
return setmetatable({
|
||||
flush = function(t)
|
||||
for i=#t,1,-1 do t[i] = nil end
|
||||
end,
|
||||
get = function(t)
|
||||
return t[#t]
|
||||
end
|
||||
}, {
|
||||
__call = function(t, zone)
|
||||
if zone then
|
||||
t[#t+1] = zone
|
||||
else
|
||||
return (assert(remove(t), "empty zone stack"))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
|
||||
** Auxiliary functions for building Lua libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lauxlib_h
|
||||
#define lauxlib_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* extra error code for `luaL_load' */
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
|
||||
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup);
|
||||
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l);
|
||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
|
||||
size_t *l);
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
|
||||
const char *def, size_t *l);
|
||||
#ifdef LUAJIT_INTONLY
|
||||
#define luaL_checknumber luaL_checkinteger
|
||||
#define luaL_optnumber luaL_optinteger
|
||||
#else
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
|
||||
#endif
|
||||
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
|
||||
lua_Integer def);
|
||||
|
||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
|
||||
|
||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
|
||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
|
||||
const char *const lst[]);
|
||||
|
||||
/* pre-defined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
|
||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||
|
||||
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
|
||||
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name);
|
||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
|
||||
const char *r);
|
||||
|
||||
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
const char *fname, int szhint);
|
||||
|
||||
/* From Lua 5.2. */
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat);
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
|
||||
int level);
|
||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
|
||||
int sizehint);
|
||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
#define luaL_argcheck(L, cond,numarg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
|
||||
#define luaL_dofile(L, fn) \
|
||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_dostring(L, s) \
|
||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
|
||||
/* From Lua 5.2. */
|
||||
#define luaL_newlibtable(L, l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0))
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
typedef struct luaL_Buffer {
|
||||
char *p; /* current position in buffer */
|
||||
int lvl; /* number of strings in the stack (level) */
|
||||
lua_State *L;
|
||||
char buffer[LUAL_BUFFERSIZE];
|
||||
} luaL_Buffer;
|
||||
|
||||
#define luaL_addchar(B,c) \
|
||||
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
|
||||
(*(B)->p++ = (char)(c)))
|
||||
|
||||
/* compatibility only */
|
||||
#define luaL_putchar(B,c) luaL_addchar(B,c)
|
||||
|
||||
#define luaL_addsize(B,n) ((B)->p += (n))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/* compatibility with ref system */
|
||||
|
||||
/* pre-defined references */
|
||||
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
|
||||
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
|
||||
|
||||
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
|
||||
|
||||
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
|
||||
|
||||
#define luaL_reg luaL_Reg
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
||||
@@ -1,401 +0,0 @@
|
||||
/*
|
||||
** Auxiliary library for the Lua/C API.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major parts taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_aux_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_lib.h"
|
||||
#include "lj_vmevent.h"
|
||||
|
||||
#if LJ_TARGET_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
/* -- I/O error handling -------------------------------------------------- */
|
||||
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname)
|
||||
{
|
||||
if (stat) {
|
||||
setboolV(L->top++, 1);
|
||||
return 1;
|
||||
} else {
|
||||
int en = errno; /* Lua API calls may change this value. */
|
||||
setnilV(L->top++);
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(en));
|
||||
setintV(L->top++, en);
|
||||
lj_trace_abort(G(L));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat)
|
||||
{
|
||||
if (stat != -1) {
|
||||
#if LJ_TARGET_POSIX
|
||||
if (WIFSIGNALED(stat)) {
|
||||
stat = WTERMSIG(stat);
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "signal");
|
||||
} else {
|
||||
if (WIFEXITED(stat))
|
||||
stat = WEXITSTATUS(stat);
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
}
|
||||
#else
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
#endif
|
||||
setintV(L->top++, stat);
|
||||
return 3;
|
||||
}
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
}
|
||||
|
||||
/* -- Module registration ------------------------------------------------- */
|
||||
|
||||
LUALIB_API const char *luaL_findtable(lua_State *L, int idx,
|
||||
const char *fname, int szhint)
|
||||
{
|
||||
const char *e;
|
||||
lua_pushvalue(L, idx);
|
||||
do {
|
||||
e = strchr(fname, '.');
|
||||
if (e == NULL) e = fname + strlen(fname);
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_rawget(L, -2);
|
||||
if (lua_isnil(L, -1)) { /* no such field? */
|
||||
lua_pop(L, 1); /* remove this nil */
|
||||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -4); /* set new table into field */
|
||||
} else if (!lua_istable(L, -1)) { /* field has a non-table value? */
|
||||
lua_pop(L, 2); /* remove table and value */
|
||||
return fname; /* return problematic part of the name */
|
||||
}
|
||||
lua_remove(L, -2); /* remove previous table */
|
||||
fname = e + 1;
|
||||
} while (*e == '.');
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int libsize(const luaL_Reg *l)
|
||||
{
|
||||
int size = 0;
|
||||
for (; l && l->name; l++) size++;
|
||||
return size;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushmodule(lua_State *L, const char *modname, int sizehint)
|
||||
{
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
|
||||
lua_getfield(L, -1, modname);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, sizehint) != NULL)
|
||||
lj_err_callerv(L, LJ_ERR_BADMODN, modname);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, modname); /* _LOADED[modname] = new table. */
|
||||
}
|
||||
lua_remove(L, -2); /* Remove _LOADED table. */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_openlib(lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup)
|
||||
{
|
||||
lj_lib_checkfpu(L);
|
||||
if (libname) {
|
||||
luaL_pushmodule(L, libname, libsize(l));
|
||||
lua_insert(L, -(nup + 1)); /* Move module table below upvalues. */
|
||||
}
|
||||
if (l)
|
||||
luaL_setfuncs(L, l, nup);
|
||||
else
|
||||
lua_pop(L, nup); /* Remove upvalues. */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_register(lua_State *L, const char *libname,
|
||||
const luaL_Reg *l)
|
||||
{
|
||||
luaL_openlib(L, libname, l, 0);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
|
||||
{
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name; l++) {
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* Copy upvalues to the top. */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup);
|
||||
lua_setfield(L, -(nup + 2), l->name);
|
||||
}
|
||||
lua_pop(L, nup); /* Remove upvalues. */
|
||||
}
|
||||
|
||||
LUALIB_API const char *luaL_gsub(lua_State *L, const char *s,
|
||||
const char *p, const char *r)
|
||||
{
|
||||
const char *wild;
|
||||
size_t l = strlen(p);
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
while ((wild = strstr(s, p)) != NULL) {
|
||||
luaL_addlstring(&b, s, (size_t)(wild - s)); /* push prefix */
|
||||
luaL_addstring(&b, r); /* push replacement in place of pattern */
|
||||
s = wild + l; /* continue after `p' */
|
||||
}
|
||||
luaL_addstring(&b, s); /* push last suffix */
|
||||
luaL_pushresult(&b);
|
||||
return lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
/* -- Buffer handling ----------------------------------------------------- */
|
||||
|
||||
#define bufflen(B) ((size_t)((B)->p - (B)->buffer))
|
||||
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
|
||||
|
||||
static int emptybuffer(luaL_Buffer *B)
|
||||
{
|
||||
size_t l = bufflen(B);
|
||||
if (l == 0)
|
||||
return 0; /* put nothing on stack */
|
||||
lua_pushlstring(B->L, B->buffer, l);
|
||||
B->p = B->buffer;
|
||||
B->lvl++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void adjuststack(luaL_Buffer *B)
|
||||
{
|
||||
if (B->lvl > 1) {
|
||||
lua_State *L = B->L;
|
||||
int toget = 1; /* number of levels to concat */
|
||||
size_t toplen = lua_strlen(L, -1);
|
||||
do {
|
||||
size_t l = lua_strlen(L, -(toget+1));
|
||||
if (!(B->lvl - toget + 1 >= LUA_MINSTACK/2 || toplen > l))
|
||||
break;
|
||||
toplen += l;
|
||||
toget++;
|
||||
} while (toget < B->lvl);
|
||||
lua_concat(L, toget);
|
||||
B->lvl = B->lvl - toget + 1;
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API char *luaL_prepbuffer(luaL_Buffer *B)
|
||||
{
|
||||
if (emptybuffer(B))
|
||||
adjuststack(B);
|
||||
return B->buffer;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l)
|
||||
{
|
||||
if (l <= bufffree(B)) {
|
||||
memcpy(B->p, s, l);
|
||||
B->p += l;
|
||||
} else {
|
||||
emptybuffer(B);
|
||||
lua_pushlstring(B->L, s, l);
|
||||
B->lvl++;
|
||||
adjuststack(B);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s)
|
||||
{
|
||||
luaL_addlstring(B, s, strlen(s));
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushresult(luaL_Buffer *B)
|
||||
{
|
||||
emptybuffer(B);
|
||||
lua_concat(B->L, B->lvl);
|
||||
B->lvl = 1;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addvalue(luaL_Buffer *B)
|
||||
{
|
||||
lua_State *L = B->L;
|
||||
size_t vl;
|
||||
const char *s = lua_tolstring(L, -1, &vl);
|
||||
if (vl <= bufffree(B)) { /* fit into buffer? */
|
||||
memcpy(B->p, s, vl); /* put it there */
|
||||
B->p += vl;
|
||||
lua_pop(L, 1); /* remove from stack */
|
||||
} else {
|
||||
if (emptybuffer(B))
|
||||
lua_insert(L, -2); /* put buffer before new value */
|
||||
B->lvl++; /* add new value into B stack */
|
||||
adjuststack(B);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B)
|
||||
{
|
||||
B->L = L;
|
||||
B->p = B->buffer;
|
||||
B->lvl = 0;
|
||||
}
|
||||
|
||||
/* -- Reference management ------------------------------------------------ */
|
||||
|
||||
#define FREELIST_REF 0
|
||||
|
||||
/* Convert a stack index to an absolute index. */
|
||||
#define abs_index(L, i) \
|
||||
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||
|
||||
LUALIB_API int luaL_ref(lua_State *L, int t)
|
||||
{
|
||||
int ref;
|
||||
t = abs_index(L, t);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1); /* remove from stack */
|
||||
return LUA_REFNIL; /* `nil' has a unique fixed reference */
|
||||
}
|
||||
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
|
||||
lua_pop(L, 1); /* remove it from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
|
||||
} else { /* no free elements */
|
||||
ref = (int)lua_objlen(L, t);
|
||||
ref++; /* create new reference */
|
||||
}
|
||||
lua_rawseti(L, t, ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
|
||||
{
|
||||
if (ref >= 0) {
|
||||
t = abs_index(L, t);
|
||||
lua_rawgeti(L, t, FREELIST_REF);
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Default allocator and panic function -------------------------------- */
|
||||
|
||||
static int panic(lua_State *L)
|
||||
{
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs("PANIC: unprotected error in call to Lua API (", stderr);
|
||||
fputs(s ? s : "?", stderr);
|
||||
fputc(')', stderr); fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
static int error_finalizer(lua_State *L)
|
||||
{
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs("ERROR in finalizer: ", stderr);
|
||||
fputs(s ? s : "?", stderr);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LUAJIT_USE_SYSMALLOC
|
||||
|
||||
#if LJ_64 && !LJ_GC64 && !defined(LUAJIT_USE_VALGRIND)
|
||||
#error "Must use builtin allocator for 64 bit target"
|
||||
#endif
|
||||
|
||||
static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
|
||||
{
|
||||
(void)ud;
|
||||
(void)osize;
|
||||
if (nsize == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return realloc(ptr, nsize);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L = lua_newstate(mem_alloc, NULL);
|
||||
if (L) {
|
||||
G(L)->panic = panic;
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
|
||||
lua_pushcfunction(L, error_finalizer);
|
||||
lua_rawseti(L, -2, VMEVENT_HASH(LJ_VMEVENT_ERRFIN));
|
||||
G(L)->vmevmask = VMEVENT_MASK(LJ_VMEVENT_ERRFIN);
|
||||
L->top--;
|
||||
#endif
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L;
|
||||
#if LJ_64 && !LJ_GC64
|
||||
L = lj_state_newstate(LJ_ALLOCF_INTERNAL, NULL);
|
||||
#else
|
||||
L = lua_newstate(LJ_ALLOCF_INTERNAL, NULL);
|
||||
#endif
|
||||
if (L) {
|
||||
G(L)->panic = panic;
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
|
||||
lua_pushcfunction(L, error_finalizer);
|
||||
lua_rawseti(L, -2, VMEVENT_HASH(LJ_VMEVENT_ERRFIN));
|
||||
G(L)->vmevmask = VMEVENT_MASK(LJ_VMEVENT_ERRFIN);
|
||||
L->top--;
|
||||
#endif
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
#if LJ_64 && !LJ_GC64
|
||||
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
|
||||
{
|
||||
UNUSED(f); UNUSED(ud);
|
||||
fputs("Must use luaL_newstate() for 64 bit target\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,709 +0,0 @@
|
||||
/*
|
||||
** Base and coroutine library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_base_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#endif
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_strscan.h"
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- Base library: checks ------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_base
|
||||
|
||||
LJLIB_ASM(assert) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
if (L->top == L->base+1)
|
||||
lj_err_caller(L, LJ_ERR_ASSERT);
|
||||
else if (tvisstr(L->base+1) || tvisnumber(L->base+1))
|
||||
lj_err_callermsg(L, strdata(lj_lib_checkstr(L, 2)));
|
||||
else
|
||||
lj_err_run(L);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
/* ORDER LJ_T */
|
||||
LJLIB_PUSH("nil")
|
||||
LJLIB_PUSH("boolean")
|
||||
LJLIB_PUSH(top-1) /* boolean */
|
||||
LJLIB_PUSH("userdata")
|
||||
LJLIB_PUSH("string")
|
||||
LJLIB_PUSH("upval")
|
||||
LJLIB_PUSH("thread")
|
||||
LJLIB_PUSH("proto")
|
||||
LJLIB_PUSH("function")
|
||||
LJLIB_PUSH("trace")
|
||||
LJLIB_PUSH("cdata")
|
||||
LJLIB_PUSH("table")
|
||||
LJLIB_PUSH(top-9) /* userdata */
|
||||
LJLIB_PUSH("number")
|
||||
LJLIB_ASM_(type) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
/* -- Base library: iterators --------------------------------------------- */
|
||||
|
||||
/* This solves a circular dependency problem -- change FF_next_N as needed. */
|
||||
LJ_STATIC_ASSERT((int)FF_next == FF_next_N);
|
||||
|
||||
LJLIB_ASM(next) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_err_msg(L, LJ_ERR_NEXTIDX);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
#if LJ_52 || LJ_HASFFI
|
||||
static int ffh_pairs(lua_State *L, MMS mm)
|
||||
{
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
cTValue *mo = lj_meta_lookup(L, o, mm);
|
||||
if ((LJ_52 || tviscdata(o)) && !tvisnil(mo)) {
|
||||
L->top = o+1; /* Only keep one argument. */
|
||||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */
|
||||
return FFH_TAILCALL;
|
||||
} else {
|
||||
if (!tvistab(o)) lj_err_argt(L, 1, LUA_TTABLE);
|
||||
if (LJ_FR2) { copyTV(L, o-1, o); o--; }
|
||||
setfuncV(L, o-1, funcV(lj_lib_upvalue(L, 1)));
|
||||
if (mm == MM_pairs) setnilV(o+1); else setintV(o+1, 0);
|
||||
return FFH_RES(3);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define ffh_pairs(L, mm) (lj_lib_checktab(L, 1), FFH_UNREACHABLE)
|
||||
#endif
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(pairs) LJLIB_REC(xpairs 0)
|
||||
{
|
||||
return ffh_pairs(L, MM_pairs);
|
||||
}
|
||||
|
||||
LJLIB_NOREGUV LJLIB_ASM(ipairs_aux) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkint(L, 2);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(ipairs) LJLIB_REC(xpairs 1)
|
||||
{
|
||||
return ffh_pairs(L, MM_ipairs);
|
||||
}
|
||||
|
||||
/* -- Base library: getters and setters ----------------------------------- */
|
||||
|
||||
LJLIB_ASM_(getmetatable) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
LJLIB_ASM(setmetatable) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCtab *mt = lj_lib_checktabornil(L, 2);
|
||||
if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable)))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
setgcref(t->metatable, obj2gco(mt));
|
||||
if (mt) { lj_gc_objbarriert(L, t, mt); }
|
||||
settabV(L, L->base-1-LJ_FR2, t);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_CF(getfenv) LJLIB_REC(.)
|
||||
{
|
||||
GCfunc *fn;
|
||||
cTValue *o = L->base;
|
||||
if (!(o < L->top && tvisfunc(o))) {
|
||||
int level = lj_lib_optint(L, 1, 1);
|
||||
o = lj_debug_frame(L, level, &level);
|
||||
if (o == NULL)
|
||||
lj_err_arg(L, 1, LJ_ERR_INVLVL);
|
||||
if (LJ_FR2) o--;
|
||||
}
|
||||
fn = &gcval(o)->fn;
|
||||
settabV(L, L->top++, isluafunc(fn) ? tabref(fn->l.env) : tabref(L->env));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(setfenv)
|
||||
{
|
||||
GCfunc *fn;
|
||||
GCtab *t = lj_lib_checktab(L, 2);
|
||||
cTValue *o = L->base;
|
||||
if (!(o < L->top && tvisfunc(o))) {
|
||||
int level = lj_lib_checkint(L, 1);
|
||||
if (level == 0) {
|
||||
/* NOBARRIER: A thread (i.e. L) is never black. */
|
||||
setgcref(L->env, obj2gco(t));
|
||||
return 0;
|
||||
}
|
||||
o = lj_debug_frame(L, level, &level);
|
||||
if (o == NULL)
|
||||
lj_err_arg(L, 1, LJ_ERR_INVLVL);
|
||||
if (LJ_FR2) o--;
|
||||
}
|
||||
fn = &gcval(o)->fn;
|
||||
if (!isluafunc(fn))
|
||||
lj_err_caller(L, LJ_ERR_SETFENV);
|
||||
setgcref(fn->l.env, obj2gco(t));
|
||||
lj_gc_objbarrier(L, obj2gco(fn), t);
|
||||
setfuncV(L, L->top++, fn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(rawget) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkany(L, 2);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_CF(rawset) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkany(L, 2);
|
||||
L->top = 1+lj_lib_checkany(L, 3);
|
||||
lua_rawset(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(rawequal) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o1 = lj_lib_checkany(L, 1);
|
||||
cTValue *o2 = lj_lib_checkany(L, 2);
|
||||
setboolV(L->top-1, lj_obj_equal(o1, o2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_CF(rawlen) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o = L->base;
|
||||
int32_t len;
|
||||
if (L->top > o && tvisstr(o))
|
||||
len = (int32_t)strV(o)->len;
|
||||
else
|
||||
len = (int32_t)lj_tab_len(lj_lib_checktab(L, 1));
|
||||
setintV(L->top-1, len);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(unpack)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = lj_lib_optint(L, 2, 1);
|
||||
int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ?
|
||||
lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t);
|
||||
uint32_t nu;
|
||||
if (i > e) return 0;
|
||||
nu = (uint32_t)e - (uint32_t)i;
|
||||
n = (int32_t)(nu+1);
|
||||
if (nu >= LUAI_MAXCSTACK || !lua_checkstack(L, n))
|
||||
lj_err_caller(L, LJ_ERR_UNPACK);
|
||||
do {
|
||||
cTValue *tv = lj_tab_getint(t, i);
|
||||
if (tv) {
|
||||
copyTV(L, L->top++, tv);
|
||||
} else {
|
||||
setnilV(L->top++);
|
||||
}
|
||||
} while (i++ < e);
|
||||
return n;
|
||||
}
|
||||
|
||||
LJLIB_CF(select) LJLIB_REC(.)
|
||||
{
|
||||
int32_t n = (int32_t)(L->top - L->base);
|
||||
if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') {
|
||||
setintV(L->top-1, n-1);
|
||||
return 1;
|
||||
} else {
|
||||
int32_t i = lj_lib_checkint(L, 1);
|
||||
if (i < 0) i = n + i; else if (i > n) i = n;
|
||||
if (i < 1)
|
||||
lj_err_arg(L, 1, LJ_ERR_IDXRNG);
|
||||
return n - i;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Base library: conversions ------------------------------------------- */
|
||||
|
||||
LJLIB_ASM(tonumber) LJLIB_REC(.)
|
||||
{
|
||||
int32_t base = lj_lib_optint(L, 2, 10);
|
||||
if (base == 10) {
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
if (lj_strscan_numberobj(o)) {
|
||||
copyTV(L, L->base-1-LJ_FR2, o);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
#if LJ_HASFFI
|
||||
if (tviscdata(o)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = lj_ctype_rawref(cts, cdataV(o)->ctypeid);
|
||||
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
|
||||
if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) {
|
||||
if (LJ_DUALNUM && ctype_isinteger_or_bool(ct->info) &&
|
||||
ct->size <= 4 && !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) {
|
||||
int32_t i;
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, 0);
|
||||
setintV(L->base-1-LJ_FR2, i);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_DOUBLE),
|
||||
(uint8_t *)&(L->base-1-LJ_FR2)->n, o, 0);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
const char *p = strdata(lj_lib_checkstr(L, 1));
|
||||
char *ep;
|
||||
unsigned int neg = 0;
|
||||
unsigned long ul;
|
||||
if (base < 2 || base > 36)
|
||||
lj_err_arg(L, 2, LJ_ERR_BASERNG);
|
||||
while (lj_char_isspace((unsigned char)(*p))) p++;
|
||||
if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; }
|
||||
if (lj_char_isalnum((unsigned char)(*p))) {
|
||||
ul = strtoul(p, &ep, base);
|
||||
if (p != ep) {
|
||||
while (lj_char_isspace((unsigned char)(*ep))) ep++;
|
||||
if (*ep == '\0') {
|
||||
if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) {
|
||||
if (neg) ul = ~ul+1u;
|
||||
setintV(L->base-1-LJ_FR2, (int32_t)ul);
|
||||
} else {
|
||||
lua_Number n = (lua_Number)ul;
|
||||
if (neg) n = -n;
|
||||
setnumV(L->base-1-LJ_FR2, n);
|
||||
}
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setnilV(L->base-1-LJ_FR2);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_ASM(tostring) LJLIB_REC(.)
|
||||
{
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
cTValue *mo;
|
||||
L->top = o+1; /* Only keep one argument. */
|
||||
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */
|
||||
return FFH_TAILCALL;
|
||||
}
|
||||
lj_gc_check(L);
|
||||
setstrV(L, L->base-1-LJ_FR2, lj_strfmt_obj(L, L->base));
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
/* -- Base library: throw and catch errors -------------------------------- */
|
||||
|
||||
LJLIB_CF(error)
|
||||
{
|
||||
int32_t level = lj_lib_optint(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_isstring(L, 1) && level > 0) {
|
||||
luaL_where(L, level);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
LJLIB_ASM(pcall) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
lj_lib_checkfunc(L, 2); /* For xpcall only. */
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
LJLIB_ASM_(xpcall) LJLIB_REC(.)
|
||||
|
||||
/* -- Base library: load Lua code ----------------------------------------- */
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
static int load_aux(lua_State *L, int status, int envarg)
|
||||
{
|
||||
if (status == LUA_OK) {
|
||||
/*
|
||||
** Set environment table for top-level function.
|
||||
** Don't do this for non-native bytecode, which returns a prototype.
|
||||
*/
|
||||
if (tvistab(L->base+envarg-1) && tvisfunc(L->top-1)) {
|
||||
GCfunc *fn = funcV(L->top-1);
|
||||
GCtab *t = tabV(L->base+envarg-1);
|
||||
setgcref(fn->c.env, obj2gco(t));
|
||||
lj_gc_objbarrier(L, fn, t);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
setnilV(L->top-2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(loadfile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
GCstr *mode = lj_lib_optstr(L, 2);
|
||||
int status;
|
||||
lua_settop(L, 3); /* Ensure env arg exists. */
|
||||
status = luaL_loadfilex(L, fname ? strdata(fname) : NULL,
|
||||
mode ? strdata(mode) : NULL);
|
||||
return load_aux(L, status, 3);
|
||||
}
|
||||
|
||||
static const char *reader_func(lua_State *L, void *ud, size_t *size)
|
||||
{
|
||||
UNUSED(ud);
|
||||
luaL_checkstack(L, 2, "too many nested functions");
|
||||
copyTV(L, L->top++, L->base);
|
||||
lua_call(L, 0, 1); /* Call user-supplied function. */
|
||||
L->top--;
|
||||
if (tvisnil(L->top)) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
} else if (tvisstr(L->top) || tvisnumber(L->top)) {
|
||||
copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */
|
||||
return lua_tolstring(L, 5, size);
|
||||
} else {
|
||||
lj_err_caller(L, LJ_ERR_RDRSTR);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(load)
|
||||
{
|
||||
GCstr *name = lj_lib_optstr(L, 2);
|
||||
GCstr *mode = lj_lib_optstr(L, 3);
|
||||
int status;
|
||||
if (L->base < L->top &&
|
||||
(tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) {
|
||||
const char *s;
|
||||
MSize len;
|
||||
if (tvisbuf(L->base)) {
|
||||
SBufExt *sbx = bufV(L->base);
|
||||
s = sbx->r;
|
||||
len = sbufxlen(sbx);
|
||||
if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */
|
||||
} else {
|
||||
GCstr *str = lj_lib_checkstr(L, 1);
|
||||
s = strdata(str);
|
||||
len = str->len;
|
||||
}
|
||||
lua_settop(L, 4); /* Ensure env arg exists. */
|
||||
status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s,
|
||||
mode ? strdata(mode) : NULL);
|
||||
} else {
|
||||
lj_lib_checkfunc(L, 1);
|
||||
lua_settop(L, 5); /* Reserve a slot for the string from the reader. */
|
||||
status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)",
|
||||
mode ? strdata(mode) : NULL);
|
||||
}
|
||||
return load_aux(L, status, 4);
|
||||
}
|
||||
|
||||
LJLIB_CF(loadstring)
|
||||
{
|
||||
return lj_cf_load(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(dofile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
setnilV(L->top);
|
||||
L->top = L->base+1;
|
||||
if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != LUA_OK)
|
||||
lua_error(L);
|
||||
lua_call(L, 0, LUA_MULTRET);
|
||||
return (int)(L->top - L->base) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -- Base library: GC control -------------------------------------------- */
|
||||
|
||||
LJLIB_CF(gcinfo)
|
||||
{
|
||||
setintV(L->top++, (int32_t)(G(L)->gc.total >> 10));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(collectgarbage)
|
||||
{
|
||||
int opt = lj_lib_checkopt(L, 1, LUA_GCCOLLECT, /* ORDER LUA_GC* */
|
||||
"\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul\1\377\11isrunning");
|
||||
int32_t data = lj_lib_optint(L, 2, 0);
|
||||
if (opt == LUA_GCCOUNT) {
|
||||
#if LJ_INTONLY
|
||||
setintV(L->top, G(L)->gc.total/1024);
|
||||
#else
|
||||
setnumV(L->top, (lua_Number)G(L)->gc.total/1024.0);
|
||||
#endif
|
||||
} else {
|
||||
int res = lua_gc(L, opt, data);
|
||||
if (opt == LUA_GCSTEP || opt == LUA_GCISRUNNING)
|
||||
setboolV(L->top, res);
|
||||
else
|
||||
setintV(L->top, res);
|
||||
}
|
||||
L->top++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -- Base library: miscellaneous functions ------------------------------- */
|
||||
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds weak table. */
|
||||
LJLIB_CF(newproxy)
|
||||
{
|
||||
lua_settop(L, 1);
|
||||
lua_newuserdata(L, 0);
|
||||
if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */
|
||||
return 1;
|
||||
} else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */
|
||||
} else { /* newproxy(proxy): inherit metatable. */
|
||||
int validproxy = 0;
|
||||
if (lua_getmetatable(L, 1)) {
|
||||
lua_rawget(L, lua_upvalueindex(1));
|
||||
validproxy = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
if (!validproxy)
|
||||
lj_err_arg(L, 1, LJ_ERR_NOPROXY);
|
||||
lua_getmetatable(L, 1);
|
||||
}
|
||||
lua_setmetatable(L, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH("tostring")
|
||||
LJLIB_CF(print)
|
||||
{
|
||||
ptrdiff_t i, nargs = L->top - L->base;
|
||||
cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1)));
|
||||
int shortcut;
|
||||
if (tv && !tvisnil(tv)) {
|
||||
copyTV(L, L->top++, tv);
|
||||
} else {
|
||||
setstrV(L, L->top++, strV(lj_lib_upvalue(L, 1)));
|
||||
lua_gettable(L, LUA_GLOBALSINDEX);
|
||||
tv = L->top-1;
|
||||
}
|
||||
shortcut = (tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring) &&
|
||||
!gcrefu(basemt_it(G(L), LJ_TNUMX));
|
||||
for (i = 0; i < nargs; i++) {
|
||||
cTValue *o = &L->base[i];
|
||||
const char *str;
|
||||
size_t size;
|
||||
MSize len;
|
||||
if (shortcut && (str = lj_strfmt_wstrnum(L, o, &len)) != NULL) {
|
||||
size = len;
|
||||
} else {
|
||||
copyTV(L, L->top+1, o);
|
||||
copyTV(L, L->top, L->top-1);
|
||||
L->top += 2;
|
||||
lua_call(L, 1, 1);
|
||||
str = lua_tolstring(L, -1, &size);
|
||||
if (!str)
|
||||
lj_err_caller(L, LJ_ERR_PRTOSTR);
|
||||
L->top--;
|
||||
}
|
||||
if (i)
|
||||
putchar('\t');
|
||||
fwrite(str, 1, size, stdout);
|
||||
}
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-3)
|
||||
LJLIB_SET(_VERSION)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- Coroutine library --------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_coroutine
|
||||
|
||||
LJLIB_CF(coroutine_status)
|
||||
{
|
||||
const char *s;
|
||||
lua_State *co;
|
||||
if (!(L->top > L->base && tvisthread(L->base)))
|
||||
lj_err_arg(L, 1, LJ_ERR_NOCORO);
|
||||
co = threadV(L->base);
|
||||
if (co == L) s = "running";
|
||||
else if (co->status == LUA_YIELD) s = "suspended";
|
||||
else if (co->status != LUA_OK) s = "dead";
|
||||
else if (co->base > tvref(co->stack)+1+LJ_FR2) s = "normal";
|
||||
else if (co->top == co->base) s = "dead";
|
||||
else s = "suspended";
|
||||
lua_pushstring(L, s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_running)
|
||||
{
|
||||
#if LJ_52
|
||||
int ismain = lua_pushthread(L);
|
||||
setboolV(L->top++, ismain);
|
||||
return 2;
|
||||
#else
|
||||
if (lua_pushthread(L))
|
||||
setnilV(L->top++);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_isyieldable)
|
||||
{
|
||||
setboolV(L->top++, cframe_canyield(L->cframe));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_create)
|
||||
{
|
||||
lua_State *L1;
|
||||
if (!(L->base < L->top && tvisfunc(L->base)))
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
L1 = lua_newthread(L);
|
||||
setfuncV(L, L1->top++, funcV(L->base));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_yield)
|
||||
{
|
||||
lj_err_caller(L, LJ_ERR_CYIELD);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
static int ffh_resume(lua_State *L, lua_State *co, int wrap)
|
||||
{
|
||||
if (co->cframe != NULL || co->status > LUA_YIELD ||
|
||||
(co->status == LUA_OK && co->top == co->base)) {
|
||||
ErrMsg em = co->cframe ? LJ_ERR_CORUN : LJ_ERR_CODEAD;
|
||||
if (wrap) lj_err_caller(L, em);
|
||||
setboolV(L->base-1-LJ_FR2, 0);
|
||||
setstrV(L, L->base-LJ_FR2, lj_err_str(L, em));
|
||||
return FFH_RES(2);
|
||||
}
|
||||
if (lj_state_cpgrowstack(co, (MSize)(L->top - L->base)) != LUA_OK) {
|
||||
cTValue *msg = --co->top;
|
||||
lj_err_callermsg(L, strVdata(msg));
|
||||
}
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_resume)
|
||||
{
|
||||
if (!(L->top > L->base && tvisthread(L->base)))
|
||||
lj_err_arg(L, 1, LJ_ERR_NOCORO);
|
||||
return ffh_resume(L, threadV(L->base), 0);
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux)
|
||||
{
|
||||
return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1);
|
||||
}
|
||||
|
||||
/* Inline declarations. */
|
||||
LJ_ASMF void lj_ff_coroutine_wrap_aux(void);
|
||||
#if !(LJ_TARGET_MIPS && defined(ljamalg_c))
|
||||
LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L,
|
||||
lua_State *co);
|
||||
#endif
|
||||
|
||||
/* Error handler, called from assembler VM. */
|
||||
void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co)
|
||||
{
|
||||
co->top--; copyTV(L, L->top, co->top); L->top++;
|
||||
if (tvisstr(L->top-1))
|
||||
lj_err_callermsg(L, strVdata(L->top-1));
|
||||
else
|
||||
lj_err_run(L);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn);
|
||||
|
||||
LJLIB_CF(coroutine_wrap)
|
||||
{
|
||||
GCfunc *fn;
|
||||
lj_cf_coroutine_create(L);
|
||||
fn = lj_lib_pushcc(L, lj_ffh_coroutine_wrap_aux, FF_coroutine_wrap_aux, 1);
|
||||
setpc_wrap_aux(L, fn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* Fix the PC of wrap_aux. Really ugly workaround. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn)
|
||||
{
|
||||
setmref(fn->c.pc, &L2GG(L)->bcff[lj_lib_init_coroutine[1]+2]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void newproxy_weaktable(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
GCtab *t = lj_tab_new(L, 0, 1);
|
||||
settabV(L, L->top++, t);
|
||||
setgcref(t->metatable, obj2gco(t));
|
||||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
|
||||
lj_str_newlit(L, "kv"));
|
||||
t->nomm = (uint8_t)(~(1u<<MM_mode));
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_base(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: Table and value are the same. */
|
||||
GCtab *env = tabref(L->env);
|
||||
settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);
|
||||
lua_pushliteral(L, LUA_VERSION); /* top-3. */
|
||||
newproxy_weaktable(L); /* top-2. */
|
||||
LJ_LIB_REG(L, "_G", base);
|
||||
LJ_LIB_REG(L, LUA_COLIBNAME, coroutine);
|
||||
return 2;
|
||||
}
|
||||
|
||||
Loaded 100 of 548 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user